From 7ff7b919ce3b6477fb98bd6ea8eb2ed5f23d4508 Mon Sep 17 00:00:00 2001 From: Brendan Hansen Date: Mon, 22 May 2023 15:26:51 -0500 Subject: [PATCH] added: type info for unions and formatting them --- compiler/src/types.c | 4 + compiler/src/utils.c | 34 +++- compiler/src/wasm_type_table.h | 334 +++++++++++++++++++++++++-------- core/conv/format.onyx | 20 ++ core/runtime/info/helper.onyx | 6 + core/runtime/info/types.onyx | 26 +++ tests/tagged_unions.onyx | 14 ++ 7 files changed, 359 insertions(+), 79 deletions(-) diff --git a/compiler/src/types.c b/compiler/src/types.c index 6fc88817..d9a265ac 100644 --- a/compiler/src/types.c +++ b/compiler/src/types.c @@ -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; diff --git a/compiler/src/utils.c b/compiler/src/utils.c index 70d3eb6a..58bf2f21 100644 --- a/compiler/src/utils.c +++ b/compiler/src/utils.c @@ -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); diff --git a/compiler/src/wasm_type_table.h b/compiler/src/wasm_type_table.h index 732bb6c6..53e6e83e 100644 --- a/compiler/src/wasm_type_table.h +++ b/compiler/src/wasm_type_table.h @@ -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; + } } } diff --git a/core/conv/format.onyx b/core/conv/format.onyx index c79a10e4..d87e34f5 100644 --- a/core/conv/format.onyx +++ b/core/conv/format.onyx @@ -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(")"); + } } } } diff --git a/core/runtime/info/helper.onyx b/core/runtime/info/helper.onyx index 50823014..79f93f2c 100644 --- a/core/runtime/info/helper.onyx +++ b/core/runtime/info/helper.onyx @@ -95,6 +95,12 @@ write_type_name :: (writer: &io.Writer, t: type_expr) { else do io.write_str(writer, ""); } + 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, ""); + } + 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 2ed01221..2c6ef18e 100644 --- a/core/runtime/info/types.onyx +++ b/core/runtime/info/types.onyx @@ -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. diff --git a/tests/tagged_unions.onyx b/tests/tagged_unions.onyx index 71704020..4c38897e 100644 --- a/tests/tagged_unions.onyx +++ b/tests/tagged_unions.onyx @@ -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();} -- 2.25.1