diff options
| -rw-r--r-- | gen/gen_parser.c | 2 | ||||
| -rw-r--r-- | src/actualize.c | 186 | ||||
| -rw-r--r-- | src/ast.c | 8 | ||||
| -rw-r--r-- | src/compiler.c | 1 | ||||
| -rw-r--r-- | src/debug.c | 5 | ||||
| -rw-r--r-- | src/parser.y | 2 | ||||
| -rw-r--r-- | src/scope.c | 101 | ||||
| -rw-r--r-- | tests/callbacks.cu | 17 | ||||
| -rw-r--r-- | tests/calls.cu | 19 | ||||
| -rw-r--r-- | tests/loops.cu | 4 |
10 files changed, 280 insertions, 65 deletions
diff --git a/gen/gen_parser.c b/gen/gen_parser.c index 2182c59..df39070 100644 --- a/gen/gen_parser.c +++ b/gen/gen_parser.c @@ -3566,7 +3566,7 @@ yyreduce: #line 445 "src/parser.y" { (yyval.node) = gen_type(AST_TYPE_POINTER, NULL, NULL, NULL); - (yyval.node)->next = (yyvsp[0].node); + (yyval.node)->_type.next = (yyvsp[0].node); } #line 3572 "gen/gen_parser.c" break; diff --git a/src/actualize.c b/src/actualize.c index 8aec279..e79bfa2 100644 --- a/src/actualize.c +++ b/src/actualize.c @@ -494,6 +494,23 @@ int alias_match(struct ast_node *a, struct ast_node *b) return types_match(a_act, b_act); } +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; + + + if (b->_type.kind == AST_TYPE_TYPEOF) + b_act = b->_type.typeo.actual; + else + b_act = b; + + return types_match(a_act, b_act); +} + int types_match(struct ast_node *a, struct ast_node *b) { if (!a && !b) @@ -508,9 +525,17 @@ int types_match(struct ast_node *a, struct ast_node *b) assert(a->node_type == AST_TYPE); assert(b->node_type == AST_TYPE); + if (a->_type.kind == AST_TYPE_TYPEOF || + b->_type.kind == AST_TYPE_TYPEOF) { + if (typeof_match(a, b)) + return 1; + return 0; + } + /* handle special cases that should match even with different type kinds */ if (a->_type.kind == AST_TYPE_TEMPLATE || b->_type.kind == AST_TYPE_TEMPLATE) { + /* TODO: check template type name */ if (template_match(a, b)) return 1; return 0; @@ -676,6 +701,7 @@ static int actualize_proc_call(struct act_state *state, struct ast_node *params = sign->_type.sign.params; struct ast_node *args = call->_call.args; + /* fuck, analyze_proc gobbles up the return type typeof */ actualize_template_types(params, args); if (actualize(state, tmp, sign)) @@ -773,6 +799,7 @@ static int actualize_call(struct act_state *state, assert(call && call->node_type == AST_CALL); /* check that arguments exist, make sure they have types etc. */ + /* TODO: procedure callbacks? */ int ret = actualize(state, scope, call->_call.args); if (ret) return ret; @@ -795,7 +822,7 @@ get_callable: if (callable->node_type == AST_PROC && !ast_flags(callable->_proc.sign, AST_FLAG_ACTUAL)) { /* since we're in types only mode, this should be fine */ - if (actualize(state, scope, callable)) + if (actualize(state, callable->scope, callable)) return -1; goto get_callable; @@ -866,26 +893,27 @@ static int actualize_proc(struct act_state *state, } struct ast_node *sign = actual->_proc.sign; - if (act_flags(state, ACT_ONLY_TYPES)) { - struct scope *param_scope = create_scope(); - scope_add_scope(scope, param_scope); + struct scope *param_scope = create_scope(); + scope_add_scope(scope, param_scope); - /* procedure type is the signature */ - sign->scope = param_scope; - actual->type = sign; + /* procedure type is the signature */ + sign->scope = param_scope; + actual->type = sign; - /* TODO: if we're actualizing the types here, how can we avoid - * duplicates in the scope proc list? Or can we at all? */ + /* TODO: if we're actualizing the types here, how can we avoid + * 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. */ - return actualize(state, param_scope, sign); - } + /* 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) { @@ -1215,21 +1243,11 @@ static int actualize_type(struct act_state *state, if (actualize(state, scope, expr)) EXIT_ACT(-1); - /* I suspect there are more ways than this that we can fail, but - * this is a decent starting point */ - if (expr->type->_type.kind == AST_TYPE_TYPEOF) { - semantic_error(scope->fctx, type, - "couldn't actualize type expression\n"); - EXIT_ACT(-1); - } - + /* TODO: for now just trust that the expression is not looped or + * anything dumb like that, but I would feel better if I figure + * out some check */ assert(type->_type.next == NULL); - /* clone node to make sure we don't accidentally step on - * anyone's toes and double free anything */ - struct ast_node *clone = clone_ast_node(expr->type); - *type = *clone; - free(clone); - destroy_ast_node(expr); + type->_type.typeo.actual = expr->type; break; } @@ -1275,13 +1293,6 @@ static int actualize_type(struct act_state *state, } ast_set_flags(type, AST_FLAG_ACTUAL); - - if (type->_type.kind == AST_TYPE_TYPEOF) { - semantic_error(scope->fctx, type, - "couldn't convert expression to type"); - EXIT_ACT(-1); - } - /* generally speaking */ EXIT_ACT(0); } @@ -1520,34 +1531,87 @@ static int init_struct(struct act_state *state, struct scope *scope, return ret; } +static struct ast_node *actual_type(struct ast_node *type) +{ + assert(type->node_type == AST_TYPE); + if (type->_type.kind == AST_TYPE_ALIAS) + return actual_type(type->_type.alias.actual); + + if (type->_type.kind == AST_TYPE_TEMPLATE) + return actual_type(type->_type.template.actual); + + if (type->_type.kind == AST_TYPE_TYPEOF) + return actual_type(type->_type.typeo.actual); + + return type; +} + static int actualize_struct_init(struct act_state *state, struct scope *scope, struct ast_node *init, struct ast_node *struct_type) { - if (struct_type->_type.kind != AST_TYPE_STRUCT) { + struct ast_node *actual = actual_type(struct_type); + if (actual->_type.kind != AST_TYPE_STRUCT) { semantic_error(scope->fctx, struct_type, "type is not a structure"); return -1; } - struct ast_node *id = struct_type->_type.struc.id; + struct ast_node *id = actual->_type.struc.id; struct ast_node *exists = file_scope_resolve_type(scope, id); assert(exists); return init_struct(state, scope, exists, init); } +static int proc_pointer(struct ast_node *type) +{ + if (type->_type.kind != AST_TYPE_POINTER) + return 0; + + struct ast_node *next = type->_type.next; + if (next->_type.kind != AST_TYPE_SIGN) + return 0; + + return 1; +} + +static int proc_choice(struct ast_node *expr, struct ast_node *type) +{ + if (expr->node_type != AST_ID) + return 0; + + if (!proc_pointer(type)) + return 0; + + return 1; +} + +/* still slightly unsure about this, but hey ho */ +static int match_proc(struct act_state *state, struct scope *scope, + struct ast_node *cast) +{ + semantic_error(scope->fctx, cast, + "procedure signature casts not yet implemented"); + return -1; +} + static int actualize_cast(struct act_state *state, struct scope *scope, struct ast_node *cast) { assert(cast->node_type == AST_CAST); struct ast_node *expr = cast->_cast.expr; + struct ast_node *type = cast->_cast.type; - if (actualize(state, scope, expr)) + if (actualize(state, scope, type)) return -1; - struct ast_node *type = cast->_cast.type; - if (actualize(state, scope, type)) + if (proc_choice(expr, type)) { + cast->type = type; + return match_proc(state, scope, cast); + } + + if (actualize(state, scope, expr)) return -1; if (expr->node_type == AST_INIT) { @@ -2186,8 +2250,44 @@ void replace_type(struct ast_node *type, struct ast_node *from, void replace_param_types(struct ast_node *param, struct ast_node *param_type, struct ast_node *arg_type) { + if (arg_type->_type.kind == AST_TYPE_TEMPLATE) { + replace_param_types(param, param_type, arg_type); + return; + } + while (param) { replace_type(param->type, param_type, arg_type); param = param->next; } } + +void init_template_type(struct ast_node *type, struct ast_node *param_type, + struct ast_node *arg_type) +{ + if (!types_match(type, param_type)) + return; + + struct ast_node *template = extract_template(type); + if (template) { + /* TODO: this shares a fair bit of similarities with + * actualize_template_types, could probably create a common + * backend? */ + while (type != template) { + type = param_type->_type.next; + type = arg_type->_type.next; + assert(param_type); + assert(arg_type); + } + + template->_type.template.actual = arg_type; + } +} + +void init_template_types(struct ast_node *params, struct ast_node *param_type, + struct ast_node *arg_type) +{ + while (params) { + init_template_type(params->type, param_type, arg_type); + params = params->next; + } +} @@ -30,9 +30,15 @@ static struct src_loc loc_span(struct ast_node *left, struct ast_node *right) { struct src_loc loc = {0}; - if (!left || !right) + if (!left && !right) return loc; + if (!left && right) + return right->loc; + + if (left && !right) + return left->loc; + /* this might eventually be a good thing to do, * but right now I'm still having issues with initializing all nodes * with some kind of even slightly accurate location diff --git a/src/compiler.c b/src/compiler.c index a1a5c92..b124a6f 100644 --- a/src/compiler.c +++ b/src/compiler.c @@ -140,6 +140,7 @@ int compile(const char *file) { int ret = -1; struct scope *root = NULL; if (process_file(&root, 0, file)) { + destroy_scope(root); error("compilation of %s stopped due to errors", file); return ret; } diff --git a/src/debug.c b/src/debug.c index e42aedc..c9a1f86 100644 --- a/src/debug.c +++ b/src/debug.c @@ -206,6 +206,11 @@ static void _type_str(FILE *fp, struct ast_node *type) fprintf(fp, ")"); } } + + case AST_TYPE_TYPEOF: { + _type_str(fp, type->_type.typeo.actual); + fprintf(fp, " (typeof)"); + } } _type_str(fp, type->_type.next); diff --git a/src/parser.y b/src/parser.y index 4ad4d8b..50edf13 100644 --- a/src/parser.y +++ b/src/parser.y @@ -444,7 +444,7 @@ variadic_sign: func_sign { $$ = $1; } type: id { $$ = gen_type(AST_TYPE_ID, $1, NULL, NULL); } | "'" func_sign { $$ = gen_type(AST_TYPE_POINTER, NULL, NULL, NULL); - $$->next = $2; + $$->_type.next = $2; } | id "(" type_list ")" { $$ = gen_type(AST_TYPE_STRUCT, $1, $3, NULL); diff --git a/src/scope.c b/src/scope.c index 721a31e..eca425b 100644 --- a/src/scope.c +++ b/src/scope.c @@ -386,7 +386,26 @@ static void remove_implementation(struct ast_node *template, static int find_implementation(struct ast_node *template, struct ast_node *type) { + if (!type) + return 0; + assert(template->node_type == AST_TEMPLATE); + if (type->_type.kind == AST_TYPE_TEMPLATE) + return find_implementation(template, + type->_type.template.actual); + + if (type->_type.kind == AST_TYPE_ALIAS) + return find_implementation(template, 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(template, type->_type.typeo.actual); + struct template_implemented *prev = template->_template.impl_by, *cur; if (prev) do { @@ -448,8 +467,8 @@ static int implements_proc(struct scope *scope, struct ast_node *arg_type, struct ast_node *params = sign->_type.sign.params; struct ast_node *ret = sign->_type.sign.ret; - replace_param_types(params, param_type, arg_type); - replace_param_types(ret, param_type, arg_type); + init_template_types(params, param_type, arg_type); + init_template_type(ret, param_type, arg_type); struct ast_node *impl = match_proc(1, scope, id, params); if (!impl) @@ -552,6 +571,40 @@ not_implemented: return 0; } +static int implements_alias(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; + + if (param_type->_type.kind == AST_TYPE_ALIAS) + p_act = param_type->_type.alias.actual; + else + p_act = param_type; + + return implements(scope, a_act, p_act); +} + +static int implements_typeof(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; + + if (param_type->_type.kind == AST_TYPE_TYPEOF) + p_act = param_type->_type.typeo.actual; + else + p_act = param_type; + + return implements(scope, a_act, p_act); +} + int implements(struct scope *scope, struct ast_node *arg_type, struct ast_node *param_type) { @@ -571,18 +624,22 @@ int implements(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) { + if (param_type->_type.kind == AST_TYPE_ALIAS || + arg_type->_type.kind == AST_TYPE_ALIAS) { assert(param_type->_type.next == NULL); - return implements(scope, arg_type, - param_type->_type.alias.actual); + return implements_alias(scope, arg_type, param_type); } - if (arg_type->_type.kind == AST_TYPE_ALIAS) { - assert(arg_type->_type.next == NULL); - return implements(scope, arg_type->_type.alias.actual, - 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); } + /* 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, + 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 * some other type? */ @@ -817,16 +874,18 @@ int scope_add_existing_proc(struct scope *scope, struct visible *visible) 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; \ +#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; \ } struct ast_node *file_scope_find_type(struct scope *scope, @@ -860,6 +919,10 @@ 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; diff --git a/tests/callbacks.cu b/tests/callbacks.cu new file mode 100644 index 0000000..0249065 --- /dev/null +++ b/tests/callbacks.cu @@ -0,0 +1,17 @@ +do_stuff(proc '(u32)) +{ + proc(); +} + +other_proc(u32) +{ +} + +other_proc() +{ +} + +main() +{ + do_stuff(other_proc as '(u32)); +} diff --git a/tests/calls.cu b/tests/calls.cu new file mode 100644 index 0000000..4978649 --- /dev/null +++ b/tests/calls.cu @@ -0,0 +1,19 @@ +typedef some_type { + add(some_type, some_type => some_type); +} + +add(a u32, b u32 => u32) +{ + return a + b; +} + +/* ah fuck, analyze_proc gobbles up the typeof */ +some_proc(a some_type, b typeof a => typeof a) +{ + return add(a, b); +} + +main() +{ + some_proc(20 as u32, 40 as u32); +} diff --git a/tests/loops.cu b/tests/loops.cu new file mode 100644 index 0000000..5b6f702 --- /dev/null +++ b/tests/loops.cu @@ -0,0 +1,4 @@ +/* this should error out with some decent message about type loops or something + * */ +f(=>typeof main()){} +main(=> typeof f()){} |
