added: type info for unions and formatting them
authorBrendan Hansen <brendan.f.hansen@gmail.com>
Mon, 22 May 2023 20:26:51 +0000 (15:26 -0500)
committerBrendan Hansen <brendan.f.hansen@gmail.com>
Mon, 22 May 2023 20:26:51 +0000 (15:26 -0500)
compiler/src/types.c
compiler/src/utils.c
compiler/src/wasm_type_table.h
core/conv/format.onyx
core/runtime/info/helper.onyx
core/runtime/info/types.onyx
tests/tagged_unions.onyx

index 6fc88817a5181a247bb6c3f2dd2555f6d15f1465..d9a265ac964cb9534fffd7e184d78728561253f9 100644 (file)
@@ -236,6 +236,10 @@ b32 types_are_compatible_(Type* t1, Type* t2, b32 recurse_pointers) {
             // If the above cases didn't catch it, then these distinct types are not compatible.
             return 0;
 
+        case Type_Kind_Union:
+            // If the above cases didn't catch it, then these union types are not compatible.
+            return 0;
+
         default:
             assert(("Invalid type", 0));
             break;
index 70d3eb6ad08e5b27bc63576b3deba3ac2fd70522..58bf2f2181d1f89f078c2a592e5a52ec0081e94d 100644 (file)
@@ -332,14 +332,33 @@ all_types_peeled_off:
 
         case Ast_Kind_Union_Type: {
             AstUnionType* utype = (AstUnionType *) node;
-            if (utype->utcache != NULL) {
-                //
-                // nocheckin Should this be hard coded?
+
+            AstNode *result = NULL;
+            if (utype->scope) {
+                Scope **tmp_parent;
+                Scope *tmp_parent_backup;
+                if (utype->utcache && utype->utcache->Union.constructed_from) {
+                    // Structs scope -> Poly Solution Scope -> Poly Struct Scope -> Enclosing Scope
+                    tmp_parent = &utype->scope->parent->parent->parent;
+                } else {
+                    tmp_parent = &utype->scope->parent;
+                }
+
+                tmp_parent_backup = *tmp_parent;
+                *tmp_parent = NULL;
+
+                result = symbol_raw_resolve(utype->scope, symbol);
+
+                *tmp_parent = tmp_parent_backup;
+            }
+
+            if (result == NULL && utype->utcache != NULL) {
                 if (!strcmp(symbol, "tag_enum")) {
-                    return (AstNode *) utype->utcache->Union.tag_type->ast_type;
+                    result = (AstNode *) utype->utcache->Union.tag_type->ast_type;
                 }
             }
-            break;
+
+            return result;
         }
 
         case Ast_Kind_Poly_Struct_Type: {
@@ -1364,6 +1383,11 @@ all_types_peeled_off:
             return &pstype->scope;
         }
 
+        case Ast_Kind_Union_Type: {
+            AstUnionType* utype = (AstUnionType *) node;
+            return &utype->scope;
+        }
+
         case Ast_Kind_Poly_Call_Type: {
             AstPolyCallType* pctype = (AstPolyCallType *) node;
             Type *t = type_build_from_ast(context.ast_alloc, (AstType *) pctype);
index 732bb6c672979daaeeabb8b2d408f3e04361efde..53e6e83e83ef0d09f03861fddc88850bdd0adeda 100644 (file)
@@ -8,6 +8,70 @@ typedef struct StructMethodData {
     u32 data_loc;
 } StructMethodData;
 
+static void build_polymorphic_solutions_array(
+        bh_arr(AstPolySolution) slns,
+        bh_buffer *table_buffer,
+        ConstExprContext *constexpr_ctx,
+        u32 *param_locations
+) {
+    u32 i = 0;
+    bh_arr_each(AstPolySolution, sln, slns) {
+        bh_buffer_align(table_buffer, 8);
+        param_locations[i++] = table_buffer->length;
+
+        switch (sln->kind) {
+            case PSK_Type: {
+                // NOTE: This assumes a little endian compiler (which is assumed in other part of the code too)
+                bh_buffer_append(table_buffer, &sln->type->id, 4);
+                break;
+            }
+
+            case PSK_Value: {
+                assert(sln->value->type);
+                u32 size = type_size_of(sln->value->type);
+
+                bh_buffer_grow(table_buffer, table_buffer->length + size);
+                constexpr_ctx->data = table_buffer->data;
+                if (emit_constexpr_(constexpr_ctx, sln->value, table_buffer->length)) {
+                    table_buffer->length += size;
+                    break;
+                }
+
+                // fallthrough
+            }
+
+            default: {
+                // Set to null if this is not known how to encode
+                param_locations[i-1] = 0;
+                break;
+            }
+        }
+    }
+}
+
+static u32 build_constexpr(
+        AstTyped *value,
+        bh_buffer *table_buffer,
+        ConstExprContext *constexpr_ctx
+) {
+    if ((value->flags & Ast_Flag_Comptime) == 0) {
+        return 0;
+    }
+
+    u32 size = type_size_of(value->type);
+    bh_buffer_align(table_buffer, type_alignment_of(value->type));
+
+    bh_buffer_grow(table_buffer, table_buffer->length + size);
+    constexpr_ctx->data = table_buffer->data;
+    if (!emit_constexpr_(constexpr_ctx, value, table_buffer->length)) {
+        return 0;
+
+    } else {
+        table_buffer->length += size;
+        return table_buffer->length - size;
+    }
+}
+
 static u64 build_type_table(OnyxWasmModule* module) {
 
     bh_arr(u32) base_patch_locations=NULL;
@@ -219,39 +283,7 @@ static u64 build_type_table(OnyxWasmModule* module) {
                 bh_buffer_align(&table_buffer, 8);
 
                 // Polymorphic solutions
-                i = 0;
-                bh_arr_each(AstPolySolution, sln, s->poly_sln) {
-                    bh_buffer_align(&table_buffer, 8);
-                    param_locations[i++] = table_buffer.length;
-
-                    switch (sln->kind) {
-                        case PSK_Type: {
-                            // NOTE: This assumes a little endian compiler (which is assumed in other part of the code too)
-                            bh_buffer_append(&table_buffer, &sln->type->id, 4);
-                            break;
-                        }
-
-                        case PSK_Value: {
-                            assert(sln->value->type);
-                            u32 size = type_size_of(sln->value->type);
-
-                            bh_buffer_grow(&table_buffer, table_buffer.length + size);
-                            constexpr_ctx.data = table_buffer.data;
-                            if (emit_constexpr_(&constexpr_ctx, sln->value, table_buffer.length)) {
-                                table_buffer.length += size;
-                                break;
-                            }
-
-                            // fallthrough
-                        }
-
-                        default: {
-                            // Set to null if this is not known how to encode
-                            param_locations[i-1] = 0;
-                            break;
-                        }
-                    }
-                }
+                build_polymorphic_solutions_array(s->poly_sln, &table_buffer, &constexpr_ctx, param_locations);
 
                 bh_buffer_align(&table_buffer, 8);
 
@@ -268,27 +300,7 @@ static u64 build_type_table(OnyxWasmModule* module) {
                     AstTyped* value = *mem->initial_value;
                     assert(value->type);
 
-                    if ((value->flags & Ast_Flag_Comptime) == 0) {
-                        // onyx_report_warning(value->token->pos, "Warning: skipping generating default value for '%s' in '%s' because it is not compile-time known.\n", mem->name, s->name);
-                        i++;
-                        continue;
-                    }
-
-                    u32 size = type_size_of(value->type);
-                    bh_buffer_align(&table_buffer, type_alignment_of(value->type));
-
-                    bh_buffer_grow(&table_buffer, table_buffer.length + size);
-                    constexpr_ctx.data = table_buffer.data;
-                    if (!emit_constexpr_(&constexpr_ctx, value, table_buffer.length)) {
-                        // Failed to generate raw data
-                        // onyx_report_warning(value->token->pos, "Warning: failed to generate default value for '%s' in '%s'.\n", mem->name, s->name);
-                        value_locations[i++] = 0;
-
-                    } else {
-                        // Success 
-                        value_locations[i++] = table_buffer.length;
-                        table_buffer.length += size;
-                    }
+                    value_locations[i++] = build_constexpr(value, &table_buffer, &constexpr_ctx);
                 }
 
                 // Member tags
@@ -313,16 +325,7 @@ static u64 build_type_table(OnyxWasmModule* module) {
                         assert(value->flags & Ast_Flag_Comptime);
                         assert(value->type);
 
-                        u32 size = type_size_of(value->type);
-                        bh_buffer_align(&table_buffer, type_alignment_of(value->type));
-                        meta_tag_locations[j] = table_buffer.length;
-
-                        bh_buffer_grow(&table_buffer, table_buffer.length + size);
-                        constexpr_ctx.data = table_buffer.data;
-                        assert(emit_constexpr_(&constexpr_ctx, value, table_buffer.length));
-                        table_buffer.length += size;
-
-                        j += 1;
+                        meta_tag_locations[j++] = build_constexpr(value, &table_buffer, &constexpr_ctx);
                     }
 
                     bh_buffer_align(&table_buffer, 8);
@@ -377,16 +380,7 @@ static u64 build_type_table(OnyxWasmModule* module) {
                     assert(value->flags & Ast_Flag_Comptime);
                     assert(value->type);
 
-                    u32 size = type_size_of(value->type);
-                    bh_buffer_align(&table_buffer, type_alignment_of(value->type));
-                    struct_tag_locations[i] = table_buffer.length;
-
-                    bh_buffer_grow(&table_buffer, table_buffer.length + size);
-                    constexpr_ctx.data = table_buffer.data;
-                    assert(emit_constexpr_(&constexpr_ctx, value, table_buffer.length));
-                    table_buffer.length += size;
-
-                    i += 1;
+                    struct_tag_locations[i++] = build_constexpr(value, &table_buffer, &constexpr_ctx);
                 }
 
                 // Struct methods
@@ -545,6 +539,198 @@ static u64 build_type_table(OnyxWasmModule* module) {
                 WRITE_SLICE(name_base, name_length);
                 break;
             }
+
+            case Type_Kind_Union: {
+                TypeUnion* u = &type->Union;
+                u32 variant_count = bh_arr_length(u->variants_ordered);
+                u32* name_locations = bh_alloc_array(global_scratch_allocator, u32, variant_count);
+                u32* param_locations = bh_alloc_array(global_scratch_allocator, u32, bh_arr_length(u->poly_sln));
+                u32* meta_locations = bh_alloc_array(global_scratch_allocator, u32, variant_count);
+                u32* struct_tag_locations = bh_alloc_array(global_scratch_allocator, u32, bh_arr_length(u->meta_tags));
+                memset(meta_locations, 0, variant_count * sizeof(u32));
+                memset(struct_tag_locations, 0, bh_arr_length(u->meta_tags) * sizeof(u32));
+
+                // Member names
+                u32 i = 0;
+                bh_arr_each(UnionVariant*, puv, u->variants_ordered) {
+                    UnionVariant* uv = *puv;
+
+                    name_locations[i++] = table_buffer.length;
+                    bh_buffer_append(&table_buffer, uv->name, strlen(uv->name));
+                }
+
+                bh_buffer_align(&table_buffer, 8);
+
+                // Polymorphic solutions
+                build_polymorphic_solutions_array(u->poly_sln, &table_buffer, &constexpr_ctx, param_locations);
+
+                bh_buffer_align(&table_buffer, 8);
+
+                // Variant tags
+                i = 0;
+                bh_arr_each(UnionVariant*, puv, u->variants_ordered) {
+                    UnionVariant* uv = *puv;
+
+                    if (uv->meta_tags == NULL) {
+                        i += 1;
+                        continue;
+                    }
+
+                    bh_arr(AstTyped *) meta_tags = uv->meta_tags;
+                    assert(meta_tags);
+
+                    bh_arr(u64) meta_tag_locations=NULL;
+                    bh_arr_new(global_heap_allocator, meta_tag_locations, bh_arr_length(meta_tags));
+
+                    int j = 0;
+                    bh_arr_each(AstTyped *, meta, meta_tags) {
+                        AstTyped* value = *meta;                        
+                        assert(value->flags & Ast_Flag_Comptime);
+                        assert(value->type);
+
+                        meta_tag_locations[j++] = build_constexpr(value, &table_buffer, &constexpr_ctx);
+                    }
+
+                    bh_buffer_align(&table_buffer, 8);
+                    meta_locations[i] = table_buffer.length;
+
+                    fori (k, 0, bh_arr_length(meta_tags)) {
+                        WRITE_SLICE(meta_tag_locations[k], meta_tags[k]->type->id);
+                    }
+
+                    bh_arr_free(meta_tag_locations);
+                    i += 1;
+                }
+
+                bh_buffer_align(&table_buffer, 8);
+                u32 variants_base = table_buffer.length;
+
+                // Variants array
+                i = 0;
+                bh_arr_each(UnionVariant*, puv, u->variants_ordered) {
+                    UnionVariant* uv = *puv;
+
+                    u32 name_loc = name_locations[i];
+                    u32 meta_loc = meta_locations[i++];
+
+                    WRITE_SLICE(name_loc, strlen(uv->name));
+                    bh_buffer_write_u32(&table_buffer, uv->tag_value);
+                    bh_buffer_write_u32(&table_buffer, uv->type->id);
+
+                    WRITE_SLICE(meta_loc, bh_arr_length(uv->meta_tags));
+                }
+
+                bh_buffer_align(&table_buffer, 8);
+                u32 params_base = table_buffer.length;
+
+                // Polymorphic solution any array
+                i = 0;
+                bh_arr_each(AstPolySolution, sln, u->poly_sln) {
+                    WRITE_PTR(param_locations[i++]);
+
+                    if (sln->kind == PSK_Type) bh_buffer_write_u32(&table_buffer, basic_types[Basic_Kind_Type_Index].id);
+                    else                       bh_buffer_write_u32(&table_buffer, sln->value->type->id);
+                }
+
+                // Union tag array
+                i = 0;
+                bh_arr_each(AstTyped *, tag, u->meta_tags) {
+                    AstTyped* value = *tag;                        
+                    assert(value->flags & Ast_Flag_Comptime);
+                    assert(value->type);
+
+                    struct_tag_locations[i++] = build_constexpr(value, &table_buffer, &constexpr_ctx);
+                }
+
+                // Union methods
+                bh_arr(StructMethodData) method_data=NULL;
+                AstType *ast_type = type->ast_type;
+                if (ast_type && ast_type->kind == Ast_Kind_Union_Type) {
+                    AstUnionType *union_type  = (AstUnionType *) ast_type;
+                    Scope*        union_scope = union_type->scope;
+
+                    if (union_scope == NULL) goto no_union_methods;
+
+                    fori (i, 0, shlen(union_scope->symbols)) {
+                        AstFunction* node = (AstFunction *) strip_aliases(union_scope->symbols[i].value);
+                        if (node->kind != Ast_Kind_Function) continue;
+                        assert(node->entity);
+                        assert(node->entity->function == node);
+
+                        // Name
+                        char *name = union_scope->symbols[i].key;
+                        u32 name_loc = table_buffer.length;
+                        u32 name_len = strlen(name);
+                        bh_buffer_append(&table_buffer, name, name_len);
+
+                        // any data member
+                        bh_buffer_align(&table_buffer, 4);
+                        u32 data_loc = table_buffer.length;
+                        u32 func_idx = get_element_idx(module, node);
+                        bh_buffer_write_u32(&table_buffer, func_idx);
+                        bh_buffer_write_u32(&table_buffer, 0);
+                        bh_buffer_write_u32(&table_buffer, 0);
+                        
+                        bh_arr_push(method_data, ((StructMethodData) {
+                            .name_loc = name_loc,
+                            .name_len = name_len,
+                            .type     = node->type->id,
+                            .data_loc = data_loc,
+                        }));
+                    }
+                }
+
+                no_union_methods:
+
+                bh_buffer_align(&table_buffer, 4);
+                u32 method_data_base = table_buffer.length;
+
+                i = 0;
+                bh_arr_each(StructMethodData, method, method_data) {
+                    WRITE_SLICE(method->name_loc, method->name_len);
+                    WRITE_PTR(method->data_loc); 
+                    bh_buffer_write_u32(&table_buffer, method->type);
+                }
+
+                bh_buffer_align(&table_buffer, 8);
+                
+                u32 union_tag_base = table_buffer.length;
+                fori (i, 0, bh_arr_length(u->meta_tags)) {
+                    WRITE_SLICE(struct_tag_locations[i], u->meta_tags[i]->type->id);
+                }
+
+                // Union name
+                u32 name_base = 0;
+                u32 name_length = 0;
+                if (u->name) {
+                    name_length = strlen(u->name);
+                    name_base = table_buffer.length;
+                    bh_buffer_append(&table_buffer, u->name, name_length);
+                }
+
+                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, type_size_of(type));
+                bh_buffer_write_u32(&table_buffer, type_alignment_of(type));
+
+                if (type->Union.constructed_from != NULL) {
+                    bh_buffer_write_u32(&table_buffer, type->Union.constructed_from->type_id);
+                } else {
+                    bh_buffer_write_u32(&table_buffer, 0);
+                }
+
+                bh_buffer_write_u32(&table_buffer, type->Union.tag_type->id);
+
+                WRITE_SLICE(name_base, name_length);
+                WRITE_SLICE(variants_base, variant_count);
+                WRITE_SLICE(params_base, bh_arr_length(u->poly_sln));
+                WRITE_SLICE(union_tag_base, bh_arr_length(u->meta_tags));
+                WRITE_SLICE(method_data_base, bh_arr_length(method_data));
+
+                bh_arr_free(method_data);
+                break;
+            }
         }
     }
 
index c79a10e4399134447f18cb8e834b6af168ca5685..d87e34f57c84ee3f21a1a2366de331c8f05427cb 100644 (file)
@@ -741,6 +741,26 @@ format_any :: (output: &Format_Output, formatting: &Format, v: any) {
                     output->write("]");
                 }
             }
+
+            if info.kind == .Union {
+                u := cast(&Type_Info_Union) info;
+
+                tag_value := *cast(&u32, v.data);
+
+                variant := array.first(u.variants, #(it.tag_value == tag_value));
+
+                if !variant {
+                    output->write("unknown_variant");
+                    return;
+                }
+
+                output->write(variant.name);
+                output->write("(");
+
+                format_any(output, formatting, any.{ cast([&] u8) v.data + u.alignment, variant.type });
+
+                output->write(")");
+            }
         }
     }
 }
