better interop between array types
authorBrendan Hansen <brendan.f.hansen@gmail.com>
Fri, 10 Sep 2021 15:30:58 +0000 (10:30 -0500)
committerBrendan Hansen <brendan.f.hansen@gmail.com>
Fri, 10 Sep 2021 15:30:58 +0000 (10:30 -0500)
bin/onyx
core/conv.onyx
core/io/writer.onyx
core/stdio.onyx
include/astnodes.h
modules/wasm_utils/parser.onyx
src/astnodes.c
src/checker.c
src/wasm.c

index bab7229bc9f7453430107ca2447e286db48a3b9a..0857ba0003720aa0040b276d1fba7f8f70e7e13f 100755 (executable)
Binary files a/bin/onyx and b/bin/onyx differ
index 00fbe9aefb6b3d07943c8398d0929fbd12ae579d..d648f2a883e898c9ad61d19e2c3d53346a648ed7 100644 (file)
@@ -221,11 +221,11 @@ f64_to_str :: (f: f64, buf: [] u8, digits_after_decimal := 4) -> str {
     return str.{ buf.data, len };
 }
 
-str_format :: (format: str, buffer: [] u8, va: ..any) -> str {
-    return str_format_va(format, buffer, ~~va); 
+str_format :: (buffer: [] u8, format: str, va: ..any) -> str {
+    return str_format_va(buffer, format, ~~va); 
 }
 
-str_format_va :: (format: str, buffer: [] u8, va: [] any) -> str {
+str_format_va :: (buffer: [] u8, format: str, va: [] any) -> str {
     Output :: struct {
         data: ^u8;
         count: u32;
@@ -255,6 +255,7 @@ str_format_va :: (format: str, buffer: [] u8, va: [] any) -> str {
     Format :: struct {
         pretty_printing      := false;
         quote_strings        := false;
+        dereference          := false;
         digits_after_decimal := cast(u32) 4;
 
         indentation   := cast(u32) 0;
@@ -282,6 +283,11 @@ str_format_va :: (format: str, buffer: [] u8, va: [] any) -> str {
                 ch = format[i];
 
                 switch ch {
+                    case #char "*" {
+                        i += 1;
+                        formatting.dereference = true;
+                    }
+
                     case #char "." {
                         i += 1;
 
@@ -362,7 +368,20 @@ str_format_va :: (format: str, buffer: [] u8, va: [] any) -> str {
 
     print_any :: (output: ^Output, formatting: Format, v: any) {
         use package builtin.type_info
-        array :: package core.array
+        array :: package core.array;
+
+        if formatting.dereference {
+            ti := get_type_info(v.type);
+            if ti.kind == .Pointer {
+                formatting.dereference = false;
+
+                new_any: any;
+                new_any.type = (cast(^Type_Info_Pointer) ti).to;
+                new_any.data = *(cast(^rawptr) v.data);
+                print_any(output, formatting, new_any);
+                return;
+            }
+        }
 
         switch v.type {
             case bool {
index 6f9563580cd8dee4a0315f07e8b3c20c31f71a2f..6d9f32a9f0cb0328d623c44569d6788bcc124916 100644 (file)
@@ -78,7 +78,7 @@ write_range :: (use writer: ^Writer, r: range, sep := " ") {
 write_format :: (use writer: ^Writer, format: str, va: ..any) {
     // POTENTIAL BUG: this buffer will need to be bigger (or dynamic).
     buffer: [2048] u8;
-    write_str(writer, conv.str_format_va(format, ~~buffer, ~~va));
+    write_str(writer, conv.str_format_va(buffer, format, va));
 }
 
 write_escaped_str :: (use writer: ^Writer, s: str) {
index 9891061baf58a79899c29ad0600d889bdc77e100..424b8ff2b5f8e09e731d3f40bd72b0c263051288 100644 (file)
@@ -31,7 +31,7 @@ println :: (x) => {
 
 printf :: (format: str, va: ..any) {
     buffer: [8196] u8;
-    print(conv.str_format_va(format, ~~buffer, ~~va));
+    print(conv.str_format_va(buffer, format, va));
 }
 
 // This works on both slices and arrays
index a3d0c4b979af53cee9d484014d0277d37b561b97..227439d89bd2e71dd2e992854fdeee05e6536539 100644 (file)
@@ -1360,6 +1360,7 @@ 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);
 AstNode*         make_symbol(bh_allocator a, OnyxToken* sym);
+AstUnaryOp*      make_cast(bh_allocator a, AstTyped* expr, Type* to);
 
 void arguments_initialize(Arguments* args);
 b32 fill_in_arguments(Arguments* args, AstNode* provider, char** err_msg);
@@ -1434,6 +1435,13 @@ static inline b32 node_is_auto_cast(AstNode* node) {
     return (node->kind == Ast_Kind_Unary_Op) && (((AstUnaryOp *) node)->operation == Unary_Op_Auto_Cast);
 }
 
+static inline Type* get_expression_type(AstTyped* expr) {
+    switch (expr->kind) {
+        case Ast_Kind_Block: case Ast_Kind_If: case Ast_Kind_While: return NULL;
+        default: return expr->type;
+    }
+}
+
 static inline CallingConvention type_function_get_cc(Type* type) {
     if (type == NULL) return CC_Undefined;
     if (type->kind != Type_Kind_Function) return CC_Undefined;
index 1e6fb5a799dd1a1d0e337e773ff8f52953e4c9f2..3475f97cb0ab4ea3a40350d1e2142ec36703b04e 100644 (file)
@@ -342,7 +342,7 @@ read_val_type :: (reader: ^io.Reader, binary: ^WasmBinary) -> WasmValueType {
 
             buf : [256] u8;
             _, loc := io.stream_tell(reader.stream);
-            s := conv.str_format("Bad wasm value type {} at {x}", ~~buf, cast(i32) byte, loc);
+            s := conv.str_format(buf, "Bad wasm value type {} at {x}", cast(i32) byte, loc);
             assert(false, s);
         }
     }
@@ -393,7 +393,7 @@ parse_section_locations :: (use bin: ^WasmBinary) -> bool {
             case #default {
                 buffer: [128] u8;
                 conv :: package core.conv
-                assert(false, conv.str_format("Bad section number {}", ~~buffer, cast(i32) section_number));
+                assert(false, conv.str_format(buffer, "Bad section number {}", cast(i32) section_number));
             }
         }
 
index 109ddf3bdc7d67a0f52f4b62dec2db8ab661be07..5479bb0499ec50a3a8e40d1e5f76b8922bb28afe 100644 (file)
@@ -543,7 +543,7 @@ b32 unify_node_and_type_(AstTyped** pnode, Type* type, b32 permanent) {
         AstNode* resolved = try_symbol_resolve_from_node((AstNode *) ast_type, node->token);
         if (resolved == NULL) return 0;
 
-        *pnode = (AstTyped *) resolved;
+        if (permanent) *pnode = (AstTyped *) resolved;
         return 1;
     }
 
@@ -568,31 +568,66 @@ b32 unify_node_and_type_(AstTyped** pnode, Type* type, b32 permanent) {
     }
 
     // HACK: NullProcHack
+    // The null_proc matches any procedure, and because of that, will cause a runtime error if you
+    // try to call it.
     if (type->kind == Type_Kind_Function && (node->flags & Ast_Flag_Proc_Is_Null) != 0) return 1;
 
-    if (types_are_compatible(node->type, type)) return 1;
+    // The normal case where everything works perfectly.
+    Type* node_type = get_expression_type(node);
+    if (types_are_compatible(node_type, type)) return 1;
+
+    // Here are some of the ways you can unify a node with a type if the type of the
+    // node does not match the given type:
+    // 
+    // If the nodes type is a function type and that function has an automatic return
+    // value placeholder, fill in that placeholder with the actual type.
     // :AutoReturnType
-    if (node->type && node->type->kind == Type_Kind_Function
-        && node->type->Function.return_type == &type_auto_return
+    if (node_type && node_type->kind == Type_Kind_Function
+        && node_type->Function.return_type == &type_auto_return
         && type->kind == Type_Kind_Function) {
 
-        node->type->Function.return_type = type->Function.return_type;
+        node_type->Function.return_type = type->Function.return_type;
         return 1;
     }
+
+    // If the node is an auto cast (~~) node, then check to see if the cast is legal
+    // to the destination type, and if it is change the type to cast to.
     if (node_is_auto_cast((AstNode *) node)) {
         char* dummy;
-        Type* from_type = ((AstUnaryOp *) node)->expr->type;
+        Type* from_type = get_expression_type(((AstUnaryOp *) node)->expr);
         if (!from_type || !cast_is_legal(from_type, type, &dummy)) {
             return 0;
 
         } else {
-            ((AstUnaryOp *) node)->type = type;
+            if (permanent) ((AstUnaryOp *) node)->type = type;
             return 1;
         }
     }
+
+    // If the destination type is a slice, then automatically convert arrays, dynamic
+    // arrays, and var args, if they are the same type. This is big convenience feature
+    // that makes working with arrays much easier.
+    // [N] T  -> [] T
+    // [..] T -> [] T
+    // ..T    -> [] T
+    else if (node_type && type->kind == Type_Kind_Slice) {
+        if (node_type->kind == Type_Kind_Array || node_type->kind == Type_Kind_DynArray || node_type->kind == Type_Kind_VarArgs) {
+            char* dummy;
+            if (cast_is_legal(node_type, type, &dummy)) {
+                *pnode = (AstTyped *) make_cast(context.ast_alloc, node, type);
+                return 1;
+            }
+        }
+    }
+
+    // If the node is a numeric literal, try to convert it to the destination type.
     else if (node->kind == Ast_Kind_NumLit) {
         if (convert_numlit_to_type((AstNumLit *) node, type)) return 1;
     }
+
+    // If the node is a compound expression, and it doesn't have a type created,
+    // recursive call this function with the individual components of the compound
+    // expression.
     else if (node->kind == Ast_Kind_Compound) {
         if (type->kind != Type_Kind_Compound) return 0;
 
@@ -609,6 +644,7 @@ b32 unify_node_and_type_(AstTyped** pnode, Type* type, b32 permanent) {
         
         return 1;
     }
+
     else if (node->kind == Ast_Kind_If_Expression) {
         AstIfExpression* if_expr = (AstIfExpression *) node;
 
@@ -616,13 +652,14 @@ b32 unify_node_and_type_(AstTyped** pnode, Type* type, b32 permanent) {
         b32 false_success = unify_node_and_type_(&if_expr->false_expr, type, permanent);
 
         if (true_success && false_success) {
-            if_expr->type = type;
+            if (permanent) if_expr->type = type;
             return 1;
 
         } else {
             return 0;
         }
     }
+
     else if (node->kind == Ast_Kind_Alias) {
         AstAlias* alias = (AstAlias *) node;
         return unify_node_and_type_(&alias->alias, type, permanent);
@@ -751,6 +788,8 @@ b32 cast_is_legal(Type* from_, Type* to_, char** err_msg) {
         return 0;
     }
 
+    // CLEANUP: These error messages should be a lot better and actually
+    // provide the types of the things in question.
     if (to->kind == Type_Kind_Slice && from->kind == Type_Kind_Array) {
         if (!types_are_compatible(to->Slice.elem, from->Array.elem)) {
             *err_msg = "Array to slice cast is not valid here because the types are different.";
@@ -760,6 +799,15 @@ b32 cast_is_legal(Type* from_, Type* to_, char** err_msg) {
         }
     }
 
+    if (to->kind == Type_Kind_Slice && from->kind == Type_Kind_DynArray) {
+        if (!types_are_compatible(to->Slice.elem, from->DynArray.elem)) {
+            *err_msg = "Dynmaic array to slice cast is not valid here because the types are different.";
+            return 0;
+        } else {
+            return 1;
+        }
+    }
+
     if (to->kind == Type_Kind_Slice && from->kind == Type_Kind_VarArgs) {
         if (!types_are_compatible(to->Slice.elem, from->VarArgs.elem)) {
             *err_msg = "Variadic argument to slice cast is not valid here because the types are different.";
@@ -960,6 +1008,15 @@ AstNode* make_symbol(bh_allocator a, OnyxToken* sym) {
     return symbol;
 }
 
+AstUnaryOp* make_cast(bh_allocator a, AstTyped* expr, Type* to) {
+    AstUnaryOp* cast = onyx_ast_node_new(a, sizeof(AstUnaryOp), Ast_Kind_Unary_Op);
+    cast->token = expr->token;
+    cast->operation = Unary_Op_Cast;
+    cast->expr = expr;
+    cast->type = to;
+    return cast;
+}
+
 void arguments_initialize(Arguments* args) {
     if (args->values == NULL)       bh_arr_new(global_heap_allocator, args->values, 2);
     if (args->named_values == NULL) bh_arr_new(global_heap_allocator, args->named_values, 2);
index b7674fcdd02080d2c368d32a140db31e0b08cf74..b948d7fa185b4adcfa5b6299ecf05b3eff8ba239 100644 (file)
@@ -696,7 +696,8 @@ CheckStatus check_binaryop_assignment(AstBinaryOp** pbinop) {
         if (binop->left->type == NULL) {
             resolve_expression_type(binop->right);
 
-            if (binop->right->type == NULL) {
+            Type* right_type = get_expression_type(binop->right);
+            if (right_type == NULL) {
                 if (binop->right->entity == NULL || binop->right->entity->state > Entity_State_Check_Types) {
                     ERROR(binop->token->pos, "Could not resolve type of right hand side to infer.");
 
@@ -705,7 +706,7 @@ CheckStatus check_binaryop_assignment(AstBinaryOp** pbinop) {
                 }
             }
 
-            if (binop->right->type->kind == Type_Kind_Compound) {
+            if (right_type->kind == Type_Kind_Compound) {
                 AstCompound* lhs = (AstCompound *) binop->left;
                 if (lhs->kind != Ast_Kind_Compound) {
                     ERROR_(binop->token->pos,
@@ -713,7 +714,7 @@ CheckStatus check_binaryop_assignment(AstBinaryOp** pbinop) {
                             binop->right->type->Compound.count);
                 }
 
-                i32 expr_count = binop->right->type->Compound.count;
+                i32 expr_count = right_type->Compound.count;
                 if (bh_arr_length(lhs->exprs) != expr_count) {
                     ERROR_(binop->token->pos,
                             "Expected left hand side to have %d expressions.",
@@ -721,13 +722,13 @@ CheckStatus check_binaryop_assignment(AstBinaryOp** pbinop) {
                 }
 
                 fori (i, 0, expr_count) {
-                    lhs->exprs[i]->type = binop->right->type->Compound.types[i];
+                    lhs->exprs[i]->type = right_type->Compound.types[i];
                 }
 
                 lhs->type = type_build_compound_type(context.ast_alloc, lhs);
 
             } else {
-                binop->left->type = binop->right->type;
+                binop->left->type = right_type;
             }
         }
 
index 43125c52531ccb9a65edf02f48ba7cce8f520623..f811c1c60ec20b610350cd0bcabc533dc048b57e 100644 (file)
@@ -2754,6 +2754,14 @@ EMIT_FUNC(cast, AstUnaryOp* cast) {
         return;
     }
 
+    if (to->kind == Type_Kind_Slice && from->kind == Type_Kind_DynArray) {
+        WI(WI_DROP);
+        WI(WI_DROP);
+        WI(WI_DROP);
+        *pcode = code;
+        return;
+    }
+
     if (to->kind == Type_Kind_Slice && from->kind == Type_Kind_VarArgs) {
         // Nothing needs to be done because they are identical
         *pcode = code;