changed how -> is code generated
authorBrendan Hansen <brendan.f.hansen@gmail.com>
Thu, 29 Sep 2022 03:24:22 +0000 (22:24 -0500)
committerBrendan Hansen <brendan.f.hansen@gmail.com>
Thu, 29 Sep 2022 03:24:22 +0000 (22:24 -0500)
compiler/include/astnodes.h
compiler/src/astnodes.c
compiler/src/checker.c
compiler/src/symres.c
compiler/src/wasm_emit.c
scripts/core_tests.onyx
tests/bugs/method_call_source_double [new file with mode: 0644]
tests/bugs/method_call_source_double.onyx [new file with mode: 0644]

index f52e2f003eaeea4079c4a6ebf8f4ffdd91b8dd6e..5246f1147bd3c005c4921c23a5cb98c69f25e284 100644 (file)
@@ -282,6 +282,8 @@ typedef enum AstFlags {
     Ast_Flag_Param_Symbol_Dirty    = BH_BIT(21),
 
     Ast_Flag_Dead                  = BH_BIT(22),
+
+    Ast_Flag_Extra_Field_Access    = BH_BIT(23)
 } AstFlags;
 
 typedef enum UnaryOp {
@@ -1727,6 +1729,7 @@ AstArgument*     make_argument(bh_allocator a, AstTyped* value);
 AstFieldAccess*  make_field_access(bh_allocator a, AstTyped* node, char* field);
 AstAddressOf*    make_address_of(bh_allocator a, AstTyped* node);
 AstLocal*        make_local(bh_allocator a, OnyxToken* token, AstType* type_node);
+AstLocal*        make_local_with_type(bh_allocator a, OnyxToken* token, Type* type);
 AstNode*         make_symbol(bh_allocator a, OnyxToken* sym);
 AstUnaryOp*      make_cast(bh_allocator a, AstTyped* expr, Type* to);
 AstZeroValue*    make_zero_value(bh_allocator a, OnyxToken *token, Type* type);
index dedddf47c568d42ca1267b5634be47cd1e664ed5..7c9d79242dd584275e26e51072e816fb53e6944b 100644 (file)
@@ -1285,6 +1285,14 @@ AstLocal* make_local(bh_allocator a, OnyxToken* token, AstType* type_node) {
     return local;
 }
 
+AstLocal* make_local_with_type(bh_allocator a, OnyxToken* token, Type* type) {
+    AstLocal* local = onyx_ast_node_new(a, sizeof(AstLocal), Ast_Kind_Local);
+    local->token = token;
+    local->type = type;
+
+    return local;
+}
+
 AstNode* make_symbol(bh_allocator a, OnyxToken* sym) {
     AstNode* symbol = onyx_ast_node_new(a, sizeof(AstNode), Ast_Kind_Symbol);
     symbol->token = sym;
index e184f08babbc8fa2776a65119c6bcf50f1cc2439..ba04efe94bdbcbaeb453177e8cb48e9d1c8a8828 100644 (file)
@@ -1728,6 +1728,7 @@ CheckStatus check_field_access(AstFieldAccess** pfield) {
         new_access->type = containing_member.type;
         new_access->expr = field->expr;
         new_access->flags |= Ast_Flag_Has_Been_Checked;
+        new_access->flags |= Ast_Flag_Extra_Field_Access;
 
         field->expr = (AstTyped *) new_access;
     }
@@ -1810,12 +1811,19 @@ CheckStatus check_method_call(AstBinaryOp** pmcall) {
         bh_arr_insertn(call_node->args.values, 0, 1);
         call_node->args.values[0] = implicit_argument;
 
-        *pmcall = (AstBinaryOp *) mcall->right;
         mcall->right->next = mcall->next;
         mcall->flags |= Ast_Flag_Has_Been_Checked;
     }
 
-    CHECK(call, (AstCall **) pmcall);
+    CHECK(call, (AstCall **) &mcall->right);
+
+    if (mcall->right->kind != Ast_Kind_Call) {
+        *pmcall = (AstBinaryOp *) mcall->right;
+
+    } else {
+        mcall->type = mcall->right->type;
+    }
+
     return Check_Success;
 }
 
index 81481d6bd424a998732d9191a785cb2202e9b3c2..78ba5cc218750b7e2af42b21a0826e72c7a0f809 100644 (file)
@@ -451,9 +451,12 @@ static SymresStatus symres_method_call(AstBinaryOp** mcall) {
         (*mcall)->flags |= Ast_Flag_Has_Been_Symres;
     }
 
-    assert((*mcall)->right->kind == Ast_Kind_Call);
     SYMRES(expression, (AstTyped **) &(*mcall)->right);
 
+    if ((*mcall)->right->kind != Ast_Kind_Call) {
+        *mcall = (AstBinaryOp *) (*mcall)->right;
+    }
+
     return Symres_Success;
 }
 
