aboutsummaryrefslogtreecommitdiff
path: root/src/typecheck.c
diff options
context:
space:
mode:
authorBruce Hill <bruce@bruce-hill.com>2025-08-10 14:44:03 -0400
committerBruce Hill <bruce@bruce-hill.com>2025-08-10 14:44:03 -0400
commit611ebeab13b64d505ad4b44d0f80b3d0e9fb8dba (patch)
treef0edc3737d3e7366aad896355f2f7628bc2d8840 /src/typecheck.c
parent98cadc2135be65abcf9dff53d87af9ea549757a2 (diff)
Add full protection against accessing fields and methods that start with
underscores outside of the scope where the type is defined.
Diffstat (limited to 'src/typecheck.c')
-rw-r--r--src/typecheck.c23
1 files changed, 22 insertions, 1 deletions
diff --git a/src/typecheck.c b/src/typecheck.c
index d4ff0e7d..494c73d8 100644
--- a/src/typecheck.c
+++ b/src/typecheck.c
@@ -353,6 +353,7 @@ void bind_statement(env_t *env, ast_t *statement)
type_t *type = Table$str_get(*env->types, def->name);
if (!type) code_err(statement, "Couldn't find type!");
assert(type);
+ ns_env->current_type = type;
if (!def->opaque) {
arg_t *fields = NULL;
for (arg_ast_t *field_ast = def->fields; field_ast; field_ast = field_ast->next) {
@@ -394,6 +395,7 @@ void bind_statement(env_t *env, ast_t *statement)
env_t *ns_env = namespace_env(env, def->name);
type_t *type = Table$str_get(*env->types, def->name);
assert(type);
+ ns_env->current_type = type;
tag_t *tags = NULL;
int64_t next_tag = 1;
bool has_any_tags_with_fields = false;
@@ -458,6 +460,7 @@ void bind_statement(env_t *env, ast_t *statement)
DeclareMatch(def, statement, LangDef);
env_t *ns_env = namespace_env(env, def->name);
type_t *type = Type(TextType, .lang=def->name, .env=ns_env);
+ ns_env->current_type = type;
Table$str_set(env->types, def->name, type);
set_binding(ns_env, "from_text", NewFunctionType(type, {.name="text", .type=TEXT_TYPE}),
@@ -823,6 +826,19 @@ type_t *get_type(env_t *env, ast_t *ast)
case FieldAccess: {
DeclareMatch(access, ast, FieldAccess);
type_t *fielded_t = get_type(env, access->fielded);
+ if (access->field[0] == '_') {
+ if (!env->current_type || !type_eq(env->current_type,
+ fielded_t->tag == TypeInfoType
+ ? Match(fielded_t, TypeInfoType)->type
+ : value_type(fielded_t)))
+ code_err(ast, "Fields beginning with underscores like '", access->field,
+ "' can't be accessed outside the scope where the type (",
+ type_to_text(
+ fielded_t->tag == TypeInfoType
+ ? Match(fielded_t, TypeInfoType)->type
+ : value_type(fielded_t)),
+ ") is defined.");
+ }
if (fielded_t->tag == ModuleType) {
const char *name = Match(fielded_t, ModuleType)->name;
env_t *module_env = Table$str_get(*env->imports, name);
@@ -876,7 +892,7 @@ type_t *get_type(env_t *env, ast_t *ast)
if (fn_type_t->tag == TypeInfoType) {
type_t *t = Match(fn_type_t, TypeInfoType)->type;
- binding_t *constructor = get_constructor(env, t, call->args);
+ binding_t *constructor = get_constructor(env, t, call->args, env->current_type != NULL && type_eq(env->current_type, t));
if (constructor)
return t;
else if (t->tag == StructType || t->tag == IntType || t->tag == BigIntType || t->tag == NumType
@@ -964,6 +980,11 @@ type_t *get_type(env_t *env, ast_t *ast)
code_err(ast, "There is no '", call->name, "' method for ", type_to_str(self_value_t), " tables");
}
default: {
+ if (call->name[0] == '_') {
+ if (env->current_type == NULL || !type_eq(env->current_type, self_value_t))
+ code_err(ast, "You can't call private methods starting with underscore (like '", call->name, "') "
+ "outside of the place where the type (", type_to_text(self_value_t), ") is defined.");
+ }
type_t *field_type = get_field_type(self_value_t, call->name);
if (field_type && field_type->tag == ClosureType)
field_type = Match(field_type, ClosureType)->fn;