From e8f78907830c525dccf1f4a5e6f97a15780c54ef Mon Sep 17 00:00:00 2001 From: Kimplul Date: Tue, 21 Jan 2025 20:32:39 +0200 Subject: add type visibility checks + Essentially require that each type used in a public symbol only use types that are exported out of the file as well --- include/ek/ast.h | 2 ++ include/ek/scope.h | 1 + src/actualize.c | 80 ++++++++++++++++++++++++++++++++++++++++++++++-------- src/ast.c | 11 ++++++++ src/compiler.c | 11 ++++---- src/scope.c | 18 ++++++------ 6 files changed, 97 insertions(+), 26 deletions(-) diff --git a/include/ek/ast.h b/include/ek/ast.h index 5c1c6e6..37e644d 100644 --- a/include/ek/ast.h +++ b/include/ek/ast.h @@ -197,6 +197,7 @@ enum ast_flags { AST_FLAG_UFCS_SIMPLE = (1 << 17), AST_FLAG_UFCS_REF = (1 << 18), AST_FLAG_UFCS_TRIVIAL = (1 << 19), + AST_FLAG_INSTANCE = (1 << 20), }; struct ast; @@ -691,6 +692,7 @@ struct ast *ast_prepend(struct ast *list, struct ast *elem); struct type *type_prepend(struct type *list, struct type *elem); void ast_set_flags(struct ast *node, enum ast_flags flags); +void ast_set_flags_recurse(struct ast *node, enum ast_flags flags); void ast_clear_flags(struct ast *node, enum ast_flags flags); unsigned ast_flags(struct ast *node, enum ast_flags flags); diff --git a/include/ek/scope.h b/include/ek/scope.h index aedeacf..810f1cc 100644 --- a/include/ek/scope.h +++ b/include/ek/scope.h @@ -22,6 +22,7 @@ enum scope_flags { SCOPE_PUBLIC = (1 << 0), /** Scope is file scope. */ SCOPE_FILE = (1 << 1), + SCOPE_ROOT = (1 << 2) }; struct visible_tuple { diff --git a/src/actualize.c b/src/actualize.c index 0d2c624..903d283 100644 --- a/src/actualize.c +++ b/src/actualize.c @@ -1201,6 +1201,8 @@ static struct ast *maybe_expand_type(struct scope *scope, struct ast *def, || def->k == AST_STRUCT_CONT_DEF || def->k == AST_TRAIT_DEF); + ast_set_flags_recurse(def, AST_FLAG_INSTANCE); + if (def->k == AST_STRUCT_DEF) return maybe_expand_struct(scope, def, loc, args); else if (def->k == AST_STRUCT_CONT_DEF) @@ -1432,6 +1434,40 @@ static int undefined_gotos(struct act_state *state, struct scope *scope) return ret; } +struct exported_helper { + struct scope *scope; + struct ast *node; +}; + +static int _check_exported(struct type *type, struct exported_helper *helper) +{ + struct ast *def = type->d; + if (!def) + return 0; + + struct ast *node = helper->node; + struct scope *scope = helper->scope; + + if (is_exported_type(scope, def)) + return 0; + + char *tstr = type_str(type); + semantic_error(node->scope->fctx, node, + "type %s should be exported", + tstr); + free(tstr); + return -1; +} + +static int check_exported(struct scope *scope, struct ast *node ,struct type *type) +{ + struct exported_helper helper = { + .scope = scope, + .node = node + }; + return type_visit((type_callback_t)_check_exported, NULL, type, &helper); +} + static int actualize_proc_sign(struct scope *scope, struct ast *proc) { assert(proc && proc->k == AST_PROC_DEF); @@ -1440,6 +1476,11 @@ static int actualize_proc_sign(struct scope *scope, struct ast *proc) proc->scope = create_scope(); scope_add_scope(scope, proc->scope); + foreach_node(p, proc_params(proc)) { + if (ast_flags(proc, AST_FLAG_PUBLIC)) + ast_set_flags(p, AST_FLAG_PUBLIC); + } + if (actualize_list(&new_state, proc->scope, proc_params(proc))) { destroy_act_state(&new_state); return -1; @@ -1454,6 +1495,12 @@ static int actualize_proc_sign(struct scope *scope, struct ast *proc) return -1; } + if (ast_flags(proc, AST_FLAG_PUBLIC) && !ast_flags(proc, AST_FLAG_INSTANCE)) + foreach_type(t, rtype) { + if (check_exported(scope, proc, t)) + return -1; + } + struct type *callable = tgen_callable(NULL, rtype, proc->loc); foreach_node(p, proc_params(proc)) { /* we must manually 'start' the chain */ @@ -1684,6 +1731,11 @@ static int actualize_var(struct act_state *state, /* declare */ set_type(var, type); + if (ast_flags(var, AST_FLAG_PUBLIC) && !ast_flags(var, AST_FLAG_INSTANCE)) { + if (check_exported(scope, var, var->t)) + return -1; + } + /* an unnamed var is a var in a signature that should not produce a * warning on not being used (if I ever get around to adding those kinds * of warnings) */ @@ -2485,18 +2537,6 @@ static int expand_struct_body(struct act_state *state, if (actualize_type(&type_state, scope, type)) return -1; - if (ast_flags(node, AST_FLAG_PUBLIC)) { - struct ast *def = type->d; - /* traits should only show up during the initial - * expansion, but should maybe make sure somehow */ - if (def->k == AST_TRAIT_DEF && !is_exported_type(scope, - def)) { - semantic_error(struct_scope->fctx, n, - "trait used in pub def must also be exported"); - return -1; - } - } - type_append(&types, type); struct ast *alias = gen_alias(strdup(id), type, n->loc); @@ -2507,6 +2547,19 @@ static int expand_struct_body(struct act_state *state, if (actualize(&state, struct_scope, alias)) return -1; + if (!ast_flags(node, AST_FLAG_PUBLIC)) + continue; + + if (ast_flags(node, AST_FLAG_INSTANCE)) + continue; + + struct ast *def = type->d; + if (is_exported_type(scope, def)) + continue; + + semantic_error(struct_scope->fctx, n, + "trait used in pub def must also be exported"); + return -1; } if (node->k == AST_STRUCT_CONT_DEF @@ -2608,6 +2661,9 @@ static int expand_struct_body(struct act_state *state, if (n->k == AST_TYPE_EXPAND) continue; + if (ast_flags(node, AST_FLAG_PUBLIC)) + ast_set_flags(n, AST_FLAG_PUBLIC); + struct act_state state = {0}; if (actualize(&state, struct_scope, n)) return -1; diff --git a/src/ast.c b/src/ast.c index c7f32a0..5561745 100644 --- a/src/ast.c +++ b/src/ast.c @@ -149,6 +149,17 @@ void ast_set_flags(struct ast *node, enum ast_flags flags) node->f |= flags; } +static int _ast_set_flags(struct ast *node, enum ast_flags *flags) +{ + ast_set_flags(node, *flags); + return 0; +} + +void ast_set_flags_recurse(struct ast *node, enum ast_flags flags) +{ + ast_visit((ast_callback_t)_ast_set_flags, NULL, node, &flags); +} + void ast_clear_flags(struct ast *node, enum ast_flags flags) { node->f &= ~(flags); diff --git a/src/compiler.c b/src/compiler.c index 881326c..d865ea5 100644 --- a/src/compiler.c +++ b/src/compiler.c @@ -171,13 +171,14 @@ struct scope *process_file(const char *file) return *exists; } else { struct scope *scope = create_scope(); - if (process(scope, base)) { - destroy_scope(scope); - free(real); - goto out; - } + if (scopes_len(&scopes) == 0) + scope_set_flags(scope, SCOPE_ROOT); scopes_insert(&scopes, real, scope); + + if (process(scope, base)) + goto out; + res_destroy(r); return scope; } diff --git a/src/scope.c b/src/scope.c index 1eae065..4c9097f 100644 --- a/src/scope.c +++ b/src/scope.c @@ -17,12 +17,6 @@ #include #include -static bool same_src_scope(struct scope *a, struct scope *b) -{ - /** @todo a bit ridiculous, is there a less hacky way? */ - return a->fctx.fbuf == b->fctx.fbuf; -} - struct scope *create_scope() { /* if I ever try making the parser multithreaded, this should be atomic. */ @@ -123,9 +117,6 @@ static bool scope_add_recurse(struct scope *scope, struct ast *node) if (!ast_flags(node, AST_FLAG_PUBLIC)) return false; - if (same_src_scope(scope, node->scope)) - return true; - return scope_flags(scope, SCOPE_PUBLIC); } @@ -459,6 +450,9 @@ void scope_add_scope(struct scope *parent, struct scope *child) bool is_exported_type(struct scope *scope, struct ast *def) { + if (scope_flags(scope, SCOPE_ROOT)) + return true; + struct ast **found = exported_find(&scope->exported_types, def); if (found) return true; @@ -471,6 +465,9 @@ bool is_exported_type(struct scope *scope, struct ast *def) bool is_exported_symbol(struct scope *scope, struct ast *def) { + if (scope_flags(scope, SCOPE_ROOT)) + return true; + struct ast **found = exported_find(&scope->exported_symbols, def); if (found) return true; @@ -483,6 +480,9 @@ bool is_exported_symbol(struct scope *scope, struct ast *def) bool is_exported_macro(struct scope *scope, struct ast *def) { + if (scope_flags(scope, SCOPE_ROOT)) + return true; + struct ast **found = exported_find(&scope->exported_macros, def); if (found) return true; -- cgit v1.3