From 7bf3e107bb2b5d329c384b24ccc2c892250ac0f4 Mon Sep 17 00:00:00 2001 From: Krzosa Karol Date: Sat, 1 Apr 2023 19:40:12 +0200 Subject: [PATCH] Polymorphic procedure, with passed in compile time type but without removing the type in params etc. --- base_data_structures.cpp | 26 ++++--- core_ast.cpp | 12 ++-- core_compiler_interface.hpp | 5 +- core_parsing.cpp | 16 ++++- core_polymorph.cpp | 12 +++- core_typechecking.cpp | 138 +++++++++++++++++++++--------------- core_types.cpp | 4 +- examples/_polymorphism.core | 25 ++++++- 8 files changed, 158 insertions(+), 80 deletions(-) diff --git a/base_data_structures.cpp b/base_data_structures.cpp index e3a0ad2..c82d57e 100644 --- a/base_data_structures.cpp +++ b/base_data_structures.cpp @@ -54,6 +54,24 @@ struct Array { } } +#if 0 + void insert(T item, int index) { + if (index == len) { + add(item); + return; + } + + assert(index < len); + assert(index >= 0); + + grow(1); + int right_len = len - index; + memmove(data + index + 1, data + index, sizeof(T) * right_len); + data[index] = item; + len += 1; + } +#endif + void add(T item) { grow(1); data[len++] = item; @@ -113,14 +131,6 @@ struct Array { } }; -template -CORE_Static Array -array_make(Allocator *a, S64 size = 16) { - Array result = {}; - result.init(a, size); - return result; -} - //----------------------------------------------------------------------------- // Map //----------------------------------------------------------------------------- diff --git a/core_ast.cpp b/core_ast.cpp index 1f0edc3..9682588 100644 --- a/core_ast.cpp +++ b/core_ast.cpp @@ -116,7 +116,14 @@ ast_lambda(Token *pos, Array params, Array ret, Ast_Scop result->args = params.tight_copy(pctx->perm); result->ret = ret.tight_copy(pctx->perm); result->scope = scope; - For(params) if (is_flag_set(it->flags, AST_POLYMORPH)) set_flag(result->flags, AST_POLYMORPH); + + For(params) { + if (is_flag_set(it->flags, AST_POLYMORPH)) { + set_flag(result->flags, AST_POLYMORPH); + break; + } + } + return result; } @@ -224,7 +231,6 @@ ast_struct(Token *pos, Ast_Scope *scope, Ast_Kind kind, Array polymo result->polymorph_parameters = polymorph_parameters; set_flag(result->flags, AST_POLYMORPH); set_flag(result->flags, AST_PARENT_POLYMORPH); - result->polymorphs.allocator = pctx->heap; } return result; } @@ -627,7 +633,6 @@ end_of_switch: CORE_Static void set_flag_typespec(Ast_Expr *expr) { for (Ast_Iter iter = iterate_depth_first(pctx->heap, expr, true); iter.ast; next(&iter)) { - assert(iter.kind == AST_UNARY || iter.kind == AST_ARRAY || iter.kind == AST_CALL || iter.kind == AST_CALL_ITEM || iter.kind == AST_IDENT); set_flag(iter.ast->flags, AST_TYPESPEC); } } @@ -642,7 +647,6 @@ unset_polymorph(Ast *ast) { CORE_Static bool is_typespec_polymorphic(Ast *ast) { for (Ast_Iter iter = iterate_depth_first(pctx->heap, ast, true); iter.ast; next(&iter)) { - assert(iter.kind == AST_UNARY || iter.kind == AST_ARRAY || iter.kind == AST_CALL || iter.kind == AST_CALL_ITEM || iter.kind == AST_IDENT); if (is_flag_set(iter.ast->flags, AST_POLYMORPH)) { return true; } diff --git a/core_compiler_interface.hpp b/core_compiler_interface.hpp index 8437d2b..97fb707 100644 --- a/core_compiler_interface.hpp +++ b/core_compiler_interface.hpp @@ -421,18 +421,17 @@ typedef uint32_t Ast_Call_Item_Flag; enum { CALL_INDEX = 1ull << 1, CALL_NAME = 1ull << 2, - CALL_INCLUDED = 1ull << 4, }; struct Ast_Call_Item : Ast_Expr { Ast_Call_Item_Flag call_flags; - int32_t resolved_index; + int32_t resolved_index; // This is probably for compound array Ast_Expr *item; union { Ast_Atom *name; Ast_Expr *index; }; - Intern_String resolved_name; + Intern_String resolved_name; // This is probably for compound struct }; struct Ast_Call : Ast_Expr { diff --git a/core_parsing.cpp b/core_parsing.cpp index 318f768..6a882cb 100644 --- a/core_parsing.cpp +++ b/core_parsing.cpp @@ -660,7 +660,7 @@ parse_expr(S64 min_bp) { } break; case TK_OpenParen: { - if (token_is(TK_CloseParen) || (token_is(TK_Identifier) && token_is(TK_Colon, 1)) || token_is(TK_ThreeDots)) + if (token_is(TK_CloseParen) || (token_is(TK_Identifier) && token_is(TK_Colon, 1)) || (token_is(TK_Polymorph) && token_is(TK_Colon, 1)) || token_is(TK_ThreeDots)) left = parse_lambda(token); else { left = parse_expr(0); @@ -930,6 +930,20 @@ parse_decl(B32 is_global) { if (is_flag_set(a->flags, AST_POLYMORPH)) { set_flag(result->flags, AST_POLYMORPH); set_flag(result->flags, AST_PARENT_POLYMORPH); + + int polymorph_count = 0; + For(a->args) { + if (it->flags & AST_POLYMORPH) { + polymorph_count += 1; + } + } + + result->polymorph_parameters.init(pctx->perm, polymorph_count); + For(a->args) { + if (it->flags & AST_POLYMORPH) { + result->polymorph_parameters.add(it); + } + } } if (is_flag_set(flags, AST_FOREIGN)) set_flag(expr->flags, flags); diff --git a/core_polymorph.cpp b/core_polymorph.cpp index c9472a7..3044eba 100644 --- a/core_polymorph.cpp +++ b/core_polymorph.cpp @@ -306,16 +306,26 @@ Ast_Decl *get_or_instantiate_polymorph_type(Token *pos, Ast_Decl *poly, Arraykind == AST_LAMBDA) Breakpoint; + if (!result) { result = (Ast_Decl *)ast_copy(poly, poly->parent_scope, &poly->polymorph_parameters, ¶ms); unset_flag(result->flags, AST_POLYMORPH); - result->type_val = type_incomplete(result); + unset_flag(result->flags, AST_PARENT_POLYMORPH); + if (poly->kind == AST_STRUCT || poly->kind == AST_UNION) result->type_val = type_incomplete(result); result->polymorph_hash = hash; assert(result->di != poly->di); result->name = get_unique_name_for_decl(result); + + poly->polymorphs.allocator = pctx->heap; poly->polymorphs.add(result); } + resolve_decl(result); + if (poly->kind == AST_STRUCT || poly->kind == AST_UNION) type_complete(result->type_val); return result; } + +// Ast_Decl *get_or_instantiate_polymorph_lambda(Token *pos, Ast_Decl *poly, Array params, Ast_Scope *field_access_scope) { +// } \ No newline at end of file diff --git a/core_typechecking.cpp b/core_typechecking.cpp index b78e232..5966e86 100644 --- a/core_typechecking.cpp +++ b/core_typechecking.cpp @@ -1565,24 +1565,12 @@ resolve_expr(Ast_Expr *ast, Resolve_Flag flags, Ast_Type *compound_and_const_str else { Operand name = resolve_expr(node->name, inherit_flag(flags, AST_CANT_BE_NULL), 0, field_access_scope); - // @cleanup: polymorphic structs probably shouldnt have types, not sure? - if (name.type == pctx->type_type) { - if (name.type_val->kind != TYPE_POLYMORPH) { - compiler_error(node->pos, "Parenthesis are not valid for types that are not polymorphic"); - } - } - if (name.resolved_decl->flags & AST_POLYMORPH) { assert(name.resolved_decl->flags & AST_PARENT_POLYMORPH); Ast_Decl *poly = name.resolved_decl; - if (poly->kind == AST_LAMBDA) { - // Ast_Decl *instance = get_or_instantiate_polymorph_type(node->pos, poly, node->exprs, field_access_scope); - } - else { + if (poly->kind == AST_STRUCT || poly->kind == AST_UNION) { Ast_Decl *instance = get_or_instantiate_polymorph_type(node->pos, poly, node->exprs, field_access_scope); - resolve_decl(instance); - type_complete(instance->type_val); return operand_type(instance->type_val); } } @@ -1623,18 +1611,14 @@ resolve_expr(Ast_Expr *ast, Resolve_Flag flags, Ast_Type *compound_and_const_str // // Regular call // - if (name.type->kind != TYPE_LAMBDA) { - compiler_error(node->pos, "Calling %Q which is not a [Lambda]", typestring(name.type)); - } if (!name.resolved_decl) { compiler_error(node->pos, "Internal compiler error: Failed to propagate a resolved lambda declaration from atom resolution"); } + if (name.resolved_decl->kind != AST_LAMBDA) { + compiler_error(node->pos, "Calling %Q which is not a [Lambda]", typestring(name.type)); + } node->resolved_decl = name.resolved_decl; - Scoped_Arena scratch(pctx->scratch); - Array items = {scratch.arena}; - S64 default_iter = 0; - /* @warning We only have one instance of a given Lambda type for example (a: Vec3): Vec3. @@ -1642,67 +1626,105 @@ resolve_expr(Ast_Expr *ast, Resolve_Flag flags, Ast_Type *compound_and_const_str This creates a problem cause sometimes we might refer to the wrong Ast. Need to be careful!!! */ - Ast_Lambda *lambda = (Ast_Lambda *)name.type->ast; + + Scoped_Arena scratch(pctx->scratch); + Ast_Lambda *lambda = name.resolved_decl->lambda; + struct Match { + Ast_Decl *lambda_arg; + Ast_Call_Item *call_arg; + }; + Array matches = {scratch.arena}; + S64 default_iter = 0; + + // For every defined argument in the lambda we seek for + // a corresponding item in the call expression. + // + // This algorithm is cool because it results in correct argument order. + // It's kind of complicated, especially the default_iter stuff is kind + // of weird but it works very well. The default_iter is only incremented + // on successful match because we are iterating through entire call expression + // every time. + // Might be wise to revise it later but the approach of starting with + // lambda_args seems correct. For_Named(lambda->args, lambda_arg) { - assert(lambda_arg->type); + // assert(lambda_arg->type); // @todo: maybe add this check at the end Ast_Call_Item *item = 0; For(node->exprs) { - assert(!is_flag_set(it->call_flags, CALL_INDEX)); - if (is_flag_set(it->call_flags, CALL_NAME)) { + int it_index = node->exprs.get_index(&it); + + bool named_argument = is_flag_set(it->call_flags, CALL_NAME); + bool named_argument_already_appeared = it_index > default_iter; + if (named_argument) { Ast_Atom *name = it->name; assert(name->kind == AST_IDENT); - if (name->intern_val.str == lambda_arg->name.str) - item = it; + if (name->intern_val.str == lambda_arg->name.str) item = it; } - else if (node->exprs.get_index(&it) == default_iter) { - default_iter++; + + else if (it_index == default_iter) { + default_iter += 1; item = it; } - else if (node->exprs.get_index(&it) > default_iter) { - compiler_error(it->pos, "Positional argument after named argument"); - } + + else if (named_argument_already_appeared) compiler_error(it->pos, "Positional argument after named argument is not permitted"); if (item) break; } - if (item) { - set_flag(item->call_flags, CALL_INCLUDED); - Operand expr = resolve_expr(item->item, AST_CANT_BE_NULL, lambda_arg->type, field_access_scope); - - make_sure_value_is_compatible_with_type(item->pos, &expr, lambda_arg->type, TYPE_AND_EXPR_REQUIRED); - item->resolved_type = lambda_arg->type; - - try_propagating_resolved_type_to_untyped_literals(item->item, item->resolved_type); - item->resolved_index = lambda->args.get_index(&lambda_arg); - items.add(item); + // If we couldn't find corresponding call item we should seek + // for a default value inside the lambda declaration + if (!item) { + if (!lambda_arg->expr) compiler_error(node->pos, node->resolved_decl->pos, "Required value: %Q in lambda call was not passed", lambda_arg->name); + item = ast_call_item(lambda_arg->expr->pos, 0, 0, lambda_arg->expr); + item->resolved_type = lambda_arg->expr->resolved_type; } + matches.add({lambda_arg, item}); + } - else { - if (!lambda_arg->expr) { - compiler_error(node->pos, node->resolved_decl->pos, "Required value: %Q in lambda call was not passed", lambda_arg->name); + // Make sure we found every item. + For_Named(node->exprs, call_it) { + bool included = false; + For_Named(matches, match_it) { + if (call_it == match_it.call_arg) { + included = true; + break; } - - // default values are typechecked when they get resolved - Ast_Call_Item *call_item = ast_call_item(lambda_arg->expr->pos, 0, 0, lambda_arg->expr); - call_item->resolved_type = lambda_arg->expr->resolved_type; - call_item->resolved_index = lambda->args.get_index(&lambda_arg); - set_flag(call_item->call_flags, CALL_INCLUDED); - items.add(call_item); } + if (!included) compiler_error(call_it->pos, "Unknown argument to a function call, couldn't match it with any of the declared arguments"); } - // check if all arguments are included and cleanup - For(node->exprs) { - if (!is_flag_set(it->call_flags, CALL_INCLUDED)) - compiler_error(it->pos, "Unknown argument to a function call, couldn't match it with any of the declared arguments"); - else unset_flag(it->call_flags, CALL_INCLUDED); + // Find the polymorph + if (lambda->flags & AST_POLYMORPH) { + Array replacements = {scratch.arena}; + For(matches) { + if (it.lambda_arg->flags & AST_POLYMORPH) { + replacements.add(it.call_arg); + } + } + + Ast_Decl *poly = name.resolved_decl; + assert(replacements.len == poly->polymorph_parameters.len); + Ast_Decl *instance = get_or_instantiate_polymorph_type(node->pos, poly, replacements, field_access_scope); + lambda = instance->lambda; } + // Typecheck the arguments and produce properly ordered list of arguments for codegeneration + Array items = {scratch.arena}; + For(matches) { + Ast_Call_Item *item = it.call_arg; + Ast_Decl *lambda_arg = it.lambda_arg; + Operand expr = resolve_expr(item->item, AST_CANT_BE_NULL, lambda_arg->type, field_access_scope); + + make_sure_value_is_compatible_with_type(item->pos, &expr, lambda_arg->type, TYPE_AND_EXPR_REQUIRED); + item->resolved_type = lambda_arg->type; + + try_propagating_resolved_type_to_untyped_literals(item->item, item->resolved_type); + items.add(item); + } node->exprs = items.tight_copy(pctx->perm); - node->resolved_type = name.type->func.ret; - return operand_rvalue(name.type->func.ret); + node->resolved_type = lambda->resolved_type->func.ret; + return operand_rvalue(node->resolved_type); // // CALL End // diff --git a/core_types.cpp b/core_types.cpp index 1d9081d..f88869f 100644 --- a/core_types.cpp +++ b/core_types.cpp @@ -132,7 +132,7 @@ type_try_tupling(Array types, Ast *ast) { // @todo alignment, offsets result = type_new(pctx->perm, TYPE_TUPLE, 0, pointer_align); - result->agg.members = array_make(pctx->perm, types.len); + result->agg.members.init(pctx->perm, types.len); For(types) { Ast_Resolved_Member m = {}; m.type = it; @@ -219,8 +219,8 @@ type_enum(Ast_Decl *ast, Ast_Type *type) { CORE_Static Ast_Type * type_incomplete(Ast *ast) { + if (is_flag_set(ast->flags, AST_POLYMORPH)) return 0; Ast_Type_Kind kind = TYPE_INCOMPLETE; - if (is_flag_set(ast->flags, AST_POLYMORPH)) kind = TYPE_POLYMORPH; Ast_Type *result = type_new(pctx->perm, kind, 0, 0); result->ast = ast; return result; diff --git a/examples/_polymorphism.core b/examples/_polymorphism.core index 8d7cb3b..bbf023c 100644 --- a/examples/_polymorphism.core +++ b/examples/_polymorphism.core @@ -30,6 +30,10 @@ Triple :: struct($A: Type, $B: Type, $C: Type) a: A b: B c: C +Variant :: union($A: Type, $B: Type, $C: Type) + a: A + b: B + c: C MakeArray :: (a: *int, count: int): Array(int) result := Array(int) { @@ -46,13 +50,19 @@ MakeArray :: (a: *int, count: int): Array(int) MultipleArgs :: (): Tuple(int, F32) return {32, 32} -PolyLambda :: (value: $T): T - return value +PolyLambda :: ($T: Type): T + return 32 GetCount :: (a: int): int return a +// @todo: this is allowed, shouldn't be +// Test :: (a: int, b: int = 10, c: int???) + +Test :: (a: int, b: int = 10, c: int = 20) + pass + main :: (argc: int, argv: **char): int buff: *int array: Array(int) = {len = 10, cap = 10, data = buff} @@ -61,12 +71,21 @@ main :: (argc: int, argv: **char): int fourth: Array(F32) fifth: Array(F32) sixth: Array(Array(F32)) + seventh: Variant(int, F32, S64) + // d: int(32) // c := MakeArray(buff, GetCount(GetCount(32))) // a,b := MultipleArgs() a := MultipleArgs() - // value := PolyLambda(32) + Test(10) + Test(10, 20) + Test(10, 20, 30) + Test(10, b = 10) + Test(10, b = 10, c = 20) + Test(a = 10, b = 10, c = 20) + + value := PolyLambda(int) return 0 \ No newline at end of file