aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/compile.c23
-rw-r--r--src/environment.c14
-rw-r--r--src/environment.h3
-rw-r--r--src/typecheck.c23
4 files changed, 51 insertions, 12 deletions
diff --git a/src/compile.c b/src/compile.c
index 95ee6e3a..1795bb38 100644
--- a/src/compile.c
+++ b/src/compile.c
@@ -134,7 +134,8 @@ static bool promote(env_t *env, ast_t *ast, Text_t *code, type_t *actual, type_t
// Numeric promotions/demotions
if ((is_numeric_type(actual) || actual->tag == BoolType) && (is_numeric_type(needed) || needed->tag == BoolType)) {
arg_ast_t *args = new(arg_ast_t, .value=LiteralCode(*code, .type=actual));
- binding_t *constructor = get_constructor(env, needed, args);
+ binding_t *constructor = get_constructor(env, needed, args,
+ env->current_type != NULL && type_eq(env->current_type, value_type(needed)));
if (constructor) {
DeclareMatch(fn, constructor->type, FunctionType);
if (fn->args->next == NULL) {
@@ -2939,7 +2940,8 @@ Text_t compile(env_t *env, ast_t *ast)
if (chunk->ast->tag == TextLiteral || type_eq(chunk_t, text_t)) {
chunk_code = compile(env, chunk->ast);
} else {
- binding_t *constructor = get_constructor(env, text_t, new(arg_ast_t, .value=chunk->ast));
+ binding_t *constructor = get_constructor(env, text_t, new(arg_ast_t, .value=chunk->ast),
+ env->current_type != NULL && type_eq(env->current_type, text_t));
if (constructor) {
arg_t *arg_spec = Match(constructor->type, FunctionType)->args;
arg_ast_t *args = new(arg_ast_t, .value=chunk->ast);
@@ -3487,7 +3489,8 @@ Text_t compile(env_t *env, ast_t *ast)
else if (t->tag == NumType && call->args && !call->args->next && call->args->value->tag == Num)
return compile_to_type(env, call->args->value, t);
- 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) {
arg_t *arg_spec = Match(constructor->type, FunctionType)->args;
return Texts(constructor->code, "(", compile_arguments(env, ast, arg_spec, call->args), ")");
@@ -3518,6 +3521,12 @@ Text_t compile(env_t *env, ast_t *ast)
} else if (t->tag == StructType) {
DeclareMatch(struct_, t, StructType);
if (!struct_->opaque && is_valid_call(env, struct_->fields, call->args, true)) {
+ if (env->current_type == NULL || !type_eq(env->current_type, t)) {
+ for (arg_t *field = struct_->fields; field; field = field->next) {
+ if (field->name[0] == '_')
+ code_err(ast, "This struct can't be initialized directly because it has private fields (starting with underscore)");
+ }
+ }
return Texts("((", compile_type(t), "){",
compile_arguments(env, ast, struct_->fields, call->args), "})");
}
@@ -3797,12 +3806,8 @@ Text_t compile(env_t *env, ast_t *ast)
case TypeInfoType: {
DeclareMatch(info, value_t, TypeInfoType);
if (f->field[0] == '_') {
- for (Table_t *locals = env->locals; locals; locals = locals->fallback) {
- if (locals == info->env->locals)
- goto is_inside_type;
- }
- code_err(ast, "Fields that start with underscores are not accessible on types outside of the type definition.");
- is_inside_type:;
+ if (!type_eq(env->current_type, info->type))
+ code_err(ast, "Fields that start with underscores are not accessible on types outside of the type definition.");
}
binding_t *b = get_binding(info->env, f->field);
if (!b) code_err(ast, "I couldn't find the field '", f->field, "' on this type");
diff --git a/src/environment.c b/src/environment.c
index 0bcdd592..93b5a16c 100644
--- a/src/environment.c
+++ b/src/environment.c
@@ -749,7 +749,7 @@ binding_t *get_namespace_binding(env_t *env, ast_t *self, const char *name)
return ns_env ? get_binding(ns_env, name) : NULL;
}
-PUREFUNC binding_t *get_constructor(env_t *env, type_t *t, arg_ast_t *args)
+PUREFUNC binding_t *get_constructor(env_t *env, type_t *t, arg_ast_t *args, bool allow_underscores)
{
env_t *type_env = get_namespace_by_type(env, t);
if (!type_env) return NULL;
@@ -758,15 +758,27 @@ PUREFUNC binding_t *get_constructor(env_t *env, type_t *t, arg_ast_t *args)
for (int64_t i = constructors.length-1; i >= 0; i--) {
binding_t *b = constructors.data + i*constructors.stride;
DeclareMatch(fn, b->type, FunctionType);
+ if (!allow_underscores) {
+ for (arg_t *arg = fn->args; arg; arg = arg->next)
+ if (arg->name[0] == '_')
+ goto next_constructor;
+ }
if (type_eq(fn->ret, t) && is_valid_call(env, fn->args, args, false))
return b;
+ next_constructor: continue;
}
// Fall back to promotion:
for (int64_t i = constructors.length-1; i >= 0; i--) {
binding_t *b = constructors.data + i*constructors.stride;
DeclareMatch(fn, b->type, FunctionType);
+ if (!allow_underscores) {
+ for (arg_t *arg = fn->args; arg; arg = arg->next)
+ if (arg->name[0] == '_')
+ goto next_constructor2;
+ }
if (type_eq(fn->ret, t) && is_valid_call(env, fn->args, args, true))
return b;
+ next_constructor2: continue;
}
return NULL;
}
diff --git a/src/environment.h b/src/environment.h
index 2d4e822c..18b749ed 100644
--- a/src/environment.h
+++ b/src/environment.h
@@ -50,6 +50,7 @@ typedef struct env_s {
deferral_t *deferred;
Closure_t *comprehension_action;
bool do_source_mapping:1;
+ type_t *current_type;
} env_t;
typedef struct {
@@ -84,7 +85,7 @@ env_t *namespace_env(env_t *env, const char *namespace_name);
exit(1); \
})
binding_t *get_binding(env_t *env, const char *name);
-binding_t *get_constructor(env_t *env, type_t *t, arg_ast_t *args);
+binding_t *get_constructor(env_t *env, type_t *t, arg_ast_t *args, bool allow_underscores);
PUREFUNC binding_t *get_metamethod_binding(env_t *env, ast_e tag, ast_t *lhs, ast_t *rhs, type_t *ret);
void set_binding(env_t *env, const char *name, type_t *type, Text_t code);
binding_t *get_namespace_binding(env_t *env, ast_t *self, const char *name);
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;