aboutsummaryrefslogtreecommitdiff
path: root/src/compile/functions.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/compile/functions.c')
-rw-r--r--src/compile/functions.c282
1 files changed, 203 insertions, 79 deletions
diff --git a/src/compile/functions.c b/src/compile/functions.c
index 63d7d23d..ec37c0ad 100644
--- a/src/compile/functions.c
+++ b/src/compile/functions.c
@@ -3,6 +3,7 @@
#include "../ast.h"
#include "../environment.h"
#include "../naming.h"
+#include "../stdlib/c_strings.h"
#include "../stdlib/datatypes.h"
#include "../stdlib/integers.h"
#include "../stdlib/nums.h"
@@ -247,85 +248,6 @@ Text_t compile_function_call(env_t *env, ast_t *ast) {
}
}
-public
-Text_t compile_lambda(env_t *env, ast_t *ast) {
- DeclareMatch(lambda, ast, Lambda);
- Text_t name = namespace_name(env, env->namespace, Texts("lambda$", lambda->id));
-
- env_t *body_scope = fresh_scope(env);
- body_scope->deferred = NULL;
- for (arg_ast_t *arg = lambda->args; arg; arg = arg->next) {
- type_t *arg_type = get_arg_ast_type(env, arg);
- set_binding(body_scope, arg->name, arg_type, Texts("_$", arg->name));
- }
-
- body_scope->fn = ast;
-
- Table_t closed_vars = get_closed_vars(env, lambda->args, ast);
- if (Table$length(closed_vars) > 0) { // Create a typedef for the lambda's closure userdata
- Text_t def = Text("typedef struct {");
- for (int64_t i = 0; i < (int64_t)closed_vars.entries.length; i++) {
- struct {
- const char *name;
- binding_t *b;
- } *entry = closed_vars.entries.data + closed_vars.entries.stride * i;
- if (has_stack_memory(entry->b->type))
- code_err(ast, "This function is holding onto a reference to ", type_to_text(entry->b->type),
- " stack memory in the variable `", entry->name,
- "`, but the function may outlive the stack memory");
- if (entry->b->type->tag == ModuleType) continue;
- set_binding(body_scope, entry->name, entry->b->type, Texts("userdata->", entry->name));
- def = Texts(def, compile_declaration(entry->b->type, Text$from_str(entry->name)), "; ");
- }
- def = Texts(def, "} ", name, "$userdata_t;");
- env->code->local_typedefs = Texts(env->code->local_typedefs, def);
- }
-
- type_t *ret_t = get_function_return_type(env, ast);
- Text_t code = Texts("static ", compile_type(ret_t), " ", name, "(");
- for (arg_ast_t *arg = lambda->args; arg; arg = arg->next) {
- type_t *arg_type = get_arg_ast_type(env, arg);
- code = Texts(code, compile_type(arg_type), " _$", arg->name, ", ");
- }
-
- Text_t userdata;
- if (Table$length(closed_vars) == 0) {
- code = Texts(code, "void *_)");
- userdata = Text("NULL");
- } else {
- userdata = Texts("new(", name, "$userdata_t");
- for (int64_t i = 0; i < (int64_t)closed_vars.entries.length; i++) {
- struct {
- const char *name;
- binding_t *b;
- } *entry = closed_vars.entries.data + closed_vars.entries.stride * i;
- if (entry->b->type->tag == ModuleType) continue;
- binding_t *b = get_binding(env, entry->name);
- assert(b);
- Text_t binding_code = b->code;
- if (entry->b->type->tag == ListType) userdata = Texts(userdata, ", LIST_COPY(", binding_code, ")");
- else if (entry->b->type->tag == TableType) userdata = Texts(userdata, ", TABLE_COPY(", binding_code, ")");
- else userdata = Texts(userdata, ", ", binding_code);
- }
- userdata = Texts(userdata, ")");
- code = Texts(code, name, "$userdata_t *userdata)");
- }
-
- Text_t body = EMPTY_TEXT;
- for (ast_list_t *stmt = Match(lambda->body, Block)->statements; stmt; stmt = stmt->next) {
- if (stmt->next || ret_t->tag == VoidType || ret_t->tag == AbortType
- || get_type(body_scope, stmt->ast)->tag == ReturnType)
- body = Texts(body, compile_statement(body_scope, stmt->ast), "\n");
- else body = Texts(body, compile_statement(body_scope, FakeAST(Return, stmt->ast)), "\n");
- bind_statement(body_scope, stmt->ast);
- }
- if ((ret_t->tag == VoidType || ret_t->tag == AbortType) && body_scope->deferred)
- body = Texts(body, compile_statement(body_scope, FakeAST(Return)), "\n");
-
- env->code->lambdas = Texts(env->code->lambdas, code, " {\n", body, "\n}\n");
- return Texts("((Closure_t){", name, ", ", userdata, "})");
-}
-
static void add_closed_vars(Table_t *closed_vars, env_t *enclosing_scope, env_t *env, ast_t *ast) {
if (ast == NULL) return;
@@ -595,6 +517,206 @@ Table_t get_closed_vars(env_t *env, arg_ast_t *args, ast_t *block) {
return closed_vars;
}
+static visit_behavior_t find_used_variables(ast_t *ast, void *userdata) {
+ Table_t *vars = (Table_t *)userdata;
+ switch (ast->tag) {
+ case Var: {
+ const char *name = Match(ast, Var)->name;
+ Table$str_set(vars, name, ast);
+ return VISIT_STOP;
+ }
+ case Assign: {
+ for (ast_list_t *target = Match(ast, Assign)->targets; target; target = target->next) {
+ ast_t *var = target->ast;
+ for (;;) {
+ if (var->tag == Index) {
+ ast_t *index = Match(var, Index)->index;
+ if (index) ast_visit(index, find_used_variables, userdata);
+ var = Match(var, Index)->indexed;
+ } else if (var->tag == FieldAccess) {
+ var = Match(var, FieldAccess)->fielded;
+ } else {
+ break;
+ }
+ }
+ }
+ for (ast_list_t *val = Match(ast, Assign)->values; val; val = val->next) {
+ ast_visit(val->ast, find_used_variables, userdata);
+ }
+ return VISIT_STOP;
+ }
+ case UPDATE_CASES: {
+ binary_operands_t operands = BINARY_OPERANDS(ast);
+ ast_t *lhs = operands.lhs;
+ for (;;) {
+ if (lhs->tag == Index) {
+ ast_t *index = Match(lhs, Index)->index;
+ if (index) ast_visit(index, find_used_variables, userdata);
+ lhs = Match(lhs, Index)->indexed;
+ } else if (lhs->tag == FieldAccess) {
+ lhs = Match(lhs, FieldAccess)->fielded;
+ } else {
+ break;
+ }
+ }
+ ast_visit(operands.rhs, find_used_variables, userdata);
+ return VISIT_STOP;
+ }
+ case Declare: {
+ ast_visit(Match(ast, Declare)->value, find_used_variables, userdata);
+ return VISIT_STOP;
+ }
+ default: return VISIT_PROCEED;
+ }
+}
+
+static visit_behavior_t find_assigned_variables(ast_t *ast, void *userdata) {
+ Table_t *vars = (Table_t *)userdata;
+ switch (ast->tag) {
+ case Assign:
+ for (ast_list_t *target = Match(ast, Assign)->targets; target; target = target->next) {
+ ast_t *var = target->ast;
+ for (;;) {
+ if (var->tag == Index) var = Match(var, Index)->indexed;
+ else if (var->tag == FieldAccess) var = Match(var, FieldAccess)->fielded;
+ else break;
+ }
+ if (var->tag == Var) {
+ const char *name = Match(var, Var)->name;
+ Table$str_set(vars, name, var);
+ }
+ }
+ return VISIT_STOP;
+ case UPDATE_CASES: {
+ binary_operands_t operands = BINARY_OPERANDS(ast);
+ ast_t *var = operands.lhs;
+ for (;;) {
+ if (var->tag == Index) var = Match(var, Index)->indexed;
+ else if (var->tag == FieldAccess) var = Match(var, FieldAccess)->fielded;
+ else break;
+ }
+ if (var->tag == Var) {
+ const char *name = Match(var, Var)->name;
+ Table$str_set(vars, name, var);
+ }
+ return VISIT_STOP;
+ }
+ case Declare: {
+ ast_t *var = Match(ast, Declare)->var;
+ const char *name = Match(var, Var)->name;
+ Table$str_set(vars, name, var);
+ return VISIT_STOP;
+ }
+ default: return VISIT_PROCEED;
+ }
+}
+
+static void check_unused_vars(env_t *env, arg_ast_t *args, ast_t *body) {
+ Table_t used_vars = EMPTY_TABLE;
+ ast_visit(body, find_used_variables, &used_vars);
+ Table_t assigned_vars = EMPTY_TABLE;
+ ast_visit(body, find_assigned_variables, &assigned_vars);
+
+ for (arg_ast_t *arg = args; arg; arg = arg->next) {
+ type_t *arg_type = get_arg_ast_type(env, arg);
+ if (arg_type->tag == PointerType) {
+ Table$str_remove(&assigned_vars, arg->name);
+ }
+ }
+
+ Table_t unused = Table$without(assigned_vars, used_vars, Table$info(&CString$info, &Present$$info));
+ for (int64_t i = 0; i < (int64_t)unused.entries.length; i++) {
+ struct {
+ const char *name;
+ } *entry = unused.entries.data + i * unused.entries.stride;
+ if (streq(entry->name, "_")) continue;
+ ast_t *var = Table$str_get(assigned_vars, entry->name);
+ code_err(var, "This variable was assigned to, but never read from.");
+ }
+}
+
+public
+Text_t compile_lambda(env_t *env, ast_t *ast) {
+ DeclareMatch(lambda, ast, Lambda);
+ Text_t name = namespace_name(env, env->namespace, Texts("lambda$", lambda->id));
+
+ env_t *body_scope = fresh_scope(env);
+ body_scope->deferred = NULL;
+ for (arg_ast_t *arg = lambda->args; arg; arg = arg->next) {
+ type_t *arg_type = get_arg_ast_type(env, arg);
+ set_binding(body_scope, arg->name, arg_type, Texts("_$", arg->name));
+ }
+
+ body_scope->fn = ast;
+
+ Table_t closed_vars = get_closed_vars(env, lambda->args, ast);
+ if (Table$length(closed_vars) > 0) { // Create a typedef for the lambda's closure userdata
+ Text_t def = Text("typedef struct {");
+ for (int64_t i = 0; i < (int64_t)closed_vars.entries.length; i++) {
+ struct {
+ const char *name;
+ binding_t *b;
+ } *entry = closed_vars.entries.data + closed_vars.entries.stride * i;
+ if (has_stack_memory(entry->b->type))
+ code_err(ast, "This function is holding onto a reference to ", type_to_text(entry->b->type),
+ " stack memory in the variable `", entry->name,
+ "`, but the function may outlive the stack memory");
+ if (entry->b->type->tag == ModuleType) continue;
+ set_binding(body_scope, entry->name, entry->b->type, Texts("userdata->", entry->name));
+ def = Texts(def, compile_declaration(entry->b->type, Text$from_str(entry->name)), "; ");
+ }
+ def = Texts(def, "} ", name, "$userdata_t;");
+ env->code->local_typedefs = Texts(env->code->local_typedefs, def);
+ }
+
+ type_t *ret_t = get_function_return_type(env, ast);
+ Text_t code = Texts("static ", compile_type(ret_t), " ", name, "(");
+ for (arg_ast_t *arg = lambda->args; arg; arg = arg->next) {
+ type_t *arg_type = get_arg_ast_type(env, arg);
+ code = Texts(code, compile_type(arg_type), " _$", arg->name, ", ");
+ }
+
+ Text_t userdata;
+ if (Table$length(closed_vars) == 0) {
+ code = Texts(code, "void *_)");
+ userdata = Text("NULL");
+ } else {
+ userdata = Texts("new(", name, "$userdata_t");
+ for (int64_t i = 0; i < (int64_t)closed_vars.entries.length; i++) {
+ struct {
+ const char *name;
+ binding_t *b;
+ } *entry = closed_vars.entries.data + closed_vars.entries.stride * i;
+ if (entry->b->type->tag == ModuleType) continue;
+ binding_t *b = get_binding(env, entry->name);
+ assert(b);
+ Text_t binding_code = b->code;
+ if (entry->b->type->tag == ListType) userdata = Texts(userdata, ", LIST_COPY(", binding_code, ")");
+ else if (entry->b->type->tag == TableType) userdata = Texts(userdata, ", TABLE_COPY(", binding_code, ")");
+ else userdata = Texts(userdata, ", ", binding_code);
+ }
+ userdata = Texts(userdata, ")");
+ code = Texts(code, name, "$userdata_t *userdata)");
+ }
+
+ Text_t body = EMPTY_TEXT;
+ for (ast_list_t *stmt = Match(lambda->body, Block)->statements; stmt; stmt = stmt->next) {
+ if (stmt->next || ret_t->tag == VoidType || ret_t->tag == AbortType
+ || get_type(body_scope, stmt->ast)->tag == ReturnType)
+ body = Texts(body, compile_statement(body_scope, stmt->ast), "\n");
+ else body = Texts(body, compile_statement(body_scope, FakeAST(Return, stmt->ast)), "\n");
+ bind_statement(body_scope, stmt->ast);
+ }
+ if ((ret_t->tag == VoidType || ret_t->tag == AbortType) && body_scope->deferred)
+ body = Texts(body, compile_statement(body_scope, FakeAST(Return)), "\n");
+
+ env->code->lambdas = Texts(env->code->lambdas, code, " {\n", body, "\n}\n");
+
+ check_unused_vars(env, lambda->args, lambda->body);
+
+ return Texts("((Closure_t){", name, ", ", userdata, "})");
+}
+
public
Text_t compile_function(env_t *env, Text_t name_code, ast_t *ast, Text_t *staticdefs) {
bool is_private = false;
@@ -785,6 +907,8 @@ Text_t compile_function(env_t *env, Text_t name_code, ast_t *ast, Text_t *static
}
}
+ check_unused_vars(env, args, body);
+
return definition;
}