index 50823014cb9fb9cead3808c3b1a69b41dd86e0a9..79f93f2c584e7b5e7ab3265da658d0b1c56b59ee 100644 (file)
@@ -95,6 +95,12 @@ write_type_name :: (writer: &io.Writer, t: type_expr) {
             else                do io.write_str(writer, "<anonymous polymorphic struct>");
         }
 
+        case .Union {
+            u := cast(&Type_Info_Union) info;
+            if u.name.count > 0 do io.write_str(writer, u.name);
+            else                do io.write_str(writer, "<anonymous union>");
+        }
+
         case .Compound {
             comp := cast(&Type_Info_Compound) info;
             io.write_str(writer, "(");
index 2ed01221e22c9f941f2b4c56cad752017ae9dc30..2c6ef18ee4c536f42b696a9a762309754e833ec1 100644 (file)
@@ -22,6 +22,7 @@ Type_Info :: struct {
         Variadic_Argument  :: 0x0B;
         Enum               :: 0x0C;
         Distinct           :: 0x0D;
+        Union              :: 0x0E;
     }
 
     kind := Kind.Invalid;
@@ -188,6 +189,31 @@ Type_Info_Distinct :: struct {
     name: str;
 }
 
+Type_Info_Union :: struct {
+    use base : Type_Info;
+
+    Variant :: struct {
+        name: str;
+        tag_value: u32;
+        type: type_expr;
+
+        tags: [] any;
+    }
+
+    constructed_from: type_expr;
+    tag_enum: type_expr;
+    name: str;
+    variants: [] Variant;
+    parameters: [] any;
+    tags: [] any;
+
+    Method :: struct {
+        name: str;
+        func: any;
+    }
+    methods: [] Method;
+}
+
 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 7170402075b6246628edd0c2617b54eb634a5379..4c38897ec86e70169d0efe37ab79ed6c2f69f6c6 100644 (file)
@@ -21,6 +21,12 @@ SimpleUnion :: union {
     large: i64;
 }
 
+#inject SimpleUnion {
+    foo :: (this: &SimpleUnion) {
+        printf("Working foo! {}\n", this);
+    }
+}
+
 call_test :: (u_: SimpleUnion) {
     u := u_;
     __byte_dump(&u, sizeof typeof u);
@@ -54,6 +60,14 @@ simple_test :: () {
     if cast(SimpleUnion.tag_enum, u) == .b {
         println(extract_variant(u, .b)?);
     }
+
+    u->foo();
+
+    use runtime
+    printf("{*p}\n", cast(&runtime.info.Type_Info_Union) runtime.info.get_type_info(typeof u));
+
+    u = .{ large = 8675309 };
+    println(u);
 }
 
 main :: () {simple_test(); link_test();}