/* SPDX-License-Identifier: copyleft-next-0.3.1 */ /* Copyright 2024 Kim Kuparinen < kimi.h.kuparinen@gmail.com > */ #include #include #include #include #include #include #include #include #include enum state_flags { STATE_PROC_REQ_FRAME = (1 << 0) }; struct state { enum state_flags flags; }; static int analyze(struct state *state, struct scope *scope, struct ast *node); static int analyze_known_block(struct state *state, struct scope *scope, struct ast *node); static int analyze_list(struct state *state, struct scope *scope, struct ast *nodes); static int analyze_type(struct state *state, struct scope *scope, struct type *type); static int analyze_type_list(struct state *state, struct scope *scope, struct type *types); static int analyze_proc(struct state *state, struct scope *scope, struct ast *node) { (void)state; (void)scope; struct scope *proc_scope = create_scope(); if (!proc_scope) { internal_error("failed allocating proc scope"); return -1; } proc_scope->flags |= SCOPE_PROC; scope_add_scope(scope, proc_scope); struct state proc_state = {}; if (analyze_list(&proc_state, proc_scope, proc_params(node))) return -1; struct type *proc_type = tgen_func_ptr(NULL, node->loc); if (!proc_type) { internal_error("failed allocating proc type"); return -1; } size_t group = 0; foreach_node(param, proc_params(node)) { if (param->ol == NULL && param->or == NULL) group++; /* no opt group */ if (param->ol == NULL && param->or) group++; /* start of new group */ /* otherwise same or ending group, don't increment */ param->t->group = group; type_append(&proc_type->t0, clone_type(param->t)); } node->t = proc_type; if (!proc_body(node)) return 0; if (analyze_known_block(&proc_state, proc_scope, proc_body(node))) return -1; if (proc_state.flags & STATE_PROC_REQ_FRAME) ast_set_flags(node, AST_REQ_FRAME); return 0; } static int analyze_unop(struct state *state, struct scope *scope, struct ast *node) { /** @todo check expr is some primitive type */ struct ast *expr = unop_expr(node); if (analyze(state, scope, expr)) return -1; node->t = expr->t; return 0; } static int analyze_binop(struct state *state, struct scope *scope, struct ast *node) { struct ast *lhs = binop_left(node); struct ast *rhs = binop_right(node); if (analyze(state, scope, lhs)) return -1; if (analyze(state, scope, rhs)) return -1; if (!types_match(lhs->t, rhs->t)) { type_mismatch(scope, node, lhs->t, rhs->t); return -1; } /** @todo check type is some primitive */ node->t = lhs->t; return 0; } static int analyze_comparison(struct state *state, struct scope *scope, struct ast *node) { struct ast *lhs = comparison_left(node); struct ast *rhs = comparison_right(node); if (analyze(state, scope, lhs)) return -1; if (analyze(state, scope, rhs)) return -1; if (!types_match(lhs->t, rhs->t)) { type_mismatch(scope, node, lhs->t, rhs->t); return -1; } /** @todo check type is some primitive */ char *tf = strdup("bool"); if (!tf) { internal_error("failed allocating comparison bool str"); return -1; } node->t = tgen_id(tf, node->loc); if (!node->t) { internal_error("failed allocating comparison bool type"); free(tf); return -1; } return 0; } static int analyze_known_block(struct state *state, struct scope *scope, struct ast *node) { assert(node && node->k == AST_BLOCK); node->scope = scope; node->t = tgen_void(node->loc); if (analyze_list(state, scope, block_body(node))) return -1; return 0; } static int analyze_block(struct state *state, struct scope *scope, struct ast *node) { struct scope *block_scope = create_scope(); if (!block_scope) { internal_error("failed to allocate block scope"); return -1; } scope_add_scope(scope, block_scope); return analyze_known_block(state, scope, node); } static int analyze_var(struct state *state, struct scope *scope, struct ast *node) { if (analyze_type(state, scope, var_type(node))) return -1; node->t = var_type(node); return scope_add_symbol(scope, node); } static int analyze_let(struct state *state, struct scope *scope, struct ast *node) { if (analyze(state, scope, let_var(node))) return -1; if (analyze(state, scope, let_expr(node))) return -1; struct type *l = (let_var(node))->t; struct type *r = (let_expr(node))->t; if (!types_match(l, r)) { type_mismatch(scope, node, l, r); return -1; } node->t = l; return 0; } static int analyze_init(struct state *state, struct scope *scope, struct ast *node) { if (analyze_type_list(state, scope, init_args(node))) return -1; if (analyze(state, scope, init_body(node))) return -1; internal_error("init unimplemented"); return -1; } static int deduce_closure_types(struct scope *scope, struct ast *node, struct type *type) { /* a bit of checking duplication here, hmm */ if (node->k != AST_CLOSURE) return 0; if (type->k != TYPE_CLOSURE && type->k != TYPE_PURE_CLOSURE) { semantic_error(scope, node, "surprise closure"); return -1; } struct ast *param = closure_bindings(node); struct type *t = tclosure_args(type); if (ast_list_len(param) != type_list_len(t)) { semantic_error(scope, node, "expected %zu closure parameters, got %zu", type_list_len(t), ast_list_len(param)); return -1; } for (; param && t; param = param->n, t = t->n) { if (var_type(param)) continue; var_type(param) = t; } return 0; } static int analyze_call(struct state *state, struct scope *scope, struct ast *node) { struct ast *expr = call_expr(node); if (analyze(state, scope, expr)) return -1; if (!is_callable(expr->t)) { semantic_error(scope, node, "expected callable expression"); return -1; } struct type *types = callable_types(expr->t); struct ast *args = call_args(node); size_t expected = type_list_len(types); size_t got = ast_list_len(args); if (expected != got) { semantic_error(scope, node, "expected %d params, got %d", expected, got); return -1; } struct type *type = types; struct ast *arg = args; for (; arg && type; type = type->n, arg = arg->n) { if (deduce_closure_types(scope, arg, type)) return -1; if (analyze(state, scope, arg)) return -1; if (arg->k == AST_CLOSURE) state->flags |= STATE_PROC_REQ_FRAME; if (!types_match(type, arg->t)) { type_mismatch(scope, arg, type, arg->t); return -1; } /* clone group info */ arg->t->group = type->group; } node->t = tgen_void(node->loc); if (!node->t) { internal_error("failed allocating void type for call"); return -1; } return 0; } static int analyze_ref(struct state *state, struct scope *scope, struct ast *node) { struct ast *expr = ref_base(node); if (analyze(state, scope, expr)) return -1; if (!is_lvalue(expr)) { semantic_error(node->scope, node, "trying to reference rvalue"); return -1; } struct type *ref = tgen_ref(expr->t, node->loc); if (!ref) { internal_error("failed allocating ref type"); return -1; } node->t = ref; return 0; } static int analyze_deref(struct state *state, struct scope *scope, struct ast *node) { struct ast *expr = deref_base(node); if (analyze(state, scope, expr)) return -1; if (expr->t->k == TYPE_PTR) { semantic_error(node->scope, node, "deref of raw ptr not allowed"); semantic_info(node->scope, node, "use a nil check to convert to ref"); return -1; } if (expr->t->k != TYPE_REF) { semantic_error(node->scope, node, "deref of something not a reference"); return -1; } node->t = tref_base(expr->t); return 0; } static int analyze_id(struct state *state, struct scope *scope, struct ast *node) { struct ast *found = file_scope_find_symbol(scope, id_str(node)); if (!found) { semantic_error(scope, node, "no symbol named \"%s\"", id_str(node)); return -1; } /* kind of hacky, functions are given their type before they've been * analyzed fully, this enables recursion */ if (!found->t && !ast_flags(found, AST_FLAG_ANALYZED)) { assert(found->k == AST_PROC_DEF); /* a proc def will use its own state and scope, but pass these * on in case the analysis wants to print errors or something */ if (analyze(state, scope, found)) return -1; } node->t = found->t; return 0; } static int analyze_closure(struct state *state, struct scope *scope, struct ast *node) { struct scope *closure_scope = create_scope(); if (!closure_scope) { internal_error("failed allocating closure scope"); return -1; } node->scope = closure_scope; closure_scope->flags |= SCOPE_PROC; scope_add_scope(scope, closure_scope); if (analyze_list(state, closure_scope, closure_bindings(node))) return -1; if (analyze_known_block(state, closure_scope, closure_body(node))) return -1; struct type *callable = NULL; if (ast_flags(node, AST_FLAG_NOMOVES)) callable = tgen_pure_closure(NULL, node->loc); else callable = tgen_closure(NULL, node->loc); if (!callable) { internal_error("failed allocating closure type"); return -1; } foreach_node(binding, closure_bindings(node)) { type_append(&callable->t0, clone_type(binding->t)); } node->t = callable; return 0; } static int analyze_int(struct state *state, struct scope *scope, struct ast *node) { (void)state; (void)scope; /* start with largest possible and work down */ node->t = tgen_type(TYPE_I64, NULL, NULL, node->loc); if (!node->t) { internal_error("failed allocating constant int type"); return -1; } return 0; } static int analyze_str(struct state *state, struct scope *scope, struct ast *node) { (void)state; (void)scope; /** @todo do this properly, very hacky, bad bad bad */ char *i = strdup("char"); if (!i) { internal_error("failed allocating constant char type string"); return -1; } struct type *ch = tgen_id(i, node->loc); if (!ch) { internal_error("failed allocating constant char type"); free(i); return -1; } struct type *str = tgen_ptr(ch, node->loc); if (!str) { internal_error("failed allocating constant str type"); return -1; } node->t = str; return 0; } static int analyze_template(struct state *state, struct scope *scope, struct ast *node) { struct scope *template_scope = create_scope(); scope_add_scope(scope, template_scope); /* enough for vec example since traits are as of yet unimplemented */ node->t = tgen_void(node->loc); return 0; } static int analyze_instance(struct state *state, struct scope *scope, struct ast *node) { (void)state; struct ast *id = instance_templ(node); struct ast *template = file_scope_find_template(scope, id_str(id)); if (!template) { semantic_error(scope, node, "no such template: %s\n", id_str(id)); return -1; } /** @todo check that type implements trait */ struct ast *params = template_type_params(template); struct type *args = instance_type_args(node); if (ast_list_len(params) != type_list_len(args)) { semantic_error(scope, node, "expected %zu types, got %zu\n", ast_list_len(params), type_list_len(args)); return -1; } /* make a working copy that we modify */ struct ast *copy = clone_ast(template); if (!copy) { internal_error("failed copying template body"); return -1; } if (rewrite_holes(copy, instance_id(node))) return -1; /* replace type args */ struct ast *p = params; struct type *a = args; for (; p && a; p = p->n, a = a->n) { if (rewrite_types(copy, var_id(p), tid_str(a))) return -1; } printf("// --- instance %s ---\n", instance_id(node)); ast_dump(0, copy); /* analyze newly initialized things (might not work for supertemplates?) */ if (analyze_root(scope, template_body(copy))) return -1; node->t = tgen_void(node->loc); return 0; } static int analyze_struct(struct state *state, struct scope *scope, struct ast *node) { struct scope *struct_scope = create_scope(); scope_add_scope(scope, struct_scope); node->scope = struct_scope; if (analyze_list(state, struct_scope, struct_body(node))) return -1; node->t = tgen_void(node->loc); return 0; } static int analyze_construct(struct state *state, struct scope *scope, struct ast *node) { struct ast *def = file_scope_find_type(scope, construct_id(node)); if (!def) { semantic_error(scope, node, "no such type"); return -1; } if (def->k != AST_STRUCT_DEF) { semantic_error(scope, node, "type is not a struct"); return -1; } struct ast *params = struct_body(def); struct ast *args = construct_members(node); if (ast_list_len(params) != ast_list_len(args)) { semantic_error(scope, node, "expected %zu args, got %zu\n", ast_list_len(params), ast_list_len(args)); return -1; } foreach_node(arg, args) { struct ast *exists = scope_find_symbol(def->scope, construction_id(arg)); if (!exists) { semantic_error(scope, arg, "no member %s in %s\n", construction_id(arg), construct_id(node)); return -1; } struct ast *expr = construction_expr(arg); if (analyze(state, scope, expr)) return -1; if (!types_match(exists->t, expr->t)) { type_mismatch(scope, arg, exists->t, expr->t); return -1; } /** @todo check for duplicates, preferably not in O(n^2) or with * a memory allocation? */ } node->t = tgen_id(strdup(construct_id(node)), node->loc); return 0; } static int analyze_if(struct state *state, struct scope *scope, struct ast *node) { if (analyze(state, scope, if_cond(node))) return -1; if (analyze(state, scope, if_body(node))) return -1; if (analyze(state, scope, if_else(node))) return -1; node->t = tgen_void(node->loc); if (!node->t) { internal_error("failed allocating 'if' void type"); return -1; } return 0; } static int analyze_nil(struct state *state, struct scope *scope, struct ast *node) { (void)state; (void)scope; node->t = tgen_nil(node->loc); return 0; } static bool castable_type(struct type *type) { switch (type->k) { case TYPE_PTR: case TYPE_FUNC_PTR: case TYPE_I8: case TYPE_I16: case TYPE_I32: case TYPE_I64: case TYPE_U8: case TYPE_U16: case TYPE_U32: case TYPE_U64: return true; default: return false; } return false; } static int analyze_as(struct state *state, struct scope *scope, struct ast *node) { if (analyze(state, scope, as_expr(node))) return -1; if (analyze_type(state, scope, as_type(node))) return -1; /* for now, any 'register' size type can be cast to any other. Might * restrict this, like signed to unsigned might be weird if its negative * etc and should be done via a helper function */ struct type *expr_type = (as_expr(node))->t; struct type *type = as_type(node); if (!castable_type(expr_type)) { char *s = type_str(expr_type); semantic_error(scope, as_expr(node), "can't cast from %s\n", s); free(s); return -1; } if (!castable_type(type)) { char *s = type_str(type); type_error(scope, type, "can't cast to %s\n", s); free(s); return -1; } node->t = type; return 0; } static int analyze_assign(struct state *state, struct scope *scope, struct ast *node) { struct ast *expr = assign_expr(node); if (analyze(state, scope, expr)) return -1; struct ast *target = assign_target(node); if (analyze(state, scope, target)) return -1; if (!types_match(expr->t, target->t)) { type_mismatch(scope, node, target->t, expr->t); return -1; } if (!is_lvalue(target)) { semantic_error(scope, target, "not an lvalue"); return -1; } node->t = target->t; return 0; } static int analyze_dot(struct state *state, struct scope *scope, struct ast *node) { struct ast *expr = dot_expr(node); if (analyze(state, scope, expr)) return -1; struct type *type = expr->t; if (type->k != TYPE_ID) { char *s = type_str(type); semantic_error(scope, node, "type %s does not have any members", s); free(s); return -1; } struct ast *def = file_scope_find_type(scope, tid_str(type)); if (!def) { semantic_error(scope, node, "no such type: %s", tid_str(type)); return -1; } if (def->k != AST_STRUCT_DEF) { semantic_error(scope, node, "not a struct type: %s", tid_str(type)); return -1; } struct ast *member = scope_find_symbol(def->scope, dot_id(node)); if (!member) { semantic_error(scope, node, "no such member: %s", dot_id(node)); return -1; } node->t = member->t; return 0; } static int analyze_sizeof(struct state *state, struct scope *scope, struct ast *node) { if (analyze_type(state, scope, sizeof_type(node))) return -1; /* hmm, no built-in size_t */ node->t = tgen_type(TYPE_U64, NULL, NULL, node->loc); return 0; } static int analyze_arr(struct state *state, struct scope *scope, struct ast *node) { struct ast *base = arr_base(node); if (analyze(state, scope, base)) return -1; if (base->t->k != TYPE_ARR) { char *s = type_str(base->t); semantic_error(scope, base, "expected array type, got %s", s); free(s); return -1; } struct ast *idx = arr_idx(node); if (analyze(state, scope, idx)) return -1; if (!is_int_type(idx->t->k)) { char *s = type_str(idx->t); semantic_error(scope, idx, "expected int type, got %s", s); free(s); return -1; } node->t = tarr_base(base->t); return 0; } static int analyze_nil_check(struct state *state, struct scope *scope, struct ast *node) { struct ast *expr = nil_check_expr(node); if (analyze(state, scope, expr)) return -1; if (!is_ptr_type(expr->t->k)) { char *s = type_str(expr->t); semantic_error(scope, expr, "expected ptr type, got %s", s); free(s); return -1; } if (analyze(state, scope, nil_check_body(node))) return -1; struct ast *var = nil_check_ref(node); struct type *base = tptr_base(expr->t); struct type *ref = tgen_ref(clone_type(base), node->loc); if (!var_type(var)) var_type(var) = ref; if (analyze(state, scope, var)) return -1; if (!types_match(ref, var->t)) { type_mismatch(scope, var, ref, var->t); return -1; } node->t = tgen_void(node->loc); return 0; } static int analyze_forget(struct state *state, struct scope *scope, struct ast *node) { struct ast *def = file_scope_find_symbol(scope, forget_id(node)); if (!def) { semantic_error(scope, node, "no such symbol"); return -1; } node->t = tgen_void(node->loc); return 0; } static int analyze(struct state *state, struct scope *scope, struct ast *node) { if (!node) return 0; if (ast_flags(node, AST_FLAG_ANALYZED)) { assert(node->t); return 0; } if (ast_flags(node, AST_FLAG_PREANALYZIS)) { semantic_error(scope, node, "semantic loop detected"); return -1; } ast_set_flags(node, AST_FLAG_PREANALYZIS); if (!node->scope) node->scope = scope; int ret = 0; if (is_binop(node)) { ret = analyze_binop(state, scope, node); goto out; } if (is_comparison(node)) { ret = analyze_comparison(state, scope, node); goto out; } if (is_unop(node)) { ret = analyze_unop(state, scope, node); goto out; } switch (node->k) { case AST_CLOSURE: ret = analyze_closure(state, scope, node); break; case AST_PROC_DEF: ret = analyze_proc(state, scope, node); break; case AST_VAR_DEF: ret = analyze_var(state, scope, node); break; case AST_BLOCK: ret = analyze_block(state, scope, node); break; case AST_DEREF: ret = analyze_deref(state, scope, node); break; case AST_INIT: ret = analyze_init(state, scope, node); break; case AST_CALL: ret = analyze_call(state, scope, node); break; case AST_REF: ret = analyze_ref(state, scope, node); break; case AST_LET: ret = analyze_let(state, scope, node); break; case AST_ID: ret = analyze_id(state, scope, node); break; case AST_IF: ret = analyze_if(state, scope, node); break; case AST_NIL_CHECK: ret = analyze_nil_check(state, scope, node); break; case AST_CONST_INT: ret = analyze_int(state, scope, node); break; case AST_CONST_STR: ret = analyze_str(state, scope, node); break; case AST_INSTANCE: ret = analyze_instance(state, scope, node); break; case AST_TEMPLATE: ret = analyze_template(state, scope, node); break; case AST_STRUCT_DEF: ret = analyze_struct(state, scope, node); break; case AST_CONSTRUCT: ret = analyze_construct(state, scope, node); break; case AST_SIZEOF: ret = analyze_sizeof(state, scope, node); break; case AST_NIL: ret = analyze_nil(state, scope, node); break; case AST_AS: ret = analyze_as(state, scope, node); break; case AST_ASSIGN: ret = analyze_assign(state, scope, node); break; case AST_ARR: ret = analyze_arr(state, scope, node); break; case AST_DOT: ret = analyze_dot(state, scope, node); break; case AST_FORGET: ret = analyze_forget(state, scope, node); break; case AST_TRAIT_DEF: node->t = tgen_void(node->loc); ret = 0; break; case AST_EMPTY: node->t = tgen_void(node->loc); ret = 0; break; case AST_IMPORT: node->t = tgen_void(node->loc); ret = 0; break; default: internal_error("missing ast analysis for %s", ast_str(node->k)); return -1; } out: if (ret) return ret; assert(node->t); assert(node->scope); ast_set_flags(node, AST_FLAG_ANALYZED); return 0; } static int analyze_list(struct state *state, struct scope *scope, struct ast *nodes) { foreach_node(node, nodes) { if (analyze(state, scope, node)) return -1; } return 0; } static int analyze_type(struct state *state, struct scope *scope, struct type *type) { if (type->t0 && analyze_type(state, scope, type->t0)) return -1; /* temporary */ if (!type->id) return 0; char *id = type->id; /* handle primitives */ if (strcmp(id, "i8") == 0) { type->k = TYPE_I8; return 0; } if (strcmp(id, "u8") == 0) { type->k = TYPE_U8; return 0; } if (strcmp(id, "i16") == 0) { type->k = TYPE_I16; return 0; } if (strcmp(id, "u16") == 0) { type->k = TYPE_U16; return 0; } if (strcmp(id, "i32") == 0) { type->k = TYPE_I32; return 0; } if (strcmp(id, "u32") == 0) { type->k = TYPE_U32; return 0; } if (strcmp(id, "i64") == 0) { type->k = TYPE_I64; return 0; } if (strcmp(id, "u64") == 0) { type->k = TYPE_U64; return 0; } /* check for user-defined types */ struct ast *def = file_scope_find_type(scope, id); if (!def) { type_error(scope, type, "no such type: %s", id); return -1; } /* don't change type->k since we might want to check up on this again * later */ return 0; } static int analyze_type_list(struct state *state, struct scope *scope, struct type *types) { foreach_type(type, types) { if (analyze_type(state, scope, type)) return -1; } return 0; } static int copy_scope(bool reexport, struct scope *to, struct scope *from) { foreach(visible, symbol, &from->symbols) { struct ast *def = symbol->data; if (!reexport && def->scope != from) continue; if (!ast_flags(def, AST_FLAG_PUBLIC)) continue; if (scope_add_symbol(to, def)) return -1; } foreach(visible, type, &from->types) { struct ast *def = type->data; if (!reexport && def->scope != from) continue; if (!ast_flags(def, AST_FLAG_PUBLIC)) continue; if (scope_add_type(to, def)) return -1; } foreach(visible, trait, &from->traits) { struct ast *def = trait->data; if (!reexport && def->scope != from) continue; if (!ast_flags(def, AST_FLAG_PUBLIC)) continue; if (scope_add_trait(to, def)) return -1; } foreach(visible, template, &from->templates) { struct ast *def = template->data; if (!reexport && def->scope != from) continue; if (!ast_flags(def, AST_FLAG_PUBLIC)) continue; if (scope_add_template(to, def)) return -1; } return 0; } /* allowed to be noisy */ static int try_import_file(struct scope *scope, struct ast *node) { const char *file = import_file(node); struct scope *child = compile_file(file); if (!child) { semantic_info(scope, node, "imported here"); return -1; } if (copy_scope(ast_flags(node, AST_FLAG_PUBLIC), scope, child)) { semantic_info(scope, node, "imported here"); return -1; } return 0; } /* should be quiet upon failure */ static int try_import_mod(struct scope *scope, struct ast *node) { /** @todo cleanup */ void *mod = dlopen(import_file(node), RTLD_LAZY); if (!mod) return -1; fwd_open_t fwdopen = dlsym(mod, "fwdopen"); if (!fwdopen) { dlclose(mod); return -1; } mod_vec_append(&scope->mods, mod); return fwdopen((void *)scope); } static struct type *fwd_type_kind(fwd_type_t type) { switch (type) { case FWD_VOID: return tgen_void(NULL_LOC()); case FWD_I64: return tgen_type(TYPE_I64, NULL, NULL, NULL_LOC()); case FWD_U64: return tgen_type(TYPE_U64, NULL, NULL, NULL_LOC()); case FWD_PTR: { /* hack? */ return tgen_nil(NULL_LOC()); } default: break; } abort(); return NULL; } int fwd_register(struct fwd_state *state, const char *name, fwd_extern_t func, fwd_type_t rtype, ...) { struct scope *scope = (void *)state; struct ast *vars = NULL; va_list args; va_start(args, rtype); size_t idx = 0; while (1) { fwd_type_t type = va_arg(args, enum fwd_type); if (type == FWD_END) break; /* eight bytes is likely more than enough, since first three are * var, then we have four bytes for indexes and one trailing * NULL */ char *name = calloc(8, sizeof(char)); assert(name); sprintf(name, "%s%zu", "var", idx); struct ast *new = gen_var(name, fwd_type_kind(type), NULL_LOC()); if (vars) new->n = vars; vars = new; idx++; } va_end(args); /* append 'return' as a continuation */ if (rtype != FWD_VOID) { char *name = strdup("ret"); struct ast *new = gen_var(name, tgen_closure(fwd_type_kind(rtype), NULL_LOC()), NULL_LOC()); if (vars) new->n = vars; vars = new; } vars = reverse_ast_list(vars); struct ast *def = gen_proc(strdup(name), vars, fwd_type_kind(rtype), NULL, NULL_LOC()); if (scope_add_symbol(scope, def)) return -1; return 0; } int analyze_root(struct scope *scope, struct ast *root) { foreach_node(node, root) { switch (node->k) { case AST_PROC_DEF: if (scope_add_symbol(scope, node)) return -1; break; case AST_STRUCT_DEF: if (scope_add_type(scope, node)) return -1; break; case AST_STRUCT_CONT: abort(); break; case AST_TRAIT_DEF: if (scope_add_trait(scope, node)) return -1; break; case AST_IMPORT: { if (!try_import_mod(scope, node)) break; if (!try_import_file(scope, node)) break; return -1; } case AST_TEMPLATE: if (scope_add_template(scope, node)) return -1; break; case AST_SUPERTEMPLATE: if (scope_add_template(scope, node)) return -1; break; case AST_INSTANCE: break; default: abort(); } } foreach_node(node, root) { struct state state = {}; if (analyze(&state, scope, node)) return -1; } return 0; }