1 // This file defines how to compile functions
4 #include "../environment.h"
6 #include "../stdlib/c_strings.h"
7 #include "../stdlib/datatypes.h"
8 #include "../stdlib/integers.h"
9 #include "../stdlib/nums.h"
10 #include "../stdlib/optionals.h"
11 #include "../stdlib/tables.h"
12 #include "../stdlib/text.h"
13 #include "../stdlib/util.h"
14 #include "../typecheck.h"
16 #include "compilation.h"
19 Text_t compile_function_declaration(env_t *env, ast_t *ast) {
20 DeclareMatch(fndef, ast, FunctionDef);
21 const char *decl_name = Match(fndef->name, Var)->name;
22 bool is_private = decl_name[0] == '_';
23 if (is_private) return EMPTY_TEXT;
24 Text_t arg_signature = Text("(");
25 for (arg_ast_t *arg = fndef->args; arg; arg = arg->next) {
26 type_t *arg_type = get_arg_ast_type(env, arg);
27 arg_signature = Texts(arg_signature, compile_declaration(arg_type, Texts("_$", arg->name)));
28 if (arg->next) arg_signature = Texts(arg_signature, ", ");
30 arg_signature = Texts(arg_signature, ")");
32 type_t *ret_t = fndef->ret_type ? parse_type_ast(env, fndef->ret_type) : Type(VoidType);
33 Text_t ret_type_code = compile_type(ret_t);
34 if (ret_t->tag == AbortType) ret_type_code = Texts("__attribute__((noreturn)) _Noreturn ", ret_type_code);
35 Text_t name = namespace_name(env, env->namespace, Text$from_str(decl_name));
36 if (env->namespace && env->namespace->parent && env->namespace->name && streq(decl_name, env->namespace->name))
37 name = namespace_name(env, env->namespace, Texts(get_line_number(ast->file, ast->start)));
38 return Texts(ret_type_code, " ", name, arg_signature, ";\n");
42 Text_t compile_convert_declaration(env_t *env, ast_t *ast) {
43 DeclareMatch(def, ast, ConvertDef);
45 Text_t arg_signature = Text("(");
46 for (arg_ast_t *arg = def->args; arg; arg = arg->next) {
47 type_t *arg_type = get_arg_ast_type(env, arg);
48 arg_signature = Texts(arg_signature, compile_declaration(arg_type, Texts("_$", arg->name)));
49 if (arg->next) arg_signature = Texts(arg_signature, ", ");
51 arg_signature = Texts(arg_signature, ")");
53 type_t *ret_t = def->ret_type ? parse_type_ast(env, def->ret_type) : Type(VoidType);
54 Text_t ret_type_code = compile_type(ret_t);
55 Text_t name = Text$from_str(get_type_name(ret_t));
58 "Conversions are only supported for text, struct, and enum "
61 Text_t name_code = namespace_name(env, env->namespace, Texts(name, "$", get_line_number(ast->file, ast->start)));
62 return Texts(ret_type_code, " ", name_code, arg_signature, ";\n");
66 Text_t compile_arguments(env_t *env, ast_t *call_ast, arg_t *spec_args, arg_ast_t *call_args) {
67 Table_t used_args = EMPTY_TABLE;
68 Text_t code = EMPTY_TEXT;
69 env_t *default_scope = new (env_t);
70 *default_scope = *env;
71 default_scope->locals = new (Table_t, .fallback = env->namespace_bindings ? env->namespace_bindings : env->globals);
72 for (arg_t *spec_arg = spec_args; spec_arg; spec_arg = spec_arg->next) {
75 assert(spec_arg->name);
76 for (arg_ast_t *call_arg = call_args; call_arg; call_arg = call_arg->next) {
77 if (!call_arg->name) continue;
78 if (!(streq(call_arg->name, spec_arg->name) || (spec_arg->alias && streq(call_arg->name, spec_arg->alias))))
82 if (spec_arg->type->tag == IntType && call_arg->value->tag == Int) {
83 value = compile_int_to_type(env, call_arg->value, spec_arg->type);
84 } else if (spec_arg->type->tag == NumType && call_arg->value->tag == Int) {
85 OptionalInt_t int_val = Int$from_str(Match(call_arg->value, Int)->str);
86 if (int_val.small == 0) code_err(call_arg->value, "Failed to parse this integer");
87 if (Match(spec_arg->type, NumType)->bits == TYPE_NBITS64)
88 value = Text$from_str(String(hex_double(Num$from_int(int_val, false))));
89 else value = Text$from_str(String(hex_double((double)Num32$from_int(int_val, false)), "f"));
91 env_t *arg_env = with_enum_scope(env, spec_arg->type);
92 value = compile_maybe_incref(arg_env, call_arg->value, spec_arg->type);
94 Table$str_set(&used_args, call_arg->name, call_arg);
95 if (code.length > 0) code = Texts(code, ", ");
96 code = Texts(code, value);
101 for (arg_ast_t *call_arg = call_args; call_arg; call_arg = call_arg->next) {
102 if (call_arg->name) continue;
103 const char *pseudoname = String(i++);
104 if (!Table$str_get(used_args, pseudoname)) {
106 if (spec_arg->type->tag == IntType && call_arg->value->tag == Int) {
107 value = compile_int_to_type(env, call_arg->value, spec_arg->type);
108 } else if (spec_arg->type->tag == NumType && call_arg->value->tag == Int) {
109 OptionalInt_t int_val = Int$from_str(Match(call_arg->value, Int)->str);
110 if (int_val.small == 0) code_err(call_arg->value, "Failed to parse this integer");
111 if (Match(spec_arg->type, NumType)->bits == TYPE_NBITS64)
112 value = Text$from_str(String(hex_double(Num$from_int(int_val, false))));
113 else value = Text$from_str(String(hex_double((double)Num32$from_int(int_val, false)), "f"));
115 env_t *arg_env = with_enum_scope(env, spec_arg->type);
116 value = compile_maybe_incref(arg_env, call_arg->value, spec_arg->type);
119 Table$str_set(&used_args, pseudoname, call_arg);
120 if (code.length > 0) code = Texts(code, ", ");
121 code = Texts(code, value);
126 if (spec_arg->default_val) {
127 if (code.length > 0) code = Texts(code, ", ");
128 code = Texts(code, compile_maybe_incref(default_scope, spec_arg->default_val, get_arg_type(env, spec_arg)));
132 assert(spec_arg->name);
133 code_err(call_ast, "The required argument '", spec_arg->name, "' was not provided");
139 for (arg_ast_t *call_arg = call_args; call_arg; call_arg = call_arg->next) {
140 if (call_arg->name) {
141 if (!Table$str_get(used_args, call_arg->name))
142 code_err(call_arg->value, "There is no argument with the name '", call_arg->name, "'");
144 const char *pseudoname = String(i++);
145 if (!Table$str_get(used_args, pseudoname)) code_err(call_arg->value, "This is one argument too many!");
152 Text_t compile_function_call(env_t *env, ast_t *ast) {
153 DeclareMatch(call, ast, FunctionCall);
154 type_t *fn_t = get_type(env, call->fn);
155 if (fn_t->tag == FunctionType) {
156 Text_t fn = compile(env, call->fn);
157 if (!is_valid_call(env, Match(fn_t, FunctionType)->args, call->args, (call_opts_t){.promotion = true})) {
158 if (is_valid_call(env, Match(fn_t, FunctionType)->args, call->args,
159 (call_opts_t){.promotion = true, .underscores = true})) {
160 code_err(ast, "You can't pass underscore arguments to this function as positional arguments. You must "
161 "use keyword arguments.");
164 for (arg_ast_t *a = call->args; a; a = a->next)
165 args = new (arg_t, .name = a->name, .type = get_type(env, a->value), .next = args);
168 "This function's signature doesn't match this call site. \n"
169 " The function takes these args: (",
170 arg_types_to_text(Match(fn_t, FunctionType)->args, ", "),
172 " But it's being called with: (",
173 arg_types_to_text(args, ", "), ")");
176 return Texts(fn, "(", compile_arguments(env, ast, Match(fn_t, FunctionType)->args, call->args), ")");
177 } else if (fn_t->tag == TypeInfoType) {
178 type_t *t = Match(fn_t, TypeInfoType)->type;
180 // Literal constructors for numeric types like `Byte(123)` should
181 // not go through any conversion, just a cast:
182 if (is_numeric_type(t) && call->args && !call->args->next && call->args->value->tag == Int)
183 return compile_to_type(env, call->args->value, t);
184 else if (t->tag == NumType && call->args && !call->args->next && call->args->value->tag == Num)
185 return compile_to_type(env, call->args->value, t);
187 binding_t *constructor =
188 get_constructor(env, t, call->args, env->current_type != NULL && type_eq(env->current_type, t));
190 arg_t *arg_spec = Match(constructor->type, FunctionType)->args;
191 return Texts(constructor->code, "(", compile_arguments(env, ast, arg_spec, call->args), ")");
194 if (t->tag == TextType) {
195 if (!call->args) code_err(ast, "This constructor needs a value");
196 if (!type_eq(t, TEXT_TYPE))
197 code_err(call->fn, "I don't have a constructor defined for "
200 if (!call->args || call->args->next) code_err(call->fn, "This constructor takes exactly 1 argument");
201 type_t *actual = call->args ? get_type(env, call->args->value) : NULL;
202 if (type_eq(actual, t)) return compile(env, call->args->value);
203 return expr_as_text(compile(env, call->args->value), actual, Text("no"));
204 } else if (t->tag == CStringType) {
205 // C String constructor:
206 if (!call->args || call->args->next) code_err(call->fn, "This constructor takes exactly 1 argument");
207 if (call->args->value->tag == TextLiteral)
208 return compile_text_literal(Match(call->args->value, TextLiteral)->text);
209 else if (call->args->value->tag == TextJoin && Match(call->args->value, TextJoin)->children == NULL)
211 else if (call->args->value->tag == TextJoin && Match(call->args->value, TextJoin)->children->next == NULL)
212 return compile_text_literal(
213 Match(Match(call->args->value, TextJoin)->children->ast, TextLiteral)->text);
214 type_t *actual = call->args ? get_type(env, call->args->value) : NULL;
215 return Texts("Text$as_c_string(", expr_as_text(compile(env, call->args->value), actual, Text("no")), ")");
216 } else if (t->tag == StructType) {
217 return compile_struct_literal(env, ast, t, call->args);
220 "I could not find a constructor matching these arguments "
223 } else if (fn_t->tag == ClosureType) {
224 fn_t = Match(fn_t, ClosureType)->fn;
225 arg_t *type_args = Match(fn_t, FunctionType)->args;
227 arg_t *closure_fn_args = NULL;
228 for (arg_t *arg = Match(fn_t, FunctionType)->args; arg; arg = arg->next)
229 closure_fn_args = new (arg_t, .name = arg->name, .type = arg->type, .default_val = arg->default_val,
230 .next = closure_fn_args);
231 closure_fn_args = new (arg_t, .name = "userdata", .type = Type(PointerType, .pointed = Type(MemoryType)),
232 .next = closure_fn_args);
233 REVERSE_LIST(closure_fn_args);
234 Text_t fn_type_code =
235 compile_type(Type(FunctionType, .args = closure_fn_args, .ret = Match(fn_t, FunctionType)->ret));
237 Text_t closure = compile(env, call->fn);
238 Text_t arg_code = compile_arguments(env, ast, type_args, call->args);
239 if (arg_code.length > 0) arg_code = Texts(arg_code, ", ");
240 if (call->fn->tag == Var) {
241 return Texts("((", fn_type_code, ")", closure, ".fn)(", arg_code, closure, ".userdata)");
243 return Texts("({ Closure_t closure = ", closure, "; ((", fn_type_code, ")closure.fn)(", arg_code,
244 "closure.userdata); })");
247 code_err(call->fn, "This is not a function, it's a ", type_to_text(fn_t));
251 static void add_closed_vars(Table_t *closed_vars, env_t *enclosing_scope, env_t *env, ast_t *ast) {
252 if (ast == NULL) return;
256 binding_t *b = get_binding(enclosing_scope, Match(ast, Var)->name);
258 binding_t *shadow = get_binding(env, Match(ast, Var)->name);
259 if (!shadow || shadow == b) Table$str_set(closed_vars, Match(ast, Var)->name, b);
264 for (ast_list_t *child = Match(ast, TextJoin)->children; child; child = child->next)
265 add_closed_vars(closed_vars, enclosing_scope, env, child->ast);
269 ast_t *value = Match(ast, Declare)->value;
270 add_closed_vars(closed_vars, enclosing_scope, env, value);
271 bind_statement(env, ast);
275 for (ast_list_t *target = Match(ast, Assign)->targets; target; target = target->next)
276 add_closed_vars(closed_vars, enclosing_scope, env, target->ast);
277 for (ast_list_t *value = Match(ast, Assign)->values; value; value = value->next)
278 add_closed_vars(closed_vars, enclosing_scope, env, value->ast);
282 binary_operands_t binop = BINARY_OPERANDS(ast);
283 add_closed_vars(closed_vars, enclosing_scope, env, binop.lhs);
284 add_closed_vars(closed_vars, enclosing_scope, env, binop.rhs);
290 case StackReference: {
292 ast_t *value = ast->__data.Not.value;
294 add_closed_vars(closed_vars, enclosing_scope, env, value);
298 add_closed_vars(closed_vars, enclosing_scope, env, Match(ast, Min)->lhs);
299 add_closed_vars(closed_vars, enclosing_scope, env, Match(ast, Min)->rhs);
300 add_closed_vars(closed_vars, enclosing_scope, env, Match(ast, Min)->key);
304 add_closed_vars(closed_vars, enclosing_scope, env, Match(ast, Max)->lhs);
305 add_closed_vars(closed_vars, enclosing_scope, env, Match(ast, Max)->rhs);
306 add_closed_vars(closed_vars, enclosing_scope, env, Match(ast, Max)->key);
310 for (ast_list_t *item = Match(ast, List)->items; item; item = item->next)
311 add_closed_vars(closed_vars, enclosing_scope, env, item->ast);
315 add_closed_vars(closed_vars, enclosing_scope, env, Match(ast, Table)->default_value);
316 add_closed_vars(closed_vars, enclosing_scope, env, Match(ast, Table)->fallback);
317 for (ast_list_t *entry = Match(ast, Table)->entries; entry; entry = entry->next)
318 add_closed_vars(closed_vars, enclosing_scope, env, entry->ast);
322 add_closed_vars(closed_vars, enclosing_scope, env, Match(ast, TableEntry)->key);
323 add_closed_vars(closed_vars, enclosing_scope, env, Match(ast, TableEntry)->value);
326 case Comprehension: {
327 DeclareMatch(comp, ast, Comprehension);
328 if (comp->expr->tag == Comprehension) { // Nested comprehension
329 ast_t *body = comp->filter ? WrapAST(ast, If, .condition = comp->filter, .body = comp->expr) : comp->expr;
330 ast_t *loop = WrapAST(ast, For, .vars = comp->vars, .iter = comp->iter, .body = body);
331 return add_closed_vars(closed_vars, enclosing_scope, env, loop);
334 // List/Table comprehension:
335 ast_t *body = comp->expr;
336 if (comp->filter) body = WrapAST(comp->expr, If, .condition = comp->filter, .body = body);
337 ast_t *loop = WrapAST(ast, For, .vars = comp->vars, .iter = comp->iter, .body = body);
338 add_closed_vars(closed_vars, enclosing_scope, env, loop);
342 DeclareMatch(lambda, ast, Lambda);
343 env_t *lambda_scope = fresh_scope(env);
344 for (arg_ast_t *arg = lambda->args; arg; arg = arg->next)
345 set_binding(lambda_scope, arg->name, get_arg_ast_type(env, arg), Texts("_$", arg->name));
346 add_closed_vars(closed_vars, enclosing_scope, lambda_scope, lambda->body);
350 add_closed_vars(closed_vars, enclosing_scope, env, Match(ast, FunctionCall)->fn);
351 for (arg_ast_t *arg = Match(ast, FunctionCall)->args; arg; arg = arg->next)
352 add_closed_vars(closed_vars, enclosing_scope, env, arg->value);
356 add_closed_vars(closed_vars, enclosing_scope, env, Match(ast, MethodCall)->self);
357 for (arg_ast_t *arg = Match(ast, MethodCall)->args; arg; arg = arg->next)
358 add_closed_vars(closed_vars, enclosing_scope, env, arg->value);
362 env = fresh_scope(env);
363 for (ast_list_t *statement = Match(ast, Block)->statements; statement; statement = statement->next)
364 add_closed_vars(closed_vars, enclosing_scope, env, statement->ast);
368 add_closed_vars(closed_vars, enclosing_scope, env, Match(ast, For)->iter);
369 env_t *body_scope = for_scope(env, ast);
370 add_closed_vars(closed_vars, enclosing_scope, body_scope, Match(ast, For)->body);
371 add_closed_vars(closed_vars, enclosing_scope, env, Match(ast, For)->empty);
375 DeclareMatch(while_, ast, While);
376 add_closed_vars(closed_vars, enclosing_scope, env, while_->condition);
377 env_t *scope = fresh_scope(env);
378 add_closed_vars(closed_vars, enclosing_scope, scope, while_->body);
382 DeclareMatch(if_, ast, If);
383 ast_t *condition = if_->condition;
384 if (condition->tag == Declare) {
385 env_t *truthy_scope = fresh_scope(env);
386 bind_statement(truthy_scope, condition);
387 if (!Match(condition, Declare)->value)
388 code_err(condition, "This declared variable must have an initial value");
389 add_closed_vars(closed_vars, enclosing_scope, env, Match(condition, Declare)->value);
390 ast_t *var = Match(condition, Declare)->var;
391 type_t *cond_t = get_type(truthy_scope, var);
392 if (cond_t->tag == OptionalType) {
393 set_binding(truthy_scope, Match(var, Var)->name, Match(cond_t, OptionalType)->type, EMPTY_TEXT);
395 add_closed_vars(closed_vars, enclosing_scope, truthy_scope, if_->body);
396 add_closed_vars(closed_vars, enclosing_scope, env, if_->else_body);
398 add_closed_vars(closed_vars, enclosing_scope, env, condition);
399 env_t *truthy_scope = env;
400 type_t *cond_t = get_type(env, condition);
401 if (condition->tag == Var && cond_t->tag == OptionalType) {
402 truthy_scope = fresh_scope(env);
403 set_binding(truthy_scope, Match(condition, Var)->name, Match(cond_t, OptionalType)->type, EMPTY_TEXT);
405 add_closed_vars(closed_vars, enclosing_scope, truthy_scope, if_->body);
406 add_closed_vars(closed_vars, enclosing_scope, env, if_->else_body);
411 DeclareMatch(when, ast, When);
412 add_closed_vars(closed_vars, enclosing_scope, env, when->subject);
413 type_t *subject_t = get_type(env, when->subject);
415 if (subject_t->tag != EnumType) {
416 for (when_clause_t *clause = when->clauses; clause; clause = clause->next) {
417 add_closed_vars(closed_vars, enclosing_scope, env, clause->pattern);
418 add_closed_vars(closed_vars, enclosing_scope, env, clause->body);
421 if (when->else_body) add_closed_vars(closed_vars, enclosing_scope, env, when->else_body);
425 DeclareMatch(enum_t, subject_t, EnumType);
426 for (when_clause_t *clause = when->clauses; clause; clause = clause->next) {
427 const char *clause_tag_name;
428 if (clause->pattern->tag == Var) clause_tag_name = Match(clause->pattern, Var)->name;
429 else if (clause->pattern->tag == FunctionCall && Match(clause->pattern, FunctionCall)->fn->tag == Var)
430 clause_tag_name = Match(Match(clause->pattern, FunctionCall)->fn, Var)->name;
431 else code_err(clause->pattern, "This is not a valid pattern for a ", type_to_text(subject_t), " enum");
433 type_t *tag_type = NULL;
434 for (tag_t *tag = enum_t->tags; tag; tag = tag->next) {
435 if (streq(tag->name, clause_tag_name)) {
436 tag_type = tag->type;
441 env_t *scope = when_clause_scope(env, subject_t, clause);
442 add_closed_vars(closed_vars, enclosing_scope, scope, clause->body);
444 if (when->else_body) add_closed_vars(closed_vars, enclosing_scope, env, when->else_body);
448 add_closed_vars(closed_vars, enclosing_scope, env, Match(ast, Repeat)->body);
452 DeclareMatch(reduction, ast, Reduction);
453 static int64_t next_id = 1;
454 ast_t *item = FakeAST(Var, String("$it", next_id++));
456 FakeAST(For, .vars = new (ast_list_t, .ast = item), .iter = reduction->iter, .body = FakeAST(Pass));
457 env_t *scope = for_scope(env, loop);
458 add_closed_vars(closed_vars, enclosing_scope, scope, reduction->key ? reduction->key : item);
462 add_closed_vars(closed_vars, enclosing_scope, env, Match(ast, Defer)->body);
466 ast_t *ret = Match(ast, Return)->value;
467 if (ret) add_closed_vars(closed_vars, enclosing_scope, env, ret);
471 add_closed_vars(closed_vars, enclosing_scope, env, Match(ast, Index)->indexed);
472 add_closed_vars(closed_vars, enclosing_scope, env, Match(ast, Index)->index);
476 add_closed_vars(closed_vars, enclosing_scope, env, Match(ast, FieldAccess)->fielded);
480 add_closed_vars(closed_vars, enclosing_scope, env, Match(ast, NonOptional)->value);
484 for (ast_list_t *value = Match(ast, DebugLog)->values; value; value = value->next)
485 add_closed_vars(closed_vars, enclosing_scope, env, value->ast);
489 add_closed_vars(closed_vars, enclosing_scope, env, Match(ast, Assert)->expr);
490 add_closed_vars(closed_vars, enclosing_scope, env, Match(ast, Assert)->message);
493 case ExplicitlyTyped: {
494 add_closed_vars(closed_vars, enclosing_scope, env, Match(ast, ExplicitlyTyped)->ast);
502 case LangDef: errx(1, "Definitions should not be reachable in a closure.");
508 Table_t get_closed_vars(env_t *env, arg_ast_t *args, ast_t *block) {
509 env_t *body_scope = fresh_scope(env);
510 for (arg_ast_t *arg = args; arg; arg = arg->next) {
511 type_t *arg_type = get_arg_ast_type(env, arg);
512 set_binding(body_scope, arg->name, arg_type, Texts("_$", arg->name));
515 Table_t closed_vars = EMPTY_TABLE;
516 add_closed_vars(&closed_vars, env, body_scope, block);
520 static visit_behavior_t find_used_variables(ast_t *ast, void *userdata) {
521 Table_t *vars = (Table_t *)userdata;
524 const char *name = Match(ast, Var)->name;
525 Table$str_set(vars, name, ast);
529 for (ast_list_t *target = Match(ast, Assign)->targets; target; target = target->next) {
530 ast_t *var = target->ast;
532 if (var->tag == Index) {
533 ast_t *index = Match(var, Index)->index;
534 if (index) ast_visit(index, find_used_variables, userdata);
535 var = Match(var, Index)->indexed;
536 } else if (var->tag == FieldAccess) {
537 var = Match(var, FieldAccess)->fielded;
543 for (ast_list_t *val = Match(ast, Assign)->values; val; val = val->next) {
544 ast_visit(val->ast, find_used_variables, userdata);
549 binary_operands_t operands = BINARY_OPERANDS(ast);
550 ast_t *lhs = operands.lhs;
552 if (lhs->tag == Index) {
553 ast_t *index = Match(lhs, Index)->index;
554 if (index) ast_visit(index, find_used_variables, userdata);
555 lhs = Match(lhs, Index)->indexed;
556 } else if (lhs->tag == FieldAccess) {
557 lhs = Match(lhs, FieldAccess)->fielded;
562 ast_visit(operands.rhs, find_used_variables, userdata);
566 ast_visit(Match(ast, Declare)->value, find_used_variables, userdata);
569 default: return VISIT_PROCEED;
573 static visit_behavior_t find_assigned_variables(ast_t *ast, void *userdata) {
574 Table_t *vars = (Table_t *)userdata;
577 for (ast_list_t *target = Match(ast, Assign)->targets; target; target = target->next) {
578 ast_t *var = target->ast;
580 if (var->tag == Index) var = Match(var, Index)->indexed;
581 else if (var->tag == FieldAccess) var = Match(var, FieldAccess)->fielded;
584 if (var->tag == Var) {
585 const char *name = Match(var, Var)->name;
586 Table$str_set(vars, name, var);
591 binary_operands_t operands = BINARY_OPERANDS(ast);
592 ast_t *var = operands.lhs;
594 if (var->tag == Index) var = Match(var, Index)->indexed;
595 else if (var->tag == FieldAccess) var = Match(var, FieldAccess)->fielded;
598 if (var->tag == Var) {
599 const char *name = Match(var, Var)->name;
600 Table$str_set(vars, name, var);
605 ast_t *var = Match(ast, Declare)->var;
606 const char *name = Match(var, Var)->name;
607 Table$str_set(vars, name, var);
610 default: return VISIT_PROCEED;
614 static void check_unused_vars(env_t *env, arg_ast_t *args, ast_t *body) {
615 Table_t used_vars = EMPTY_TABLE;
616 ast_visit(body, find_used_variables, &used_vars);
617 Table_t assigned_vars = EMPTY_TABLE;
618 ast_visit(body, find_assigned_variables, &assigned_vars);
620 for (arg_ast_t *arg = args; arg; arg = arg->next) {
621 type_t *arg_type = get_arg_ast_type(env, arg);
622 if (arg_type->tag == PointerType) {
623 Table$str_remove(&assigned_vars, arg->name);
627 Table_t unused = Table$without(assigned_vars, used_vars, Table$info(&CString$info, &Present$$info));
628 for (int64_t i = 0; i < (int64_t)unused.entries.length; i++) {
631 } *entry = unused.entries.data + i * unused.entries.stride;
632 if (streq(entry->name, "_")) continue;
633 // Global/file scoped vars are okay to mutate without reading
634 if (get_binding(env, entry->name) != NULL) continue;
635 ast_t *var = Table$str_get(assigned_vars, entry->name);
637 code_err(var, "This variable was assigned to, but never read from.");
642 Text_t compile_lambda(env_t *env, ast_t *ast) {
643 DeclareMatch(lambda, ast, Lambda);
644 Text_t name = namespace_name(env, env->namespace, Texts("lambda$", lambda->id));
646 env_t *body_scope = fresh_scope(env);
647 body_scope->deferred = NULL;
648 for (arg_ast_t *arg = lambda->args; arg; arg = arg->next) {
649 type_t *arg_type = get_arg_ast_type(env, arg);
650 set_binding(body_scope, arg->name, arg_type, Texts("_$", arg->name));
653 body_scope->fn = ast;
655 Table_t closed_vars = get_closed_vars(env, lambda->args, ast);
656 if (Table$length(closed_vars) > 0) { // Create a typedef for the lambda's closure userdata
657 Text_t def = Text("typedef struct {");
658 for (int64_t i = 0; i < (int64_t)closed_vars.entries.length; i++) {
662 } *entry = closed_vars.entries.data + closed_vars.entries.stride * i;
663 if (has_stack_memory(entry->b->type))
664 code_err(ast, "This function is holding onto a reference to ", type_to_text(entry->b->type),
665 " stack memory in the variable `", entry->name,
666 "`, but the function may outlive the stack memory");
667 if (entry->b->type->tag == ModuleType) continue;
668 set_binding(body_scope, entry->name, entry->b->type, Texts("userdata->", entry->name));
669 def = Texts(def, compile_declaration(entry->b->type, Text$from_str(entry->name)), "; ");
671 def = Texts(def, "} ", name, "$userdata_t;");
672 env->code->local_typedefs = Texts(env->code->local_typedefs, def);
675 type_t *ret_t = get_function_return_type(env, ast);
676 Text_t code = Texts("static ", compile_type(ret_t), " ", name, "(");
677 for (arg_ast_t *arg = lambda->args; arg; arg = arg->next) {
678 type_t *arg_type = get_arg_ast_type(env, arg);
679 code = Texts(code, compile_type(arg_type), " _$", arg->name, ", ");
683 if (Table$length(closed_vars) == 0) {
684 code = Texts(code, "void *_)");
685 userdata = Text("NULL");
687 userdata = Texts("new(", name, "$userdata_t");
688 for (int64_t i = 0; i < (int64_t)closed_vars.entries.length; i++) {
692 } *entry = closed_vars.entries.data + closed_vars.entries.stride * i;
693 if (entry->b->type->tag == ModuleType) continue;
694 binding_t *b = get_binding(env, entry->name);
696 Text_t binding_code = b->code;
697 if (entry->b->type->tag == ListType) userdata = Texts(userdata, ", LIST_COPY(", binding_code, ")");
698 else if (entry->b->type->tag == TableType) userdata = Texts(userdata, ", TABLE_COPY(", binding_code, ")");
699 else userdata = Texts(userdata, ", ", binding_code);
701 userdata = Texts(userdata, ")");
702 code = Texts(code, name, "$userdata_t *userdata)");
705 Text_t body = EMPTY_TEXT;
706 for (ast_list_t *stmt = Match(lambda->body, Block)->statements; stmt; stmt = stmt->next) {
707 if (stmt->next || ret_t->tag == VoidType || ret_t->tag == AbortType
708 || get_type(body_scope, stmt->ast)->tag == ReturnType)
709 body = Texts(body, compile_statement(body_scope, stmt->ast), "\n");
710 else body = Texts(body, compile_statement(body_scope, FakeAST(Return, stmt->ast)), "\n");
711 bind_statement(body_scope, stmt->ast);
713 if ((ret_t->tag == VoidType || ret_t->tag == AbortType) && body_scope->deferred)
714 body = Texts(body, compile_statement(body_scope, FakeAST(Return)), "\n");
716 env->code->lambdas = Texts(env->code->lambdas, code, " {\n", body, "\n}\n");
718 check_unused_vars(env, lambda->args, lambda->body);
720 return Texts("((Closure_t){", name, ", ", userdata, "})");
724 Text_t compile_function(env_t *env, Text_t name_code, ast_t *ast, Text_t *staticdefs) {
725 bool is_private = false;
726 const char *function_name;
728 type_t *ret_t = get_function_return_type(env, ast);
732 if (ast->tag == FunctionDef) {
733 DeclareMatch(fndef, ast, FunctionDef);
734 function_name = Match(fndef->name, Var)->name;
735 is_private = function_name[0] == '_';
738 cache = fndef->cache;
739 is_inline = fndef->is_inline;
741 DeclareMatch(convertdef, ast, ConvertDef);
742 args = convertdef->args;
743 function_name = get_type_name(ret_t);
746 "Conversions are only supported for text, struct, and enum "
748 type_to_text(ret_t));
749 body = convertdef->body;
750 cache = convertdef->cache;
751 is_inline = convertdef->is_inline;
754 Text_t arg_signature = Text("(");
755 Table_t used_names = EMPTY_TABLE;
756 for (arg_ast_t *arg = args; arg; arg = arg->next) {
757 type_t *arg_type = get_arg_ast_type(env, arg);
758 arg_signature = Texts(arg_signature, compile_declaration(arg_type, Texts("_$", arg->name)));
759 if (arg->next) arg_signature = Texts(arg_signature, ", ");
760 if (Table$str_get(used_names, arg->name))
761 code_err(ast, "The argument name '", arg->name, "' is used more than once");
762 Table$str_set(&used_names, arg->name, arg->name);
764 arg_signature = Texts(arg_signature, ")");
766 Text_t ret_type_code = compile_type(ret_t);
767 if (ret_t->tag == AbortType) ret_type_code = Texts("__attribute__((noreturn)) _Noreturn ", ret_type_code);
769 if (is_private) *staticdefs = Texts(*staticdefs, "static ", ret_type_code, " ", name_code, arg_signature, ";\n");
773 code = Texts("static ", ret_type_code, " ", name_code, "$uncached", arg_signature);
775 code = Texts(ret_type_code, " ", name_code, arg_signature);
776 if (is_inline) code = Texts("INLINE ", code);
777 if (!is_private) code = Texts("public ", code);
780 env_t *body_scope = fresh_scope(env);
781 while (body_scope->namespace) {
782 body_scope->locals->fallback = body_scope->locals->fallback->fallback;
783 body_scope->namespace = body_scope->namespace->parent;
786 body_scope->deferred = NULL;
787 for (arg_ast_t *arg = args; arg; arg = arg->next) {
788 type_t *arg_type = get_arg_ast_type(env, arg);
789 set_binding(body_scope, arg->name, arg_type, Texts("_$", arg->name));
792 body_scope->fn = ast;
794 type_t *body_type = get_type(body_scope, body);
795 if (ret_t->tag == AbortType) {
796 if (body_type->tag != AbortType) code_err(ast, "This function can reach the end without aborting!");
797 } else if (ret_t->tag == VoidType) {
798 if (body_type->tag == AbortType)
799 code_err(ast, "This function will always abort before it reaches the "
800 "end, but it's declared as having a Void return. It should "
801 "be declared as an Abort return instead.");
803 if (body_type->tag != ReturnType && body_type->tag != AbortType)
805 "This function looks like it can reach the end without "
809 "If this is not the case, please add a call to "
810 "`fail(\"Unreachable\")` at the end of the function to "
815 Text_t body_code = Texts("{\n", compile_inline_block(body_scope, body), "}\n");
816 Text_t definition = with_source_info(env, ast, Texts(code, " ", body_code, "\n"));
818 if (cache && args == NULL) { // no-args cache just uses a static var
820 Texts(is_private ? EMPTY_TEXT : Text("public "), ret_type_code, " ", name_code,
823 compile_declaration(ret_t, Text("cached_result")), ";\n", "static bool initialized = false;\n",
824 "if (!initialized) {\n"
825 "\tcached_result = ",
826 name_code, "$uncached();\n", "\tinitialized = true;\n", "}\n",
827 "return cached_result;\n"
829 definition = Texts(definition, wrapper);
830 } else if (cache && cache->tag == Int) {
832 OptionalInt64_t cache_size = Int64$parse(Text$from_str(Match(cache, Int)->str), NONE_INT, NULL);
833 Text_t pop_code = EMPTY_TEXT;
834 if (cache->tag == Int && cache_size.has_value && cache_size.value > 0) {
835 // FIXME: this currently just deletes the first entry, but this
836 // should be more like a least-recently-used cache eviction policy
837 // or least-frequently-used
838 pop_code = Texts("if (cache.entries.length > ", cache_size.value,
839 ") Table$remove(&cache, cache.entries.data + "
840 "cache.entries.stride*0, table_type);\n");
844 // Single-argument functions have simplified caching logic
845 type_t *arg_type = get_arg_ast_type(env, args);
847 Texts(is_private ? EMPTY_TEXT : Text("public "), ret_type_code, " ", name_code, arg_signature,
849 "static Table_t cache = EMPTY_TABLE;\n",
850 "const TypeInfo_t *table_type = Table$info(", compile_type_info(arg_type), ", ",
851 compile_type_info(ret_t), ");\n",
852 compile_declaration(Type(PointerType, .pointed = ret_t), Text("cached")),
853 " = Table$get_raw(cache, &_$", args->name,
855 "if (cached) return *cached;\n",
856 compile_declaration(ret_t, Text("ret")), " = ", name_code, "$uncached(_$", args->name, ");\n",
857 pop_code, "Table$set(&cache, &_$", args->name,
858 ", &ret, table_type);\n"
861 definition = Texts(definition, wrapper);
863 // Multi-argument functions use a custom struct type (only defined
864 // internally) as a cache key:
865 arg_t *fields = NULL;
866 for (arg_ast_t *arg = args; arg; arg = arg->next)
867 fields = new (arg_t, .name = arg->name, .type = get_arg_ast_type(env, arg), .next = fields);
868 REVERSE_LIST(fields);
869 type_t *t = Type(StructType, .name = String("func$", get_line_number(ast->file, ast->start), "$args"),
870 .fields = fields, .env = env);
872 int64_t num_fields = (int64_t)used_names.entries.length;
873 const char *metamethods = is_packed_data(t) ? "PackedData$metamethods" : "Struct$metamethods";
874 Text_t args_typeinfo = Texts("((TypeInfo_t[1]){{.size=sizeof(args), "
875 ".align=__alignof__(args), .metamethods=",
877 ", .tag=StructInfo, "
878 ".StructInfo.name=\"FunctionArguments\", "
879 ".StructInfo.num_fields=",
880 num_fields, ", .StructInfo.fields=(NamedType_t[", num_fields, "]){");
881 Text_t args_type = Text("struct { ");
882 for (arg_t *f = fields; f; f = f->next) {
883 args_typeinfo = Texts(args_typeinfo, "{\"", f->name, "\", ", compile_type_info(f->type), "}");
884 args_type = Texts(args_type, compile_declaration(f->type, Text$from_str(f->name)), "; ");
885 if (f->next) args_typeinfo = Texts(args_typeinfo, ", ");
887 args_type = Texts(args_type, "}");
888 args_typeinfo = Texts(args_typeinfo, "}}})");
890 Text_t all_args = EMPTY_TEXT;
891 for (arg_ast_t *arg = args; arg; arg = arg->next)
892 all_args = Texts(all_args, "_$", arg->name, arg->next ? Text(", ") : EMPTY_TEXT);
894 Text_t wrapper = Texts(
895 is_private ? EMPTY_TEXT : Text("public "), ret_type_code, " ", name_code, arg_signature,
897 "static Table_t cache = EMPTY_TABLE;\n",
898 args_type, " args = {", all_args,
900 "const TypeInfo_t *table_type = Table$info(",
901 args_typeinfo, ", ", compile_type_info(ret_t), ");\n",
902 compile_declaration(Type(PointerType, .pointed = ret_t), Text("cached")),
903 " = Table$get_raw(cache, &args, table_type);\n"
904 "if (cached) return *cached;\n",
905 compile_declaration(ret_t, Text("ret")), " = ", name_code, "$uncached(", all_args, ");\n", pop_code,
906 "Table$set(&cache, &args, &ret, table_type);\n"
909 definition = Texts(definition, wrapper);
913 check_unused_vars(env, args, body);
919 Text_t compile_method_call(env_t *env, ast_t *ast) {
920 DeclareMatch(call, ast, MethodCall);
921 type_t *self_t = get_type(env, call->self);
922 type_t *self_value_t = value_type(self_t);
923 if (self_value_t->tag == TypeInfoType || self_value_t->tag == ModuleType) {
924 return compile(env, WrapAST(ast, FunctionCall,
925 .fn = WrapAST(call->self, FieldAccess, .fielded = call->self, .field = call->name),
926 .args = call->args));
929 type_t *field_type = get_field_type(self_value_t, call->name);
930 if (field_type && field_type->tag == ClosureType) field_type = Match(field_type, ClosureType)->fn;
931 if (field_type && field_type->tag == FunctionType)
932 return compile(env, WrapAST(ast, FunctionCall,
933 .fn = WrapAST(call->self, FieldAccess, .fielded = call->self, .field = call->name),
934 .args = call->args));
936 switch (self_value_t->tag) {
937 case ListType: return compile_list_method_call(env, ast);
938 case TableType: return compile_table_method_call(env, ast);
940 DeclareMatch(methodcall, ast, MethodCall);
941 type_t *fn_t = get_method_type(env, methodcall->self, methodcall->name);
942 arg_ast_t *args = new (arg_ast_t, .value = methodcall->self, .next = methodcall->args);
943 binding_t *b = get_namespace_binding(env, methodcall->self, methodcall->name);
945 OptionalText_t suggestion = suggest_best_name(call->name, get_method_names(env, self_value_t));
946 code_err(ast, "No such method!", suggestion);
948 return Texts(b->code, "(", compile_arguments(env, ast, Match(fn_t, FunctionType)->args, args), ")");