From: Brendan Hansen Date: Wed, 22 Jul 2020 19:07:37 +0000 (-0500) Subject: Added struct member parameter splatted; effectively structs can pass by value X-Git-Url: https://git.brendanfh.com/?a=commitdiff_plain;h=b21925cd89aee21a26b488fe3b6e38efa1ceea1c;p=onyx.git Added struct member parameter splatted; effectively structs can pass by value --- diff --git a/include/onyxastnodes.h b/include/onyxastnodes.h index e13af87d..8d6d42fe 100644 --- a/include/onyxastnodes.h +++ b/include/onyxastnodes.h @@ -120,6 +120,7 @@ typedef enum AstFlags { // Expression flags Ast_Flag_Expr_Ignored = BH_BIT(8), + Ast_Flag_Param_Splatted = BH_BIT(9), // Type flags Ast_Flag_Type_Is_Resolved = BH_BIT(8), diff --git a/include/onyxtypes.h b/include/onyxtypes.h index d90faefb..f0cf4714 100644 --- a/include/onyxtypes.h +++ b/include/onyxtypes.h @@ -47,7 +47,7 @@ typedef struct TypeBasic { typedef struct Type Type; typedef struct StructMember { - u64 offset; + u32 offset, idx; Type *type; } StructMember; @@ -101,16 +101,21 @@ struct Type { extern Type basic_types[]; struct AstType; +struct AstFunction; + b32 types_are_compatible(Type* t1, Type* t2); u32 type_size_of(Type* type); Type* type_build_from_ast(bh_allocator alloc, struct AstType* type_node); +Type* type_build_function_type(bh_allocator alloc, struct AstFunction* func, struct AstType* return_type); + Type* type_make_pointer(bh_allocator alloc, Type* to); const char* type_get_name(Type* type); u32 type_get_alignment_log2(Type* type); b32 type_struct_lookup_member(Type* type, char* member, StructMember* smem); +b32 type_struct_is_simple(Type* type); b32 type_is_pointer(Type* type); b32 type_is_array(Type* tyoe); diff --git a/onyx b/onyx index 8e1d6afe..2b68828b 100755 Binary files a/onyx and b/onyx differ diff --git a/progs/structs.onyx b/progs/structs.onyx index f07ef921..bfe93372 100644 --- a/progs/structs.onyx +++ b/progs/structs.onyx @@ -133,7 +133,7 @@ proc #export "main" { v3.z = 1; print(v3.magnitude()); - vec2_splat(*v2, *(v3 as ^Vec2)); + vec2_splat(*v2, *v3); print((1).minus(2)); } @@ -149,12 +149,13 @@ vec2_set :: proc (v: ^Vec2) { v.y = 5678; } -vec2_splat :: proc (vx: i32, vy: i32, other: i32, otherother: i32) { +vec2_splat :: proc (vx: i32, vy: i32, other: Vec3) { + print(other.x); + print(other.y); + print(other.z); + print(vx); print(vy); - - print(other); - print(otherother); } soa_test :: proc #export "main9" { diff --git a/src/onyxchecker.c b/src/onyxchecker.c index 34a75f2f..44dbd8a8 100644 --- a/src/onyxchecker.c +++ b/src/onyxchecker.c @@ -14,11 +14,11 @@ CHECK(while, AstWhile* whilenode); CHECK(for, AstFor* fornode); CHECK(call, AstCall* call); CHECK(binaryop, AstBinaryOp* binop, b32 assignment_is_ok); -CHECK(expression, AstTyped* expr); +CHECK(expression, AstTyped** expr); CHECK(address_of, AstAddressOf* aof); CHECK(dereference, AstDereference* deref); CHECK(array_access, AstArrayAccess* expr); -CHECK(field_access, AstFieldAccess* field); +CHECK(field_access, AstFieldAccess** pfield); CHECK(size_of, AstSizeOf* so); CHECK(global, AstGlobal* global); CHECK(function, AstFunction* func); @@ -33,7 +33,7 @@ static inline void fill_in_type(AstTyped* node) { CHECK(return, AstReturn* retnode) { if (retnode->expr) { - if (check_expression(retnode->expr)) return 1; + if (check_expression(&retnode->expr)) return 1; if (!types_are_compatible(retnode->expr->type, semstate.expected_return_type)) { onyx_message_add(Msg_Type_Function_Return_Mismatch, @@ -55,7 +55,7 @@ CHECK(return, AstReturn* retnode) { } CHECK(if, AstIf* ifnode) { - if (check_expression(ifnode->cond)) return 1; + if (check_expression(&ifnode->cond)) return 1; if (!type_is_bool(ifnode->cond->type)) { onyx_message_add(Msg_Type_Literal, @@ -71,7 +71,7 @@ CHECK(if, AstIf* ifnode) { } CHECK(while, AstWhile* whilenode) { - if (check_expression(whilenode->cond)) return 1; + if (check_expression(&whilenode->cond)) return 1; if (!type_is_bool(whilenode->cond->type)) { onyx_message_add(Msg_Type_Literal, @@ -84,12 +84,12 @@ CHECK(while, AstWhile* whilenode) { } CHECK(for, AstFor* fornode) { - if (check_expression(fornode->start)) return 1; - if (check_expression(fornode->end)) return 1; + if (check_expression(&fornode->start)) return 1; + if (check_expression(&fornode->end)) return 1; if (fornode->step) - if (check_expression(fornode->step)) return 1; + if (check_expression(&fornode->step)) return 1; - if (check_expression((AstTyped *) fornode->var)) return 1; + if (check_expression((AstTyped **) &fornode->var)) return 1; // HACK if (!types_are_compatible(fornode->start->type, &basic_types[Basic_Kind_I32])) { @@ -168,10 +168,17 @@ CHECK(call, AstCall* call) { AstNode** prev_param = (AstNode **) &call->arguments; AstArgument* actual_param = call->arguments; while (actual_param != NULL) { - if (check_expression((AstTyped *) actual_param)) return 1; + if (check_expression((AstTyped **) &actual_param)) return 1; // NOTE: Splat structures into multiple arguments if (actual_param->type->kind == Type_Kind_Struct) { + if (!type_struct_is_simple(actual_param->type)) { + onyx_message_add(Msg_Type_Literal, + actual_param->token->pos, + "can only splat simple structure. nested structures or struct of arrays is not allowed."); + return 1; + } + bh_arr_each(StructMember *, smem, actual_param->type->Struct.memarr) { AstFieldAccess* field = onyx_ast_node_new(semstate.node_allocator, sizeof(AstFieldAccess), Ast_Kind_Field_Access); field->expr = actual_param->value; @@ -323,8 +330,8 @@ CHECK(call, AstCall* call) { } CHECK(binaryop, AstBinaryOp* binop, b32 assignment_is_ok) { - if (check_expression(binop->left)) return 1; - if (check_expression(binop->right)) return 1; + if (check_expression(&binop->left)) return 1; + if (check_expression(&binop->right)) return 1; if (binop_is_assignment(binop)) { if (!assignment_is_ok) { @@ -423,7 +430,7 @@ CHECK(binaryop, AstBinaryOp* binop, b32 assignment_is_ok) { } CHECK(address_of, AstAddressOf* aof) { - if (check_expression(aof->expr)) return 1; + if (check_expression(&aof->expr)) return 1; if (aof->expr->kind != Ast_Kind_Array_Access && aof->expr->kind != Ast_Kind_Dereference @@ -440,7 +447,7 @@ CHECK(address_of, AstAddressOf* aof) { } CHECK(dereference, AstDereference* deref) { - if (check_expression(deref->expr)) return 1; + if (check_expression(&deref->expr)) return 1; if (!type_is_pointer(deref->expr->type)) { onyx_message_add(Msg_Type_Literal, @@ -462,8 +469,8 @@ CHECK(dereference, AstDereference* deref) { } CHECK(array_access, AstArrayAccess* aa) { - if (check_expression(aa->addr)) return 1; - if (check_expression(aa->expr)) return 1; + if (check_expression(&aa->addr)) return 1; + if (check_expression(&aa->expr)) return 1; if (!type_is_pointer(aa->addr->type)) { onyx_message_add(Msg_Type_Literal, @@ -496,8 +503,9 @@ CHECK(array_access, AstArrayAccess* aa) { return 0; } -CHECK(field_access, AstFieldAccess* field) { - if (check_expression(field->expr)) return 1; +CHECK(field_access, AstFieldAccess** pfield) { + AstFieldAccess* field = *pfield; + if (check_expression(&field->expr)) return 1; if (!type_is_struct(field->expr->type)) { onyx_message_add(Msg_Type_Literal, @@ -517,6 +525,18 @@ CHECK(field_access, AstFieldAccess* field) { return 1; } + if (field->expr->flags & Ast_Flag_Param_Splatted) { + AstLocal* param = (AstLocal *) field->expr; + + u32 steps = smem.idx + 1; + while (steps--) param = (AstLocal *) param->next; + + // NOTE: Not actually a field access + *pfield = (AstFieldAccess *) param; + token_toggle_end(field->token); + return 0; + } + field->offset = smem.offset; field->type = smem.type; @@ -530,7 +550,8 @@ CHECK(size_of, AstSizeOf* so) { return 0; } -CHECK(expression, AstTyped* expr) { +CHECK(expression, AstTyped** pexpr) { + AstTyped* expr = *pexpr; if (expr->kind > Ast_Kind_Type_Start && expr->kind < Ast_Kind_Type_End) { onyx_message_add(Msg_Type_Literal, (OnyxFilePos) { 0 }, @@ -545,7 +566,7 @@ CHECK(expression, AstTyped* expr) { case Ast_Kind_Binary_Op: retval = check_binaryop((AstBinaryOp *) expr, 0); break; case Ast_Kind_Unary_Op: - retval = check_expression(((AstUnaryOp *) expr)->expr); + retval = check_expression(&((AstUnaryOp *) expr)->expr); if (((AstUnaryOp *) expr)->operation != Unary_Op_Cast) { expr->type = ((AstUnaryOp *) expr)->expr->type; @@ -576,7 +597,7 @@ CHECK(expression, AstTyped* expr) { case Ast_Kind_Address_Of: retval = check_address_of((AstAddressOf *) expr); break; case Ast_Kind_Dereference: retval = check_dereference((AstDereference *) expr); break; case Ast_Kind_Array_Access: retval = check_array_access((AstArrayAccess *) expr); break; - case Ast_Kind_Field_Access: retval = check_field_access((AstFieldAccess *) expr); break; + case Ast_Kind_Field_Access: retval = check_field_access((AstFieldAccess **) pexpr); break; case Ast_Kind_Size_Of: retval = check_size_of((AstSizeOf *) expr); break; case Ast_Kind_Global: @@ -589,7 +610,7 @@ CHECK(expression, AstTyped* expr) { break; case Ast_Kind_Argument: - retval = check_expression(((AstArgument *) expr)->value); + retval = check_expression(&((AstArgument *) expr)->value); expr->type = ((AstArgument *) expr)->value->type; break; @@ -643,7 +664,7 @@ CHECK(statement, AstNode* stmt) { default: stmt->flags |= Ast_Flag_Expr_Ignored; - return check_expression((AstTyped *) stmt); + return check_expression((AstTyped **) &stmt); } } @@ -706,6 +727,13 @@ CHECK(struct, AstStructType* s_node) { bh_table(i32) mem_set; bh_table_init(global_heap_allocator, mem_set, bh_arr_length(s_node->members)); + if (bh_arr_length(s_node->members) == 0) { + onyx_message_add(Msg_Type_Literal, + s_node->token->pos, + "empty structure"); + return 1; + } + bh_arr_each(AstStructMember *, member, s_node->members) { token_toggle_end((*member)->token); @@ -731,7 +759,11 @@ CHECK(struct, AstStructType* s_node) { } CHECK(function_header, AstFunction* func) { - for (AstLocal *param = func->params; param != NULL; param = (AstLocal *) param->next) { + i32 changed_params = 0; + + AstLocal **prev_param = &func->params; + AstLocal *param = func->params; + while (param != NULL) { fill_in_type((AstTyped *) param); if (param->type == NULL) { @@ -741,15 +773,63 @@ CHECK(function_header, AstFunction* func) { return 1; } - if (param->type->Basic.size == 0) { + if (param->type->kind != Type_Kind_Array + && type_size_of(param->type) == 0) { onyx_message_add(Msg_Type_Literal, param->token->pos, "function parameters must have non-void types"); return 1; } + + if (param->type->kind == Type_Kind_Struct) { + param->flags |= Ast_Flag_Param_Splatted; + + if (!type_struct_is_simple(param->type)) { + onyx_message_add(Msg_Type_Literal, + param->token->pos, + "only simple structures can be passed by value"); + return 1; + } + + changed_params = 1; + + AstLocal *first_new_param = NULL, *last_new_param = NULL; + AstLocal** insertion = prev_param; + bh_arr_each(StructMember *, smem, param->type->Struct.memarr) { + AstLocal* new_param = onyx_ast_node_new(semstate.node_allocator, sizeof(AstLocal), Ast_Kind_Param); + new_param->token = param->token; + new_param->type = (*smem)->type; + new_param->flags |= Ast_Flag_Const; + + if (first_new_param == NULL) first_new_param = new_param; + last_new_param = new_param; + + *insertion = new_param; + insertion = (AstLocal **) &new_param->next; + } + + *prev_param = first_new_param; + last_new_param->next = param->next; + param->next = (AstNode *) first_new_param; + + prev_param = (AstLocal **) &last_new_param->next; + } else { + prev_param = (AstLocal **) ¶m->next; + } + + param = (AstLocal *) param->next; } - fill_in_type((AstTyped *) func); + if (changed_params) { + // NOTE: Need to rebuild the function parameters in a special way once the are modified. + // This may/will get cleaned up at a later point. + + func->type = type_build_function_type( + semstate.node_allocator, func, + ((AstFunctionType *) func->type_node)->return_type); + } else { + fill_in_type((AstTyped *) func); + } if ((func->flags & Ast_Flag_Exported) != 0) { if ((func->flags & Ast_Flag_Foreign) != 0) { @@ -794,7 +874,7 @@ CHECK(node, AstNode* node) { case Ast_Kind_While: return check_while((AstWhile *) node); case Ast_Kind_Call: return check_call((AstCall *) node); case Ast_Kind_Binary_Op: return check_binaryop((AstBinaryOp *) node, 1); - default: return check_expression((AstTyped *) node); + default: return check_expression((AstTyped **) &node); } } @@ -821,7 +901,7 @@ void onyx_type_check(ProgramInfo* program) { break; case Entity_Type_Expression: - if (check_expression(entity->expr)) return; + if (check_expression(&entity->expr)) return; break; case Entity_Type_Struct: diff --git a/src/onyxtypes.c b/src/onyxtypes.c index dbd6db84..bc366369 100644 --- a/src/onyxtypes.c +++ b/src/onyxtypes.c @@ -45,6 +45,15 @@ b32 types_are_surface_compatible(Type* t1, Type* t2) { if (t2->kind == Type_Kind_Pointer) return 1; break; + case Type_Kind_Array: { + if (t2->kind != Type_Kind_Array) return 0; + + if (t1->Array.count != 0) + if (t1->Array.count != t2->Array.count) return 0; + + return types_are_compatible(t1->Array.elem, t2->Array.elem); + } + case Type_Kind_Struct: { if (t2->kind != Type_Kind_Struct) return 0; if (t1->Struct.mem_count != t2->Struct.mem_count) return 0; @@ -219,6 +228,7 @@ Type* type_build_from_ast(bh_allocator alloc, AstType* type_node) { u32 offset = 0; u32 min_alignment = 1; + u32 idx = 0; bh_arr_each(AstStructMember *, member, s_node->members) { (*member)->type = type_build_from_ast(alloc, (*member)->type_node); @@ -226,7 +236,8 @@ Type* type_build_from_ast(bh_allocator alloc, AstType* type_node) { StructMember smem = { .offset = offset, - .type = (*member)->type + .type = (*member)->type, + .idx = idx, }; token_toggle_end((*member)->token); @@ -235,6 +246,7 @@ Type* type_build_from_ast(bh_allocator alloc, AstType* type_node) { token_toggle_end((*member)->token); offset += type_size_of((*member)->type); + idx++; } // TODO: Again, add alignment @@ -256,6 +268,33 @@ Type* type_build_from_ast(bh_allocator alloc, AstType* type_node) { } } +// NOTE: Kinda hacky way of building the functions type +Type* type_build_function_type(bh_allocator alloc, AstFunction* func, AstType* return_type) { + u64 param_count = 0; + for (AstLocal* param = func->params; + param != NULL; + param = (AstLocal *) param->next) + param_count++; + + AstFunctionType* old_ftype = (AstFunctionType *) func->type_node; + Type* func_type = bh_alloc(alloc, sizeof(Type) + sizeof(Type *) * param_count); + + func_type->kind = Type_Kind_Function; + func_type->Function.param_count = param_count; + func_type->Function.return_type = type_build_from_ast(alloc, return_type); + + if (param_count > 0) { + i32 i = 0; + for (AstLocal* param = func->params; + param != NULL; + param = (AstLocal *) param->next) { + func_type->Function.params[i++] = param->type; + } + } + + return func_type; +} + Type* type_make_pointer(bh_allocator alloc, Type* to) { Type* ptr_type = bh_alloc_item(alloc, Type); @@ -312,6 +351,21 @@ b32 type_struct_lookup_member(Type* type, char* member, StructMember* smem) { return 1; } +b32 type_struct_is_simple(Type* type) { + if (type->kind != Type_Kind_Struct) return 0; + + b32 is_simple = 1; + bh_table_each_start(StructMember, type->Struct.members); + if (value.type->kind == Type_Kind_Struct + || value.type->kind == Type_Kind_Array) { + is_simple = 0; + break; + } + bh_table_each_end; + + return is_simple; +} + b32 type_is_pointer(Type* type) { return (type->kind == Type_Kind_Pointer) || (type->kind == Type_Kind_Array); diff --git a/src/onyxwasm.c b/src/onyxwasm.c index 7f98398d..35053e65 100644 --- a/src/onyxwasm.c +++ b/src/onyxwasm.c @@ -201,6 +201,10 @@ static const char* wi_string(WasmInstructionType wit) { } static WasmType onyx_type_to_wasm_type(Type* type) { + if (type->kind == Type_Kind_Struct) { + DEBUG_HERE; + } + if (type->kind == Type_Kind_Pointer) { return WASM_TYPE_INT32; }