diff options
| author | Kimplul <kimi.h.kuparinen@gmail.com> | 2023-11-19 18:41:55 +0200 |
|---|---|---|
| committer | Kimplul <kimi.h.kuparinen@gmail.com> | 2023-11-19 18:41:55 +0200 |
| commit | 8f754f8b13e55e4bb92d72f105c5aaaba1754b7d (patch) | |
| tree | b70b813c091691d6645f8cb82295f41f24f3be93 /src/actualize.c | |
| parent | 5304dbcf4df1fc211b5099f4d6eb7f23dfa8c454 (diff) | |
| download | ek-8f754f8b13e55e4bb92d72f105c5aaaba1754b7d.tar.gz ek-8f754f8b13e55e4bb92d72f105c5aaaba1754b7d.zip | |
start working on instruction lowering
Diffstat (limited to 'src/actualize.c')
| -rw-r--r-- | src/actualize.c | 296 |
1 files changed, 98 insertions, 198 deletions
diff --git a/src/actualize.c b/src/actualize.c index aa4d409..ab72589 100644 --- a/src/actualize.c +++ b/src/actualize.c @@ -26,9 +26,7 @@ struct act_stack { enum act_flags { ACT_IN_LOOP = (1 << 0), ACT_IN_SWITCH = (1 << 1), - ACT_ONLY_TYPES = (1 << 2), - ACT_HAS_RETURN = (1 << 3), - ACT_REQUIRE_FULLY_QUALIFIED = (1 << 4), + ACT_HAS_RETURN = (1 << 2), }; struct act_state { @@ -42,6 +40,21 @@ struct act_state { struct act_stack *label_stack; }; +static int is_lvalue(struct ast_node *n) +{ + if (n->node_type == AST_ARR_ACCESS) + return 1; + + if (n->node_type == AST_ID) + return 1; + + if (n->node_type == AST_UNOP && AST_UNOP(n).op == AST_DEREF) + return 1; + + return 0; +} + + static void act_set_flags(struct act_state *state, enum act_flags flags) { state->flags |= flags; @@ -65,7 +78,7 @@ static int is_void(struct ast_node *type) static struct ast_node *void_type() { struct ast_node *v = gen_primitive(AST_VOID, NULL_LOC()); - ast_set_flags(v, AST_FLAG_ACTUAL); + ast_set_flags(v, AST_FLAG_INIT | AST_FLAG_ACTUAL); v->type = v; return v; } @@ -73,7 +86,7 @@ static struct ast_node *void_type() static struct ast_node *i27_type() { struct ast_node *a = gen_primitive(AST_I27, NULL_LOC()); - ast_set_flags(a, AST_FLAG_ACTUAL); + ast_set_flags(a, AST_FLAG_INIT | AST_FLAG_ACTUAL); a->type = a; return a; } @@ -189,7 +202,6 @@ static void clear_labels(struct act_state *state, struct act_stack *to) static void destroy_act_state(struct act_state *state) { - /* clears all defers */ clear_defers(state, NULL); clear_labels(state, NULL); clear_gotos(state, NULL); @@ -442,11 +454,7 @@ static int _replace_id(struct ast_node *node, void *data) clone->next = node->next; clone->scope = node->scope; - /* we know this is an ID node, so we know exactly what to free */ - free((void *)node->_id.id); - *node = *clone; - free(clone); /* a succesful replacement needs no futher replacements, I think */ return 0; @@ -504,50 +512,6 @@ static int actualize_proc_call(struct act_state *state, struct scope *scope, struct ast_node *call, struct ast_node *proc) { - /* proc has already been actualized, so we don't have to do anything */ - if (ast_flags(proc, AST_FLAG_ACTUAL)) { - /* call node type is the return type */ - struct ast_node *sign = proc->type; - assert(sign->node_type == AST_TYPE); - - call->type = AST_SIGN_TYPE(sign).ret; - return 0; - } - - if (act_flags(state, ACT_ONLY_TYPES)) { - /* TODO: better cleanup */ - /* at this point we're really only interested in the return - * type, but if it's a typeof expression we need to actualize - * the arguments as well, unfortunately */ - struct ast_node *sign = clone_ast_node(proc->_proc.sign); - if (!sign) { - internal_error("failed cloning proc signature"); - return -1; - } - - struct scope *tmp = create_scope(); - if (!tmp) { - internal_error( - "failed creating temporary signature scope"); - return -1; - } - - /* if the function is in some completely different file for - * example, we want to use that file's scope */ - scope_add_scope(proc->scope, tmp); - - struct ast_node *params = AST_SIGN_TYPE(sign).params; - struct ast_node *args = call->_call.args; - /* fuck, analyze_proc gobbles up the return type typeof */ - actualize_trait_types(params, args); - - if (actualize(state, tmp, sign)) - return -1; - - call->type = AST_SIGN_TYPE(sign).ret; - return 0; - } - /* clone procedure definition to * replace trait types with actual types and actualize it */ struct ast_node *def = clone_ast_node(proc); @@ -557,9 +521,9 @@ static int actualize_proc_call(struct act_state *state, return -1; } - struct ast_node *sign = def->_proc.sign; + struct ast_node *sign = AST_PROC(def).sign; struct ast_node *params = AST_SIGN_TYPE(sign).params; - struct ast_node *args = call->_call.args; + struct ast_node *args = AST_CALL(call).args; actualize_trait_types(params, args); if (actualize(state, def->scope, def)) @@ -569,11 +533,18 @@ static int actualize_proc_call(struct act_state *state, return 0; } -static int actualize_macro_call(struct act_state *state, - struct scope *scope, struct ast_node *call, - struct ast_node *macro) +static int actualize_macro_expand(struct act_state *state, + struct scope *scope, struct ast_node *macro_expand) { - assert(call->node_type == AST_CALL && macro->node_type == AST_MACRO_CONSTRUCT); + assert(macro_expand->node_type == AST_MACRO_EXPAND); + struct ast_node *id = AST_MACRO_EXPAND(macro_expand).id; + struct ast_node *macro = file_scope_resolve_macro(scope, id); + if (!macro) { + semantic_error(scope->fctx, macro_expand, "no such macro"); + return -1; + } + + assert(macro->node_type == AST_MACRO_CONSTRUCT); if (ast_flags(macro, AST_FLAG_VARIADIC)) { semantic_error(scope->fctx, macro, "variadic macros not yet implemented"); @@ -586,11 +557,12 @@ static int actualize_macro_call(struct act_state *state, return -1; } - body->scope = call->scope; - body->next = call->next; + /** @todo update all macro IDs to the correct scope */ + body->scope = macro_expand->scope; + body->next = macro_expand->next; struct ast_node *param = AST_MACRO_CONSTRUCT(macro).params; - struct ast_node *arg = AST_CALL(call).args; + struct ast_node *arg = AST_MACRO_EXPAND(macro_expand).args; /* TODO: actual replacements */ while (param && arg) { @@ -602,7 +574,7 @@ static int actualize_macro_call(struct act_state *state, arg->next = NULL; if (replace_id(body, param, arg)) { - semantic_error(scope->fctx, call, + semantic_error(scope->fctx, macro_expand, "failed replacing params with args"); arg->next = next_arg; return -1; @@ -612,18 +584,9 @@ static int actualize_macro_call(struct act_state *state, arg = arg->next = next_arg; } - /* variadic macros could just replace this bit with with replacing the - * rest of the list with the parameter? */ - if (param || arg) { - /* TODO: internal error more like */ - semantic_error(scope->fctx, call, "uneven number of arguments"); - return -1; - } - - *call = *body; - free(body); + *macro_expand = *body; /* actualize the new content */ - return actualize(state, scope, call); + return actualize(state, scope, macro_expand); } static int actualize_call(struct act_state *state, @@ -637,12 +600,6 @@ static int actualize_call(struct act_state *state, if (ret) return ret; - /* this label implements a kind of on demand signature actualization, - * might work? */ - /* if it does, I might flesh this out into its own procedure that - * reinserts the found proc after actualization, see if maybe that way - * we get rid of duplicates? */ -get_callable: struct ast_node *callable = file_scope_resolve_call(scope, call); if (!callable) { char *str = call_str(call); @@ -651,56 +608,41 @@ get_callable: return -1; } - if (act_flags(state, ACT_ONLY_TYPES)) { - 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, callable->scope, callable)) - return -1; - - goto get_callable; - } - } - - if (callable->node_type == AST_PROC) - return actualize_proc_call(state, scope, call, callable); - - if (callable->node_type == AST_MACRO_EXPAND) - return actualize_macro_call(state, scope, call, callable); - - /* TODO: add lambdas and arrays */ - semantic_error(scope->fctx, call, - "currently only procedures and macros are callable"); - return -1; + assert(callable->node_type == AST_PROC); + return actualize_proc_call(state, scope, call, callable); } static void warn_unused_labels(struct act_state *state, struct scope *scope) { struct act_stack *labels = state->label_stack; - if (labels) - do { - struct ast_node *label = labels->node; - if (!ast_flags(label, AST_FLAG_ACTUAL)) - semantic_warn(scope->fctx, label, - "unused label"); + if (!labels) + return; + + do { + struct ast_node *label = labels->node; + if (!ast_flags(label, AST_FLAG_ACTUAL)) + semantic_warn(scope->fctx, label, + "unused label"); - } while ((labels = labels->next)); + } while ((labels = labels->next)); } static int undefined_gotos(struct act_state *state, struct scope *scope) { int ret = 0; struct act_stack *gotos = state->goto_stack; - if (gotos) - do { - struct ast_node *got = gotos->node; - if (!ast_flags(got, AST_FLAG_ACTUAL)) { - semantic_warn(scope->fctx, got, - "undefined label"); - ret = -1; - } + if (!gotos) + return ret; - } while ((gotos = gotos->next)); + do { + struct ast_node *got = gotos->node; + if (!ast_flags(got, AST_FLAG_ACTUAL)) { + semantic_warn(scope->fctx, got, + "undefined label"); + ret = -1; + } + + } while ((gotos = gotos->next)); return ret; } @@ -714,41 +656,27 @@ static int actualize_proc(struct act_state *state, * that's fine? */ assert(proc && proc->node_type == AST_PROC); int ret = 0; - /* mark the function as initialized, assume previously allocated for us */ - struct ast_node *actual = proc; - ast_set_flags(actual, AST_FLAG_INIT); - if (!act_flags(state, ACT_ONLY_TYPES)) { - ret = scope_add_actual(scope, actual); - if (ret) { - return ret; - } - } - struct ast_node *sign = actual->_proc.sign; + struct ast_node *actual = proc; + struct ast_node *sign = AST_PROC(actual).sign; struct scope *param_scope = create_scope(); scope_add_scope(scope, param_scope); /* 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? */ /* actualize types in signature */ if (actualize(state, param_scope, sign)) return -1; - if (act_flags(state, ACT_ONLY_TYPES)) - return 0; + actual->type = sign; /* we don't have to maintain the same flags as the parent state, I don't * think */ struct act_state new_state = {0}; new_state.cur_proc = actual; - act_set_flags(&new_state, ACT_REQUIRE_FULLY_QUALIFIED); /* actualize body */ - ret |= actualize(&new_state, sign->scope, actual->_proc.body); + ret |= actualize(&new_state, sign->scope, AST_PROC(actual).body); if (!act_flags(&new_state, ACT_HAS_RETURN)) { if (!is_void(AST_SIGN_TYPE(sign).ret)) { semantic_error(scope->fctx, actual, @@ -772,8 +700,8 @@ static int actualize_proc(struct act_state *state, printf("Actualized func:\n"); dump_ast(0, actual); + scope_add_actual(scope, actual); - ast_set_flags(actual, AST_FLAG_ACTUAL); /* we have successfully actualized the procedure */ return 0; } @@ -964,18 +892,8 @@ static int actualize_type(struct act_state *state, * etc. */ assert(type->node_type == AST_TYPE); ENTER_ACT(); - act_set_flags(state, ACT_ONLY_TYPES); - if (ast_flags(type, - AST_FLAG_INIT) && !ast_flags(type, AST_FLAG_ACTUAL)) { - semantic_error(scope->fctx, type, "detected type loop"); - EXIT_ACT(-1); - } - - /* mark type initialize. We can detect type loops if this is set but - * AST_FLAG_ACTUAL isn't. */ - ast_set_flags(type, AST_FLAG_INIT); - if (actualize(state, scope, type->_type.next)) { + if (actualize(state, scope, AST_TYPE(type).next)) { EXIT_ACT(-1); } @@ -1000,11 +918,8 @@ static int actualize_type(struct act_state *state, break; } - /* actualize whatever type we have on demand, either alias or - * trait */ - if (!ast_flags(exists, AST_FLAG_ACTUAL)) - if (actualize(state, exists->scope, exists)) - EXIT_ACT(-1); + if (actualize(state, exists->scope, exists)) + EXIT_ACT(-1); assert(AST_TYPE(type).next == NULL); if (exists->node_type == AST_ALIAS) { @@ -1047,8 +962,9 @@ static int actualize_type(struct act_state *state, } case AST_TYPE_POINTER: - assert(type->_type.next); - type->loc = type->_type.next->loc; + assert(AST_PTR_TYPE(type).base); + if (actualize(state, scope, AST_PTR_TYPE(type).base)) + EXIT_ACT(-1); break; case AST_TYPE_SIGN: { @@ -1082,7 +998,6 @@ static int actualize_type(struct act_state *state, EXIT_ACT(-1); } - ast_set_flags(type, AST_FLAG_ACTUAL); /* generally speaking */ EXIT_ACT(0); } @@ -1136,13 +1051,11 @@ static int pointer_conversion(struct ast_node *a, struct ast_node *b) assert(a->node_type == AST_TYPE); assert(b->node_type == AST_TYPE); if (pointer_type(a)) { - if (b->_type.kind != AST_TYPE_ID) + if (AST_TYPE(b).kind != AST_TYPE_PRIMITIVE) return 0; - struct ast_node *id = AST_ID_TYPE(b).id; - /* there's gotta be a better way */ - if (strcmp(id->_id.id, "usize") == 0) - return 1; + /* for now */ + return AST_PRIMITIVE_TYPE(b).type == AST_I27; } return 0; @@ -1387,7 +1300,7 @@ static int actualize_cast(struct act_state *state, return 0; } - /* TODO: arrays? lambdas? */ + /* TODO: arrays? */ char *left_type = type_str(expr); char *right_type = type_str(type); @@ -1420,12 +1333,6 @@ static int actualize_alias(struct act_state *state, struct scope *scope, * aliases might be a bit cumbersome to work with. Still, this works * well enough I suppose. */ assert(alias->node_type == AST_ALIAS); - if (ast_flags(alias, AST_FLAG_INIT)) { - semantic_error(scope->fctx, alias, "alias loop"); - return -1; - } - - ast_set_flags(alias, AST_FLAG_INIT); if (actualize(state, scope, AST_ALIAS(alias).type)) { /* usually we don't want to output errors upon errors, but this * is likely a useful message as it might show where a loop is @@ -1449,7 +1356,6 @@ static int actualize_trait(struct act_state *state, struct scope *scope, /* will still have to figure out how I want to inform the actualizer * that a some_type in a some_type just means whichever type we're * testing for */ - act_set_flags(state, ACT_ONLY_TYPES); if (actualize(state, trait->scope, trait->_trait.body)) { semantic_error(scope->fctx, trait, "failed actualizing trait"); @@ -1641,14 +1547,14 @@ static int actualize_unop(struct act_state *state, switch (node->_unop.op) { case AST_DEREF: { struct ast_node *type = expr->type; - if (type->_type.kind != AST_TYPE_POINTER) { + if (AST_TYPE(type).kind != AST_TYPE_POINTER) { /* TODO: or array */ semantic_error(scope->fctx, expr, "trying to dereference something that's not a pointer"); return -1; } - node->type = type->_type.next; + node->type = AST_PTR_TYPE(type).base; assert(node->type); break; } @@ -1671,13 +1577,7 @@ static int actualize_as(struct act_state *state, struct scope *scope, struct ast_node *as) { assert(as->node_type == AST_AS); - if (!act_flags(state, ACT_ONLY_TYPES)) { - semantic_error(scope->fctx, as, - "as used outside of type context"); - return -1; - } - - struct ast_node *type = as->_as.type; + struct ast_node *type = AST_AS(as).type; if (actualize(state, scope, type)) return -1; @@ -1689,7 +1589,6 @@ static int actualize_struct(struct act_state *state, struct scope *scope, struct ast_node *node) { assert(node->node_type == AST_STRUCT); - ast_set_flags(node, AST_FLAG_INIT); struct ast_node *generics = node->_struct.generics; struct scope *struct_scope = create_scope(); if (!struct_scope) @@ -1749,7 +1648,7 @@ static int actualize_dot(struct act_state *state, if (actualize(state, scope, expr)) return -1; - struct ast_node *id = node->_dot.id; + struct ast_node *id = AST_DOT(node).id; if (!has_members(expr->type)) { char *tstr = type_str(expr->type); semantic_error(scope->fctx, node, "%s does not have member %s", @@ -1759,7 +1658,6 @@ static int actualize_dot(struct act_state *state, } /* TODO: actually look through stuff */ - /* TODO: figure out how to match traitd structures to actual */ return 0; } @@ -1771,7 +1669,6 @@ static int actualize_init(struct act_state *state, /* TODO: how to make sure all members are initialized? */ int ret = 0; enum act_flags old_flags = state->flags; - act_set_flags(state, ACT_ONLY_TYPES); ret = actualize(state, scope, node->_init.body); state->flags = old_flags; return ret; @@ -1804,6 +1701,12 @@ static int actualize_assign(struct act_state *state, struct scope *scope, return -1; } + /** @todo rvalue vs lvalue? */ + if (!is_lvalue(to)) { + semantic_error(scope->fctx, node, "rvalue used where lvalue required"); + return -1; + } + node->type = to->type; return 0; } @@ -1898,6 +1801,14 @@ static int actualize(struct act_state *state, struct scope *scope, if (!node->scope) node->scope = scope; + if (ast_flags(node, AST_FLAG_INIT) && !ast_flags(node, AST_FLAG_ACTUAL)) { + semantic_error(scope->fctx, node, "detected dependency loop"); + return -1; + } + + /* actualization started */ + ast_set_flags(node, AST_FLAG_INIT); + switch (node->node_type) { case AST_PROC: /* procedure definitions only allowed at file scope */ @@ -1908,6 +1819,7 @@ static int actualize(struct act_state *state, struct scope *scope, case AST_TRAIT: ret |= actualize_trait(state, scope, node); break; case AST_ALIAS: ret |= actualize_alias(state, scope, node); break; case AST_MACRO_CONSTRUCT: ret |= actualize_macro_construct(state, scope, node); break; + case AST_MACRO_EXPAND: ret |= actualize_macro_expand(state, scope, node); break; case AST_CALL: ret |= actualize_call(state, scope, node); break; case AST_BINOP: ret |= actualize_binop(state, scope, node); break; case AST_BLOCK: ret |= actualize_block(state, scope, node); break; @@ -1937,6 +1849,9 @@ static int actualize(struct act_state *state, struct scope *scope, break; } + /* actualization done for this node */ + ast_set_flags(node, AST_FLAG_ACTUAL); + /* should it automatically handle next nodes or should it be saved to * actualize_block or something? */ ret |= actualize(state, scope, node->next); @@ -1957,7 +1872,7 @@ int actualize_main(struct scope *root) error("no main"); return -1; } - int ret = actualize(&state, root, clone_ast_node(main)); + int ret = actualize(&state, root, main); destroy_act_state(&state); return ret; } @@ -1972,12 +1887,10 @@ void replace_type(struct ast_node *type, struct ast_node *from, assert(from->node_type == AST_TYPE); assert(to->node_type == AST_TYPE); - /* TODO: cleanup? */ if (types_match(type, from)) { assert(type->_type.next == NULL); struct ast_node *clone = clone_ast_node(to); *type = *clone; - free(clone); return; } @@ -1997,16 +1910,3 @@ void replace_param_types(struct ast_node *param, struct ast_node *param_type, param = param->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; -} |
