added '__initialize' intrinsic
authorBrendan Hansen <brendan.f.hansen@gmail.com>
Wed, 3 Mar 2021 19:58:13 +0000 (13:58 -0600)
committerBrendan Hansen <brendan.f.hansen@gmail.com>
Wed, 3 Mar 2021 19:58:13 +0000 (13:58 -0600)
bin/onyx
core/builtin.onyx
core/intrinsics/wasm.onyx
docs/bugs
include/onyxastnodes.h
src/onyxastnodes.c
src/onyxbuiltins.c
src/onyxchecker.c
src/onyxwasm.c
src/onyxwasm_intrinsics.c
tests/operator_overload.onyx

index 9cf22eb9f53586da2c1d1fa7a1ccfe4dfca8c01a..b99437eacdb67c9c89010591d12ece7772c69e18 100755 (executable)
Binary files a/bin/onyx and b/bin/onyx differ
index a0733bab005974e850f6658a7d34435c5f09b40e..48635ad47ec08bebe0fbe1b80fe5ef9efd0f16e5 100644 (file)
@@ -102,11 +102,18 @@ calloc  :: (size: u32) -> rawptr do return raw_alloc(context.allocator, size);
 cresize :: (ptr: rawptr, size: u32) -> rawptr do return raw_resize(context.allocator, ptr, size);
 cfree   :: (ptr: rawptr) do raw_free(context.allocator, ptr);
 
-new :: ($T: type_expr, allocator := context.allocator) -> ^T {
-    res := cast(^T) calloc(sizeof T);
-    
-    // At some point, it would be nice to initialize the resulting memory, i.e.
-    // *res = T.{};
-
-    return res;
+use package build_opts as build_opts
+#if build_opts.Runtime != build_opts.Runtime_Custom {
+    use package core.intrinsics.wasm { __initialize }
+
+    new :: ($T: type_expr, allocator := context.allocator, initialize := true) -> ^T {
+        res := cast(^T) calloc(sizeof T);
+
+        // @Robustness: This should be a '#if' when those are added in procedures because
+        // otherwise the __initialize intrinsic is going to be generated no matter what.
+        // This could be a problem if the type is not something that can be initialized.
+        if initialize do __initialize(res);
+        
+        return res;
+    }
 }
index 0d2d115d6e701a76d137db36d919555ac7f39479..fd7174e47a100a675c3c72124434f22e223378a4 100644 (file)
@@ -5,6 +5,8 @@ memory_grow  :: (val: i32) -> i32 #intrinsic ---
 memory_copy  :: (dst: rawptr, src: rawptr, count: i32) -> void #intrinsic ---
 memory_fill  :: (dst: rawptr, byte: u8, count: i32) -> void #intrinsic ---
 
+__initialize :: (val: ^$T) -> void #intrinsic ---
+
 clz_i32      :: (val: i32) -> i32 #intrinsic ---
 ctz_i32      :: (val: i32) -> i32 #intrinsic ---
 popcnt_i32   :: (val: i32) -> i32 #intrinsic ---
index a1ae819c1428cd5e40e77e43721a6b3104448ef5..a0fa4ac2e63a66881a95fc95f1cc1ff367c2500d 100644 (file)
--- a/docs/bugs
+++ b/docs/bugs
@@ -4,6 +4,17 @@ List of known bugs:
     enum { Foo, Bar, Baz };
     enum { Foo; Bar; Baz };
 
