added: field accessing on unions results in optional
authorBrendan Hansen <brendan.f.hansen@gmail.com>
Tue, 30 May 2023 23:24:15 +0000 (18:24 -0500)
committerBrendan Hansen <brendan.f.hansen@gmail.com>
Tue, 30 May 2023 23:24:15 +0000 (18:24 -0500)
compiler/include/astnodes.h
compiler/include/types.h
compiler/src/checker.c
compiler/src/types.c
compiler/src/wasm_emit.c
core/container/optional.onyx
tests/tagged_unions
tests/tagged_unions.onyx

index 787e9a3cc70134075c6e2f6d606434ff31eec9ea..e145347128b7745932944d1aea1be75ad30f0c74 100644 (file)
@@ -710,6 +710,8 @@ struct AstFieldAccess   {
     u32 offset;
     u32 idx;
     char* field; // If token is null, defer to field
+
+    b32 is_union_variant_access : 1;
 };
 struct AstFileContents  {
     AstTyped_base;
index ce8c773b547b17501c2780f085bb75a0f47702c1..3df4e5d12c11324b5731568a94c2c07a602e7432 100644 (file)
@@ -244,6 +244,7 @@ Type* type_make_array(bh_allocator alloc, Type* to, u32 count);
 Type* type_make_slice(bh_allocator alloc, Type* of);
 Type* type_make_dynarray(bh_allocator alloc, Type* of);
 Type* type_make_varargs(bh_allocator alloc, Type* of);
+Type* type_make_optional(bh_allocator alloc, Type* of);
 
 void build_linear_types_with_offset(Type* type, bh_arr(TypeWithOffset)* pdest, u32 offset);
 b32  type_struct_member_apply_use(bh_allocator alloc, Type *s_type, StructMember *smem);
@@ -285,5 +286,6 @@ b32 type_constructed_from_poly(Type* base, struct AstType* from);
 Type* type_struct_is_just_one_basic_value(Type *type);
 u32 type_union_get_variant_count(Type *type);
 UnionVariant* type_lookup_union_variant_by_idx(Type* type, i32 idx);
+UnionVariant* type_lookup_union_variant_by_name(Type* type, char *name);
 
 #endif // #ifndef ONYX_TYPES
index 6bad112f2698d3acb1d9e53487a285334c3741e9..2abf59de3b4c6521cededa733cd93f35e8434bae 100644 (file)
@@ -2046,6 +2046,31 @@ CheckStatus check_field_access(AstFieldAccess** pfield) {
             }
         }
 
+        if (type_union_get_variant_count(field->expr->type) > 0) {
+            UnionVariant *uv = type_lookup_union_variant_by_name(field->expr->type, field->field);
+            if (uv) {
+                field->is_union_variant_access = 1;
+                field->idx = uv->tag_value;
+
+                // HACK make a function for this.
+                if (!field->type_node) {
+                    AstPolyCallType* pctype = onyx_ast_node_new(context.ast_alloc, sizeof(AstPolyCallType), Ast_Kind_Poly_Call_Type);
+                    pctype->token = field->token;
+                    pctype->callee = builtin_optional_type;
+                    bh_arr_new(context.ast_alloc, pctype->params, 1);
+                    bh_arr_push(pctype->params, (AstNode *) uv->type->ast_type);
+
+                    field->type_node = (AstType *) pctype;
+                }
+
+                field->type = type_build_from_ast(context.ast_alloc, field->type_node);
+                if (!field->type) YIELD(field->token->pos, "Waiting for field access type to be constructed.");
+
+                field->flags |= Ast_Flag_Has_Been_Checked;
+                return Check_Success;
+            }
+        }
+
         goto try_resolve_from_type;
     }
 
index aed11dde7a7ba040caa809e4f14e1eb033fe1be4..1121b7a3b3e092511cbfbdd69cb30ed0a8789e0b 100644 (file)
@@ -1841,3 +1841,13 @@ UnionVariant* type_lookup_union_variant_by_idx(Type* type, i32 idx) {
     return type->Union.variants_ordered[idx];
 }
 
+UnionVariant* type_lookup_union_variant_by_name(Type* type, char *name) {
+    if (!type) return NULL;
+    if (type->kind == Type_Kind_Pointer) type = type->Pointer.elem;
+    if (type->kind != Type_Kind_Union) return NULL;
+
+    i32 index = shgeti(type->Union.variants, name);
+    if (index == -1) return NULL;
+    return type->Union.variants[index].value;
+}
+
index 4c6b1124c4e05ad3ec1b2314c558e0f9e7d9f391..a1fdec28de36f0f9f1845cdb500a08d64ec1ebc1 100644 (file)
@@ -162,6 +162,14 @@ static u64 local_allocate_type_in_memory(LocalAllocator* la, Type *type) {
     return la->curr_stack - size;
 }
 
