From: Brendan Hansen Date: Fri, 10 Sep 2021 15:30:58 +0000 (-0500) Subject: better interop between array types X-Git-Url: https://git.brendanfh.com/?a=commitdiff_plain;h=33bfac2117d3a1d86cd73d7cc5bbc697d6925beb;p=onyx.git better interop between array types --- diff --git a/bin/onyx b/bin/onyx index bab7229b..0857ba00 100755 Binary files a/bin/onyx and b/bin/onyx differ diff --git a/core/conv.onyx b/core/conv.onyx index 00fbe9ae..d648f2a8 100644 --- a/core/conv.onyx +++ b/core/conv.onyx @@ -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 { diff --git a/core/io/writer.onyx b/core/io/writer.onyx index 6f956358..6d9f32a9 100644 --- a/core/io/writer.onyx +++ b/core/io/writer.onyx @@ -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) { diff --git a/core/stdio.onyx b/core/stdio.onyx index 9891061b..424b8ff2 100644 --- a/core/stdio.onyx +++ b/core/stdio.onyx @@ -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 diff --git a/include/astnodes.h b/include/astnodes.h index a3d0c4b9..227439d8 100644 --- a/include/astnodes.h +++ b/include/astnodes.h @@ -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; diff --git a/modules/wasm_utils/parser.onyx b/modules/wasm_utils/parser.onyx index 1e6fb5a7..3475f97c 100644 --- a/modules/wasm_utils/parser.onyx +++ b/modules/wasm_utils/parser.onyx @@ -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)); } } diff --git a/src/astnodes.c b/src/astnodes.c index 109ddf3b..5479bb04 100644 --- a/src/astnodes.c +++ b/src/astnodes.c @@ -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); diff --git a/src/checker.c b/src/checker.c index b7674fcd..b948d7fa 100644 --- a/src/checker.c +++ b/src/checker.c @@ -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; } } diff --git a/src/wasm.c b/src/wasm.c index 43125c52..f811c1c6 100644 --- a/src/wasm.c +++ b/src/wasm.c @@ -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;