+[ ] Using a package in the middle of a procedures removes all remaining statements in the procedure.
+    This is mostly likely due to the changes made with use package statements being treated as entities now.
+    
+    use_package_breaking :: () {
+        println("Test 1");
+
+        use package foo
+
+        println("Test 2");
+    }
+
 [ ] Aliasing in many cases does not work. For example:
     
     SomeNamespace :: struct {
index 5ef1184d4e19f833832081de219751022d79d612..7303159bc75a7165e80e5f06dfed907b1b127e97 100644 (file)
@@ -287,6 +287,8 @@ typedef enum OnyxIntrinsic {
     ONYX_INTRINSIC_MEMORY_SIZE, ONYX_INTRINSIC_MEMORY_GROW,
     ONYX_INTRINSIC_MEMORY_COPY, ONYX_INTRINSIC_MEMORY_FILL,
 
+    ONYX_INTRINSIC_INITIALIZE,
+
     ONYX_INTRINSIC_I32_CLZ,   ONYX_INTRINSIC_I32_CTZ, ONYX_INTRINSIC_I32_POPCNT,
     ONYX_INTRINSIC_I32_AND,   ONYX_INTRINSIC_I32_OR,  ONYX_INTRINSIC_I32_XOR,
     ONYX_INTRINSIC_I32_SHL,   ONYX_INTRINSIC_I32_SLR, ONYX_INTRINSIC_I32_SAR,
index 5bef0fccd947fcca2054205ea5c1c4f864bc9743..c1ae983cdb8c30da2cbb9a0d1d586c07c6e7c5bd 100644 (file)
@@ -490,6 +490,8 @@ b32 type_check_or_auto_cast(AstTyped** pnode, Type* type) {
 }
 
 Type* resolve_expression_type(AstTyped* node) {
+    if (node == NULL) return NULL;
+
     if (node->kind == Ast_Kind_Compound) {
         bh_arr_each(AstTyped *, expr, ((AstCompound *) node)->exprs) {
             resolve_expression_type(*expr);
index a0936dc6763cc1339b2215e585e076323d16b146..0d8a9b4cd356e57100a9c257d9b139f7fa64eaac 100644 (file)
@@ -86,6 +86,8 @@ static IntrinsicMap builtin_intrinsics[] = {
     { "memory_copy",  ONYX_INTRINSIC_MEMORY_COPY },
     { "memory_fill",  ONYX_INTRINSIC_MEMORY_FILL },
 
+    { "__initialize", ONYX_INTRINSIC_INITIALIZE },
+
     { "clz_i32",      ONYX_INTRINSIC_I32_CLZ },
     { "ctz_i32",      ONYX_INTRINSIC_I32_CTZ },
     { "popcnt_i32",   ONYX_INTRINSIC_I32_POPCNT },
@@ -385,4 +387,4 @@ void introduce_build_options(bh_allocator a) {
 
     AstNumLit* runtime_type = make_int_literal(a, context.options->runtime);
     symbol_builtin_introduce(p->scope, "Runtime", (AstNode *) runtime_type);
-}
\ No newline at end of file
+}
index 244c202bc86bf2d89cd6b108b2513a135e1e3565..1f5c7b0678d34020b1d0b1fb4bee1ecbb5804ccb 100644 (file)
@@ -1638,9 +1638,14 @@ CheckStatus check_overloaded_function(AstOverloadedFunction* func) {
 CheckStatus check_struct(AstStructType* s_node) {
     bh_arr_each(AstStructMember *, smem, s_node->members) {
         if ((*smem)->type_node == NULL && (*smem)->initial_value != NULL) {
-            check_expression(&(*smem)->initial_value);
+            CHECK(expression, &(*smem)->initial_value);
             fill_in_type((*smem)->initial_value);
             (*smem)->type = resolve_expression_type((*smem)->initial_value);
+
+            if ((*smem)->type == NULL) {
+                onyx_report_error((*smem)->initial_value->token->pos, "Unable to deduce type of initial value. This is probably a compiler bug.");
+                return Check_Error;
+            }
         }
     }
 
@@ -1658,6 +1663,26 @@ CheckStatus check_struct(AstStructType* s_node) {
     return Check_Success;
 }
 
+CheckStatus check_struct_defaults(AstStructType* s_node) {
+    bh_arr_each(StructMember *, smem, s_node->stcache->Struct.memarr) {
+        if ((*smem)->initial_value && *(*smem)->initial_value) {
+            CHECK(expression, (*smem)->initial_value);
+
+            if (!type_check_or_auto_cast((*smem)->initial_value, (*smem)->type)) {
+                onyx_report_error((*(*smem)->initial_value)->token->pos,
+                        "Mismatched type for initial value, expected '%s', got '%s'.",
+                        type_get_name((*smem)->type),
+                        type_get_name((*(*smem)->initial_value)->type));
+                return Check_Error;
+            }
+
+            resolve_expression_type(*(*smem)->initial_value);
+        }
+    }
+
+    return Check_Success;
+}
+
 CheckStatus check_function_header(AstFunction* func) {
     b32 expect_default_param = 0;
     b32 has_had_varargs = 0;
@@ -1905,6 +1930,10 @@ void check_entity(Entity* ent) {
                 cs = check_type(ent->type_alias);
             break;
 
+        case Entity_Type_Struct_Member_Default:
+            cs = check_struct_defaults((AstStructType *) ent->type_alias);
+            break;
+
         case Entity_Type_Memory_Reservation_Type:
             cs = check_memres_type(ent->mem_res);
             break;
index 505d00bfce2ef293d8d151a7635d174ebe1683e8..2f9dc4027fe023f11685d2642095f91dfbc96455 100644 (file)
@@ -239,6 +239,7 @@ EMIT_FUNC(cast,                          AstUnaryOp* cast);
 EMIT_FUNC(return,                        AstReturn* ret);
 EMIT_FUNC(stack_enter,                   u64 stacksize);
 EMIT_FUNC(stack_leave,                   u32 unused);
+EMIT_FUNC(zero_value,                    WasmType wt);
 
 EMIT_FUNC(enter_structured_block,        StructuredBlockType sbt);
 EMIT_FUNC_NO_ARGS(leave_structured_block);
@@ -1385,6 +1386,12 @@ EMIT_FUNC(intrinsic_call, AstCall* call) {
             }
             break;
 
+        case ONYX_INTRINSIC_INITIALIZE: {
+            Type* type_to_initialize = ((AstArgument *) call->args.values[0])->value->type->Pointer.elem;
+            emit_initialize_type(mod, &code, type_to_initialize, call->token);
+            break;
+        }
+
         case ONYX_INTRINSIC_I32_CLZ:      WI(WI_I32_CLZ); break;
         case ONYX_INTRINSIC_I32_CTZ:      WI(WI_I32_CTZ); break;
         case ONYX_INTRINSIC_I32_POPCNT:   WI(WI_I32_POPCNT); break;
@@ -2424,6 +2431,24 @@ EMIT_FUNC(stack_leave, u32 unused) {
     *pcode = code;
 }
 
+EMIT_FUNC(zero_value, WasmType wt) {
+    bh_arr(WasmInstruction) code = *pcode;
+
+    switch (wt) {
+        case WASM_TYPE_INT32:   WIL(WI_I32_CONST, 0); break;
+        case WASM_TYPE_INT64:   WIL(WI_I64_CONST, 0); break;
+        case WASM_TYPE_FLOAT32: WIL(WI_F32_CONST, 0); break;
+        case WASM_TYPE_FLOAT64: WIL(WI_F64_CONST, 0); break;
+        case WASM_TYPE_VAR128:  {
+            static u8 zero_v128[] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
+            WIP(WI_V128_CONST, &zero_v128);
+            break;
+        }
+    }
+
+    *pcode = code;
+}
+
 static i32 generate_type_idx(OnyxWasmModule* mod, Type* ft) {
     if (ft->kind != Type_Kind_Function) return -1;
 
@@ -2608,20 +2633,8 @@ static void emit_function(OnyxWasmModule* mod, AstFunction* fd) {
         }
     }
 
-    {    
-        WasmFuncType* ft = mod->types[type_idx];
-        switch (ft->return_type) {
-            case WASM_TYPE_INT32:   bh_arr_push(wasm_func.code, ((WasmInstruction){ WI_I32_CONST, 0x00 })); break;
-            case WASM_TYPE_INT64:   bh_arr_push(wasm_func.code, ((WasmInstruction){ WI_I64_CONST, 0x00 })); break;
-            case WASM_TYPE_FLOAT32: bh_arr_push(wasm_func.code, ((WasmInstruction){ WI_F32_CONST, 0x00 })); break;
-            case WASM_TYPE_FLOAT64: bh_arr_push(wasm_func.code, ((WasmInstruction){ WI_F64_CONST, 0x00 })); break;
-            case WASM_TYPE_VAR128:  {
-                static u8 zero_v128[] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
-                bh_arr_push(wasm_func.code, ((WasmInstruction){ WI_V128_CONST, { .p = &zero_v128 } }));
-                break;
-            }
-        }
-    }
+    WasmFuncType* ft = mod->types[type_idx];
+    emit_zero_value(mod, &wasm_func.code, ft->return_type);
 
     bh_arr_push(wasm_func.code, ((WasmInstruction){ WI_BLOCK_END, 0x00 }));
 
index c65a3e8a077841ce66387e4fec6449b6b5aab305..b39fad86a4cdfcbb467300caf66ddfceb8ba4988 100644 (file)
@@ -112,3 +112,42 @@ EMIT_FUNC_NO_ARGS(intrinsic_memory_fill) {
     
     *pcode = code;
 }
+
+EMIT_FUNC(initialize_type, Type* type, OnyxToken* where) {
+    bh_arr(WasmInstruction) code = *pcode;
+
+    switch (type->kind) {
+        case Type_Kind_Pointer:
+        case Type_Kind_Basic: {
+            WasmType basic_type = onyx_type_to_wasm_type(type);
+            emit_zero_value(mod, &code, basic_type);
+            emit_store_instruction(mod, &code, type, 0);
+            break;
+        }
+
+        case Type_Kind_Struct: {
+            u64 value_ptr = local_raw_allocate(mod->local_alloc, WASM_TYPE_PTR);
+            WIL(WI_LOCAL_SET, value_ptr);
+
+            bh_arr_each(StructMember *, psmem, type->Struct.memarr) {
+                StructMember* smem = *psmem;
+                if (smem->initial_value == NULL || *smem->initial_value == NULL) continue;
+
+                WIL(WI_LOCAL_GET, value_ptr);
+                emit_expression(mod, &code, *smem->initial_value);
+                emit_store_instruction(mod, &code, smem->type, smem->offset);
+            }
+            
+            local_raw_free(mod->local_alloc, WASM_TYPE_PTR);
+            break;
+        }
+
+        default:
+            onyx_report_error(where->pos,
+                    "Unable to initialize type, '%s'. The reason for this is largely due to the compiler not knowing what the initial value should be.",
+                    type_get_name(type)); 
+            break;
+    }
+    
+    *pcode = code;
+}
index f3aa5a90c6e17056cfc6e64923198b665ad38522..41e6b3e4aa876f78e92cac02d4f8938a4016c67a 100644 (file)
@@ -103,4 +103,4 @@ main :: proc (args: [] cstr) {
 test_overload :: proc {
     proc (x: $T, y: T)  -> T { return x; },
     proc (x: $T, y: $R) -> R { return y; },
-}
\ No newline at end of file
+}