Polymorphic procedure, with passed in compile time type but without removing the type in params etc.

This commit is contained in:
Krzosa Karol
2023-04-01 19:40:12 +02:00
parent 3d438645a0
commit 7bf3e107bb
8 changed files with 158 additions and 80 deletions

View File

@@ -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 <class T>
CORE_Static Array<T>
array_make(Allocator *a, S64 size = 16) {
Array<T> result = {};
result.init(a, size);
return result;
}
//-----------------------------------------------------------------------------
// Map
//-----------------------------------------------------------------------------

View File

@@ -116,7 +116,14 @@ ast_lambda(Token *pos, Array<Ast_Decl *> params, Array<Ast_Expr *> 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<Ast_Decl *> 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;
}

View File

@@ -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 {

View File

@@ -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);

View File

@@ -306,16 +306,26 @@ Ast_Decl *get_or_instantiate_polymorph_type(Token *pos, Ast_Decl *poly, Array<As
}
}
if (poly->kind == AST_LAMBDA) Breakpoint;
if (!result) {
result = (Ast_Decl *)ast_copy(poly, poly->parent_scope, &poly->polymorph_parameters, &params);
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<Ast_Call_Item *> params, Ast_Scope *field_access_scope) {
// }

View File

@@ -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<Ast_Call_Item *> 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<Match> 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)
if (name->intern_val.str == lambda_arg->name.str) item = it;
}
else if (it_index == default_iter) {
default_iter += 1;
item = it;
}
else if (node->exprs.get_index(&it) == default_iter) {
default_iter++;
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);
// 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});
}
// 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;
}
}
if (!included) compiler_error(call_it->pos, "Unknown argument to a function call, couldn't match it with any of the declared arguments");
}
// Find the polymorph
if (lambda->flags & AST_POLYMORPH) {
Array<Ast_Call_Item *> 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<Ast_Call_Item *> 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);
item->resolved_index = lambda->args.get_index(&lambda_arg);
items.add(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);
}
// 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);
}
}
// 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);
}
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
//

View File

@@ -132,7 +132,7 @@ type_try_tupling(Array<Ast_Type *> types, Ast *ast) {
// @todo alignment, offsets
result = type_new(pctx->perm, TYPE_TUPLE, 0, pointer_align);
result->agg.members = array_make<Ast_Resolved_Member>(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;

View File

@@ -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