+static void local_free_type_in_memory(LocalAllocator* la, Type* type) {
+    u32 size = type_size_of(type);
+    u32 alignment = type_alignment_of(type);
+    bh_align(size, alignment);
+
+    la->curr_stack -= size;
+}
+
 static u64 local_allocate(LocalAllocator* la, AstTyped* local) {
     if (local_is_wasm_local(local)) {
         WasmType wt = onyx_type_to_wasm_type(local->type);
@@ -178,11 +186,7 @@ static void local_free(LocalAllocator* la, AstTyped* local) {
         local_raw_free(la, wt);
 
     } else {
-        u32 size = type_size_of(local->type);
-        u32 alignment = type_alignment_of(local->type);
-        bh_align(size, alignment);
-
-        la->curr_stack -= size;
+        local_free_type_in_memory(la, local->type);
     }
 }
 
@@ -3552,6 +3556,38 @@ EMIT_FUNC(expression, AstTyped* expr) {
                 WIL(NULL, WI_LOCAL_GET, localidx);
             }
 
+            else if (field->is_union_variant_access) {
+                u64 intermediate_local = emit_local_allocation(mod, &code, (AstTyped *) field);
+                assert((intermediate_local & LOCAL_IS_WASM) == 0);
+
+                emit_expression(mod, &code, field->expr);
+                u64 source_base_ptr = local_raw_allocate(mod->local_alloc, WASM_TYPE_PTR);
+                WIL(NULL, WI_LOCAL_TEE, source_base_ptr);
+                emit_load_instruction(mod, &code, &basic_types[Basic_Kind_U32], 0);
+                WIL(NULL, WI_I32_CONST, field->idx);
+                WI(NULL, WI_I32_EQ);
+                emit_enter_structured_block(mod, &code, SBT_Basic_If, field->token);
+                    emit_stack_address(mod, &code, intermediate_local, field->token);
+                    WIL(NULL, WI_I32_CONST, 2); // 2 is Some
+                    emit_store_instruction(mod, &code, &basic_types[Basic_Kind_I32], 0);
+
+                    emit_stack_address(mod, &code, intermediate_local + type_alignment_of(field->type), field->token);
+                    WIL(NULL, WI_LOCAL_GET, source_base_ptr);
+                    WIL(NULL, WI_I32_CONST, type_alignment_of(field->expr->type));
+                    WI(NULL, WI_I32_ADD);
+                    WIL(NULL, WI_I32_CONST, type_size_of(field->type->Union.variants_ordered[1]->type));
+                    emit_wasm_copy(mod, &code, NULL);
+
+                WI(NULL, WI_ELSE);
+                    emit_stack_address(mod, &code, intermediate_local, field->token);
+                    WIL(NULL, WI_I32_CONST, 1); // 1 is None
+                    emit_store_instruction(mod, &code, &basic_types[Basic_Kind_I32], 0);
+                emit_leave_structured_block(mod, &code);
+
+                local_raw_free(mod->local_alloc, WASM_TYPE_PTR);
+                emit_stack_address(mod, &code, intermediate_local, field->token);
+            }
+
             else if (is_lval((AstNode *) field->expr) || type_is_pointer(field->expr->type)) {
                 u64 offset = 0;
                 emit_field_access_location(mod, &code, field, &offset);
index ca40195d95a8b0885852a871e83a10ac14b3289c..6acb33e582fc9278ae2a190b609111b8d24e8349 100644 (file)
@@ -118,21 +118,32 @@ use core
         }
     }
 
-    or_return :: macro (o: ?$T) -> T {
-        switch value := o; value {
-            case .Some => v do return v;
-            case #default {
-                return return .{};
+    or_return :: #match {
+        macro (o: ?$T) -> T {
+            switch value := o; value {
+                case .Some => v do return v;
+                case #default {
+                    return return .{};
+                }
             }
-        }
+        },
+        macro (o: ?$T, return_value: $R) -> T {
+            switch value := o; value {
+                case .Some => v do return v;
+                case #default {
+                    return return return_value;
+                }
+            }
+        },
     }
 
     catch :: macro (o: ?$T, body: Code) -> T {
         switch value := o; value {
             case .Some => v do return v;
+            case .None {
+                #unquote body;
+            }
         }
-
-        #unquote body;
     }
 
     #doc """
index f1d76f21cc90bbf741f3821b0fd0b7aa1a15452c..a3ee253c5ccd0edabd785d448624af58a4f92d60 100644 (file)
@@ -6,3 +6,4 @@ This is an integer: 9876
 123
 456
 Error(Process_Error("bad data"))
+A wrapped value
index 7592db53b7998e9fe5e089abffe882e768073dd7..ececc1c807c8b0c21b1c860f5d21329fa6556a71 100644 (file)
@@ -143,6 +143,19 @@ polymorphic_example :: () {
     println(v);
 }
 
+direct_access_is_an_optional :: () {
+    Methoded :: union {
+        int: i32;
+        float: f32;
+        string: str;
+    }
+
+    v := Methoded.{ string = "A wrapped value" };
+
+    the_string := v.string->unwrap();
+    println(the_string);
+}
+
 
 main :: () {
     simple_example();
@@ -150,4 +163,5 @@ main :: () {
     method_example();
     linked_list_example();
     polymorphic_example();
+    direct_access_is_an_optional();
 }