diff options
| author | Kimplul <kimi.h.kuparinen@gmail.com> | 2023-11-13 18:05:33 +0200 |
|---|---|---|
| committer | Kimplul <kimi.h.kuparinen@gmail.com> | 2023-11-13 18:05:33 +0200 |
| commit | b00da3db7c1d7f1e16b2e0446234dc42d54b8708 (patch) | |
| tree | 3292fa94dfe02aad7036b7627e17f6980d2bcbc0 /src/scope.c | |
| parent | 5b5f6321c743e9a521e45865312a76ae0bffe450 (diff) | |
| download | ek-b00da3db7c1d7f1e16b2e0446234dc42d54b8708.tar.gz ek-b00da3db7c1d7f1e16b2e0446234dc42d54b8708.zip | |
further simplification of scope concept
Diffstat (limited to 'src/scope.c')
| -rw-r--r-- | src/scope.c | 995 |
1 files changed, 210 insertions, 785 deletions
diff --git a/src/scope.c b/src/scope.c index d6481c3..2b8ef12 100644 --- a/src/scope.c +++ b/src/scope.c @@ -24,88 +24,53 @@ static struct ast_node *match_proc(struct scope *scope, static struct ast_node *match_macro(struct scope *scope, struct ast_node *id, struct ast_node *args); -static int generics_trait_type(struct ast_node *generics) +static struct param_node *find_matching_param(struct resolve_node *node, + struct ast_node *type) { - if (!generics) - return 0; + struct param_node *param = node->params; + while (param) { + if (types_match(type, param->type)) + return param; - if (actual_type(generics)->_type.kind == AST_TYPE_TRAIT) - return 1; + param = param->next; + } - return generics_trait_type(generics->_type.next); + return NULL; } -static int generic_type(struct ast_node *type) +static int traits_resolve(struct ast_node *arg_type, struct ast_node *param_type) { - if (!type) - return 0; - - if (type->_type.kind == AST_TYPE_STRUCT) - return generics_trait_type(type->_type.struc.impls); - - if (type->_type.kind == AST_TYPE_TRAIT) - return type->_type.trait.actual == NULL; - - return generic_type(type->_type.next); + /** @todo are more checks required? */ + return AST_TYPE(arg_type).as == AST_TRAIT_TYPE(param_type).def; } -static int referential_type(struct ast_node *type) +static int typeofs_resolve(struct ast_node *arg_type, struct ast_node *param_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(type->_type.next); -} - -int primitive_type(struct ast_node *type) -{ - if (!type) - return 1; - - if (referential_type(type)) - return 0; - - if (generic_type(type)) - return 0; - - return 1; + internal_error("typeof resolve unimplemented"); + return 0; } -int fully_qualified(struct ast_node *type) +static int types_resolve(struct ast_node *arg_type, struct ast_node *param_type) { - if (!type) + /* untyped resolves all */ + if (!param_type) return 1; - assert(type->_type.kind != AST_TYPE_TRAIT); - if (type->_type.kind == AST_TYPE_STRUCT) { - if (!ast_flags(type, AST_FLAG_GENERIC)) - return 1; - - if (type->_type.struc.impls) - return fully_qualified(type->_type.struc.impls); + if (AST_TYPE(param_type).kind == AST_TYPE_TRAIT) + return traits_resolve(arg_type, param_type); - return 0; - } + if (AST_TYPE(param_type).kind == AST_TYPE_TYPEOF) + return typeofs_resolve(arg_type, param_type); - return fully_qualified(type->_type.next); + return types_match(arg_type, param_type); } -static struct param_node *find_matching_param(struct resolve_node *node, - struct ast_node *type) +static struct param_node *find_resolving_param(struct resolve_node *node, + struct ast_node *type) { struct param_node *param = node->params; while (param) { - /* untyped matches everything, yay */ - if (!param->type) - return param; - - if (types_match(type, param->type)) + if (types_resolve(type, param->type)) return param; param = param->next; @@ -181,12 +146,9 @@ static int add_next_resolve(struct scope *scope, struct ast_node *resolve, static int add_resolve(struct scope *scope, struct resolve *resolve, struct ast_node *proc) { - 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, resolve->root, params); + struct ast_node *sign = AST_PROC(proc).sign; + struct ast_node *params = AST_SIGN_TYPE(sign).params; + return add_next_resolve(scope, proc, resolve->root, params); } static struct ast_node *resolve(struct scope *scope, @@ -194,6 +156,7 @@ static struct ast_node *resolve(struct scope *scope, struct ast_node *args) { assert(node); + /* check for no parameters case */ if (!args) { if (node->resolved) return node->resolved; @@ -201,20 +164,18 @@ static struct ast_node *resolve(struct scope *scope, return NULL; } - /* first check if we match a primitive type */ - struct param_node *found = find_matching_param(node, args->type); + struct param_node *found = find_resolving_param(node, args->type); if (found) return resolve(scope, found->resolved, 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() { - /* TODO: add in a scope counter, might make things easier to see in the - * AST dump */ + /* if I ever try making the parser multithreaded, this should be atomic. */ + static size_t counter = 0; + struct scope *scope = calloc(1, sizeof(struct scope)); if (!scope) { internal_error("ran out of memory allocating scope"); @@ -242,22 +203,7 @@ void destroy_visible(struct scope *scope, struct visible *visible) if (prev) do { cur = prev->next; - /* file scope actually owns all the AST nodes, so don't - * try to destroy them in lower level scopes */ - if (scope_flags(scope, SCOPE_FILE)) - if (prev->owner == scope) - destroy_ast_node(prev->node); - free(prev); - } while ((prev = cur)); -} - -static void destroy_scratch(struct scratch *scratch) -{ - struct scratch *prev = scratch, *cur; - if (prev) - do { - cur = prev->next; - destroy_ast_node(prev->node); + /* destroy AST nodes globally somewhere? */ free(prev); } while ((prev = cur)); } @@ -268,7 +214,6 @@ void destroy_actuals(struct actual *actuals) if (prev) do { cur = prev->next; - destroy_ast_node(prev->node); free(prev); } while ((prev = cur)); } @@ -301,7 +246,6 @@ void destroy_resolve(struct resolve *resolve) do { cur = prev->next; destroy_resolve_node(prev->root); - destroy_ast_node(prev->id); free(prev); } while ((prev = cur)); } @@ -317,19 +261,12 @@ void destroy_scope(struct scope *scope) free((void *)scope->fctx.fname); } - destroy_scratch(scope->scratch); destroy_resolve(scope->proc_resolve); destroy_resolve(scope->macro_resolve); destroy_resolve(scope->type_construct_resolve); destroy_visible(scope, scope->vars); - destroy_visible(scope, scope->procs); - destroy_visible(scope, scope->builtins); - - destroy_visible(scope, scope->enums); - destroy_visible(scope, scope->structs); - destroy_visible(scope, scope->aliases); - destroy_visible(scope, scope->traits); + destroy_visible(scope, scope->types); struct scope *prev = scope->children, *cur; if (prev) @@ -353,247 +290,71 @@ int scope_flags(struct scope *scope, enum scope_flags flags) return scope->flags & flags; } -static struct visible *create_visible(struct scope *owner, +static struct visible *create_visible(struct ast_node *id, struct ast_node *node) { struct visible *visible = calloc(1, sizeof(struct visible)); + visible->id = id; visible->node = node; - visible->owner = owner; return visible; } -static struct scratch *create_scratch(struct ast_node *scratch) +struct visible *create_type(struct scope *scope, struct ast_node *id, struct ast_node *type) { - struct scratch *new = calloc(1, sizeof(struct scratch)); - new->node = scratch; - return new; -} - -/* set newest member as head of linked list */ -#define CREATE_VISIBLE(name, type, ast_type) \ - static struct visible *name(struct scope *owner, struct ast_node *node) \ - { \ - assert(node->node_type == ast_type); \ - struct visible *visible = create_visible(owner, node); \ - visible->next = owner->type; \ - owner->type = visible; \ - return visible; \ - } - -CREATE_VISIBLE(create_var, vars, AST_VAR); -CREATE_VISIBLE(create_proc, procs, AST_PROC); -CREATE_VISIBLE(create_macro, macros, AST_MACRO_CONSTRUCT); -CREATE_VISIBLE(create_type_construct, type_constructs, AST_TYPE_CONSTRUCT); - -CREATE_VISIBLE(create_enum, enums, AST_ENUM); -CREATE_VISIBLE(create_alias, aliases, AST_ALIAS); -CREATE_VISIBLE(create_struct, structs, AST_STRUCT); -CREATE_VISIBLE(create_builtin, builtins, AST_TYPE); -CREATE_VISIBLE(create_trait, traits, AST_TRAIT); - -/* TODO: check for identical names in the scope? */ -#define REFERENCE_VISIBLE(name, list, ast_type) \ - static int name(int public, struct scope *scope, struct visible *obj) \ - { \ - if (!scope) \ - return 0; \ - assert(obj->node->node_type == ast_type); \ - struct visible *ref = create_visible(obj->owner, obj->node); \ - ref->next = scope->list; \ - scope->list = ref; \ - if (scope_flags(scope, SCOPE_FILE) && public) \ - name(scope_flags(scope, SCOPE_PUBLIC), scope->parent, obj); \ - return 0; \ - } - -REFERENCE_VISIBLE(reference_var, vars, AST_VAR); -REFERENCE_VISIBLE(reference_proc, procs, AST_PROC); -REFERENCE_VISIBLE(reference_macro, macros, AST_MACRO_CONSTRUCT); - -REFERENCE_VISIBLE(reference_enum, enums, AST_ENUM); -REFERENCE_VISIBLE(reference_trait, traits, AST_TRAIT); -REFERENCE_VISIBLE(reference_alias, aliases, AST_ALIAS); -REFERENCE_VISIBLE(reference_struct, structs, AST_STRUCT); -REFERENCE_VISIBLE(reference_builtin, builtins, AST_TYPE); -REFERENCE_VISIBLE(reference_type_construct, type_constructs, AST_TYPE_CONSTRUCT); - -/* does NOT walk the scope tree upward if it doesn't find the var in the scope - * */ -#define FIND_VISIBLE(name, list, ast_type, ast_name) \ - struct ast_node *name(struct scope *scope, struct ast_node *id) \ - { \ - assert(id->node_type == AST_ID); \ - struct visible *prev = scope->list, *cur; \ - if (prev) { \ - do { \ - cur = prev->next; \ - if (identical_ast_nodes(0, \ - prev->node->ast_name.id, \ - id)) { \ - return prev->node; \ - } \ - } while ((prev = cur)); \ - } \ - return NULL; \ - } - -FIND_VISIBLE(scope_find_enum, enums, AST_ENUM, _enum); -FIND_VISIBLE(scope_find_alias, aliases, AST_ALIAS, _alias); -FIND_VISIBLE(scope_find_builtin, builtins, AST_TYPE, _type); -FIND_VISIBLE(scope_find_struct, structs, AST_STRUCT, _struct); -FIND_VISIBLE(scope_find_trait, traits, AST_TRAIT, _trait); -/* 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_macro, macros, AST_MACRO_CONSTRUCT, _macro); -FIND_VISIBLE(scope_find_type_construct, type_constructs, AST_TYPE_CONSTRUCT, type_construct); - -struct ast_node *scope_find(struct scope *scope, struct ast_node *id) -{ - assert(id->node_type == AST_ID); - - struct ast_node *found = scope_find_var(scope, id); - if (found) - return found; - - found = scope_find_proc(scope, id); - if (found) - return found; - - found = scope_find_macro(scope, id); - if (found) - return found; - - found = scope_find_alias(scope, id); - if (found) - return found; - - found = scope_find_trait(scope, id); - if (found) - return found; + struct visible *n = create_visible(id, type); + if (!n) + return NULL; - return NULL; + n->next = scope->types; + scope->types = n; + return n; } -/* procedure adding requires a bit of tweaking, as a different number of - * arguments effectively means different functions, not just the name */ -#define ADD_VISIBLE(name, obj_type, ast_type, ast_name) \ - int name(struct scope *scope, struct ast_node *node) \ - { \ - assert(node->node_type == ast_type); \ - struct ast_node *shadow = file_scope_find_##obj_type(scope, \ - node->ast_name.id); \ - if (shadow) { \ - semantic_error(scope->fctx, node, \ - "shadowing is not allowed"); \ - semantic_info(scope->fctx, shadow, \ - "previous declaration was here"); \ - return -1; \ - } \ - int public = scope_flags(scope, SCOPE_PUBLIC); \ - struct visible *visible = create_##obj_type(scope, node); \ - if (scope_flags(scope, \ - SCOPE_FILE) && ast_flags(node, AST_FLAG_PUBLIC)) \ - return reference_##obj_type(public, scope->parent, visible); \ - return 0; \ - } - -struct visible *create_type(struct scope *scope, struct ast_node *type) +struct visible *create_var(struct scope *scope, struct ast_node *id, struct ast_node *var) { - switch (type->node_type) { - case AST_TYPE: return create_builtin(scope, type); - case AST_ALIAS: return create_alias(scope, type); - case AST_TRAIT: return create_trait(scope, type); - case AST_ENUM: return create_enum(scope, type); - case AST_STRUCT: return create_struct(scope, type); - default: - semantic_error(scope->fctx, type, "unknown type"); + struct visible *n = create_visible(id, var); + if (!n) return NULL; - } + + n->next = scope->vars; + scope->vars = n; + return n; } -int reference_type(int public, struct scope *scope, struct visible *visible) +int scope_add_var(struct scope *scope, struct ast_node *var) { - switch (visible->node->node_type) { - case AST_TYPE: return reference_builtin(public, scope, visible); - case AST_ALIAS: return reference_alias(public, scope, visible); - case AST_TRAIT: return reference_trait(public, scope, visible); - case AST_ENUM: return reference_enum(public, scope, visible); - case AST_STRUCT: return reference_struct(public, scope, visible); - default: - semantic_error(scope->fctx, visible->node, "unknown type"); - return 1; + struct ast_node *exists = file_scope_find_var(scope, AST_VAR(var).id); + if (exists) { + semantic_error(scope->fctx, var, "var redefined"); + semantic_info(scope->fctx, exists, "previously here"); + return -1; } + + struct visible *visible = create_var(scope, AST_VAR(var).id, var); + if (scope_flags(scope, SCOPE_FILE) && ast_flags(var, AST_FLAG_PUBLIC)) + return scope_add_var(scope->parent, var); + + return 0; } -int scope_add_type(struct scope *scope, struct ast_node *type) +int scope_add_type(struct scope *scope, struct ast_node *id, struct ast_node *type) { - struct ast_node *exists = file_scope_resolve_type(scope, type); - + struct ast_node *exists = file_scope_find_type(scope, id); /* redefining a type to itself is allowed, and might be necessary for * some generics operations. I'll have to double check this later, * though */ - /* e.g. struct (vec{t}) or something, all instances of this 'type' - * detected anywhere should be allowed to be 'added' */ - if (exists) - return 0; + if (exists) { + semantic_error(scope->fctx, type, "type redefined"); + semantic_info(scope->fctx, exists, "previously here"); + return -1; + } - int public = scope_flags(scope, SCOPE_PUBLIC); /* during a redefine, if it's redefined to public should it be * propagated upward? Probably not, but dunno for sure yet */ - struct visible *visible = create_type(scope, type); + struct visible *visible = create_type(scope, id, type); if (scope_flags(scope, SCOPE_FILE) && ast_flags(type, AST_FLAG_PUBLIC)) - return reference_type(public, scope->parent, visible); - - return 0; -} - -struct ast_node *scope_find_type(struct scope *scope, struct ast_node *id) -{ - assert(id->node_type == AST_ID); - - struct ast_node *found = scope_find_builtin(scope, id); - if (found) - return found; - - found = scope_find_enum(scope, id); - if (found) - return found; - - found = scope_find_struct(scope, id); - if (found) - return found; - - found = scope_find_alias(scope, id); - if (found) - return found; - - found = scope_find_trait(scope, id); - if (found) - return found; - - return NULL; -} - -ADD_VISIBLE(scope_add_var, var, AST_VAR, _var); -ADD_VISIBLE(scope_add_alias, alias, AST_ALIAS, _alias); -ADD_VISIBLE(scope_add_trait, trait, AST_TRAIT, _trait); - -static int add_implementation(struct ast_node *trait, struct ast_node *type) -{ - assert( - trait->node_type == AST_TRAIT && - type->node_type == AST_TYPE); - struct trait_implemented *by = calloc(1, sizeof(*type)); - if (!by) { - internal_error("failed allocating memory for implementation"); - return 1; - } + return scope_add_type(scope->parent, id, type); - by->type = type; - by->next = trait->_trait.impl_by; - trait->_trait.impl_by = by; return 0; } @@ -617,81 +378,17 @@ static void remove_implementation(struct ast_node *trait, } while ((prev = cur)); } -static int find_implementation(struct ast_node *trait, struct ast_node *type) -{ - if (!type) - return 0; - - assert(trait->node_type == AST_TRAIT); - if (type->_type.kind == AST_TYPE_TRAIT) - return find_implementation(trait, - type->_type.trait.actual); - - if (type->_type.kind == AST_TYPE_ALIAS) - return find_implementation(trait, type->_type.alias.actual); - - /* I'm not 100% sold on having to handle these special cases multiple - * times in different places, but I'm not sure what alternatives I have. - * For debugging purposes, maintaining as much info about the original - * code is useful, but I wonder if I can somehow maybe clone this stuff - * and keep a reference to the original or something without too much - * work? TODO */ - if (type->_type.kind == AST_TYPE_TYPEOF) - return find_implementation(trait, type->_type.typeo.actual); - - struct trait_implemented *prev = trait->_trait.impl_by, *cur; - if (prev) - do { - cur = prev->next; - if (identical_ast_nodes(0, prev->type, type)) { - return 1; - } - } while ((prev = cur)); - - return 0; -} - 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) { + (void)(flags); + (void)(scope); + (void)(arg_type); + (void)(param_type); assert(proc->node_type == AST_PROC); - /* temp */ - struct ast_node *id = proc->_proc.id; - /* this removes type info, bit of an issue */ - struct ast_node *sign = clone_ast_node(proc->_proc.sign); - struct ast_node *params = sign->_type.sign.params; - struct ast_node *ret = sign->_type.sign.ret; - - init_trait_types(params, param_type, arg_type); - init_trait_type(ret, param_type, arg_type); - - struct ast_node *impl = match_proc(scope, id, params); - if (!impl) - goto out; - - struct ast_node *impl_ret = impl->_proc.sign->_type.sign.ret; - /* note important distinction between when to use ->type and when to not - * in short: ->type can be a trait or alias, not using it is the type - * before being resolved. - */ - /* TODO: detect loops, such as when two trait return params rely on - * eachother */ - 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"); - semantic_info(scope->fctx, impl, - "found return type %s, expected %s", - irt, prt); - free(irt); - free(prt); - impl = NULL; - } - -out: - destroy_ast_node(sign); - return impl != NULL; + /** @todo implement */ + return 0; } static int implements_var(enum match_flags flags, struct scope *scope, @@ -703,7 +400,7 @@ static int implements_var(enum match_flags flags, struct scope *scope, (void)(arg_type); (void)(param_type); assert(var->node_type == AST_VAR); - /* temp */ + /** @todo implement */ return 0; } @@ -711,66 +408,53 @@ static int implements_trait(enum match_flags flags, struct scope *scope, struct ast_node *arg_type, struct ast_node *param_type) { - assert(param_type->_type.kind == AST_TYPE_TRAIT); - if (param_type->_type.trait.actual) - return implements(flags, scope, arg_type, - param_type->_type.trait.actual); - - struct ast_node *trait = param_type->_type.trait.trait; - /* if we already know we implement this trait, nothing to do */ - if (find_implementation(trait, arg_type)) - return 1; - - /* optimistically assume we implement type trait */ - /* TODO: this optimism might be questionable, as some trait might rely - * on another trait being implemented by the same type. - * The other type will return an error message, and the compilation will - * fail, but the error messages generated might be misleading. Look into - * it at some point. */ - add_implementation(trait, arg_type); - - struct ast_node *body = trait->_trait.body; + assert(AST_TYPE(param_type).kind == AST_TYPE_TRAIT); + struct ast_node *trait = AST_TRAIT_TYPE(param_type).def; + struct ast_node *body = AST_TRAIT(trait).body; /* if the body is empty, match */ struct ast_node *elem = body; - if (elem) - do { - if (elem->node_type == AST_VAR) { - if (implements_var(flags, scope, arg_type, - param_type, - elem)) - continue; + if (!elem) + return 1; - char *type = type_str(arg_type); - struct ast_node *id = elem->_proc.id; - semantic_error(scope->fctx, elem, - "%s does not have member %s", - type, id->_id.id); - free(type); - goto not_implemented; - } + /* this is somewhat ugly, hmmm */ + do { + if (elem->node_type == AST_VAR) { + if (implements_var(flags, scope, arg_type, + param_type, + elem)) + continue; - else if (elem->node_type == AST_PROC) { - if (implements_proc(flags, scope, arg_type, - param_type, - elem)) - continue; + char *type = type_str(arg_type); + struct ast_node *id = elem->_proc.id; + semantic_error(scope->fctx, elem, + "%s does not have member %s", + type, id->_id.id); + free(type); + goto not_implemented; + } - char *type = type_str(arg_type); - struct ast_node *id = elem->_proc.id; - semantic_error(scope->fctx, elem, - "%s does not implement %s", - type, id->_id.id); - free(type); - goto not_implemented; - } + else if (elem->node_type == AST_PROC) { + if (implements_proc(flags, scope, arg_type, + param_type, + elem)) + continue; - else { - semantic_error(scope->fctx, elem, - "illegal trait element"); - goto not_implemented; - } - } while ((elem = elem->next)); + char *type = type_str(arg_type); + struct ast_node *id = elem->_proc.id; + semantic_error(scope->fctx, elem, + "%s does not implement %s", + type, id->_id.id); + free(type); + goto not_implemented; + } + + else { + semantic_error(scope->fctx, elem, + "illegal trait element"); + goto not_implemented; + } + } while ((elem = elem->next)); return 1; @@ -779,29 +463,11 @@ not_implemented: return 0; } -static int implements_alias(enum match_flags flags, struct scope *scope, - struct ast_node *arg_type, - struct ast_node *param_type) -{ - while (arg_type && arg_type->_type.kind == AST_TYPE_ALIAS) - arg_type = arg_type->_type.alias.actual; - - while (param_type && param_type->_type.kind == AST_TYPE_ALIAS) - param_type = param_type->_type.alias.actual; - - return implements(flags, scope, arg_type, param_type); -} - static int implements_typeof(enum match_flags flags, struct scope *scope, struct ast_node *arg_type, struct ast_node *param_type) { - while (arg_type && arg_type->_type.kind == AST_TYPE_TYPEOF) - arg_type = arg_type->_type.typeo.actual; - - while (param_type && param_type->_type.kind == AST_TYPE_TYPEOF) - param_type = param_type->_type.typeo.actual; - + internal_error("typeof implementation unimplemented"); return implements(flags, scope, arg_type, param_type); } @@ -814,8 +480,6 @@ int implements(enum match_flags flags, struct scope *scope, if (!arg_type && !param_type) return 0; - /* only the variadic argument in parameters has no type, so any actual - * type implements it */ /* slight hack: macro arguments also don't have a type, so they will * also 'implement' type */ if (!param_type) @@ -824,127 +488,20 @@ int implements(enum match_flags flags, struct scope *scope, /* at this point, we should always have some type for the argument */ assert(arg_type); - if (param_type->_type.kind == AST_TYPE_ALIAS || - arg_type->_type.kind == AST_TYPE_ALIAS) { - assert(param_type->_type.next == NULL); - return implements_alias(flags, scope, arg_type, param_type); - } - - - if (param_type->_type.kind == AST_TYPE_TYPEOF || - arg_type->_type.kind == AST_TYPE_TYPEOF) { - /* if we're comparing procedure definitions, be more lenient */ - if (!(flags & MATCH_CALL) && - param_type->_type.kind != arg_type->_type.kind) - return 0; - + if (AST_TYPE(param_type).kind == AST_TYPE_TYPEOF) return implements_typeof(flags, scope, arg_type, param_type); - } - - /* having the arg be a trait is a bit of a special case */ - if (arg_type->_type.kind == AST_TYPE_TRAIT) { - if (arg_type->_type.trait.actual == NULL) - return types_match(arg_type, param_type); - - return implements(flags, scope, arg_type->_type.trait.actual, - param_type); - } - - /* TODO: do aliases and traits have to be converted to types? Are - * there any situations where a trait will have to be followed by - * some other type? */ - /* if the parameter type is not a trait, it's an actual type and therefore the - * argument type must be identical to it */ - if (param_type->_type.kind == AST_TYPE_TRAIT) { - if (param_type->_type.trait.actual == NULL) - return implements_trait(flags, scope, arg_type, - param_type); - - return implements(flags, scope, arg_type, - param_type->_type.trait.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)) - return 0; - - /* if both types have next elements in them, analyze them as well */ - if (arg_type->_type.next && param_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 */ - if (!arg_type->_type.next && !param_type->_type.next) - return 1; - - /* otherwise, no match */ - return 0; -} - -/* 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) -{ - /* no arguments matches to no parameters */ - if (!params && !args) - return 1; - - while (params && args) { - if (!implements(flags, scope, args->type, params->type)) - return 0; - - init_trait_type(params->type, params->type, args->type); - - params = params->next; - args = args->next; - } - - if (args) { - /* if we have arguments left over and the proc is variadic, we - * match. The last argument is replaced with the trailing list - * of arguments beyond the parameter list. */ - if (variadic) - return 1; - - return 0; - } - /* we have more parameters compared to arguments, so there's no way we - * match */ - if (params) - return 0; + if (AST_TYPE(param_type).kind == AST_TYPE_TRAIT) + return implements_trait(flags, scope, arg_type, param_type); - /* we have the same number of arguments and they all implement the - * parameters, so we match */ - return 1; + return types_match(arg_type, param_type); } -static int match_params(enum match_flags flags, struct scope *scope, - int variadic, +static int match_actual_params(enum match_flags flags, struct scope *scope, struct ast_node *args, struct ast_node *params) { - 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; - } - - int ret = match_args(flags, tmp_scope, variadic, args, params_clone); - destroy_ast_tree(params_clone); - destroy_scope(tmp_scope); - return ret; + /** @todo essentially just iterate over the parameters, right? */ + return 0; } static struct ast_node *match_resolve(struct scope *scope, @@ -984,32 +541,8 @@ static struct ast_node *match_type_construct(struct scope *scope, return match_resolve(scope, scope->type_construct_resolve, id, args); } -int scope_add_macro(struct scope *scope, struct ast_node *macro) -{ - assert(macro->node_type == AST_MACRO_CONSTRUCT); - /* TODO: separate between arrays and macros? */ - struct ast_node *id = macro->_macro.id; - struct ast_node *params = macro->_macro.params; - - int macro_exists = (match_macro(scope, id, params) != NULL); - if (macro_exists) { - semantic_error(scope->fctx, macro, "macro redefined"); - return -1; - } - - struct visible *new = create_macro(scope, macro); - if (!new) - return -1; - - int public = scope_flags(scope, SCOPE_PUBLIC); - if (scope_flags(scope, SCOPE_FILE) && ast_flags(macro, AST_FLAG_PUBLIC)) - return reference_macro(public, scope->parent, new); - - return 0; -} - -int add_proc_resolve(struct scope *scope, struct ast_node *proc) +static int add_proc_resolve(struct scope *scope, struct ast_node *proc) { if (!scope->proc_resolve) { scope->proc_resolve = calloc(1, sizeof(struct resolve)); @@ -1032,7 +565,7 @@ int add_proc_resolve(struct scope *scope, struct ast_node *proc) return add_resolve(scope, resolve, proc); } -int add_macro_resolve(struct scope *scope, struct ast_node *macro) +static int add_macro_resolve(struct scope *scope, struct ast_node *macro) { if (!scope->macro_resolve) { scope->macro_resolve = calloc(1, sizeof(struct resolve)); @@ -1040,7 +573,7 @@ int add_macro_resolve(struct scope *scope, struct ast_node *macro) struct resolve *resolve = scope->macro_resolve; while (resolve) { - if (identical_ast_nodes(0, resolve->id, macro->_macro.id)) + if (identical_ast_nodes(0, resolve->id, AST_MACRO_CONSTRUCT(macro).id)) return add_resolve(scope, resolve, macro); resolve = resolve->next; @@ -1048,13 +581,34 @@ int add_macro_resolve(struct scope *scope, struct ast_node *macro) resolve = calloc(1, sizeof(struct resolve)); resolve->root = calloc(1, sizeof(struct resolve_node)); - resolve->id = clone_ast_node(macro->_macro.id); + resolve->id = clone_ast_node(AST_MACRO_CONSTRUCT(macro).id); resolve->next = scope->macro_resolve; scope->macro_resolve = resolve; return add_resolve(scope, resolve, macro); } +int scope_add_macro(struct scope *scope, struct ast_node *macro) +{ + assert(macro->node_type == AST_MACRO_CONSTRUCT); + struct ast_node *id = AST_MACRO_CONSTRUCT(macro).id; + struct ast_node *params = AST_MACRO_CONSTRUCT(macro).params; + + struct ast_node *exists = match_macro(scope, id, params);; + if (exists) { + semantic_error(scope->fctx, macro, "macro redefined"); + semantic_info(scope->fctx, exists, "previously here"); + return -1; + } + + add_macro_resolve(scope, macro); + + if (scope_flags(scope, SCOPE_FILE) && ast_flags(macro, AST_FLAG_PUBLIC)) + return scope_add_macro(scope->parent, macro); + + return 0; +} + int add_type_construct_resolve(struct scope *scope, struct ast_node *type_construct) { if (!scope->type_construct_resolve) { @@ -1063,7 +617,7 @@ int add_type_construct_resolve(struct scope *scope, struct ast_node *type_constr struct resolve *resolve = scope->type_construct_resolve; while (resolve) { - if (identical_ast_nodes(0, resolve->id, AST_GET(type_construct, id))) + if (identical_ast_nodes(0, resolve->id, AST_TYPE_CONSTRUCT(type_construct).id)) return add_resolve(scope, resolve, type_construct); resolve = resolve->next; @@ -1071,39 +625,33 @@ int add_type_construct_resolve(struct scope *scope, struct ast_node *type_constr resolve = calloc(1, sizeof(struct resolve)); resolve->root = calloc(1, sizeof(struct resolve_node)); - resolve->id = clone_ast_node(AST_GET(type_construct, id)); + resolve->id = clone_ast_node(AST_TYPE_CONSTRUCT(type_construct).id); resolve->next = scope->type_construct_resolve; scope->type_construct_resolve = resolve; return add_resolve(scope, resolve, type_construct); } -/* would be useful with scope_remove_proc which also removed all references? */ int scope_add_proc(struct scope *scope, struct ast_node *proc) { assert(proc->node_type == AST_PROC); - struct ast_node *id = proc->_proc.id; - struct ast_node *sign = proc->_proc.sign; - struct ast_node *params = sign->_type.sign.params; + struct ast_node *id = AST_PROC(proc).id; + struct ast_node *sign = AST_PROC(proc).sign; + struct ast_node *params = AST_SIGN_TYPE(sign).params; - struct ast_node *macro_exists = match_proc(scope, id, params); + struct ast_node *exists = match_proc(scope, id, params); - if (macro_exists) { + if (exists) { semantic_error(scope->fctx, proc, "proc redefined"); - semantic_info(scope->fctx, macro_exists, "previously as macro"); + semantic_info(scope->fctx, exists, "previously here"); return -1; } - struct visible *new = create_proc(scope, proc); - if (!new) - return -1; - add_proc_resolve(scope, proc); - int public = scope_flags(scope, SCOPE_PUBLIC); if (scope_flags(scope, SCOPE_FILE) && ast_flags(proc, AST_FLAG_PUBLIC)) - return reference_proc(public, scope->parent, new); + return scope_add_proc(scope->parent, proc); return 0; } @@ -1112,86 +660,78 @@ int scope_add_type_construct(struct scope *scope, struct ast_node *type_construc { assert(type_construct->node_type == AST_TYPE_CONSTRUCT); - struct ast_node *id = AST_GET(type_construct, id); - struct ast_node *params = AST_GET(type_construct, params); + struct ast_node *id = AST_TYPE_CONSTRUCT(type_construct).id; + struct ast_node *params = AST_TYPE_CONSTRUCT(type_construct).params; - int type_construct_exists = (match_type_construct(scope, id, params) != NULL); - if (type_construct_exists) { + struct ast_node *exists = match_type_construct(scope, id, params); + if (exists) { semantic_error(scope->fctx, type_construct, "type construct redefined"); + semantic_info(scope->fctx, exists, "previously here"); return -1; } - struct visible *new = create_type_construct(scope, type_construct); - if (!new) - return -1; + add_type_construct_resolve(scope, type_construct); - int public = scope_flags(scope, SCOPE_PUBLIC); if (scope_flags(scope, SCOPE_FILE) && ast_flags(type_construct, AST_FLAG_PUBLIC)) - return reference_type_construct(public, scope->parent, new); + return scope_add_type_construct(scope->parent, type_construct); return 0; } -#define FIND_FILE_VISIBLE(name, obj_type) \ - struct ast_node *name(struct scope *scope, struct ast_node *id) \ - { \ - assert(id->node_type == AST_ID); \ - struct ast_node *found = scope_find_##obj_type(scope, id); \ - if (found) { \ - return found; \ - } \ - if (!scope_flags(scope, SCOPE_FILE)) { \ - return file_scope_find_##obj_type(scope->parent, id); \ - } \ - return NULL; \ +static struct ast_node *scope_find_visible(struct visible *v, struct ast_node *id) +{ + if (!v) + return NULL; + + while (v) { + if (identical_ast_nodes(0, v->id, id)) + return v->node; + + v = v->next; } + return NULL; +} + +struct ast_node *scope_find_type(struct scope *scope, struct ast_node *type) +{ + return scope_find_visible(scope->types, type); +} + struct ast_node *file_scope_find_type(struct scope *scope, struct ast_node *type) { assert(type->node_type == AST_ID); + if (!scope) + return NULL; + struct ast_node *found = scope_find_type(scope, type); if (found) return found; - if (!found && !scope_flags(scope, SCOPE_FILE)) + if (!scope_flags(scope, SCOPE_FILE)) return file_scope_find_type(scope->parent, type); return NULL; } -FIND_FILE_VISIBLE(file_scope_find_var, var); -FIND_FILE_VISIBLE(file_scope_find_proc, proc); -FIND_FILE_VISIBLE(file_scope_find_macro, macro); - -FIND_FILE_VISIBLE(file_scope_find_alias, alias); -FIND_FILE_VISIBLE(file_scope_find_trait, trait); - -struct ast_node *file_scope_find(struct scope *scope, struct ast_node *id) +struct ast_node *scope_find_var(struct scope *scope, struct ast_node *var) { - /* TODO: should probably check for incoming search and filter out params - * etc */ - assert(id->node_type == AST_ID); - - struct ast_node *found = file_scope_find_var(scope, id); - if (found) - return found; - - found = file_scope_find_proc(scope, id); - if (found) - return found; + return scope_find_visible(scope->vars, var); +} - found = file_scope_find_macro(scope, id); - if (found) - return found; +struct ast_node *file_scope_find_var(struct scope *scope, struct ast_node *var) +{ + assert(var->node_type == AST_ID); + if (!scope) + return NULL; - found = file_scope_find_alias(scope, id); + struct ast_node *found = scope_find_var(scope, var); if (found) return found; - found = file_scope_find_trait(scope, id); - if (found) - return found; + if (!scope_flags(scope, SCOPE_FILE)) + return file_scope_find_var(scope->parent, var); return NULL; } @@ -1204,43 +744,11 @@ struct ast_node *scope_resolve_macro(struct scope *scope, struct ast_node *macro return match_macro(scope, id, args); } -static int trait_contains_proc(enum match_flags flags, struct scope *scope, - struct ast_node *trait, - struct ast_node *id, struct ast_node *args) -{ - assert(trait->_type.kind == AST_TYPE_TRAIT); - trait = trait->_type.trait.trait; - - struct ast_node *elem = trait->_trait.body; - if (elem) - do { - if (elem->node_type != AST_PROC) - continue; - - if (!identical_ast_nodes(0, elem->_proc.id, id)) - continue; - - /* TODO: should I check for return type here as well? */ - 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_params(flags, scope, variadic, args, params)) - return 1; - - } while ((elem = elem->next)); - - return 0; -} - struct ast_node *scope_resolve_proc(struct scope *scope, struct ast_node *call) { - /* TODO: we should prefer specialized procs over generic ones. - * i.e. do_stuff(a u32){} should be preferred over do_stuff(a some_type){}, - * when u32 implements some_type. */ assert(call->node_type == AST_CALL); struct ast_node *id = call->_call.id; struct ast_node *args = call->_call.args; - return match_proc(scope, id, args); } @@ -1263,10 +771,10 @@ struct ast_node *scope_resolve_actual(struct scope *scope, assert(!ast_flags(actual, AST_FLAG_VARIADIC)); /* could also check that arguments aren't traits */ - 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)) + struct ast_node *args = AST_CALL(call).args; + struct ast_node *sign = AST_PROC(actual).sign; + struct ast_node *params = AST_SIGN_TYPE(sign).params; + if (match_actual_params(0, scope, args, params)) return actual; } while ((prev = cur)); @@ -1302,53 +810,6 @@ struct ast_node *file_scope_resolve_call(struct scope *scope, return NULL; } -struct ast_node *scope_resolve_type(struct scope *scope, struct ast_node *type) -{ - struct ast_node *id = NULL; - switch (type->node_type) { - case AST_ID: id = type; break; - case AST_TYPE: - assert(type->_type.kind == AST_TYPE_ID); - id = type->_type.id; - break; - - case AST_ALIAS: - id = type->_alias.id; - break; - - case AST_TRAIT: - id = type->_trait.id; - break; - - case AST_STRUCT: - id = type->_struct.id; - break; - - case AST_ENUM: - id = type->_enum.id; - break; - - default: - semantic_error(scope->fctx, type, "unknown type"); - return NULL; - } - - return scope_find_type(scope, id); -} - -struct ast_node *file_scope_resolve_type(struct scope *scope, - struct ast_node *type) -{ - struct ast_node *found = scope_resolve_type(scope, type); - if (found) - return found; - - if (!scope_flags(scope, SCOPE_FILE)) - return file_scope_resolve_type(scope->parent, type); - - return NULL; -} - struct ast_node *file_scope_resolve_macro(struct scope *scope, struct ast_node *macro) { struct ast_node *found = scope_resolve_macro(scope, macro); @@ -1356,17 +817,13 @@ struct ast_node *file_scope_resolve_macro(struct scope *scope, struct ast_node * return found; if (!scope_flags(scope, SCOPE_FILE)) - return file_scope_resolve_type(scope->parent, macro); + return file_scope_resolve_macro(scope->parent, macro); return NULL; } /* this might be useful somewhere else as well */ -static const char *default_types[] = {"u8", "u16", "u32", "u64", - "i8" "i16", "i32", "i64", - "usize", "isize", - "f32", "f64", - "bool", "void"}; +static const char *default_types[] = {"void", "i9", "i27"}; /* TODO: add error checking */ int scope_add_defaults(struct scope *root) @@ -1379,35 +836,16 @@ int scope_add_defaults(struct scope *root) if (!n) return -1; - struct ast_node *a = gen_type(AST_TYPE_ID, n, NULL, NULL); + struct ast_node *a = gen_type(AST_TYPE_PRIMITIVE, n, NULL, NULL); if (!a) return -1; - scope_add_type(root, a); + scope_add_type(root, n, a); } return 0; } -void scope_destroy_defaults(struct scope *scope) -{ - /* just enough data to get through to the actual default aliases */ - struct ast_node type = {0}; - type.node_type = AST_ID; - - for (size_t i = 0; - i < sizeof(default_types) / sizeof(default_types[0]); - ++i) { - type._id.id = default_types[i]; - struct ast_node *n = scope_find_alias(scope, &type); - /* something is afoot, but at least try to free the rest */ - if (!n) - continue; - - destroy_ast_node(n); - } -} - void scope_add_scope(struct scope *parent, struct scope *child) { assert(parent); @@ -1462,19 +900,6 @@ struct ast_node *scope_find_actual(struct scope *scope, struct ast_node *node) return find_actual(scope->actuals, node); } -int scope_add_scratch(struct scope *scope, struct ast_node *scratch) -{ - struct scratch *new = create_scratch(scratch); - if (!new) { - internal_error("failed allocating scratch node"); - return -1; - } - - new->next = scope->scratch; - scope->scratch = new; - return 0; -} - struct scope *create_temp_scope(struct scope *parent) { struct scope *scope = create_scope(); |
