From b8211cda90f8a9cb14657d3328cfa9ec78722c5c Mon Sep 17 00:00:00 2001 From: Kimplul Date: Thu, 30 Mar 2023 21:13:47 +0300 Subject: change to left-to-right resolver --- src/actualize.c | 165 +++++++++++------ src/ast.c | 36 +++- src/debug.c | 1 + src/parser.y | 12 +- src/scope.c | 543 ++++++++++++++++++++++++++++++++++++++++++-------------- 5 files changed, 561 insertions(+), 196 deletions(-) (limited to 'src') diff --git a/src/actualize.c b/src/actualize.c index e79bfa2..a210c9b 100644 --- a/src/actualize.c +++ b/src/actualize.c @@ -399,9 +399,10 @@ static int analyze(struct scope *scope, struct ast_node *tree) * forwarding, the main issue is detecting duplicates. */ static int analyze_procs(struct scope *scope) { + struct visible *procs = scope->procs; + /* struct act_state state = {0}; act_set_flags(&state, ACT_ONLY_TYPES); - struct visible *procs = scope->procs; if (procs) do { if (procs->owner != scope) @@ -414,11 +415,11 @@ static int analyze_procs(struct scope *scope) skip_actualize: procs = procs->next; } while (procs); + */ /* reinsert procs with actualized signatures, should make sure we don't * have duplicates after all aliases etc. have been eliminated */ procs = scope->procs; - scope->procs = NULL; if (procs) do { struct visible *next = procs->next; @@ -428,7 +429,6 @@ skip_actualize: goto skip_add; } if (scope_add_existing_proc(scope, procs)) { - destroy_visible(scope, procs); return -1; } @@ -462,53 +462,47 @@ int analyze_root(struct scope *scope, struct ast_node *tree) int template_match(struct ast_node *a, struct ast_node *b) { - struct ast_node *a_act = NULL, *b_act = NULL; - if (a->_type.kind == AST_TYPE_TEMPLATE) - a_act = a->_type.template.actual; - else - a_act = a; - + while (a && a->_type.kind == AST_TYPE_TEMPLATE) + a = a->_type.template.actual; - if (b->_type.kind == AST_TYPE_TEMPLATE) - b_act = b->_type.template.actual; - else - b_act = b; + while (b && b->_type.kind == AST_TYPE_TEMPLATE) + b = b->_type.template.actual; - return types_match(a_act, b_act); + return types_match(a, b); } -int alias_match(struct ast_node *a, struct ast_node *b) +static int alias_match(struct ast_node *a, struct ast_node *b) { - struct ast_node *a_act = NULL, *b_act = NULL; - if (a->_type.kind == AST_TYPE_ALIAS) - a_act = a->_type.alias.actual; - else - a_act = a; + while (a && a->_type.kind == AST_TYPE_ALIAS) + a = a->_type.alias.actual; + while (b && b->_type.kind == AST_TYPE_ALIAS) + b = b->_type.alias.actual; - if (b->_type.kind == AST_TYPE_ALIAS) - b_act = b->_type.alias.actual; - else - b_act = b; + return types_match(a, b); +} + +static int pointer_match(struct ast_node *a, struct ast_node *b) +{ + if (a->_type.kind != AST_TYPE_POINTER) + return 0; - return types_match(a_act, b_act); + if (b->_type.kind != AST_TYPE_POINTER) + return 0; + + return types_match(a->_type.next, b->_type.next); } static int typeof_match(struct ast_node *a, struct ast_node *b) { - struct ast_node *a_act = NULL, *b_act = NULL; - if (a->_type.kind == AST_TYPE_TYPEOF) - a_act = a->_type.typeo.actual; - else - a_act = a; + while (a && a->_type.kind == AST_TYPE_TYPEOF) + a = a->_type.typeo.actual; - if (b->_type.kind == AST_TYPE_TYPEOF) - b_act = b->_type.typeo.actual; - else - b_act = b; + while (b && b->_type.kind == AST_TYPE_TYPEOF) + b = b->_type.typeo.actual; - return types_match(a_act, b_act); + return types_match(a, b); } int types_match(struct ast_node *a, struct ast_node *b) @@ -516,10 +510,7 @@ int types_match(struct ast_node *a, struct ast_node *b) if (!a && !b) return 1; - if (!a) - return 0; - - if (!b) + if (!a || !b) return 0; assert(a->node_type == AST_TYPE); @@ -551,6 +542,14 @@ int types_match(struct ast_node *a, struct ast_node *b) if (a->_type.kind != b->_type.kind) return 0; + if (a->_type.kind == AST_TYPE_POINTER || + b->_type.kind == AST_TYPE_POINTER) { + if (pointer_match(a, b)) + return 1; + + return 0; + } + /* from here on, we know that both types are identical */ if (!identical_ast_nodes(0, a, b)) return 0; @@ -608,6 +607,18 @@ static int actualize_macro(struct act_state *state, return scope_add_macro(scope, macro); } +struct ast_node *extract_typeof(struct ast_node *type) +{ + if (!type) + return 0; + + assert(type->node_type == AST_TYPE); + if (type->_type.kind == AST_TYPE_TYPEOF) + return type; + + return extract_typeof(type->_type.next); +} + struct ast_node *extract_template(struct ast_node *type) { if (!type) @@ -904,26 +915,11 @@ static int actualize_proc(struct act_state *state, * duplicates in the scope proc list? Or can we at all? */ /* actualize types in signature */ - /* note to self: this could likely be made more explicit, but - * essentially we only want to actualize the signature when - * we're in the analysis phase. After that, the call to the proc - * will initialize the types for us, so this step would - * overwrite the type info. I think, at least. */ if (actualize(state, param_scope, sign)) return -1; if (act_flags(state, ACT_ONLY_TYPES)) return 0; - /* - struct ast_node *params = sign->_type.sign.params; - while (params) { - if (params->_var.id) - if (scope_add_var(param_scope, params)) - return -1; - - params = params->next; - } - */ /* we don't have to maintain the same flags as the parent state, I don't * think */ @@ -1282,7 +1278,7 @@ static int actualize_type(struct act_state *state, if (!ast_flags(exists, AST_FLAG_ACTUAL)) if (actualize(state, exists->scope, exists)) - return -1; + EXIT_ACT(-1); struct ast_node *types = type->_type.struc.impls; if (actualize(state, scope, types)) @@ -1290,6 +1286,11 @@ static int actualize_type(struct act_state *state, break; } + case AST_TYPE_MEMBER: { + /* TODO */ + break; + } + } ast_set_flags(type, AST_FLAG_ACTUAL); @@ -1511,7 +1512,7 @@ static int init_struct(struct act_state *state, struct scope *scope, break; } - if (!implements(scope, args->type, member->type)) { + if (!implements(0, scope, args->type, member->type)) { char *astr = type_str(args->type); char *mstr = type_str(member->type); semantic_error(scope->fctx, args, @@ -1918,6 +1919,12 @@ static int actualize_unop(struct act_state *state, break; } + case AST_REF: { + node->type = gen_type(AST_TYPE_POINTER, NULL, NULL, NULL); + node->type->_type.next = expr->type; + break; + } + default: semantic_error(scope->fctx, node, "unimplemented unary operation"); return -1; @@ -2215,7 +2222,7 @@ int actualize_main(struct scope *root) struct act_state state = {0}; /* skip checking signature for now */ - struct ast_node *main = file_scope_find_override(root, &main_id); + struct ast_node *main = file_scope_find_proc(root, &main_id); if (!main) { /* libraries are not really compilable... */ error("no main"); @@ -2240,8 +2247,35 @@ void replace_type(struct ast_node *type, struct ast_node *from, if (types_match(type, from)) { assert(type->_type.next == NULL); struct ast_node *clone = clone_ast_node(to); + /* TODO: unsure if this is everything */ + switch (type->_type.kind) { + case AST_TYPE_ID: + destroy_ast_node(type->_type.id); + break; + + case AST_TYPE_STRUCT: + destroy_ast_node(type->_type.struc.id); + break; + + case AST_TYPE_ENUM: + destroy_ast_node(type->_type.enu.id); + break; + + case AST_TYPE_UNION: + destroy_ast_node(type->_type.unio.id); + break; + + case AST_TYPE_PROC: + destroy_ast_node(type->_type.proc.id); + break; + + case AST_TYPE_TYPEOF: + destroy_ast_node(type->_type.typeo.expr); + break; + } *type = *clone; free(clone); + return; } replace_type(type->_type.next, from, to); @@ -2274,8 +2308,8 @@ void init_template_type(struct ast_node *type, struct ast_node *param_type, * backend? */ while (type != template) { type = param_type->_type.next; - type = arg_type->_type.next; - assert(param_type); + arg_type = arg_type->_type.next; + assert(type); assert(arg_type); } @@ -2291,3 +2325,16 @@ void init_template_types(struct ast_node *params, struct ast_node *param_type, params = params->next; } } + +int actualize_temp_type(struct scope *scope, struct ast_node *type) +{ + struct act_state state; + act_set_flags(&state, ACT_ONLY_TYPES); + struct ast_node *next = type->next; + + type->next = NULL; + int ret = actualize(&state, scope, type); + type->next = next; + + return ret; +} diff --git a/src/ast.c b/src/ast.c index 056cc73..9be3f89 100644 --- a/src/ast.c +++ b/src/ast.c @@ -423,6 +423,12 @@ struct ast_node *gen_type(enum ast_type_kind kind, struct ast_node *id, n->node_type = AST_TYPE; n->_type.kind = kind; switch (kind) { + case AST_TYPE_MEMBER: + n->_type.member.id = id; + n->_type.member.expr = expr; + n->loc = id->loc; + break; + case AST_TYPE_ALIAS: n->_type.alias.alias = expr; n->_type.alias.actual = ret; @@ -955,6 +961,11 @@ void ast_set_flags(struct ast_node *node, enum ast_flag flags) node->flags |= flags; } +void ast_clear_flags(struct ast_node *node, enum ast_flag flags) +{ + node->flags &= ~(flags); +} + void ast_append(struct ast_node *list, struct ast_node *elem) { struct ast_node *cur = list; @@ -1278,6 +1289,12 @@ static void __dump_ast(int depth, struct ast_node *node) dump_flags(node); switch (node->_type.kind) { + case AST_TYPE_MEMBER: + printf(" MEMBER\n"); + dump_ast(depth + 1, node->_type.member.id); + dump_ast(depth + 1, node->_type.member.expr); + break; + case AST_TYPE_ALIAS: printf(" ALIAS\n"); dump_ast(depth + 1, node->_type.alias.alias->_alias.id); @@ -1308,6 +1325,7 @@ static void __dump_ast(int depth, struct ast_node *node) case AST_TYPE_TYPEOF: printf(" TYPEOF\n"); dump_ast(depth + 1, node->_type.typeo.expr); + dump_ast(depth + 1, node->_type.typeo.actual); break; case AST_TYPE_PROC: @@ -1616,12 +1634,20 @@ struct ast_node *clone_ast_node(struct ast_node *node) break; case AST_VAR: { - struct ast_node *type = clone_ast_node(node->_var.type); + /* I don't like how messy this is, should maybe try and come up + * with something better */ + struct ast_node *type = NULL; + if (node->type) + type = clone_ast_node(node->type); + else + type = clone_ast_node(node->_var.type); + new = gen_var(clone_ast_node(node->_var.id), type, clone_ast_node(node->_var.init)); /* vars always reference the type associated with them? */ - new->type = type; + if (node->type) + new->type = type; break; } @@ -1651,6 +1677,12 @@ struct ast_node *clone_ast_node(struct ast_node *node) /* oh, if a node has a ->type it probably isn't cloned * correctly... */ switch (node->_type.kind) { + case AST_TYPE_MEMBER: + new = gen_type(AST_TYPE_MEMBER, node->_type.member.id, + node->_type.member.expr, + NULL); + break; + case AST_TYPE_ALIAS: new = gen_type(AST_TYPE_ALIAS, NULL, node->_type.alias.alias, diff --git a/src/debug.c b/src/debug.c index c9a1f86..6aa4283 100644 --- a/src/debug.c +++ b/src/debug.c @@ -205,6 +205,7 @@ static void _type_str(FILE *fp, struct ast_node *type) } fprintf(fp, ")"); } + break; } case AST_TYPE_TYPEOF: { diff --git a/src/parser.y b/src/parser.y index 50edf13..329e072 100644 --- a/src/parser.y +++ b/src/parser.y @@ -80,7 +80,7 @@ %token PUB "pub" %token STRUCT "struct" %token UNION "union" -%token TYPEDEF "type" +%token TYPEDEF "typedef" %token IMPORT "import" %token DEFER "defer" %token GOTO "goto" @@ -111,6 +111,7 @@ %token SCOPE "::" %token FATARROW "=>" +%right "[" "]" /* precedence */ %left "," %right "=" "+=" "-=" "*=" "/=" "%=" "<<=" ">>=" "&=" "^=" "|=" "^^=" @@ -464,6 +465,9 @@ type: id { $$ = gen_type(AST_TYPE_ID, $1, NULL, NULL); } | "typeof" expr { $$ = gen_type(AST_TYPE_TYPEOF, NULL, $2, NULL); } + | id "::" type { + $$ = gen_type(AST_TYPE_MEMBER, $1, $3, NULL); + } ; var_decl: id "mut" type { @@ -542,15 +546,15 @@ type_list: type "," type_list { $$ = $1; $1->next = $3; } | type { $$ = $1; } ; -type_alias: "type" id type { $$ = gen_alias($2, $3); } +type_alias: "typedef" id type { $$ = gen_alias($2, $3); } ; /* we'll parse the arg list later in the AST and check that each node is of some * specific type */ -type_template: "type" id "{" template_list "}" { +type_template: "typedef" id "{" template_list "}" { $$ = gen_template($2, $4); } - | "type" id "{" "}" { + | "typedef" id "{" "}" { /* should match anything, but doesn't implement anything */ $$ = gen_template($2, NULL); } diff --git a/src/scope.c b/src/scope.c index eca425b..e9cf75c 100644 --- a/src/scope.c +++ b/src/scope.c @@ -11,6 +11,231 @@ #include #include +static int generic_type(struct scope *scope, struct ast_node *type) +{ + if (!type) + return 0; + + if (type->_type.kind == AST_TYPE_STRUCT) { + /* check if the structure takes template parameters */ + struct ast_node *struc = file_scope_find_type(scope, type); + assert(struc); + assert(struc->node_type == AST_STRUCT); + return struc->_struct.generics != NULL; + } + + if (type->_type.kind == AST_TYPE_UNION) { + /* check if the structure takes template parameters */ + struct ast_node *unio = file_scope_find_type(scope, + type->_type.unio.id); + assert(unio); + assert(unio->node_type == AST_UNION); + return unio->_union.generics != NULL; + } + + if (type->_type.kind == AST_TYPE_TEMPLATE) + return type->_type.template.actual == NULL; + + return generic_type(scope, type->_type.next); +} + +static int referential_type(struct scope *scope, struct ast_node *type) +{ + if (!type) + return 0; + + if (type->_type.kind == AST_TYPE_TYPEOF) + return 1; + + if (type->_type.kind == AST_TYPE_MEMBER) + return 1; + + return referential_type(scope, type->_type.next); +} + +static int primitive_type(struct scope *scope, struct ast_node *type) +{ + if (!type) + return 1; + + if (referential_type(scope, type)) + return 0; + + if (generic_type(scope, type)) + return 0; + + return 1; +} + +static struct param_node *find_primitive(struct scope *scope, struct proc_node *node, + struct ast_node *type) +{ + struct param_node *param = node->primitives; + while (param) { + if (types_match(param->type, type)) + return param; + + param = param->next; + } + + return NULL; +} + +static int match_generic(struct scope *scope, struct ast_node *a, + struct ast_node *b) +{ + return implements(0, scope, a, b); +} + +static int add_next_resolve(struct scope *scope, struct ast_node *proc, + struct proc_node *node, struct ast_node *params) +{ + assert(node); + if (params && actualize_temp_type(scope, params)) + return -1; + + /* TODO: variadics? */ + /* we've run out of params, check if this is a suitable node */ + if (!params) { + /* node is already occupied, error on ambiguous definition */ + if (node->proc) { + semantic_error(scope->fctx, proc, "ambiguous callable"); + semantic_error(scope->fctx, node->proc, "matches here"); + return -1; + } + + node->proc = proc; + return 0; + } + + assert(params->node_type == AST_VAR); + if (primitive_type(scope, params->type)) { + struct param_node *found = find_primitive(scope, node, params->type); + if (found) + return add_next_resolve(scope, proc, found->proc, + params->next); + + found = calloc(1, sizeof(struct param_node)); + found->type = params->type; + found->next = node->primitives; + node->primitives = found; + + struct proc_node *next = calloc(1, sizeof(struct proc_node)); + found->proc = next; + return add_next_resolve(scope, proc, next, params->next); + } + + if (referential_type(scope, params->type)) { + /* TODO: I don't think there's a good way to check if the + * referential types are identical, but could be worth a shot */ + if (!node->referential) { + node->referential = calloc(1, sizeof(struct param_node)); + node->referential->type = params->type; + + struct proc_node *next = calloc(1, sizeof(struct proc_node)); + node->referential->proc = next; + + return add_next_resolve(scope, proc, next, params->next); + } + + if (!types_match(node->referential->type, params->type)) { + semantic_error(scope->fctx, params->type, + "ambiguous referential"); + semantic_error(scope->fctx, node->referential->type, + "matches here"); + return -1; + } + + /* common reference */ + destroy_ast_tree(params->type); + params->_var.type = NULL; + params->type = node->referential->type; + return add_next_resolve(scope, proc, node->referential->proc, params->next); + } + + /* otherwise try to use type as fallback */ + if (!node->fallback) { + node->fallback = calloc(1, sizeof(struct param_node)); + node->fallback->type = params->type; + + struct proc_node *next = calloc(1, sizeof(struct proc_node)); + node->fallback->proc = next; + return add_next_resolve(scope, proc, next, params->next); + } + + if (!match_generic(scope, node->fallback->type, params->type)) { + semantic_error(scope->fctx, params->type, "ambiguous generic"); + semantic_info(scope->fctx, node->fallback->type, + "matches here"); + return -1; + } + + /* common reference */ + destroy_ast_tree(params->type); + params->_var.type = NULL; + params->type = node->fallback->type; + return add_next_resolve(scope, proc, node->fallback->proc, + params->next); +} + +static int add_resolve(struct scope *scope, struct proc_node *root, + struct ast_node *proc) +{ + assert(root); + + struct ast_node *sign = proc->_proc.sign; + struct ast_node *params = sign->_type.sign.params; + + struct scope *resolv_scope = create_scope(); + scope_add_scope(scope, resolv_scope); + return add_next_resolve(resolv_scope, proc, root, params); +} + +static struct ast_node *proc_resolve(struct scope *scope, + struct proc_node *node, + struct ast_node *args) +{ + assert(node); + if (!args) { + if (node->proc) + return node->proc; + + return NULL; + } + + /* first check if we match a primitive type */ + struct param_node *found = find_primitive(scope, node, args->type); + if (found) + return proc_resolve(scope, found->proc, args->next); + + /* no primitives, check referentials */ + struct param_node *ref = node->referential; + if (ref) { + /* this works on the assumption that references actually are + * references to previous nodes, which we've hopefully + * initialized with real types by now. + * However, that doesn't happen, because the fallback isn't the + * one that the type is assigned to. Therefore, fuck. */ + if (types_match(args->type, ref->type)) + return proc_resolve(scope, ref->proc, args->next); + } + + /* referential didn't match, check fallback */ + struct param_node *fallback = node->fallback; + if (!fallback) + return NULL; + + if (implements(0, scope, args->type, fallback->type)) { + /* my idea is that we could lock each node individually and + * allow multithreading scopes, but I realize that recursively + * checking templates might cause a lock... */ + init_template_type(fallback->type, fallback->type, args->type); + return proc_resolve(scope, fallback->proc, args->next); + } + + return NULL; +} + /* if I ever try making the parser multithreaded, this should be atomic. */ static size_t counter = 0; struct scope *create_scope() @@ -75,6 +300,38 @@ void destroy_actuals(struct actual *actuals) } while ((prev = cur)); } +void destroy_proc_node(struct proc_node *); + +void destroy_param_nodes(struct param_node *param) +{ + if (!param) + return; + + destroy_proc_node(param->proc); + destroy_param_nodes(param->next); + free(param); +} + +void destroy_proc_node(struct proc_node *proc) +{ + destroy_param_nodes(proc->primitives); + destroy_param_nodes(proc->referential); + destroy_param_nodes(proc->fallback); + free(proc); +} + +void destroy_callable(struct callable *callable) +{ + struct callable *prev = callable, *cur; + if (prev) + do { + cur = prev->next; + destroy_proc_node(prev->root); + destroy_ast_node(prev->id); + free(prev); + } while ((prev = cur)); +} + void destroy_scope(struct scope *scope) { if (!scope) @@ -87,12 +344,11 @@ void destroy_scope(struct scope *scope) } destroy_scratch(scope->scratch); + destroy_callable(scope->callable); destroy_visible(scope, scope->vars); destroy_visible(scope, scope->procs); - destroy_visible(scope, scope->macros); destroy_visible(scope, scope->builtins); - destroy_visible(scope, scope->overrides); destroy_visible(scope, scope->enums); destroy_visible(scope, scope->structs); @@ -149,8 +405,8 @@ static struct scratch *create_scratch(struct ast_node *scratch) } CREATE_VISIBLE(create_var, vars, AST_VAR); -CREATE_VISIBLE(create_proc, procs, AST_PROC); CREATE_VISIBLE(create_macro, macros, AST_MACRO); +CREATE_VISIBLE(create_proc, procs, AST_PROC); CREATE_VISIBLE(create_enum, enums, AST_ENUM); CREATE_VISIBLE(create_alias, aliases, AST_ALIAS); @@ -174,9 +430,8 @@ CREATE_VISIBLE(create_template, templates, AST_TEMPLATE); } REFERENCE_VISIBLE(reference_var, vars, AST_VAR); -REFERENCE_VISIBLE(reference_proc, procs, AST_PROC); REFERENCE_VISIBLE(reference_macro, macros, AST_MACRO); -REFERENCE_VISIBLE(reference_override, overrides, AST_PROC); +REFERENCE_VISIBLE(reference_proc, procs, AST_PROC); REFERENCE_VISIBLE(reference_enum, enums, AST_ENUM); REFERENCE_VISIBLE(reference_alias, aliases, AST_ALIAS); @@ -212,9 +467,8 @@ FIND_VISIBLE(scope_find_template, templates, AST_TEMPLATE, _template); /* note that these return the first match for the ID, and as such might not be * what should be called. */ FIND_VISIBLE(scope_find_var, vars, AST_VAR, _var); -FIND_VISIBLE(scope_find_proc, procs, AST_PROC, _proc); -FIND_VISIBLE(scope_find_override, overrides, AST_PROC, _proc); FIND_VISIBLE(scope_find_macro, macros, AST_MACRO, _macro); +FIND_VISIBLE(scope_find_proc, procs, AST_PROC, _proc); struct ast_node *scope_find(struct scope *scope, struct ast_node *id) { @@ -453,10 +707,11 @@ static struct ast_node *match_macro(int global, struct scope *scope, return NULL; } -static struct ast_node *match_proc(int global, struct scope *scope, +static struct ast_node *match_proc(enum match_flags flags, struct scope *scope, struct ast_node *id, struct ast_node *args); -static int implements_proc(struct scope *scope, struct ast_node *arg_type, +static int implements_proc(enum match_flags flags, struct scope *scope, + struct ast_node *arg_type, struct ast_node *param_type, struct ast_node *proc) { assert(proc->node_type == AST_PROC); @@ -481,7 +736,7 @@ static int implements_proc(struct scope *scope, struct ast_node *arg_type, */ /* TODO: detect loops, such as when two template return params rely on * eachother */ - if (!implements(scope, impl_ret, ret)) { + if (!implements(flags, scope, impl_ret, ret)) { char *irt = type_str(impl_ret); char *prt = type_str(ret); semantic_error(scope->fctx, proc, "return type mismatch"); @@ -498,7 +753,8 @@ out: return impl != NULL; } -static int implements_var(struct scope *scope, struct ast_node *arg_type, +static int implements_var(enum match_flags flags, struct scope *scope, + struct ast_node *arg_type, struct ast_node *param_type, struct ast_node *var) { assert(var->node_type == AST_VAR); @@ -506,10 +762,15 @@ static int implements_var(struct scope *scope, struct ast_node *arg_type, return 0; } -static int implements_template(struct scope *scope, struct ast_node *arg_type, +static int implements_template(enum match_flags flags, struct scope *scope, + struct ast_node *arg_type, struct ast_node *param_type) { assert(param_type->_type.kind == AST_TYPE_TEMPLATE); + if (param_type->_type.template.actual) + return implements(flags, scope, arg_type, + param_type->_type.template.actual); + struct ast_node *template = param_type->_type.template.template; /* if we already know we implement this template, nothing to do */ if (find_implementation(template, arg_type)) @@ -530,7 +791,8 @@ static int implements_template(struct scope *scope, struct ast_node *arg_type, if (elem) do { if (elem->node_type == AST_VAR) { - if (implements_var(scope, arg_type, param_type, + if (implements_var(flags, scope, arg_type, + param_type, elem)) continue; @@ -544,7 +806,8 @@ static int implements_template(struct scope *scope, struct ast_node *arg_type, } else if (elem->node_type == AST_PROC) { - if (implements_proc(scope, arg_type, param_type, + if (implements_proc(flags, scope, arg_type, + param_type, elem)) continue; @@ -571,41 +834,40 @@ not_implemented: return 0; } -static int implements_alias(struct scope *scope, struct ast_node *arg_type, +static int implements_alias(enum match_flags flags, struct scope *scope, + struct ast_node *arg_type, struct ast_node *param_type) { - struct ast_node *a_act = NULL, *p_act = NULL; - if (arg_type->_type.kind == AST_TYPE_ALIAS) - a_act = arg_type->_type.alias.actual; - else - a_act = arg_type; + while (arg_type && arg_type->_type.kind == AST_TYPE_ALIAS) + arg_type = arg_type->_type.alias.actual; - if (param_type->_type.kind == AST_TYPE_ALIAS) - p_act = param_type->_type.alias.actual; - else - p_act = param_type; + while (param_type && param_type->_type.kind == AST_TYPE_ALIAS) + param_type = param_type->_type.alias.actual; - return implements(scope, a_act, p_act); + return implements(flags, scope, arg_type, param_type); } -static int implements_typeof(struct scope *scope, struct ast_node *arg_type, +static int implements_typeof(enum match_flags flags, struct scope *scope, + struct ast_node *arg_type, struct ast_node *param_type) { - struct ast_node *a_act = NULL, *p_act = NULL; - if (arg_type->_type.kind == AST_TYPE_TYPEOF) - a_act = arg_type->_type.typeo.actual; - else - a_act = arg_type; + while (arg_type && arg_type->_type.kind == AST_TYPE_TYPEOF) + arg_type = arg_type->_type.typeo.actual; - if (param_type->_type.kind == AST_TYPE_TYPEOF) - p_act = param_type->_type.typeo.actual; - else - p_act = param_type; + while (param_type && param_type->_type.kind == AST_TYPE_TYPEOF) + param_type = param_type->_type.typeo.actual; - return implements(scope, a_act, p_act); + return implements(flags, scope, arg_type, param_type); } -int implements(struct scope *scope, +static int implements_pointer(enum match_flags flags, struct scope *scope, + struct ast_node *arg_type, + struct ast_node *param_type) +{ + return implements(flags, scope, arg_type, param_type); +} + +int implements(enum match_flags flags, struct scope *scope, struct ast_node *arg_type, struct ast_node *param_type) { /* if both types are null, they are uninitialized and we'll assume they @@ -627,18 +889,28 @@ int implements(struct scope *scope, if (param_type->_type.kind == AST_TYPE_ALIAS || arg_type->_type.kind == AST_TYPE_ALIAS) { assert(param_type->_type.next == NULL); - return implements_alias(scope, arg_type, param_type); + return implements_alias(flags, scope, arg_type, param_type); } + if (param_type->_type.kind == AST_TYPE_TYPEOF || arg_type->_type.kind == AST_TYPE_TYPEOF) { - return implements_typeof(scope, arg_type, param_type); + /* if we're comparing procedure definitions, be more lenient */ + if (!(flags & MATCH_CALL) && + param_type->_type.kind != arg_type->_type.kind) + return 0; + + return implements_typeof(flags, scope, arg_type, param_type); } /* having the arg be a template is a bit of a special case */ - if (arg_type->_type.kind == AST_TYPE_TEMPLATE) - return implements(scope, arg_type->_type.template.actual, + if (arg_type->_type.kind == AST_TYPE_TEMPLATE) { + if (arg_type->_type.template.actual == NULL) + return types_match(arg_type, param_type); + + return implements(flags, scope, arg_type->_type.template.actual, param_type); + } /* TODO: do aliases and templates have to be converted to types? Are * there any situations where a template will have to be followed by @@ -646,14 +918,20 @@ int implements(struct scope *scope, /* if the parameter type is not a template, it's an actual type and therefore the * argument type must be identical to it */ if (param_type->_type.kind == AST_TYPE_TEMPLATE) { - /* the argument type must at this point be templateable, i.e. - * 'i64 does not implement some_type, but would implement - * 'some_type - if (!is_templateable(arg_type)) - return 0; - */ - - return implements_template(scope, arg_type, param_type); + if (param_type->_type.template.actual == NULL) + return implements_template(flags, scope, arg_type, + param_type); + + return implements(flags, scope, arg_type, + param_type->_type.template.actual); + } + + if (param_type->_type.kind == AST_TYPE_POINTER) { + if (arg_type->_type.kind != AST_TYPE_POINTER) + return 0; + + return implements(flags, scope, arg_type->_type.next, + param_type->_type.next); } if (!types_match(arg_type, param_type)) @@ -661,7 +939,7 @@ int implements(struct scope *scope, /* if both types have next elements in them, analyze them as well */ if (arg_type->_type.next && param_type->_type.next) - return implements(scope, arg_type->_type.next, + return implements(flags, scope, arg_type->_type.next, param_type->_type.next); /* if this is the last type element in both types, they match */ @@ -672,7 +950,8 @@ int implements(struct scope *scope, return 0; } -static int match_args(struct scope *scope, int variadic, +/* has to be executed in a temporary scope */ +static int match_args(enum match_flags flags, struct scope *scope, int variadic, const struct ast_node *args, const struct ast_node *params) { @@ -681,9 +960,11 @@ static int match_args(struct scope *scope, int variadic, return 1; while (params && args) { - if (!implements(scope, args->type, params->type)) + if (!implements(flags, scope, args->type, params->type)) return 0; + init_template_type(params->type, params->type, args->type); + params = params->next; args = args->next; } @@ -708,44 +989,34 @@ static int match_args(struct scope *scope, int variadic, return 1; } -static struct ast_node *iter_procs(struct scope *scope, struct visible *first, - struct ast_node *id, struct ast_node *args) +static int match_params(enum match_flags flags, struct scope *scope, + int variadic, + struct ast_node *args, struct ast_node *params) { - struct visible *prev = first, *cur; - if (prev) - do { - cur = prev->next; - struct ast_node *proc = prev->node; - /* must have identical IDs */ - if (!identical_ast_nodes(0, proc->_proc.id, id)) - continue; - - const int variadic = ast_flags(proc, AST_FLAG_VARIADIC); - const struct ast_node *sign = proc->_proc.sign; - const struct ast_node *params = sign->_type.sign.params; - if (match_args(scope, variadic, args, params)) - return proc; - - } while ((prev = cur)); + struct scope *tmp_scope = create_temp_scope(scope); + /* if the args aren't actualized, we're in the analysis phase? */ + /* TODO: this isn't necessary for actualized procedures */ + struct ast_node *params_clone = clone_ast_node(params); + if (args->type && actualize_temp_type(tmp_scope, params_clone)) { + destroy_ast_tree(params_clone); + destroy_scope(tmp_scope); + return 0; + } - return NULL; + int ret = match_args(flags, tmp_scope, variadic, args, params_clone); + destroy_ast_tree(params_clone); + destroy_scope(tmp_scope); + return ret; } -static struct ast_node *match_proc(int global, struct scope *scope, +static struct ast_node *match_proc(enum match_flags flags, struct scope *scope, struct ast_node *id, struct ast_node *args) { - struct ast_node *override = - iter_procs(scope, scope->overrides, id, args); - if (override) - return override; - - struct ast_node *proc = iter_procs(scope, scope->procs, id, args); - if (proc) - return proc; - - if (global && !scope_flags(scope, SCOPE_FILE)) - return match_proc(global, scope->parent, id, args); - + struct callable *cb = scope->callable; + while (cb) { + if (identical_ast_nodes(0, cb->id, id)) + return proc_resolve(scope, cb->root, args); + } return NULL; } @@ -758,7 +1029,9 @@ int scope_add_macro(struct scope *scope, struct ast_node *macro) struct ast_node *params = macro->_macro.params; int macro_exists = match_macro(0, scope, id, params) != NULL; - int proc_exists = match_proc(0, scope, id, params) != NULL; + // TODO: search for any proc with same number of parameters as macro */ + // int proc_exists = match_proc(0, scope, id, params) != NULL; + int proc_exists = 0; if (macro_exists || proc_exists) { semantic_error(scope->fctx, macro, "macro redefined"); @@ -785,11 +1058,11 @@ int scope_add_proc(struct scope *scope, struct ast_node *proc) struct ast_node *sign = proc->_proc.sign; struct ast_node *params = sign->_type.sign.params; - int macro_exists = match_macro(0, scope, id, params) != NULL; - int proc_exists = match_proc(0, scope, id, params) != NULL; + struct ast_node *macro_exists = match_macro(0, scope, id, params); - if (macro_exists || proc_exists) { + if (macro_exists) { semantic_error(scope->fctx, proc, "proc redefined"); + semantic_info(scope->fctx, macro_exists, "previously as macro"); return -1; } @@ -835,43 +1108,34 @@ int scope_add_existing_proc(struct scope *scope, struct visible *visible) struct ast_node *sign = proc->_proc.sign; struct ast_node *params = sign->_type.sign.params; - int macro_exists = match_macro(0, scope, id, params) != NULL; - int proc_exists = match_proc(0, scope, id, params) != NULL; - - if (macro_exists || proc_exists) { + struct ast_node *macro_exists = match_macro(0, scope, id, params); + if (macro_exists) { semantic_error(scope->fctx, proc, "proc redefined"); + semantic_info(scope->fctx, macro_exists, "previously as macro"); return -1; } - struct ast_node *template = NULL; - while (params) { - if ((template = extract_template(params->type))) - break; - - params = params->next; + if (!scope->callable) { + scope->callable = calloc(1, sizeof(struct callable)); + scope->callable->root = calloc(1, sizeof(struct proc_node)); + scope->callable->id = clone_ast_node(id); + return add_resolve(scope, scope->callable->root, proc); } - if (template) { - visible->next = scope->procs; - scope->procs = visible; - } - else { - /* override is maybe a bit misleading, but essentially any - * procedure with no templates */ - visible->next = scope->overrides; - scope->overrides = visible; - } - - int public = scope_flags(scope, SCOPE_PUBLIC); - if (scope_flags(scope, - SCOPE_FILE) && ast_flags(proc, AST_FLAG_PUBLIC)) { - if (template) - return reference_proc(public, scope->parent, visible); + struct callable *cb = scope->callable; + while (cb) { + if (identical_ast_nodes(0, cb->id, id)) + return add_resolve(scope, cb->root, proc); - return reference_override(public, scope->parent, visible); + cb = cb->next; } - return 0; + cb = calloc(1, sizeof(struct callable)); + cb->root = calloc(1, sizeof(struct proc_node)); + cb->id = clone_ast_node(id); + cb->next = scope->callable; + scope->callable = cb; + return add_resolve(scope, cb->root, proc); } #define FIND_FILE_VISIBLE(name, obj_type) \ @@ -904,8 +1168,8 @@ struct ast_node *file_scope_find_type(struct scope *scope, FIND_FILE_VISIBLE(file_scope_find_var, var); FIND_FILE_VISIBLE(file_scope_find_proc, proc); -FIND_FILE_VISIBLE(file_scope_find_override, override); FIND_FILE_VISIBLE(file_scope_find_macro, macro); + FIND_FILE_VISIBLE(file_scope_find_alias, alias); FIND_FILE_VISIBLE(file_scope_find_template, template); @@ -919,10 +1183,6 @@ struct ast_node *file_scope_find(struct scope *scope, struct ast_node *id) if (found) return found; - found = file_scope_find_override(scope, id); - if (found) - return found; - found = file_scope_find_proc(scope, id); if (found) return found; @@ -950,7 +1210,7 @@ struct ast_node *scope_resolve_macro(struct scope *scope, struct ast_node *call) return match_macro(0, scope, id, args); } -static int template_contains_proc(struct scope *scope, +static int template_contains_proc(enum match_flags flags, struct scope *scope, struct ast_node *template, struct ast_node *id, struct ast_node *args) { @@ -970,7 +1230,7 @@ static int template_contains_proc(struct scope *scope, int variadic = ast_flags(elem, AST_FLAG_VARIADIC); struct ast_node *sign = elem->_proc.sign; struct ast_node *params = sign->_type.sign.params; - if (match_args(scope, variadic, args, params)) + if (match_params(flags, scope, variadic, args, params)) return 1; } while ((elem = elem->next)); @@ -998,7 +1258,8 @@ struct ast_node *scope_resolve_proc(struct scope *scope, struct ast_node *call) if (!template) goto next; - if (!template_contains_proc(scope, template, id, args)) { + if (!template_contains_proc(MATCH_CALL, scope, template, id, + args)) { char *cstr = call_str(call); char *tstr = type_str(arg); semantic_error(scope->fctx, arg, @@ -1014,7 +1275,7 @@ next: arg = arg->next; } - struct ast_node *proc = match_proc(0, scope, id, args); + struct ast_node *proc = match_proc(MATCH_CALL, scope, id, args); if (!proc) return NULL; @@ -1040,10 +1301,10 @@ struct ast_node *scope_resolve_actual(struct scope *scope, assert(!ast_flags(actual, AST_FLAG_VARIADIC)); /* could also check that arguments aren't templates */ - const struct ast_node *args = call->_call.args; - const struct ast_node *sign = actual->_proc.sign; - const struct ast_node *params = sign->_type.sign.params; - if (match_args(scope, 0, args, params)) + struct ast_node *args = call->_call.args; + struct ast_node *sign = actual->_proc.sign; + struct ast_node *params = sign->_type.sign.params; + if (match_params(0, scope, 0, args, params)) return actual; } while ((prev = cur)); @@ -1234,8 +1495,16 @@ int scope_add_actual(struct scope *scope, struct ast_node *node) static struct ast_node *find_actual(struct actual *actuals, struct ast_node *node) { - /* TODO */ - internal_error("finding actuals not yet implemented"); + assert(node->node_type == AST_ID); + + if (actuals) + do { + struct ast_node *actual = actuals->node; + if (identical_ast_nodes(0, actual->_proc.id, node)) + return actual; + + } while ((actuals = actuals->next)); + return NULL; } @@ -1256,3 +1525,15 @@ int scope_add_scratch(struct scope *scope, struct ast_node *scratch) scope->scratch = new; return 0; } + +struct scope *create_temp_scope(struct scope *parent) +{ + struct scope *scope = create_scope(); + if (!scope) { + internal_error("failed allocating temp scope"); + } + + scope->parent = parent; + scope->fctx = parent->fctx; + return scope; +} -- cgit v1.3