index 8c7fd5df758feec471045905a494be246ce36f77..c724a1361ba2d4ea66ff1be501de38a30e4a8651 100644 (file)
@@ -709,7 +709,7 @@ EMIT_FUNC(local_allocation, AstTyped* stmt) {
     u64 local_idx = local_allocate(mod->local_alloc, stmt);
     bh_imap_put(&mod->local_map, (u64) stmt, local_idx);
 
-    if (local_is_wasm_local(stmt)) {
+    if (stmt->token && local_is_wasm_local(stmt)) {
         debug_introduce_symbol(mod, stmt->token, DSL_REGISTER, local_idx, stmt->type);
     } else {
         debug_introduce_symbol(mod, stmt->token, DSL_STACK, local_idx, stmt->type);
@@ -2080,6 +2080,78 @@ EMIT_FUNC(call, AstCall* call) {
     *pcode = code;
 }
 
+EMIT_FUNC(method_call, AstBinaryOp *mcall) {
+    AstCall *call_node = (AstCall *) mcall->right;
+
+    //
+    // This this is calling a function directly, just emit the call.
+    if (call_node->callee->kind == Ast_Kind_Function) {
+        emit_call(mod, pcode, call_node);
+        return;
+    }
+
+    //
+    // If this method call is anything more complicated, there should
+    // be a field access for the call's callee node.
+    assert(call_node->callee->kind == Ast_Kind_Field_Access);
+
+    bh_arr(WasmInstruction) code = *pcode;
+
+    AstFieldAccess *fa      = (AstFieldAccess *) call_node->callee;
+    AstFieldAccess **object = (AstFieldAccess **) &fa->expr;
+
+    //
+    // If this field access has another field access from a use
+    // by pointer member, descend into that structure.
+    if (fa->expr->flags & Ast_Flag_Extra_Field_Access) {
+        object = (AstFieldAccess**) &(*object)->expr;
+    }
+
+    //
+    // Create a local variable to store the result of the lookup.
+    AstLocal *tmp_local = make_local_with_type(context.ast_alloc, NULL, (*object)->type);
+    tmp_local->flags |= Ast_Flag_Decl_Followed_By_Init;
+    emit_local_allocation(mod, &code, (AstTyped *) tmp_local);
+
+    //
+    // Lookup information about the local variable.
+    u64 tmp_local_idx = bh_imap_get(&mod->local_map, (u64) tmp_local);
+    b32 tmp_is_wasm_local = (b32) ((tmp_local_idx & LOCAL_IS_WASM) != 0);
+    u64 offset = 0;
+
+    //
+    // Do the common assignment pattern found everywhere else.
+    if (!tmp_is_wasm_local) emit_local_location(mod, &code, tmp_local, &offset);
+
+    emit_expression(mod, &code, *object);
+
+    if (!tmp_is_wasm_local) emit_store_instruction(mod, &code, tmp_local->type, offset);
+    else                    WIL(mcall->token, WI_LOCAL_SET, tmp_local_idx);
+
+    //
+    // Replace the field access with the local variable
+    *object = (AstFieldAccess *) tmp_local;
+
+    //
+    // Replace the first argument of the function with a field access.
+    // If the first argument was an implicit address of, take the address
+    // of the local variable.
+    AstArgument *first_arg = (AstArgument *) call_node->args.values[0];
+    if (first_arg->value->kind == Ast_Kind_Address_Of && ((AstAddressOf *) first_arg->value)->can_be_removed) {
+        first_arg->value = (AstTyped *) make_address_of(context.ast_alloc, (AstTyped *) tmp_local);
+    } else {
+        first_arg->value = (AstTyped *) tmp_local;
+    }
+    
+    //
+    // Actually emit the function call.
+    emit_call(mod, &code, call_node);
+
+    *pcode = code;
+}
+
+
+
 // BUG: This implementation assumes that the host system C's implementation is using
 // little endian integers.
 #define SIMD_INT_CONST_INTRINSIC(type, count) { \
@@ -3162,6 +3234,7 @@ EMIT_FUNC(expression, AstTyped* expr) {
         case Ast_Kind_Binary_Op:      emit_binop(mod, &code, (AstBinaryOp *) expr); break;
         case Ast_Kind_Unary_Op:       emit_unaryop(mod, &code, (AstUnaryOp *) expr); break;
         case Ast_Kind_Alias:          emit_expression(mod, &code, ((AstAlias *) expr)->alias); break;
+        case Ast_Kind_Method_Call:    emit_method_call(mod, &code, (AstBinaryOp *) expr); break;
 
         case Ast_Kind_Address_Of: {
             AstAddressOf* aof = (AstAddressOf *) expr;
index 5a09646985cad0ea69c029d7b1b949e317115aa9..526ee8b1eccd021d5350fb45499d33f285a32c25 100644 (file)
@@ -4,8 +4,9 @@
 
 use core
 
-#inject
-core.Running_Tests :: true 
+#inject core {
+    Running_Tests :: true 
+}
 
 main :: () {
     test.run_tests();
diff --git a/tests/bugs/method_call_source_double b/tests/bugs/method_call_source_double
new file mode 100644 (file)
index 0000000..d4d5592
--- /dev/null
@@ -0,0 +1,5 @@
+Made foo!
+FOOOO!!!
+1234
+FOOOO!!!
+5678
diff --git a/tests/bugs/method_call_source_double.onyx b/tests/bugs/method_call_source_double.onyx
new file mode 100644 (file)
index 0000000..8ec3db6
--- /dev/null
@@ -0,0 +1,36 @@
+#load "core/std"
+
+use core
+
+Foo :: struct {
+    use vtable: ^VTable;
+    x: u32;
+
+    VTable :: struct {
+        f: (^Foo) -> void;
+    }
+}
+
+good_foo := Foo.VTable.{
+    f = (f) => {
+        println("FOOOO!!!");
+        println(f.x);
+    }
+}
+
+make_foo :: () -> Foo {
+    foo : Foo;
+    foo.vtable = ^good_foo;
+    foo.x = 1234;
+
+    printf("Made foo!\n");
+    return foo;
+}
+
+main :: () {
+    foo := make_foo();
+    foo->f();
+
+    Foo.{^good_foo, 5678}->f();
+}
+