aboutsummaryrefslogtreecommitdiff
path: root/src/compile
diff options
context:
space:
mode:
authorBruce Hill <bruce@bruce-hill.com>2025-12-11 13:50:01 -0500
committerBruce Hill <bruce@bruce-hill.com>2025-12-11 13:52:46 -0500
commit7f8f2117799cdfa6b62909a9182b5adade1d0bd2 (patch)
tree1db466db870768e952f50572453660e090e434e0 /src/compile
parent630f910563b6f27dd34a4a0496a43d32539eadcb (diff)
parent02886fab651d3f64d2c8ded5597e6c075dc69b5f (diff)
Merge branch 'dev' into constructive-reals
Diffstat (limited to 'src/compile')
-rw-r--r--src/compile/assertions.c4
-rw-r--r--src/compile/assignments.c10
-rw-r--r--src/compile/cli.c159
-rw-r--r--src/compile/cli.h5
-rw-r--r--src/compile/comparisons.c8
-rw-r--r--src/compile/conditionals.c15
-rw-r--r--src/compile/enums.c42
-rw-r--r--src/compile/expressions.c45
-rw-r--r--src/compile/files.c6
-rw-r--r--src/compile/functions.c298
-rw-r--r--src/compile/headers.c73
-rw-r--r--src/compile/lists.c4
-rw-r--r--src/compile/optionals.c69
-rw-r--r--src/compile/pointers.c4
-rw-r--r--src/compile/promotions.c31
-rw-r--r--src/compile/statements.c11
-rw-r--r--src/compile/tables.c10
-rw-r--r--src/compile/types.c3
-rw-r--r--src/compile/whens.c20
19 files changed, 493 insertions, 324 deletions
diff --git a/src/compile/assertions.c b/src/compile/assertions.c
index 5746b21e..34055998 100644
--- a/src/compile/assertions.c
+++ b/src/compile/assertions.c
@@ -33,7 +33,9 @@ Text_t compile_assertion(env_t *env, ast_t *ast) {
type_t *lhs_t = get_type(env, cmp.lhs);
type_t *rhs_t = get_type(with_enum_scope(env, lhs_t), cmp.rhs);
type_t *operand_t;
- if (cmp.lhs->tag == Int && is_numeric_type(rhs_t)) {
+ if (type_eq(lhs_t, rhs_t)) {
+ operand_t = lhs_t;
+ } else if (cmp.lhs->tag == Int && is_numeric_type(rhs_t)) {
operand_t = rhs_t;
} else if (cmp.rhs->tag == Int && is_numeric_type(lhs_t)) {
operand_t = lhs_t;
diff --git a/src/compile/assignments.c b/src/compile/assignments.c
index 84c43153..8c6af3ee 100644
--- a/src/compile/assignments.c
+++ b/src/compile/assignments.c
@@ -101,7 +101,7 @@ Text_t compile_assignment_statement(env_t *env, ast_t *ast) {
"variable's scope may outlive the scope of the "
"stack memory.");
env_t *val_env = with_enum_scope(env, lhs_t);
- Text_t val = compile_to_type(val_env, assign->values->ast, lhs_t);
+ Text_t val = compile_maybe_incref(val_env, assign->values->ast, lhs_t);
return Texts(compile_assignment(env, assign->targets->ast, val), ";\n");
}
@@ -120,7 +120,7 @@ Text_t compile_assignment_statement(env_t *env, ast_t *ast) {
"variable's scope may outlive the scope of the "
"stack memory.");
env_t *val_env = with_enum_scope(env, lhs_t);
- Text_t val = compile_to_type(val_env, value->ast, lhs_t);
+ Text_t val = compile_maybe_incref(val_env, value->ast, lhs_t);
code = Texts(code, compile_type(lhs_t), " $", i, " = ", val, ";\n");
i += 1;
}
@@ -178,13 +178,13 @@ Text_t compile_lvalue(env_t *env, ast_t *ast) {
type_t *value_type = get_type(env, table_type->default_value);
return Texts("*Table$get_or_setdefault(", compile_to_pointer_depth(env, index->indexed, 1, false), ", ",
compile_type(table_type->key_type), ", ", compile_type(value_type), ", ",
- compile_to_type(env, index->index, table_type->key_type), ", ",
- compile_to_type(env, table_type->default_value, table_type->value_type), ", ",
+ compile_maybe_incref(env, index->index, table_type->key_type), ", ",
+ compile_maybe_incref(env, table_type->default_value, table_type->value_type), ", ",
compile_type_info(container_t), ")");
}
return Texts("*(", compile_type(Type(PointerType, table_type->value_type)), ")Table$reserve(",
compile_to_pointer_depth(env, index->indexed, 1, false), ", ", "stack(",
- compile_to_type(env, index->index, table_type->key_type), ")", ", NULL,",
+ compile_maybe_incref(env, index->index, table_type->key_type), ")", ", NULL,",
compile_type_info(container_t), ")");
} else {
code_err(ast, "I don't know how to assign to this target");
diff --git a/src/compile/cli.c b/src/compile/cli.c
index e3d2329f..63a467ca 100644
--- a/src/compile/cli.c
+++ b/src/compile/cli.c
@@ -38,7 +38,7 @@ static Text_t get_flag_options(type_t *t, Text_t separator) {
} else if (t->tag == ListType) {
Text_t item_option = get_flag_options(Match(t, ListType)->item_type, separator);
return Texts(item_option, "1 ", item_option, "2...");
- } else if (t->tag == TableType && Match(t, TableType)->value_type == EMPTY_TYPE) {
+ } else if (t->tag == TableType && Match(t, TableType)->value_type == PRESENT_TYPE) {
Text_t item_option = get_flag_options(Match(t, TableType)->key_type, separator);
return Texts(item_option, "1 ", item_option, "2...");
} else if (t->tag == TableType) {
@@ -58,80 +58,87 @@ static OptionalText_t flagify(const char *name, bool prefix) {
return flag;
}
-public
-Text_t compile_cli_arg_call(env_t *env, Text_t fn_name, type_t *fn_type, const char *version) {
+static Text_t generate_usage(env_t *env, type_t *fn_type) {
DeclareMatch(fn_info, fn_type, FunctionType);
-
- env_t *main_env = fresh_scope(env);
-
- Text_t code = EMPTY_TEXT;
- binding_t *usage_binding = get_binding(env, "_USAGE");
- Text_t usage_code = usage_binding ? usage_binding->code : Text("usage");
- binding_t *help_binding = get_binding(env, "_HELP");
- Text_t help_code = help_binding ? help_binding->code : usage_code;
- if (!usage_binding) {
- bool explicit_help_flag = false;
- for (arg_t *arg = fn_info->args; arg; arg = arg->next) {
- if (streq(arg->name, "help")) {
- explicit_help_flag = true;
- break;
- }
+ bool explicit_help_flag = false;
+ for (arg_t *arg = fn_info->args; arg; arg = arg->next) {
+ if (streq(arg->name, "help")) {
+ explicit_help_flag = true;
+ break;
}
-
- Text_t usage = explicit_help_flag ? EMPTY_TEXT : Text(" [\x1b[1m--help\x1b[m]");
- for (arg_t *arg = fn_info->args; arg; arg = arg->next) {
- usage = Texts(usage, " ");
- type_t *t = get_arg_type(main_env, arg);
- OptionalText_t flag = flagify(arg->name, arg->default_val != NULL);
- assert(flag.tag != TEXT_NONE);
- OptionalText_t alias_flag = flagify(arg->alias, arg->default_val != NULL);
- Text_t flags = Texts("\x1b[1m", flag, "\x1b[m");
- if (alias_flag.tag != TEXT_NONE) flags = Texts(flags, ",\x1b[1m", alias_flag, "\x1b[m");
+ }
+ env_t *main_env = fresh_scope(env);
+ Text_t usage = explicit_help_flag ? EMPTY_TEXT : Text(" [\x1b[1m--help\x1b[m]");
+ for (arg_t *arg = fn_info->args; arg; arg = arg->next) {
+ usage = Texts(usage, " ");
+ type_t *t = get_arg_type(main_env, arg);
+ OptionalText_t flag = flagify(arg->name, arg->default_val != NULL);
+ assert(flag.tag != TEXT_NONE);
+ Text_t flags = Texts("\x1b[1m", flag, "\x1b[m");
+ if (t->tag == BoolType || (t->tag == OptionalType && Match(t, OptionalType)->type->tag == BoolType))
+ flags = Texts(flags, "|\x1b[1m--no-", Text$without_prefix(flag, Text("--")), "\x1b[m");
+ if (arg->default_val || value_type(t)->tag == BoolType) {
if (t->tag == BoolType || (t->tag == OptionalType && Match(t, OptionalType)->type->tag == BoolType))
- flags = Texts(flags, "|\x1b[1m--no-", Text$without_prefix(flag, Text("--")), "\x1b[m");
- if (arg->default_val || value_type(t)->tag == BoolType) {
- if (t->tag == BoolType || (t->tag == OptionalType && Match(t, OptionalType)->type->tag == BoolType))
- usage = Texts(usage, "[", flags, "]");
- else if (t->tag == ListType) usage = Texts(usage, "[", flags, " ", get_flag_options(t, Text("|")), "]");
- else usage = Texts(usage, "[", flags, " ", get_flag_options(t, Text("|")), "]");
- } else {
- usage = Texts(usage, "\x1b[1m", get_flag_options(t, Text("|")), "\x1b[m");
- }
+ usage = Texts(usage, "[", flags, "]");
+ else if (t->tag == ListType) usage = Texts(usage, "[", flags, " ", get_flag_options(t, Text("|")), "]");
+ else if (t->tag == EnumType) usage = Texts(usage, "[", flags, " val]");
+ else usage = Texts(usage, "[", flags, " ", get_flag_options(t, Text("|")), "]");
+ } else if (t->tag == EnumType) {
+ usage = Texts(usage, "\x1b[1m", flag, "\x1b[m");
+ } else {
+ usage = Texts(usage, "\x1b[1m", get_flag_options(t, Text("|")), "\x1b[m");
}
- code = Texts(code,
- "Text_t usage = Texts(Text(\"\\x1b[1mUsage:\\x1b[m \"), "
- "Text$from_str(argv[0])",
- usage.length == 0 ? EMPTY_TEXT : Texts(", Text(", quoted_text(usage), ")"), ");\n");
}
- if (!help_binding) {
- Text_t help_text = fn_info->args ? Text("\n") : Text("\n\n\x1b[2;3m No arguments...\x1b[m");
-
- for (arg_t *arg = fn_info->args; arg; arg = arg->next) {
- help_text = Texts(help_text, "\n");
- type_t *t = get_arg_type(main_env, arg);
- OptionalText_t flag = flagify(arg->name, true);
- assert(flag.tag != TEXT_NONE);
- OptionalText_t alias_flag = flagify(arg->alias, true);
- Text_t flags = Texts("\x1b[33;1m", flag, "\x1b[m");
- if (alias_flag.tag != TEXT_NONE) flags = Texts(flags, ",\x1b[33;1m", alias_flag, "\x1b[m");
- if (t->tag == BoolType || (t->tag == OptionalType && Match(t, OptionalType)->type->tag == BoolType))
- flags = Texts(flags, "|\x1b[33;1m--no-", Text$without_prefix(flag, Text("--")), "\x1b[m");
- if (t->tag == BoolType || (t->tag == OptionalType && Match(t, OptionalType)->type->tag == BoolType))
- help_text = Texts(help_text, " ", flags);
- else
- help_text = Texts(help_text, " ", flags, " \x1b[1;34m",
- get_flag_options(t, Text("\x1b[m | \x1b[1;34m")), "\x1b[m");
-
- if (arg->comment.length > 0) help_text = Texts(help_text, " \x1b[3m", arg->comment, "\x1b[m");
- if (arg->default_val) {
- Text_t default_text =
- Text$from_strn(arg->default_val->start, (size_t)(arg->default_val->end - arg->default_val->start));
- help_text = Texts(help_text, " \x1b[2m(default:", default_text, ")\x1b[m");
- }
+ return usage;
+}
+
+static Text_t generate_help(env_t *env, type_t *fn_type) {
+ DeclareMatch(fn_info, fn_type, FunctionType);
+ env_t *main_env = fresh_scope(env);
+ Text_t help_text = EMPTY_TEXT;
+
+ for (arg_t *arg = fn_info->args; arg; arg = arg->next) {
+ help_text = Texts(help_text, "\n");
+ type_t *t = get_arg_type(main_env, arg);
+ OptionalText_t flag = flagify(arg->name, true);
+ assert(flag.tag != TEXT_NONE);
+ OptionalText_t alias_flag = flagify(arg->alias, true);
+ Text_t flags = Texts("\x1b[33;1m", flag, "\x1b[m");
+ if (alias_flag.tag != TEXT_NONE) flags = Texts("\x1b[33;1m", alias_flag, "\x1b[0;2m,\x1b[m ", flags);
+ if (t->tag == BoolType || (t->tag == OptionalType && Match(t, OptionalType)->type->tag == BoolType))
+ flags = Texts(flags, "|\x1b[33;1m--no-", Text$without_prefix(flag, Text("--")), "\x1b[m");
+ if (t->tag == BoolType || (t->tag == OptionalType && Match(t, OptionalType)->type->tag == BoolType))
+ help_text = Texts(help_text, " ", flags);
+ else
+ help_text = Texts(help_text, " ", flags, " \x1b[1;34m", get_flag_options(t, Text("\x1b[m | \x1b[1;34m")),
+ "\x1b[m");
+
+ if (arg->comment.length > 0) help_text = Texts(help_text, " \x1b[3m", arg->comment, "\x1b[m");
+ if (arg->default_val) {
+ Text_t default_text =
+ Text$from_strn(arg->default_val->start, (size_t)(arg->default_val->end - arg->default_val->start));
+ help_text = Texts(help_text, " \x1b[2m(default:", default_text, ")\x1b[m");
}
- code = Texts(code, "Text_t help = Texts(usage, ", quoted_text(Texts(help_text, "\n")), ");\n");
- help_code = Text("help");
}
+ return help_text;
+}
+
+public
+Text_t compile_cli_arg_call(env_t *env, ast_t *ast, Text_t fn_name, type_t *fn_type, const char *version) {
+ DeclareMatch(fn_info, fn_type, FunctionType);
+
+ Text_t code = EMPTY_TEXT;
+ OptionalText_t usage = ast_metadata(ast, "USAGE");
+ if (usage.tag == TEXT_NONE) usage = generate_usage(env, fn_type);
+
+ OptionalText_t help = ast_metadata(ast, "HELP");
+ if (help.tag == TEXT_NONE) help = generate_help(env, fn_type);
+
+ code = Texts(code,
+ "Text_t usage = Texts(Text(\"\\x1b[1mUsage:\\x1b[m \"), "
+ "Text$from_str(argv[0]), Text(\" \")",
+ usage.length == 0 ? EMPTY_TEXT : Texts(", Text(", quoted_text(usage), ")"), ");\n",
+ "Text_t help = Texts(usage, Text(\"\\n\"", quoted_text(help), "\"\\n\"));\n");
for (arg_t *arg = fn_info->args; arg; arg = arg->next) {
code = Texts(code, compile_declaration(arg->type, Texts("_$", Text$from_str(arg->name))), " = ",
@@ -144,14 +151,14 @@ Text_t compile_cli_arg_call(env_t *env, Text_t fn_name, type_t *fn_type, const c
code = Texts(code, "{", quoted_text(Text$replace(Text$from_str(arg->name), Text("_"), Text("-"))), ", &",
Texts("_$", Text$from_str(arg->name)), ", ", compile_type_info(arg->type),
arg->default_val ? Text("") : Text(", .required=true"),
- arg->alias ? Texts(", .short_flag=", quoted_text(Text$from_str(arg->name)),
+ arg->alias ? Texts(", .short_flag=", quoted_text(Text$from_str(arg->alias)),
"[0]") // TODO: escape char properly
: Text(""),
"},\n");
}
code = Texts(code, "};\n");
- code = Texts(code, "tomo_parse_args(argc, argv, ", usage_code, ", ", help_code, ", ", version_code,
+ code = Texts(code, "tomo_parse_args(argc, argv, usage, help, ", version_code,
", sizeof(cli_args)/sizeof(cli_args[0]), cli_args);\n");
// Lazily initialize default values to prevent side effects
@@ -161,7 +168,6 @@ Text_t compile_cli_arg_call(env_t *env, Text_t fn_name, type_t *fn_type, const c
Text_t default_val;
if (arg->type) {
default_val = compile_to_type(env, arg->default_val, arg->type);
- if (arg->type->tag != OptionalType) default_val = promote_to_optional(arg->type, default_val);
} else {
default_val = compile(env, arg->default_val);
}
@@ -181,14 +187,21 @@ Text_t compile_cli_arg_call(env_t *env, Text_t fn_name, type_t *fn_type, const c
}
public
-Text_t compile_manpage(Text_t program, OptionalText_t synopsis, OptionalText_t description, arg_t *args) {
+Text_t compile_manpage(Text_t program, ast_t *ast, arg_t *args) {
+ OptionalText_t user_manpage = ast_metadata(ast, "MANPAGE");
+ if (user_manpage.tag != TEXT_NONE) {
+ return user_manpage;
+ }
+
+ OptionalText_t synopsys = ast_metadata(ast, "MANPAGE_SYNOPSYS");
+ OptionalText_t description = ast_metadata(ast, "MANPAGE_DESCRIPTION");
Text_t date = Text(""); // TODO: use date
Text_t man = Texts(".\\\" Automatically generated by Tomo\n"
".TH \"",
Text$upper(program, Text("C")), "\" \"1\" \"", date,
"\" \"\" \"\"\n"
".SH NAME\n",
- program, " \\- ", synopsis.tag == TEXT_NONE ? Text("a Tomo program") : synopsis, "\n");
+ program, " \\- ", synopsys.tag == TEXT_NONE ? Text("a Tomo program") : synopsys, "\n");
if (description.tag != TEXT_NONE) {
man = Texts(man, ".SH DESCRIPTION\n", description, "\n");
diff --git a/src/compile/cli.h b/src/compile/cli.h
index fa60eccf..907554a6 100644
--- a/src/compile/cli.h
+++ b/src/compile/cli.h
@@ -2,9 +2,10 @@
#pragma once
+#include "../ast.h"
#include "../environment.h"
#include "../stdlib/datatypes.h"
#include "../types.h"
-Text_t compile_cli_arg_call(env_t *env, Text_t fn_name, type_t *fn_type, const char *version);
-Text_t compile_manpage(Text_t program, OptionalText_t synopsis, OptionalText_t description, arg_t *args);
+Text_t compile_cli_arg_call(env_t *env, ast_t *ast, Text_t fn_name, type_t *fn_type, const char *version);
+Text_t compile_manpage(Text_t program, ast_t *ast, arg_t *args);
diff --git a/src/compile/comparisons.c b/src/compile/comparisons.c
index bc927599..5e95459c 100644
--- a/src/compile/comparisons.c
+++ b/src/compile/comparisons.c
@@ -28,7 +28,9 @@ Text_t compile_comparison(env_t *env, ast_t *ast) {
type_t *lhs_t = get_type(env, binop.lhs);
type_t *rhs_t = get_type(with_enum_scope(env, lhs_t), binop.rhs);
type_t *operand_t;
- if (binop.lhs->tag == Int && is_numeric_type(rhs_t)) {
+ if (type_eq(lhs_t, rhs_t)) {
+ operand_t = lhs_t;
+ } else if (binop.lhs->tag == Int && is_numeric_type(rhs_t)) {
operand_t = rhs_t;
} else if (binop.rhs->tag == Int && is_numeric_type(lhs_t)) {
operand_t = lhs_t;
@@ -68,7 +70,9 @@ Text_t compile_comparison(env_t *env, ast_t *ast) {
type_t *lhs_t = get_type(env, cmp.lhs);
type_t *rhs_t = get_type(env, cmp.rhs);
type_t *operand_t;
- if (cmp.lhs->tag == Int && is_numeric_type(rhs_t)) {
+ if (type_eq(lhs_t, rhs_t)) {
+ operand_t = lhs_t;
+ } else if (cmp.lhs->tag == Int && is_numeric_type(rhs_t)) {
operand_t = rhs_t;
} else if (cmp.rhs->tag == Int && is_numeric_type(lhs_t)) {
operand_t = lhs_t;
diff --git a/src/compile/conditionals.c b/src/compile/conditionals.c
index caebdbde..64be29fa 100644
--- a/src/compile/conditionals.c
+++ b/src/compile/conditionals.c
@@ -126,17 +126,22 @@ Text_t compile_if_expression(env_t *env, ast_t *ast) {
}
type_t *true_type = get_type(truthy_scope, if_->body);
- type_t *false_type = get_type(falsey_scope, if_->else_body);
+ ast_t *else_body = if_->else_body;
+ if (else_body && else_body->tag == Block && Match(else_body, Block)->statements
+ && !Match(else_body, Block)->statements->next)
+ else_body = Match(else_body, Block)->statements->ast;
+ if (else_body == NULL || else_body->tag == None) else_body = WrapAST(ast, None, .type = true_type);
+ type_t *false_type = get_type(falsey_scope, else_body);
if (true_type->tag == AbortType || true_type->tag == ReturnType)
return Texts("({ ", decl_code, "if (", condition_code, ") ", compile_statement(truthy_scope, if_->body), "\n",
- compile(falsey_scope, if_->else_body), "; })");
+ compile(falsey_scope, else_body), "; })");
else if (false_type->tag == AbortType || false_type->tag == ReturnType)
- return Texts("({ ", decl_code, "if (!(", condition_code, ")) ", compile_statement(falsey_scope, if_->else_body),
+ return Texts("({ ", decl_code, "if (!(", condition_code, ")) ", compile_statement(falsey_scope, else_body),
"\n", compile(truthy_scope, if_->body), "; })");
else if (decl_code.length > 0)
return Texts("({ ", decl_code, "(", condition_code, ") ? ", compile(truthy_scope, if_->body), " : ",
- compile(falsey_scope, if_->else_body), ";})");
+ compile(falsey_scope, else_body), ";})");
else
return Texts("((", condition_code, ") ? ", compile(truthy_scope, if_->body), " : ",
- compile(falsey_scope, if_->else_body), ")");
+ compile(falsey_scope, else_body), ")");
}
diff --git a/src/compile/enums.c b/src/compile/enums.c
index ec7a1755..24da79b8 100644
--- a/src/compile/enums.c
+++ b/src/compile/enums.c
@@ -12,8 +12,6 @@ Text_t compile_enum_typeinfo(env_t *env, const char *name, tag_ast_t *tags) {
// Compile member types and constructors:
Text_t member_typeinfos = EMPTY_TEXT;
for (tag_ast_t *tag = tags; tag; tag = tag->next) {
- if (!tag->fields) continue;
-
const char *tag_name = String(name, "$", tag->name);
type_t *tag_type = Table$str_get(*env->types, tag_name);
assert(tag_type && tag_type->tag == StructType);
@@ -38,9 +36,7 @@ Text_t compile_enum_typeinfo(env_t *env, const char *name, tag_ast_t *tags) {
for (tag_ast_t *tag = tags; tag; tag = tag->next) {
const char *tag_type_name = String(name, "$", tag->name);
type_t *tag_type = Table$str_get(*env->types, tag_type_name);
- if (tag_type && Match(tag_type, StructType)->fields)
- typeinfo = Texts(typeinfo, "{\"", tag->name, "\", ", compile_type_info(tag_type), "}, ");
- else typeinfo = Texts(typeinfo, "{\"", tag->name, "\"}, ");
+ typeinfo = Texts(typeinfo, "{\"", tag->name, "\", ", compile_type_info(tag_type), "}, ");
}
typeinfo = Texts(typeinfo, "}}}};\n");
return Texts(member_typeinfos, typeinfo);
@@ -49,8 +45,6 @@ Text_t compile_enum_typeinfo(env_t *env, const char *name, tag_ast_t *tags) {
Text_t compile_enum_constructors(env_t *env, const char *name, tag_ast_t *tags) {
Text_t constructors = EMPTY_TEXT;
for (tag_ast_t *tag = tags; tag; tag = tag->next) {
- if (!tag->fields) continue;
-
Text_t arg_sig = EMPTY_TEXT;
for (arg_ast_t *field = tag->fields; field; field = field->next) {
type_t *field_t = get_arg_ast_type(env, field);
@@ -76,25 +70,16 @@ Text_t compile_enum_constructors(env_t *env, const char *name, tag_ast_t *tags)
Text_t compile_enum_header(env_t *env, const char *name, tag_ast_t *tags) {
Text_t all_defs = EMPTY_TEXT;
Text_t none_name = namespace_name(env, env->namespace, Texts(name, "$none"));
- Text_t enum_name = namespace_name(env, env->namespace, Texts(name, "$$enum"));
Text_t enum_tags = Texts("{ ", none_name, "=0, ");
assert(Table$str_get(*env->types, name));
- bool has_any_tags_with_fields = false;
for (tag_ast_t *tag = tags; tag; tag = tag->next) {
Text_t tag_name = namespace_name(env, env->namespace, Texts(name, "$tag$", tag->name));
enum_tags = Texts(enum_tags, tag_name);
if (tag->next) enum_tags = Texts(enum_tags, ", ");
- has_any_tags_with_fields = has_any_tags_with_fields || (tag->fields != NULL);
}
enum_tags = Texts(enum_tags, " }");
- if (!has_any_tags_with_fields) {
- Text_t enum_def = Texts("enum ", enum_name, " ", enum_tags, ";\n");
- Text_t info = namespace_name(env, env->namespace, Texts(name, "$$info"));
- return Texts(enum_def, "extern const TypeInfo_t ", info, ";\n");
- }
-
Text_t struct_name = namespace_name(env, env->namespace, Texts(name, "$$struct"));
Text_t enum_def = Texts("struct ", struct_name,
" {\n"
@@ -103,7 +88,6 @@ Text_t compile_enum_header(env_t *env, const char *name, tag_ast_t *tags) {
" $tag;\n"
"union {\n");
for (tag_ast_t *tag = tags; tag; tag = tag->next) {
- if (!tag->fields) continue;
Text_t field_def = compile_struct_header(env, NewAST(tag->file, tag->start, tag->end, StructDef,
.name = Text$as_c_string(Texts(name, "$", tag->name)),
.fields = tag->fields));
@@ -117,8 +101,6 @@ Text_t compile_enum_header(env_t *env, const char *name, tag_ast_t *tags) {
Text_t info = namespace_name(env, env->namespace, Texts(name, "$$info"));
all_defs = Texts(all_defs, "extern const TypeInfo_t ", info, ";\n");
for (tag_ast_t *tag = tags; tag; tag = tag->next) {
- if (!tag->fields) continue;
-
Text_t arg_sig = EMPTY_TEXT;
for (arg_ast_t *field = tag->fields; field; field = field->next) {
type_t *field_t = get_arg_ast_type(env, field);
@@ -140,11 +122,8 @@ Text_t compile_empty_enum(type_t *t) {
tag_t *tag = enum_->tags;
assert(tag);
assert(tag->type);
- if (Match(tag->type, StructType)->fields)
- return Texts("((", compile_type(t), "){.$tag=", tag->tag_value, ", .", tag->name, "=", compile_empty(tag->type),
- "})");
- else if (enum_has_fields(t)) return Texts("((", compile_type(t), "){.$tag=", tag->tag_value, "})");
- else return Texts("((", compile_type(t), ")", tag->tag_value, ")");
+ return Texts("((", compile_type(t), "){.$tag=", tag->tag_value, ", .", tag->name, "=", compile_empty(tag->type),
+ "})");
}
public
@@ -156,16 +135,11 @@ Text_t compile_enum_field_access(env_t *env, ast_t *ast) {
for (tag_t *tag = e->tags; tag; tag = tag->next) {
if (streq(f->field, tag->name)) {
Text_t tag_name = namespace_name(e->env, e->env->namespace, Texts("tag$", tag->name));
- if (fielded_t->tag == PointerType) {
- Text_t fielded = compile_to_pointer_depth(env, f->fielded, 1, false);
- return Texts("((", fielded, ")->$tag == ", tag_name, ")");
- } else if (enum_has_fields(value_t)) {
- Text_t fielded = compile(env, f->fielded);
- return Texts("((", fielded, ").$tag == ", tag_name, ")");
- } else {
- Text_t fielded = compile(env, f->fielded);
- return Texts("((", fielded, ") == ", tag_name, ")");
- }
+ Text_t member =
+ compile_maybe_incref(env, WrapLiteralCode(ast, Texts("_e.", tag->name), .type = tag->type), tag->type);
+ return Texts("({ ", compile_declaration(value_t, Text("_e")), " = ",
+ compile_to_pointer_depth(env, f->fielded, 0, false), "; ", "_e.$tag == ", tag_name, " ? ",
+ promote_to_optional(tag->type, member), " : ", compile_none(tag->type), "; })");
}
}
code_err(ast, "The field '", f->field, "' is not a valid tag name of ", type_to_text(value_t));
diff --git a/src/compile/expressions.c b/src/compile/expressions.c
index c28185ce..f249993f 100644
--- a/src/compile/expressions.c
+++ b/src/compile/expressions.c
@@ -11,11 +11,41 @@
public
Text_t compile_maybe_incref(env_t *env, ast_t *ast, type_t *t) {
- if (is_idempotent(ast) && can_be_mutated(env, ast)) {
- type_t *actual = get_type(with_enum_scope(env, t), ast);
- if (t->tag == ListType && type_eq(t, actual)) return Texts("LIST_COPY(", compile_to_type(env, ast, t), ")");
- else if (t->tag == TableType && type_eq(t, actual))
- return Texts("TABLE_COPY(", compile_to_type(env, ast, t), ")");
+ if (!has_refcounts(t) || !can_be_mutated(env, ast)) {
+ return compile_to_type(env, ast, t);
+ }
+
+ // When using a struct as a value, we need to increment the refcounts of the inner fields as well:
+ if (t->tag == StructType) {
+ // If the struct is non-idempotent, we have to stash it in a local var first
+ if (is_idempotent(ast)) {
+ Text_t code = Texts("((", compile_type(t), "){");
+ for (arg_t *field = Match(t, StructType)->fields; field; field = field->next) {
+ Text_t val = compile_maybe_incref(env, WrapAST(ast, FieldAccess, .fielded = ast, .field = field->name),
+ get_arg_type(env, field));
+ code = Texts(code, val);
+ if (field->next) code = Texts(code, ", ");
+ }
+ return Texts(code, "})");
+ } else {
+ static int64_t tmp_index = 1;
+ Text_t tmp_name = Texts("_tmp", tmp_index);
+ tmp_index += 1;
+ Text_t code = Texts("({ ", compile_declaration(t, tmp_name), " = ", compile_to_type(env, ast, t), "; ",
+ "((", compile_type(t), "){");
+ ast_t *tmp = WrapLiteralCode(ast, tmp_name, .type = t);
+ for (arg_t *field = Match(t, StructType)->fields; field; field = field->next) {
+ Text_t val = compile_maybe_incref(env, WrapAST(ast, FieldAccess, .fielded = tmp, .field = field->name),
+ get_arg_type(env, field));
+ code = Texts(code, val);
+ if (field->next) code = Texts(code, ", ");
+ }
+ return Texts(code, "}); })");
+ }
+ } else if (t->tag == ListType && ast->tag != List && can_be_mutated(env, ast) && type_eq(get_type(env, ast), t)) {
+ return Texts("LIST_COPY(", compile_to_type(env, ast, t), ")");
+ } else if (t->tag == TableType && ast->tag != Table && can_be_mutated(env, ast) && type_eq(get_type(env, ast), t)) {
+ return Texts("TABLE_COPY(", compile_to_type(env, ast, t), ")");
}
return compile_to_type(env, ast, t);
}
@@ -27,7 +57,6 @@ Text_t compile_empty(type_t *t) {
if (t->tag == OptionalType) return compile_none(t);
if (t == PATH_TYPE) return Text("NONE_PATH");
- else if (t == PATH_TYPE_TYPE) return Text("PATHTYPE_ABSOLUTE");
switch (t->tag) {
case BigIntType: return Text("I(0)");
@@ -66,7 +95,9 @@ Text_t compile_empty(type_t *t) {
Text_t compile(env_t *env, ast_t *ast) {
switch (ast->tag) {
case None: {
- code_err(ast, "I can't figure out what this `none`'s type is!");
+ type_t *type = Match(ast, None)->type;
+ if (type == NULL) code_err(ast, "I can't figure out what this `none`'s type is!");
+ return compile_none(non_optional(type));
}
case Bool: return Match(ast, Bool)->b ? Text("yes") : Text("no");
case Var: {
diff --git a/src/compile/files.c b/src/compile/files.c
index a4cc07fe..27c2e041 100644
--- a/src/compile/files.c
+++ b/src/compile/files.c
@@ -142,6 +142,7 @@ Text_t compile_top_level_code(env_t *env, ast_t *ast) {
}
return code;
}
+ case Metadata:
default: return EMPTY_TEXT;
}
}
@@ -151,7 +152,7 @@ typedef struct {
Text_t *code;
} compile_info_t;
-static void add_type_infos(type_ast_t *type_ast, void *userdata) {
+static visit_behavior_t add_type_infos(type_ast_t *type_ast, void *userdata) {
if (type_ast && type_ast->tag == EnumTypeAST) {
compile_info_t *info = (compile_info_t *)userdata;
// Force the type to get defined:
@@ -163,6 +164,7 @@ static void add_type_infos(type_ast_t *type_ast, void *userdata) {
compile_enum_constructors(info->env, String("enum$", (int64_t)(type_ast->start - type_ast->file->text)),
Match(type_ast, EnumTypeAST)->tags));
}
+ return VISIT_PROCEED;
}
public
@@ -193,7 +195,7 @@ Text_t compile_file(env_t *env, ast_t *ast) {
const char *name = file_base_name(ast->file->filename);
return Texts(env->do_source_mapping ? Texts("#line 1 ", quoted_str(ast->file->filename), "\n") : EMPTY_TEXT,
"#define __SOURCE_FILE__ ", quoted_str(ast->file->filename), "\n",
- "#include <tomo_" TOMO_VERSION "/tomo.h>\n"
+ "#include <tomo@" TOMO_VERSION "/tomo.h>\n"
"#include \"",
name, ".tm.h\"\n\n", includes, env->code->local_typedefs, "\n", env->code->lambdas, "\n",
env->code->staticdefs, "\n", top_level_code, "public void ",
diff --git a/src/compile/functions.c b/src/compile/functions.c
index a14c0455..46acd780 100644
--- a/src/compile/functions.c
+++ b/src/compile/functions.c
@@ -3,9 +3,12 @@
#include "../ast.h"
#include "../environment.h"
#include "../naming.h"
+#include "../stdlib/c_strings.h"
#include "../stdlib/datatypes.h"
#include "../stdlib/floats.h"
#include "../stdlib/integers.h"
+#include "../stdlib/nums.h"
+#include "../stdlib/optionals.h"
#include "../stdlib/tables.h"
#include "../stdlib/text.h"
#include "../stdlib/util.h"
@@ -163,12 +166,12 @@ Text_t compile_function_call(env_t *env, ast_t *ast) {
args = new (arg_t, .name = a->name, .type = get_type(env, a->value), .next = args);
REVERSE_LIST(args);
code_err(ast,
- "This function's signature doesn't match this call site.\n"
- "The signature is: ",
- type_to_text(fn_t),
- "\n"
- "But it's being called with: ",
- type_to_text(Type(FunctionType, .args = args)));
+ "This function's signature doesn't match this call site. \n"
+ " The function takes these args: (",
+ arg_types_to_text(Match(fn_t, FunctionType)->args, ", "),
+ ") \n"
+ " But it's being called with: (",
+ arg_types_to_text(args, ", "), ")");
}
}
return Texts(fn, "(", compile_arguments(env, ast, Match(fn_t, FunctionType)->args, call->args), ")");
@@ -246,85 +249,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;
@@ -594,6 +518,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;
@@ -703,7 +827,7 @@ Text_t compile_function(env_t *env, Text_t name_code, ast_t *ast, Text_t *static
definition = Texts(definition, wrapper);
} else if (cache && cache->tag == Int) {
assert(args);
- OptionalInt64_t cache_size = Int64$parse(Text$from_str(Match(cache, Int)->str), NULL);
+ OptionalInt64_t cache_size = Int64$parse(Text$from_str(Match(cache, Int)->str), NONE_INT, NULL);
Text_t pop_code = EMPTY_TEXT;
if (cache->tag == Int && cache_size.has_value && cache_size.value > 0) {
// FIXME: this currently just deletes the first entry, but this
@@ -784,6 +908,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;
}
diff --git a/src/compile/headers.c b/src/compile/headers.c
index f132b312..e90556a1 100644
--- a/src/compile/headers.c
+++ b/src/compile/headers.c
@@ -79,28 +79,16 @@ static void _make_typedefs(compile_typedef_info_t *info, ast_t *ast) {
*info->header = Texts(*info->header, "typedef struct ", struct_name, " ", type_name, ";\n");
} else if (ast->tag == EnumDef) {
DeclareMatch(def, ast, EnumDef);
- bool has_any_tags_with_fields = false;
- for (tag_ast_t *tag = def->tags; tag; tag = tag->next) {
- has_any_tags_with_fields = has_any_tags_with_fields || (tag->fields != NULL);
- }
+ Text_t struct_name = namespace_name(info->env, info->env->namespace, Texts(def->name, "$$struct"));
+ Text_t type_name = namespace_name(info->env, info->env->namespace, Texts(def->name, "$$type"));
+ *info->header = Texts(*info->header, "typedef struct ", struct_name, " ", type_name, ";\n");
- if (has_any_tags_with_fields) {
- Text_t struct_name = namespace_name(info->env, info->env->namespace, Texts(def->name, "$$struct"));
- Text_t type_name = namespace_name(info->env, info->env->namespace, Texts(def->name, "$$type"));
- *info->header = Texts(*info->header, "typedef struct ", struct_name, " ", type_name, ";\n");
-
- for (tag_ast_t *tag = def->tags; tag; tag = tag->next) {
- if (!tag->fields) continue;
- Text_t tag_struct =
- namespace_name(info->env, info->env->namespace, Texts(def->name, "$", tag->name, "$$struct"));
- Text_t tag_type =
- namespace_name(info->env, info->env->namespace, Texts(def->name, "$", tag->name, "$$type"));
- *info->header = Texts(*info->header, "typedef struct ", tag_struct, " ", tag_type, ";\n");
- }
- } else {
- Text_t enum_name = namespace_name(info->env, info->env->namespace, Texts(def->name, "$$enum"));
- Text_t type_name = namespace_name(info->env, info->env->namespace, Texts(def->name, "$$type"));
- *info->header = Texts(*info->header, "typedef enum ", enum_name, " ", type_name, ";\n");
+ for (tag_ast_t *tag = def->tags; tag; tag = tag->next) {
+ Text_t tag_struct =
+ namespace_name(info->env, info->env->namespace, Texts(def->name, "$", tag->name, "$$struct"));
+ Text_t tag_type =
+ namespace_name(info->env, info->env->namespace, Texts(def->name, "$", tag->name, "$$type"));
+ *info->header = Texts(*info->header, "typedef struct ", tag_struct, " ", tag_type, ";\n");
}
} else if (ast->tag == LangDef) {
DeclareMatch(def, ast, LangDef);
@@ -116,41 +104,30 @@ static void _define_types_and_funcs(compile_typedef_info_t *info, ast_t *ast) {
compile_statement_namespace_header(info->env, info->header_path, ast));
}
-static void add_type_headers(type_ast_t *type_ast, void *userdata) {
- if (!type_ast) return;
+static visit_behavior_t add_type_headers(type_ast_t *type_ast, void *userdata) {
+ if (!type_ast) return VISIT_STOP;
if (type_ast->tag == EnumTypeAST) {
compile_typedef_info_t *info = (compile_typedef_info_t *)userdata;
// Force the type to get defined:
(void)parse_type_ast(info->env, type_ast);
DeclareMatch(enum_, type_ast, EnumTypeAST);
- bool has_any_tags_with_fields = false;
- for (tag_ast_t *tag = enum_->tags; tag; tag = tag->next) {
- has_any_tags_with_fields = has_any_tags_with_fields || (tag->fields != NULL);
- }
-
const char *name = String("enum$", (int64_t)(type_ast->start - type_ast->file->text));
- if (has_any_tags_with_fields) {
- Text_t struct_name = namespace_name(info->env, info->env->namespace, Texts(name, "$$struct"));
- Text_t type_name = namespace_name(info->env, info->env->namespace, Texts(name, "$$type"));
- *info->header = Texts(*info->header, "typedef struct ", struct_name, " ", type_name, ";\n");
-
- for (tag_ast_t *tag = enum_->tags; tag; tag = tag->next) {
- if (!tag->fields) continue;
- Text_t tag_struct =
- namespace_name(info->env, info->env->namespace, Texts(name, "$", tag->name, "$$struct"));
- Text_t tag_type =
- namespace_name(info->env, info->env->namespace, Texts(name, "$", tag->name, "$$type"));
- *info->header = Texts(*info->header, "typedef struct ", tag_struct, " ", tag_type, ";\n");
- }
- } else {
- Text_t enum_name = namespace_name(info->env, info->env->namespace, Texts(name, "$$enum"));
- Text_t type_name = namespace_name(info->env, info->env->namespace, Texts(name, "$$type"));
- *info->header = Texts(*info->header, "typedef enum ", enum_name, " ", type_name, ";\n");
+ Text_t struct_name = namespace_name(info->env, info->env->namespace, Texts(name, "$$struct"));
+ Text_t type_name = namespace_name(info->env, info->env->namespace, Texts(name, "$$type"));
+ *info->header = Texts(*info->header, "typedef struct ", struct_name, " ", type_name, ";\n");
+
+ for (tag_ast_t *tag = enum_->tags; tag; tag = tag->next) {
+ Text_t tag_struct =
+ namespace_name(info->env, info->env->namespace, Texts(name, "$", tag->name, "$$struct"));
+ Text_t tag_type = namespace_name(info->env, info->env->namespace, Texts(name, "$", tag->name, "$$type"));
+ *info->header = Texts(*info->header, "typedef struct ", tag_struct, " ", tag_type, ";\n");
}
*info->header = Texts(*info->header, compile_enum_header(info->env, name, enum_->tags));
}
+
+ return VISIT_PROCEED;
}
public
@@ -158,7 +135,7 @@ Text_t compile_file_header(env_t *env, Path_t header_path, ast_t *ast) {
Text_t header =
Texts("#pragma once\n",
env->do_source_mapping ? Texts("#line 1 ", quoted_str(ast->file->filename), "\n") : EMPTY_TEXT,
- "#include <tomo_" TOMO_VERSION "/tomo.h>\n");
+ "#include <tomo@" TOMO_VERSION "/tomo.h>\n");
compile_typedef_info_t info = {.env = env, .header = &header, .header_path = header_path};
visit_topologically(Match(ast, Block)->statements, (Closure_t){.fn = (void *)_make_typedefs, &info});
@@ -183,8 +160,8 @@ Text_t compile_statement_type_header(env_t *env, Path_t header_path, ast_t *ast)
case USE_MODULE: {
module_info_t mod = get_used_module_info(ast);
glob_t tm_files;
- const char *folder = mod.version ? String(mod.name, "_", mod.version) : mod.name;
- if (glob(String(TOMO_PATH, "/lib/tomo_" TOMO_VERSION "/", folder, "/[!._0-9]*.tm"), GLOB_TILDE, NULL,
+ const char *folder = mod.version ? String(mod.name, "@", mod.version) : mod.name;
+ if (glob(String(TOMO_PATH, "/lib/tomo@" TOMO_VERSION "/", folder, "/[!._0-9]*.tm"), GLOB_TILDE, NULL,
&tm_files)
!= 0) {
if (!try_install_module(mod, true)) code_err(ast, "Could not find library");
diff --git a/src/compile/lists.c b/src/compile/lists.c
index 1f3590a9..f39f61d8 100644
--- a/src/compile/lists.c
+++ b/src/compile/lists.c
@@ -53,7 +53,7 @@ list_comprehension: {
// set_binding(scope, comprehension_name, list_type, comprehension_name);
for (ast_list_t *item = list->items; item; item = item->next) {
if (item->ast->tag == Comprehension) code = Texts(code, "\n", compile_statement(scope, item->ast));
- else code = Texts(code, compile_statement(env, add_to_list_comprehension(item->ast, comprehension_var)));
+ else code = Texts(code, compile_statement(scope, add_to_list_comprehension(item->ast, comprehension_var)));
}
code = Texts(code, " ", comprehension_name, "; })");
return code;
@@ -254,7 +254,7 @@ Text_t compile_list_method_call(env_t *env, ast_t *ast) {
} else if (streq(call->name, "unique")) {
self = compile_to_pointer_depth(env, call->self, 0, false);
(void)compile_arguments(env, ast, NULL, call->args);
- return Texts("Table$from_entries(", self, ", Table$info(", compile_type_info(item_t), ", &Empty$$info))");
+ return Texts("Table$from_entries(", self, ", Table$info(", compile_type_info(item_t), ", &Present$$info))");
} else if (streq(call->name, "pop")) {
EXPECT_POINTER();
arg_t *arg_spec = new (arg_t, .name = "index", .type = INT_TYPE, .default_val = FakeAST(Int, "-1"));
diff --git a/src/compile/optionals.c b/src/compile/optionals.c
index 7da50b0b..9aca84d0 100644
--- a/src/compile/optionals.c
+++ b/src/compile/optionals.c
@@ -15,18 +15,14 @@ Text_t optional_into_nonnone(type_t *t, Text_t value) {
switch (t->tag) {
case IntType:
case ByteType: return Texts(value, ".value");
- case StructType:
- if (t == PATH_TYPE || t == PATH_TYPE_TYPE) return value;
- return Texts(value, ".value");
+ case StructType: return Texts(value, ".value");
default: return value;
}
}
public
Text_t promote_to_optional(type_t *t, Text_t code) {
- if (t == PATH_TYPE || t == PATH_TYPE_TYPE) {
- return code;
- } else if (t->tag == IntType) {
+ if (t->tag == IntType) {
switch (Match(t, IntType)->bits) {
case TYPE_IBITS8: return Texts("((OptionalInt8_t){.has_value=true, .value=", code, "})");
case TYPE_IBITS16: return Texts("((OptionalInt16_t){.has_value=true, .value=", code, "})");
@@ -53,7 +49,6 @@ Text_t compile_none(type_t *t) {
if (t == NULL) compiler_err(NULL, NULL, NULL, "I can't compile a `none` value with no type");
if (t == PATH_TYPE) return Text("NONE_PATH");
- else if (t == PATH_TYPE_TYPE) return Text("PATHTYPE_NONE");
switch (t->tag) {
case BigIntType: return Text("NONE_INT");
@@ -92,8 +87,6 @@ Text_t check_none(type_t *t, Text_t value) {
// NOTE: these use statement expressions ({...;}) because some compilers
// complain about excessive parens around equality comparisons
if (t->tag == PointerType || t->tag == FunctionType || t->tag == CStringType) return Texts("(", value, " == NULL)");
- else if (t == PATH_TYPE) return Texts("((", value, ").type.$tag == PATHTYPE_NONE)");
- else if (t == PATH_TYPE_TYPE) return Texts("((", value, ").$tag == PATHTYPE_NONE)");
else if (t->tag == BigIntType) return Texts("((", value, ").small == 0)");
else if (t->tag == ClosureType) return Texts("((", value, ").fn == NULL)");
else if (t->tag == FloatType)
@@ -103,10 +96,7 @@ Text_t check_none(type_t *t, Text_t value) {
else if (t->tag == BoolType) return Texts("((", value, ") == NONE_BOOL)");
else if (t->tag == TextType) return Texts("((", value, ").tag == TEXT_NONE)");
else if (t->tag == IntType || t->tag == ByteType || t->tag == StructType) return Texts("!(", value, ").has_value");
- else if (t->tag == EnumType) {
- if (enum_has_fields(t)) return Texts("((", value, ").$tag == 0)");
- else return Texts("((", value, ") == 0)");
- }
+ else if (t->tag == EnumType) return Texts("((", value, ").$tag == 0)");
print_err("Optional check not implemented for: ", type_to_text(t));
return EMPTY_TEXT;
}
@@ -115,12 +105,51 @@ public
Text_t compile_non_optional(env_t *env, ast_t *ast) {
ast_t *value = Match(ast, NonOptional)->value;
if (value->tag == Index && Match(value, Index)->index != NULL) return compile_indexing(env, value, true);
- type_t *t = get_type(env, value);
- Text_t value_code = compile(env, value);
+ type_t *value_t = get_type(env, value);
+ if (value_t->tag == PointerType) {
+ // Dereference pointers automatically
+ return compile_non_optional(env, WrapAST(ast, NonOptional, WrapAST(ast, Index, .indexed = value)));
+ }
int64_t line = get_line_number(ast->file, ast->start);
- return Texts(
- "({ ", compile_declaration(t, Text("opt")), " = ", value_code, "; ", "if unlikely (",
- check_none(t, Text("opt")), ")\n", "#line ", line, "\n", "fail_source(", quoted_str(ast->file->filename), ", ",
- (int64_t)(value->start - value->file->text), ", ", (int64_t)(value->end - value->file->text), ", ",
- "\"This was expected to be a value, but it's `none`\\n\");\n", optional_into_nonnone(t, Text("opt")), "; })");
+ if (value_t->tag == EnumType) {
+ // For this case:
+ // enum Foo(FirstField, SecondField(msg:Text))
+ // e := ...
+ // e!
+ // We desugar into `e.FirstField!` using the first enum field
+ tag_t *first_tag = Match(value_t, EnumType)->tags;
+ if (!first_tag) code_err(ast, "'!' cannot be used on an empty enum");
+ return compile_non_optional(
+ env, WrapAST(ast, NonOptional, WrapAST(value, FieldAccess, .fielded = value, .field = first_tag->name)));
+ } else if (value->tag == FieldAccess
+ && value_type(get_type(env, Match(value, FieldAccess)->fielded))->tag == EnumType) {
+ type_t *enum_t = value_type(get_type(env, Match(value, FieldAccess)->fielded));
+ DeclareMatch(e, enum_t, EnumType);
+ DeclareMatch(f, value, FieldAccess);
+ for (tag_t *tag = e->tags; tag; tag = tag->next) {
+ if (streq(f->field, tag->name)) {
+ Text_t tag_name = namespace_name(e->env, e->env->namespace, Texts("tag$", tag->name));
+ return Texts(
+ "({ ", compile_declaration(enum_t, Text("_test_enum")), " = ",
+ compile_to_pointer_depth(env, f->fielded, 0, true), ";",
+ "if unlikely (_test_enum.$tag != ", tag_name, ") {\n", "#line ", line, "\n", "fail_source(",
+ quoted_str(f->fielded->file->filename), ", ", (int64_t)(f->fielded->start - f->fielded->file->text),
+ ", ", (int64_t)(f->fielded->end - f->fielded->file->text), ", ", "\"This was expected to be ",
+ tag->name, ", but it was: \", ", expr_as_text(Text("_test_enum"), enum_t, Text("false")),
+ ", \"\\n\");\n}\n",
+ compile_maybe_incref(
+ env, WrapLiteralCode(value, Texts("_test_enum.", tag->name), .type = tag->type), tag->type),
+ "; })");
+ }
+ }
+ code_err(ast, "The field '", f->field, "' is not a valid tag name of ", type_to_text(enum_t));
+ } else {
+ Text_t value_code = compile(env, value);
+ return Texts("({ ", compile_declaration(value_t, Text("opt")), " = ", value_code, "; ", "if unlikely (",
+ check_none(value_t, Text("opt")), ")\n", "#line ", line, "\n", "fail_source(",
+ quoted_str(value->file->filename), ", ", (int64_t)(value->start - value->file->text), ", ",
+ (int64_t)(value->end - value->file->text), ", ",
+ "\"This was expected to be a value, but it's `none`\\n\");\n",
+ optional_into_nonnone(value_t, Text("opt")), "; })");
+ }
}
diff --git a/src/compile/pointers.c b/src/compile/pointers.c
index 11348330..98274cc8 100644
--- a/src/compile/pointers.c
+++ b/src/compile/pointers.c
@@ -55,13 +55,13 @@ Text_t compile_typed_allocation(env_t *env, ast_t *ast, type_t *pointer_type) {
type_t *pointed = Match(pointer_type, PointerType)->pointed;
switch (ast->tag) {
case HeapAllocate: {
- return Texts("heap(", compile_to_type(env, Match(ast, HeapAllocate)->value, pointed), ")");
+ return Texts("heap(", compile_maybe_incref(env, Match(ast, HeapAllocate)->value, pointed), ")");
}
case StackReference: {
ast_t *subject = Match(ast, StackReference)->value;
if (can_be_mutated(env, subject) && type_eq(pointed, get_type(env, subject)))
return Texts("(&", compile_lvalue(env, subject), ")");
- else return Texts("stack(", compile_to_type(env, subject, pointed), ")");
+ else return Texts("stack(", compile_maybe_incref(env, subject, pointed), ")");
}
default: code_err(ast, "Not an allocation!");
}
diff --git a/src/compile/promotions.c b/src/compile/promotions.c
index 2a346668..4b5458c9 100644
--- a/src/compile/promotions.c
+++ b/src/compile/promotions.c
@@ -26,18 +26,21 @@ bool promote(env_t *env, ast_t *ast, Text_t *code, type_t *actual, type_t *neede
if (more_complete) return true;
// Serialization/deserialization:
- if (type_eq(needed, Type(ListType, Type(ByteType)))) {
- *code = Texts("generic_serialize((", compile_declaration(actual, Text("[1]")), "){", *code, "}, ",
- compile_type_info(actual), ")");
- return true;
- } else if (type_eq(actual, Type(ListType, Type(ByteType)))) {
- *code = Texts("({ ", compile_declaration(needed, Text("deserialized")),
- ";\n"
- "generic_deserialize(",
- *code, ", &deserialized, ", compile_type_info(needed),
- ");\n"
- "deserialized; })");
- return true;
+ if (!type_eq(non_optional(value_type(needed)), Type(ListType, Type(ByteType)))
+ || !type_eq(non_optional(value_type(actual)), Type(ListType, Type(ByteType)))) {
+ if (type_eq(needed, Type(ListType, Type(ByteType)))) {
+ *code = Texts("generic_serialize((", compile_declaration(actual, Text("[1]")), "){", *code, "}, ",
+ compile_type_info(actual), ")");
+ return true;
+ } else if (type_eq(actual, Type(ListType, Type(ByteType)))) {
+ *code = Texts("({ ", compile_declaration(needed, Text("deserialized")),
+ ";\n"
+ "generic_deserialize(",
+ *code, ", &deserialized, ", compile_type_info(needed),
+ ");\n"
+ "deserialized; })");
+ return true;
+ }
}
// Optional promotion:
@@ -128,6 +131,10 @@ Text_t compile_to_type(env_t *env, ast_t *ast, type_t *t) {
env = with_enum_scope(env, t);
}
+ if (ast->tag == Block && Match(ast, Block)->statements && !Match(ast, Block)->statements->next) {
+ ast = Match(ast, Block)->statements->ast;
+ }
+
if (ast->tag == Int && is_numeric_type(non_optional(t))) {
return compile_int_to_type(env, ast, t);
} else if (ast->tag == Num && t->tag == FloatType) {
diff --git a/src/compile/statements.c b/src/compile/statements.c
index f554263c..81a10ddd 100644
--- a/src/compile/statements.c
+++ b/src/compile/statements.c
@@ -196,8 +196,8 @@ static Text_t _compile_statement(env_t *env, ast_t *ast) {
} else if (use->what == USE_MODULE) {
module_info_t mod = get_used_module_info(ast);
glob_t tm_files;
- const char *folder = mod.version ? String(mod.name, "_", mod.version) : mod.name;
- if (glob(String(TOMO_PATH, "/lib/tomo_" TOMO_VERSION "/", folder, "/[!._0-9]*.tm"), GLOB_TILDE, NULL,
+ const char *folder = mod.version ? String(mod.name, "@", mod.version) : mod.name;
+ if (glob(String(TOMO_PATH, "/lib/tomo@" TOMO_VERSION "/", folder, "/[!._0-9]*.tm"), GLOB_TILDE, NULL,
&tm_files)
!= 0) {
if (!try_install_module(mod, true)) code_err(ast, "Could not find library");
@@ -216,11 +216,12 @@ static Text_t _compile_statement(env_t *env, ast_t *ast) {
return EMPTY_TEXT;
}
}
+ case Metadata: return EMPTY_TEXT;
default:
- // print("Is discardable: ", ast_to_sexp_str(ast), " ==> ",
- // is_discardable(env, ast));
if (!is_discardable(env, ast))
- code_err(ast, "The ", type_to_text(get_type(env, ast)), " result of this statement cannot be discarded");
+ code_err(
+ ast, "The ", type_to_text(get_type(env, ast)),
+ " value of this statement is implicitly ignored. \n Use `_ := ` if you want to explicitly discard it.");
return Texts("(void)", compile(env, ast), ";");
}
}
diff --git a/src/compile/tables.c b/src/compile/tables.c
index 54276c3b..e624f9fb 100644
--- a/src/compile/tables.c
+++ b/src/compile/tables.c
@@ -13,7 +13,7 @@ static ast_t *add_to_table_comprehension(ast_t *entry, ast_t *subject) {
return WrapAST(
entry, MethodCall, .name = "set", .self = subject,
.args = new (arg_ast_t, .value = e->key,
- .next = new (arg_ast_t, .value = e->value ? e->value : WrapAST(entry, Var, .name = "EMPTY"))));
+ .next = new (arg_ast_t, .value = e->value ? e->value : WrapAST(entry, Var, .name = "PRESENT"))));
}
Text_t compile_typed_table(env_t *env, ast_t *ast, type_t *table_type) {
@@ -51,10 +51,10 @@ Text_t compile_typed_table(env_t *env, ast_t *ast, type_t *table_type) {
for (ast_list_t *entry = table->entries; entry; entry = entry->next) {
DeclareMatch(e, entry->ast, TableEntry);
- code = Texts(
- code, ",\n\t{", compile_to_type(key_scope, e->key, key_t), ", ",
- compile_to_type(value_scope, e->value ? e->value : WrapAST(entry->ast, Var, .name = "EMPTY"), value_t),
- "}");
+ code = Texts(code, ",\n\t{", compile_to_type(key_scope, e->key, key_t), ", ",
+ compile_to_type(value_scope, e->value ? e->value : WrapAST(entry->ast, Var, .name = "PRESENT"),
+ value_t),
+ "}");
}
return Texts(code, ")");
}
diff --git a/src/compile/types.c b/src/compile/types.c
index 24790e46..a581fb61 100644
--- a/src/compile/types.c
+++ b/src/compile/types.c
@@ -12,7 +12,6 @@
public
Text_t compile_type(type_t *t) {
if (t == PATH_TYPE) return Text("Path_t");
- else if (t == PATH_TYPE_TYPE) return Text("PathType_t");
switch (t->tag) {
case ReturnType: errx(1, "Shouldn't be compiling ReturnType to a type");
@@ -73,7 +72,6 @@ Text_t compile_type(type_t *t) {
case TableType: return Texts("Optional", compile_type(nonnull));
case StructType: {
if (nonnull == PATH_TYPE) return Text("OptionalPath_t");
- if (nonnull == PATH_TYPE_TYPE) return Text("OptionalPathType_t");
DeclareMatch(s, nonnull, StructType);
return namespace_name(s->env, s->env->namespace->parent, Texts("$Optional", s->name, "$$type"));
}
@@ -90,7 +88,6 @@ public
Text_t compile_type_info(type_t *t) {
if (t == NULL) compiler_err(NULL, NULL, NULL, "Attempt to compile a NULL type");
if (t == PATH_TYPE) return Text("&Path$info");
- else if (t == PATH_TYPE_TYPE) return Text("&PathType$info");
switch (t->tag) {
case BoolType:
diff --git a/src/compile/whens.c b/src/compile/whens.c
index 4f6a2a40..618a667c 100644
--- a/src/compile/whens.c
+++ b/src/compile/whens.c
@@ -43,11 +43,7 @@ Text_t compile_when_statement(env_t *env, ast_t *ast) {
DeclareMatch(enum_t, subject_t, EnumType);
- Text_t code;
- if (enum_has_fields(subject_t))
- code = Texts("WHEN(", compile_type(subject_t), ", ", compile(env, when->subject), ", _when_subject, {\n");
- else code = Texts("switch(", compile(env, when->subject), ") {\n");
-
+ Text_t code = Texts("WHEN(", compile_type(subject_t), ", ", compile(env, when->subject), ", _when_subject, {\n");
for (when_clause_t *clause = when->clauses; clause; clause = clause->next) {
if (clause->pattern->tag == Var) {
const char *clause_tag_name = Match(clause->pattern, Var)->name;
@@ -83,8 +79,10 @@ Text_t compile_when_statement(env_t *env, ast_t *ast) {
const char *var_name = Match(args->value, Var)->name;
if (!streq(var_name, "_")) {
Text_t var = Texts("_$", var_name);
- code = Texts(code, compile_declaration(tag_type, var), " = _when_subject.",
- valid_c_name(clause_tag_name), ";\n");
+ ast_t *member =
+ WrapLiteralCode(ast, Texts("_when_subject.", valid_c_name(clause_tag_name)), .type = tag_type);
+ code = Texts(code, compile_declaration(tag_type, var), " = ",
+ compile_maybe_incref(env, member, tag_type), ";\n");
scope = fresh_scope(scope);
set_binding(scope, Match(args->value, Var)->name, tag_type, EMPTY_TEXT);
}
@@ -101,8 +99,10 @@ Text_t compile_when_statement(env_t *env, ast_t *ast) {
const char *var_name = Match(arg->value, Var)->name;
if (!streq(var_name, "_")) {
Text_t var = Texts("_$", var_name);
- code = Texts(code, compile_declaration(field->type, var), " = _when_subject.",
- valid_c_name(clause_tag_name), ".", valid_c_name(field->name), ";\n");
+ ast_t *member =
+ WrapLiteralCode(ast, Texts("_when_subject.", valid_c_name(clause_tag_name)), .type = tag_type);
+ code = Texts(code, compile_declaration(field->type, var), " = ",
+ compile_maybe_incref(env, member, tag_type), ".", valid_c_name(field->name), ";\n");
set_binding(scope, Match(arg->value, Var)->name, field->type, var);
}
field = field->next;
@@ -128,7 +128,7 @@ Text_t compile_when_statement(env_t *env, ast_t *ast) {
} else {
code = Texts(code, "default: errx(1, \"Invalid tag!\");\n");
}
- code = Texts(code, "\n}", enum_has_fields(subject_t) ? Text(")") : EMPTY_TEXT, "\n");
+ code = Texts(code, "\n}", Text(")"), "\n");
return code;
}