aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/compile.c28
-rw-r--r--src/typecheck.c36
2 files changed, 52 insertions, 12 deletions
diff --git a/src/compile.c b/src/compile.c
index 2cc20f39..43b9ef1e 100644
--- a/src/compile.c
+++ b/src/compile.c
@@ -4200,8 +4200,12 @@ CORD compile_top_level_code(env_t *env, ast_t *ast)
case Extend: {
auto extend = Match(ast, Extend);
env_t *ns_env = namespace_env(env, extend->name);
- ns_env->libname = env->libname;
- return compile_top_level_code(ns_env, extend->body);
+ env_t *extended = new(env_t);
+ *extended = *ns_env;
+ extended->locals = new(Table_t, .fallback=env->locals);
+ extended->namespace_bindings = new(Table_t, .fallback=env->namespace_bindings);
+ extended->libname = env->libname;
+ return compile_top_level_code(extended, extend->body);
}
case Extern: return CORD_EMPTY;
case Block: {
@@ -4358,30 +4362,38 @@ CORD compile_statement_type_header(env_t *env, Path_t header_path, ast_t *ast)
CORD compile_statement_namespace_header(env_t *env, Path_t header_path, ast_t *ast)
{
- const char *ns_name = NULL;
+ env_t *ns_env = NULL;
ast_t *block = NULL;
switch (ast->tag) {
case LangDef: {
auto def = Match(ast, LangDef);
- ns_name = def->name;
+ ns_env = namespace_env(env, def->name);
block = def->namespace;
break;
}
case Extend: {
auto extend = Match(ast, Extend);
- ns_name = extend->name;
+ ns_env = namespace_env(env, extend->name);
+
+ env_t *extended = new(env_t);
+ *extended = *ns_env;
+ extended->locals = new(Table_t, .fallback=env->locals);
+ extended->namespace_bindings = new(Table_t, .fallback=env->namespace_bindings);
+ extended->libname = env->libname;
+ ns_env = extended;
+
block = extend->body;
break;
}
case StructDef: {
auto def = Match(ast, StructDef);
- ns_name = def->name;
+ ns_env = namespace_env(env, def->name);
block = def->namespace;
break;
}
case EnumDef: {
auto def = Match(ast, EnumDef);
- ns_name = def->name;
+ ns_env = namespace_env(env, def->name);
block = def->namespace;
break;
}
@@ -4465,7 +4477,7 @@ CORD compile_statement_namespace_header(env_t *env, Path_t header_path, ast_t *a
}
default: return CORD_EMPTY;
}
- env_t *ns_env = namespace_env(env, ns_name);
+ assert(ns_env);
CORD header = CORD_EMPTY;
for (ast_list_t *stmt = block ? Match(block, Block)->statements : NULL; stmt; stmt = stmt->next) {
header = CORD_all(header, compile_statement_namespace_header(ns_env, header_path, stmt->ast));
diff --git a/src/typecheck.c b/src/typecheck.c
index ff609435..648ffbc8 100644
--- a/src/typecheck.c
+++ b/src/typecheck.c
@@ -274,9 +274,23 @@ void prebind_statement(env_t *env, ast_t *statement)
case Extend: {
auto extend = Match(statement, Extend);
env_t *ns_env = namespace_env(env, extend->name);
- ns_env->libname = env->libname;
+ env_t *extended = new(env_t);
+ *extended = *ns_env;
+ extended->locals = new(Table_t, .fallback=env->locals);
+ extended->namespace_bindings = new(Table_t, .fallback=env->namespace_bindings);
+ extended->libname = env->libname;
for (ast_list_t *stmt = extend->body ? Match(extend->body, Block)->statements : NULL; stmt; stmt = stmt->next)
- prebind_statement(ns_env, stmt->ast);
+ prebind_statement(extended, stmt->ast);
+ Array_t new_bindings = extended->locals->entries;
+ for (int64_t i = 0; i < new_bindings.length; i++) {
+ struct { const char *name; binding_t *binding; } *entry = new_bindings.data + i*new_bindings.stride;
+ binding_t *clobbered = Table$str_get(*ns_env->locals, entry->name);
+ if (clobbered && !type_eq(clobbered->type, entry->binding->type))
+ code_err(statement, "This `extend` block overwrites the binding for ", quoted(entry->name),
+ " in the original namespace (with type ", type_to_str(clobbered->type), ") with a new binding with type ",
+ type_to_str(entry->binding->type));
+ Table$str_set(ns_env->locals, entry->name, entry->binding);
+ }
break;
}
default: break;
@@ -448,9 +462,23 @@ void bind_statement(env_t *env, ast_t *statement)
case Extend: {
auto extend = Match(statement, Extend);
env_t *ns_env = namespace_env(env, extend->name);
- ns_env->libname = env->libname;
+ env_t *extended = new(env_t);
+ *extended = *ns_env;
+ extended->locals = new(Table_t, .fallback=env->locals);
+ extended->namespace_bindings = new(Table_t, .fallback=env->namespace_bindings);
+ extended->libname = env->libname;
for (ast_list_t *stmt = extend->body ? Match(extend->body, Block)->statements : NULL; stmt; stmt = stmt->next)
- bind_statement(ns_env, stmt->ast);
+ bind_statement(extended, stmt->ast);
+ Array_t new_bindings = extended->locals->entries;
+ for (int64_t i = 0; i < new_bindings.length; i++) {
+ struct { const char *name; binding_t *binding; } *entry = new_bindings.data + i*new_bindings.stride;
+ binding_t *clobbered = Table$str_get(*ns_env->locals, entry->name);
+ if (clobbered && !type_eq(clobbered->type, entry->binding->type))
+ code_err(statement, "This `extend` block overwrites the binding for ", quoted(entry->name),
+ " in the original namespace (with type ", type_to_str(clobbered->type), ") with a new binding with type ",
+ type_to_str(entry->binding->type));
+ Table$str_set(ns_env->locals, entry->name, entry->binding);
+ }
break;
}
case Use: {