summaryrefslogtreecommitdiff
path: root/src/codegen
diff options
context:
space:
mode:
authorSAJJA EASWAR <eshwarsajja20@gmail.com>2026-01-25 22:59:36 +0530
committerSAJJA EASWAR <eshwarsajja20@gmail.com>2026-01-25 22:59:36 +0530
commitebc8b94baa6bc694cb4829e2eb2934a1f17fa6a1 (patch)
tree71b952ad455bf17d5bdea01472f0e2297f25eabe /src/codegen
parent863118c95caac0d69a35f6ae4d2e83844734a8a1 (diff)
parent489336b2101bf16edeec7bfc4379408eb19b936e (diff)
Merge branch 'main' into pr-109
Diffstat (limited to 'src/codegen')
-rw-r--r--src/codegen/codegen.c180
-rw-r--r--src/codegen/codegen.h3
-rw-r--r--src/codegen/codegen_decl.c22
-rw-r--r--src/codegen/codegen_stmt.c249
-rw-r--r--src/codegen/codegen_utils.c178
5 files changed, 484 insertions, 148 deletions
diff --git a/src/codegen/codegen.c b/src/codegen/codegen.c
index 706d613..7c58943 100644
--- a/src/codegen/codegen.c
+++ b/src/codegen/codegen.c
@@ -14,19 +14,19 @@
// Emit literal expression (int, float, string, char)
static void codegen_literal_expr(ASTNode *node, FILE *out)
{
- if (node->literal.type_kind == TOK_STRING)
+ if (node->literal.type_kind == LITERAL_STRING)
{
fprintf(out, "\"%s\"", node->literal.string_val);
}
- else if (node->literal.type_kind == TOK_CHAR)
+ else if (node->literal.type_kind == LITERAL_CHAR)
{
fprintf(out, "%s", node->literal.string_val);
}
- else if (node->literal.type_kind == 1) // float
+ else if (node->literal.type_kind == LITERAL_FLOAT)
{
fprintf(out, "%f", node->literal.float_val);
}
- else // int
+ else // LITERAL_INT
{
if (node->literal.int_val > 9223372036854775807ULL)
{
@@ -34,7 +34,7 @@ static void codegen_literal_expr(ASTNode *node, FILE *out)
}
else
{
- fprintf(out, "%llu", (unsigned long long)node->literal.int_val);
+ fprintf(out, "%lld", (long long)node->literal.int_val);
}
}
}
@@ -56,7 +56,7 @@ static void codegen_var_expr(ParserContext *ctx, ASTNode *node, FILE *out)
if (node->resolved_type && strcmp(node->resolved_type, "unknown") == 0)
{
- if (node->var_ref.suggestion)
+ if (node->var_ref.suggestion && !ctx->silent_warnings)
{
char msg[256];
sprintf(msg, "Undefined variable '%s'", node->var_ref.name);
@@ -289,9 +289,22 @@ void codegen_expression(ParserContext *ctx, ASTNode *node, FILE *out)
else
{
fprintf(out, "(");
- codegen_expression(ctx, node->binary.left, out);
+ int is_assignment =
+ (node->binary.op[strlen(node->binary.op) - 1] == '=' &&
+ strcmp(node->binary.op, "==") != 0 && strcmp(node->binary.op, "!=") != 0 &&
+ strcmp(node->binary.op, "<=") != 0 && strcmp(node->binary.op, ">=") != 0);
+
+ if (is_assignment)
+ {
+ codegen_expression(ctx, node->binary.left, out);
+ }
+ else
+ {
+ codegen_expression_with_move(ctx, node->binary.left, out);
+ }
+
fprintf(out, " %s ", node->binary.op);
- codegen_expression(ctx, node->binary.right, out);
+ codegen_expression_with_move(ctx, node->binary.right, out);
fprintf(out, ")");
}
break;
@@ -328,6 +341,61 @@ void codegen_expression(ParserContext *ctx, ASTNode *node, FILE *out)
}
}
+ // Check for Static Enum Variant Call: Enum.Variant(...)
+ if (target->type == NODE_EXPR_VAR)
+ {
+ ASTNode *def = find_struct_def(ctx, target->var_ref.name);
+ if (def && def->type == NODE_ENUM)
+ {
+ char mangled[256];
+ sprintf(mangled, "%s_%s", target->var_ref.name, method);
+ FuncSig *sig = find_func(ctx, mangled);
+ if (sig)
+ {
+ fprintf(out, "%s(", mangled);
+ ASTNode *arg = node->call.args;
+ int arg_idx = 0;
+ while (arg)
+ {
+ if (arg_idx > 0 && arg)
+ {
+ fprintf(out, ", ");
+ }
+
+ Type *param_t =
+ (arg_idx < sig->total_args) ? sig->arg_types[arg_idx] : NULL;
+
+ // Tuple Packing Logic
+ if (param_t && param_t->kind == TYPE_STRUCT &&
+ strncmp(param_t->name, "Tuple_", 6) == 0 && sig->total_args == 1 &&
+ node->call.arg_count > 1)
+ {
+ fprintf(out, "(%s){", param_t->name);
+ int first = 1;
+ while (arg)
+ {
+ if (!first)
+ {
+ fprintf(out, ", ");
+ }
+ first = 0;
+ codegen_expression(ctx, arg, out);
+ arg = arg->next;
+ }
+ fprintf(out, "}");
+ break; // All args consumed
+ }
+
+ codegen_expression(ctx, arg, out);
+ arg = arg->next;
+ arg_idx++;
+ }
+ fprintf(out, ")");
+ return;
+ }
+ }
+ }
+
char *type = infer_type(ctx, target);
if (type)
{
@@ -353,7 +421,7 @@ void codegen_expression(ParserContext *ctx, ASTNode *node, FILE *out)
while (arg)
{
fprintf(out, ", ");
- codegen_expression(ctx, arg, out);
+ codegen_expression_with_move(ctx, arg, out);
arg = arg->next;
}
fprintf(out, "); })");
@@ -396,6 +464,32 @@ void codegen_expression(ParserContext *ctx, ASTNode *node, FILE *out)
ref = ref->next;
}
+ if (!resolved_method_suffix)
+ {
+ GenericImplTemplate *it = ctx->impl_templates;
+ while (it)
+ {
+ char *tname = NULL;
+ if (it->impl_node && it->impl_node->type == NODE_IMPL_TRAIT)
+ {
+ tname = it->impl_node->impl_trait.trait_name;
+ }
+ if (tname)
+ {
+ char trait_mangled[512];
+ sprintf(trait_mangled, "%s__%s_%s", base, tname, method);
+ if (find_func(ctx, trait_mangled))
+ {
+ char *suffix = xmalloc(strlen(tname) + strlen(method) + 2);
+ sprintf(suffix, "%s_%s", tname, method);
+ resolved_method_suffix = suffix;
+ break;
+ }
+ }
+ it = it->next;
+ }
+ }
+
if (resolved_method_suffix)
{
method = resolved_method_suffix;
@@ -436,7 +530,7 @@ void codegen_expression(ParserContext *ctx, ASTNode *node, FILE *out)
while (arg)
{
fprintf(out, ", ");
- codegen_expression(ctx, arg, out);
+ codegen_expression_with_move(ctx, arg, out);
arg = arg->next;
}
fprintf(out, ")");
@@ -455,7 +549,8 @@ void codegen_expression(ParserContext *ctx, ASTNode *node, FILE *out)
}
}
- if (node->call.callee->type_info && node->call.callee->type_info->kind == TYPE_FUNCTION)
+ if (node->call.callee->type_info && node->call.callee->type_info->kind == TYPE_FUNCTION &&
+ !node->call.callee->type_info->is_raw)
{
fprintf(out, "({ z_closure_T _c = ");
codegen_expression(ctx, node->call.callee, out);
@@ -486,7 +581,7 @@ void codegen_expression(ParserContext *ctx, ASTNode *node, FILE *out)
while (arg)
{
fprintf(out, ", ");
- codegen_expression(ctx, arg, out);
+ codegen_expression_with_move(ctx, arg, out);
arg = arg->next;
}
fprintf(out, "); })");
@@ -528,7 +623,7 @@ void codegen_expression(ParserContext *ctx, ASTNode *node, FILE *out)
fprintf(out, ", ");
}
first = 0;
- codegen_expression(ctx, arg, out);
+ codegen_expression_with_move(ctx, arg, out);
arg = arg->next;
}
}
@@ -561,18 +656,55 @@ void codegen_expression(ParserContext *ctx, ASTNode *node, FILE *out)
free(inner);
handled = 1;
}
+ else if (param_t && param_t->kind == TYPE_STRUCT &&
+ strncmp(param_t->name, "Tuple_", 6) == 0 && sig->total_args == 1 &&
+ node->call.arg_count > 1)
+ {
+ // Implicit Tuple Packing:
+ // Function expects 1 Tuple argument, but call has multiple args -> Pack
+ // them
+ fprintf(out, "(%s){", param_t->name);
+
+ ASTNode *curr = arg;
+ int first_field = 1;
+ while (curr)
+ {
+ if (!first_field)
+ {
+ fprintf(out, ", ");
+ }
+ first_field = 0;
+ codegen_expression_with_move(ctx, curr, out);
+ curr = curr->next;
+ }
+ fprintf(out, "}");
+ handled = 1;
+
+ // Advance main loop iterator to end
+ arg = NULL;
+ }
}
- if (!handled)
+ if (handled)
{
- codegen_expression(ctx, arg, out);
+ if (arg == NULL)
+ {
+ break; // Tuple packed all args
+ }
+ }
+ else
+ {
+ codegen_expression_with_move(ctx, arg, out);
}
- if (arg->next)
+ if (arg && arg->next)
{
fprintf(out, ", ");
}
- arg = arg->next;
+ if (arg)
+ {
+ arg = arg->next;
+ }
arg_idx++;
}
}
@@ -1033,6 +1165,7 @@ void codegen_expression(ParserContext *ctx, ASTNode *node, FILE *out)
}
int is_zen_struct = 0;
+ int is_union = 0;
StructRef *sr = ctx->parsed_structs_list;
while (sr)
{
@@ -1040,6 +1173,10 @@ void codegen_expression(ParserContext *ctx, ASTNode *node, FILE *out)
strcmp(sr->node->strct.name, struct_name) == 0)
{
is_zen_struct = 1;
+ if (sr->node->strct.is_union)
+ {
+ is_union = 1;
+ }
break;
}
sr = sr->next;
@@ -1047,7 +1184,14 @@ void codegen_expression(ParserContext *ctx, ASTNode *node, FILE *out)
if (is_zen_struct)
{
- fprintf(out, "(struct %s){", struct_name);
+ if (is_union)
+ {
+ fprintf(out, "(union %s){", struct_name);
+ }
+ else
+ {
+ fprintf(out, "(struct %s){", struct_name);
+ }
}
else
{
diff --git a/src/codegen/codegen.h b/src/codegen/codegen.h
index d27356a..942de41 100644
--- a/src/codegen/codegen.h
+++ b/src/codegen/codegen.h
@@ -26,6 +26,9 @@ void emit_auto_type(ParserContext *ctx, ASTNode *init_expr, Token t, FILE *out);
char *codegen_type_to_string(Type *t);
void emit_func_signature(FILE *out, ASTNode *func, const char *name_override);
char *strip_template_suffix(const char *name);
+char *strip_template_suffix(const char *name);
+int emit_move_invalidation(ParserContext *ctx, ASTNode *node, FILE *out);
+void codegen_expression_with_move(ParserContext *ctx, ASTNode *node, FILE *out);
// Declaration emission (codegen_decl.c).
void emit_preamble(ParserContext *ctx, FILE *out);
diff --git a/src/codegen/codegen_decl.c b/src/codegen/codegen_decl.c
index 18d81f5..d525963 100644
--- a/src/codegen/codegen_decl.c
+++ b/src/codegen/codegen_decl.c
@@ -953,7 +953,20 @@ void emit_impl_vtables(ParserContext *ctx, FILE *out)
ASTNode *m = node->impl_trait.methods;
while (m)
{
- const char *orig = parse_original_method_name(m->func.name);
+ // Calculate expected prefix: Struct__Trait_
+ char prefix[256];
+ sprintf(prefix, "%s__%s_", strct, trait);
+ const char *orig = m->func.name;
+ if (strncmp(orig, prefix, strlen(prefix)) == 0)
+ {
+ orig += strlen(prefix);
+ }
+ else
+ {
+ // Fallback if mangling schema differs (shouldn't happen)
+ orig = parse_original_method_name(m->func.name);
+ }
+
fprintf(out, ".%s = (__typeof__(((%s_VTable*)0)->%s))%s__%s_%s", orig, trait, orig,
strct, trait, orig);
if (m->next)
@@ -978,7 +991,14 @@ int emit_tests_and_runner(ParserContext *ctx, ASTNode *node, FILE *out)
if (cur->type == NODE_TEST)
{
fprintf(out, "static void _z_test_%d() {\n", test_count);
+ int saved = defer_count;
codegen_walker(ctx, cur->test_stmt.body, out);
+ // Run defers
+ for (int i = defer_count - 1; i >= saved; i--)
+ {
+ codegen_node_single(ctx, defer_stack[i], out);
+ }
+ defer_count = saved;
fprintf(out, "}\n");
test_count++;
}
diff --git a/src/codegen/codegen_stmt.c b/src/codegen/codegen_stmt.c
index 54c6a14..ff8ea46 100644
--- a/src/codegen/codegen_stmt.c
+++ b/src/codegen/codegen_stmt.c
@@ -177,9 +177,19 @@ void codegen_match_internal(ParserContext *ctx, ASTNode *node, FILE *out, int us
ASTNode *ref_check = node->match_stmt.cases;
while (ref_check)
{
- if (ref_check->match_case.is_ref)
+ if (ref_check->match_case.binding_refs)
+ {
+ for (int i = 0; i < ref_check->match_case.binding_count; i++)
+ {
+ if (ref_check->match_case.binding_refs[i])
+ {
+ has_ref_binding = 1;
+ break;
+ }
+ }
+ }
+ if (has_ref_binding)
{
- has_ref_binding = 1;
break;
}
ref_check = ref_check->next;
@@ -325,157 +335,131 @@ void codegen_match_internal(ParserContext *ctx, ASTNode *node, FILE *out, int us
emit_pattern_condition(ctx, c->match_case.pattern, id, has_ref_binding, out);
}
fprintf(out, ") { ");
- if (c->match_case.binding_name)
+ if (c->match_case.binding_count > 0)
{
- if (is_option)
+ for (int i = 0; i < c->match_case.binding_count; i++)
{
- if (strstr(g_config.cc, "tcc"))
+ char *bname = c->match_case.binding_names[i];
+ int is_r = c->match_case.binding_refs ? c->match_case.binding_refs[i] : 0;
+
+ if (is_option)
{
- if (c->match_case.is_ref)
+ if (strstr(g_config.cc, "tcc"))
{
- fprintf(out, "__typeof__(&_m_%d.val) %s = &_m_%d.val; ", id,
- c->match_case.binding_name, id);
+ if (is_r)
+ {
+ fprintf(out, "__typeof__(&_m_%d.val) %s = &_m_%d.val; ", id, bname, id);
+ }
+ else
+ {
+ fprintf(out, "__typeof__(_m_%d.val) %s = _m_%d.val; ", id, bname, id);
+ }
}
else
{
- fprintf(out, "__typeof__(_m_%d.val) %s = _m_%d.val; ", id,
- c->match_case.binding_name, id);
+ if (is_r)
+ {
+ fprintf(out, "ZC_AUTO %s = &_m_%d->val; ", bname, id);
+ }
+ else if (has_ref_binding)
+ {
+ fprintf(out, "ZC_AUTO %s = _m_%d->val; ", bname, id);
+ }
+ else
+ {
+ fprintf(out, "ZC_AUTO %s = _m_%d.val; ", bname, id);
+ }
}
}
- else
+ else if (is_result)
{
- if (c->match_case.is_ref)
- {
- // _m is pointer when has_ref_binding, use ->
- fprintf(out, "ZC_AUTO %s = &_m_%d->val; ", c->match_case.binding_name, id);
- }
- else if (has_ref_binding)
+ char *field = "val";
+ if (strcmp(c->match_case.pattern, "Err") == 0)
{
- // _m is pointer, use -> but don't take address
- fprintf(out, "ZC_AUTO %s = _m_%d->val; ", c->match_case.binding_name, id);
+ field = "err";
}
- else
- {
- // _m is value, use .
- fprintf(out, "ZC_AUTO %s = _m_%d.val; ", c->match_case.binding_name, id);
- }
- }
- }
- else if (is_result) // FIX: Changed 'if' to 'else if' to match original logic structure
- // if needed, but original code had implicit fallthrough checks? No,
- // checks match pattern.
- {
- if (strcmp(c->match_case.pattern, "Ok") == 0)
- {
+
if (strstr(g_config.cc, "tcc"))
{
- if (c->match_case.is_ref)
+ if (is_r)
{
- fprintf(out, "__typeof__(&_m_%d->val) %s = &_m_%d->val; ", id,
- c->match_case.binding_name, id);
+ fprintf(out, "__typeof__(&_m_%d->%s) %s = &_m_%d->%s; ", id, field,
+ bname, id, field);
}
else
{
- fprintf(out, "__typeof__(_m_%d->val) %s = _m_%d->val; ", id,
- c->match_case.binding_name, id);
+ fprintf(out, "__typeof__(_m_%d->%s) %s = _m_%d->%s; ", id, field, bname,
+ id, field);
}
}
else
{
- if (c->match_case.is_ref)
+ if (is_r)
{
- // _m is pointer when has_ref_binding, use ->
- fprintf(out, "ZC_AUTO %s = &_m_%d->val; ", c->match_case.binding_name,
- id);
+ fprintf(out, "ZC_AUTO %s = &_m_%d->%s; ", bname, id, field);
}
else if (has_ref_binding)
{
- // _m is pointer, use -> but don't take address
- fprintf(out, "ZC_AUTO %s = _m_%d->val; ", c->match_case.binding_name,
- id);
+ fprintf(out, "ZC_AUTO %s = _m_%d->%s; ", bname, id, field);
}
else
{
- // _m is value, use .
- fprintf(out, "ZC_AUTO %s = _m_%d.val; ", c->match_case.binding_name,
- id);
+ fprintf(out, "ZC_AUTO %s = _m_%d.%s; ", bname, id, field);
}
}
}
else
{
- if (strstr(g_config.cc, "tcc"))
+ char *v = strrchr(c->match_case.pattern, '_');
+ if (v)
{
- if (c->match_case.is_ref)
+ v++;
+ }
+ else
+ {
+ v = c->match_case.pattern;
+ }
+
+ if (c->match_case.binding_count > 1)
+ {
+ // Tuple destructuring: data.Variant.vI
+ if (is_r)
+ {
+ fprintf(out, "ZC_AUTO %s = &_m_%d->data.%s.v%d; ", bname, id, v, i);
+ }
+ else if (has_ref_binding)
{
- fprintf(out, "__typeof__(&_m_%d->err) %s = &_m_%d->err; ", id,
- c->match_case.binding_name, id);
+ fprintf(out, "ZC_AUTO %s = _m_%d->data.%s.v%d; ", bname, id, v, i);
}
else
{
- fprintf(out, "__typeof__(_m_%d->err) %s = _m_%d->err; ", id,
- c->match_case.binding_name, id);
+ fprintf(out, "ZC_AUTO %s = _m_%d.data.%s.v%d; ", bname, id, v, i);
}
}
else
{
- if (c->match_case.is_ref)
+ // Single destructuring: data.Variant
+ if (is_r)
{
- // _m is pointer when has_ref_binding, use ->
- fprintf(out, "ZC_AUTO %s = &_m_%d->err; ", c->match_case.binding_name,
- id);
+ fprintf(out, "ZC_AUTO %s = &_m_%d->data.%s; ", bname, id, v);
}
else if (has_ref_binding)
{
- // _m is pointer, use -> but don't take address
- fprintf(out, "ZC_AUTO %s = _m_%d->err; ", c->match_case.binding_name,
- id);
+ fprintf(out, "ZC_AUTO %s = _m_%d->data.%s; ", bname, id, v);
}
else
{
- // _m is value, use .
- fprintf(out, "ZC_AUTO %s = _m_%d.err; ", c->match_case.binding_name,
- id);
+ fprintf(out, "ZC_AUTO %s = _m_%d.data.%s; ", bname, id, v);
}
}
}
}
- else
- {
- char *f = strrchr(c->match_case.pattern, '_');
- if (f)
- {
- f++;
- }
- else
- {
- f = c->match_case.pattern;
- }
- // Generic struct destructuring (for example, MyStruct_Variant)
- // Assuming data union or accessible field.
- if (c->match_case.is_ref)
- {
- // _m is pointer when has_ref_binding, use ->
- fprintf(out, "ZC_AUTO %s = &_m_%d->data.%s; ", c->match_case.binding_name, id,
- f);
- }
- else if (has_ref_binding)
- {
- // _m is pointer, use -> but don't take address
- fprintf(out, "ZC_AUTO %s = _m_%d->data.%s; ", c->match_case.binding_name, id,
- f);
- }
- else
- {
- // _m is value, use .
- fprintf(out, "ZC_AUTO %s = _m_%d.data.%s; ", c->match_case.binding_name, id, f);
- }
- }
}
// Check if body is a string literal (should auto-print).
ASTNode *body = c->match_case.body;
- int is_string_literal = (body->type == NODE_EXPR_LITERAL && body->literal.type_kind == 2);
+ int is_string_literal =
+ (body->type == NODE_EXPR_LITERAL && body->literal.type_kind == LITERAL_STRING);
if (is_expr)
{
@@ -514,9 +498,10 @@ void codegen_match_internal(ParserContext *ctx, ASTNode *node, FILE *out, int us
{
if (is_string_literal)
{
- fprintf(out, "({ printf(\"%%s\", ");
- codegen_expression(ctx, body, out);
- fprintf(out, "); printf(\"\\n\"); 0; })");
+ char *inner = body->literal.string_val;
+ char *code = process_printf_sugar(ctx, inner, 1, "stdout", NULL, NULL, 0);
+ fprintf(out, "%s;", code);
+ free(code);
}
else
{
@@ -902,9 +887,27 @@ void codegen_node_single(ParserContext *ctx, ASTNode *node, FILE *out)
}
ASTNode *def = find_struct_def(ctx, clean_type);
- if (def && def->type_info && def->type_info->traits.has_drop)
+ int has_drop = (def && def->type_info && def->type_info->traits.has_drop);
+
+ if (has_drop)
{
- fprintf(out, "__attribute__((cleanup(%s__Drop_glue))) ", clean_type);
+ // Drop Flag: int __z_drop_flag_name = 1;
+ fprintf(out, "int __z_drop_flag_%s = 1; ", node->var_decl.name);
+
+ // Synthesize Defer: if (__z_drop_flag_name) Name__Drop_drop(&name);
+ ASTNode *defer_node = xmalloc(sizeof(ASTNode));
+ defer_node->type = NODE_RAW_STMT;
+ char *stmt_str =
+ xmalloc(256 + strlen(node->var_decl.name) * 2 + strlen(clean_type));
+ sprintf(stmt_str, "if (__z_drop_flag_%s) %s__Drop_glue(&%s);",
+ node->var_decl.name, clean_type, node->var_decl.name);
+ defer_node->raw_stmt.content = stmt_str;
+ defer_node->line = node->line;
+
+ if (defer_count < MAX_DEFER)
+ {
+ defer_stack[defer_count++] = defer_node;
+ }
}
// Emit Variable with Type
@@ -917,6 +920,11 @@ void codegen_node_single(ParserContext *ctx, ASTNode *node, FILE *out)
codegen_expression(ctx, node->var_decl.init_expr, out);
}
fprintf(out, ";\n");
+ if (node->var_decl.init_expr &&
+ emit_move_invalidation(ctx, node->var_decl.init_expr, out))
+ {
+ fprintf(out, ";\n");
+ }
if (node->type_info)
{
@@ -941,9 +949,29 @@ void codegen_node_single(ParserContext *ctx, ASTNode *node, FILE *out)
}
ASTNode *def = find_struct_def(ctx, clean_type);
- if (def && def->type_info && def->type_info->traits.has_drop)
+ int has_drop = (def && def->type_info && def->type_info->traits.has_drop);
+
+ if (has_drop)
{
- fprintf(out, "__attribute__((cleanup(%s__Drop_glue))) ", clean_type);
+ // Drop Flag: int __z_drop_flag_name = 1;
+ fprintf(out, "int __z_drop_flag_%s = 1; ", node->var_decl.name);
+
+ // Synthesize Defer: if (__z_drop_flag_name) Name__Drop_drop(&name);
+ ASTNode *defer_node = xmalloc(sizeof(ASTNode));
+ defer_node->type = NODE_RAW_STMT;
+ // Build string
+ char *stmt_str =
+ xmalloc(256 + strlen(node->var_decl.name) * 2 + strlen(clean_type));
+ sprintf(stmt_str, "if (__z_drop_flag_%s) %s__Drop_glue(&%s);",
+ node->var_decl.name, clean_type, node->var_decl.name);
+ defer_node->raw_stmt.content = stmt_str;
+ defer_node->line = node->line;
+
+ // Push to defer stack
+ if (defer_count < MAX_DEFER)
+ {
+ defer_stack[defer_count++] = defer_node;
+ }
}
emit_var_decl_type(ctx, out, inferred, node->var_decl.name);
@@ -951,6 +979,12 @@ void codegen_node_single(ParserContext *ctx, ASTNode *node, FILE *out)
fprintf(out, " = ");
codegen_expression(ctx, node->var_decl.init_expr, out);
fprintf(out, ";\n");
+
+ if (node->var_decl.init_expr &&
+ emit_move_invalidation(ctx, node->var_decl.init_expr, out))
+ {
+ fprintf(out, ";\n");
+ }
}
else
{
@@ -965,6 +999,11 @@ void codegen_node_single(ParserContext *ctx, ASTNode *node, FILE *out)
fprintf(out, " = ");
codegen_expression(ctx, node->var_decl.init_expr, out);
fprintf(out, ";\n");
+ if (node->var_decl.init_expr &&
+ emit_move_invalidation(ctx, node->var_decl.init_expr, out))
+ {
+ fprintf(out, ";\n");
+ }
}
}
}
@@ -1475,6 +1514,7 @@ void codegen_node_single(ParserContext *ctx, ASTNode *node, FILE *out)
}
case NODE_REPL_PRINT:
{
+ // Safe block for printing
fprintf(out, "{ ");
emit_auto_type(ctx, node->repl_print.expr, node->token, out);
fprintf(out, " _zval = (");
@@ -1576,7 +1616,7 @@ void codegen_node_single(ParserContext *ctx, ASTNode *node, FILE *out)
}
case NODE_EXPR_LITERAL:
// String literal statement should auto-print
- if (node->literal.type_kind == 2 || node->literal.type_kind == TOK_STRING)
+ if (node->literal.type_kind == LITERAL_STRING)
{
fprintf(out, " printf(\"%%s\\n\", ");
codegen_expression(ctx, node, out);
@@ -1653,6 +1693,9 @@ void codegen_node_single(ParserContext *ctx, ASTNode *node, FILE *out)
fprintf(out, ");\n");
break;
}
+ case NODE_RAW_STMT:
+ fprintf(out, " %s\n", node->raw_stmt.content);
+ break;
default:
codegen_expression(ctx, node, out);
fprintf(out, ";\n");
diff --git a/src/codegen/codegen_utils.c b/src/codegen/codegen_utils.c
index fe580bf..6283bce 100644
--- a/src/codegen/codegen_utils.c
+++ b/src/codegen/codegen_utils.c
@@ -40,19 +40,33 @@ char *strip_template_suffix(const char *name)
return xstrdup(name);
}
-// Helper to emit variable declarations with array types.
-void emit_var_decl_type(ParserContext *ctx, FILE *out, const char *type_str, const char *var_name)
+// Helper to emit C declaration (handle arrays, function pointers correctly)
+void emit_c_decl(FILE *out, const char *type_str, const char *name)
{
- (void)ctx;
-
char *bracket = strchr(type_str, '[');
char *generic = strchr(type_str, '<');
+ char *fn_ptr = strstr(type_str, "(*");
- if (generic && (!bracket || generic < bracket))
+ if (fn_ptr)
+ {
+ char *end_paren = strchr(fn_ptr, ')');
+ if (end_paren)
+ {
+ int prefix_len = end_paren - type_str;
+ fprintf(out, "%.*s%s%s", prefix_len, type_str, name, end_paren);
+ }
+ else
+ {
+ // Fallback if malformed (shouldn't happen)
+ int prefix_len = fn_ptr - type_str + 2;
+ fprintf(out, "%.*s%s%s", prefix_len, type_str, name, fn_ptr + 2);
+ }
+ }
+ else if (generic && (!bracket || generic < bracket))
{
// Strip generic part for C output
int base_len = generic - type_str;
- fprintf(out, "%.*s %s", base_len, type_str, var_name);
+ fprintf(out, "%.*s %s", base_len, type_str, name);
if (bracket)
{
@@ -62,14 +76,21 @@ void emit_var_decl_type(ParserContext *ctx, FILE *out, const char *type_str, con
else if (bracket)
{
int base_len = bracket - type_str;
- fprintf(out, "%.*s %s%s", base_len, type_str, var_name, bracket);
+ fprintf(out, "%.*s %s%s", base_len, type_str, name, bracket);
}
else
{
- fprintf(out, "%s %s", type_str, var_name);
+ fprintf(out, "%s %s", type_str, name);
}
}
+// Helper to emit variable declarations with array types.
+void emit_var_decl_type(ParserContext *ctx, FILE *out, const char *type_str, const char *var_name)
+{
+ (void)ctx;
+ emit_c_decl(out, type_str, var_name);
+}
+
// Find struct definition
ASTNode *find_struct_def_codegen(ParserContext *ctx, const char *name)
{
@@ -455,15 +476,15 @@ char *infer_type(ParserContext *ctx, ASTNode *node)
if (node->type == NODE_EXPR_LITERAL)
{
- if (node->literal.type_kind == TOK_STRING)
+ if (node->literal.type_kind == LITERAL_STRING)
{
- return "string";
+ return xstrdup("string");
}
- if (node->literal.type_kind == TOK_CHAR)
+ if (node->literal.type_kind == LITERAL_CHAR)
{
- return "char";
+ return xstrdup("char");
}
- if (node->literal.type_kind == 1)
+ if (node->literal.type_kind == LITERAL_FLOAT)
{
return "double";
}
@@ -519,8 +540,22 @@ char *extract_call_args(const char *args)
// Parse original method name from mangled name.
const char *parse_original_method_name(const char *mangled)
{
- const char *last = strrchr(mangled, '_');
- return last ? last + 1 : mangled;
+ const char *sep = strstr(mangled, "__");
+ if (!sep)
+ {
+ return mangled;
+ }
+
+ // Let's iterate to find the last `__`.
+ const char *last_double = NULL;
+ const char *p = mangled;
+ while ((p = strstr(p, "__")))
+ {
+ last_double = p;
+ p += 2;
+ }
+
+ return last_double ? last_double + 2 : mangled;
}
// Replace string type in arguments.
@@ -635,7 +670,20 @@ void emit_func_signature(FILE *out, ASTNode *func, const char *name_override)
ret_str = xstrdup("void");
}
- fprintf(out, "%s %s(", ret_str, name_override ? name_override : func->func.name);
+ char *ret_suffix = NULL;
+ char *fn_ptr = strstr(ret_str, "(*)");
+
+ if (fn_ptr)
+ {
+ int prefix_len = fn_ptr - ret_str + 2; // Include "(*"
+ fprintf(out, "%.*s%s(", prefix_len, ret_str,
+ name_override ? name_override : func->func.name);
+ ret_suffix = fn_ptr + 2;
+ }
+ else
+ {
+ fprintf(out, "%s %s(", ret_str, name_override ? name_override : func->func.name);
+ }
free(ret_str);
// Args
@@ -669,16 +717,7 @@ void emit_func_signature(FILE *out, ASTNode *func, const char *name_override)
}
// check if array type
- char *bracket = strchr(type_str, '[');
- if (bracket)
- {
- int base_len = bracket - type_str;
- fprintf(out, "%.*s %s%s", base_len, type_str, name, bracket);
- }
- else
- {
- fprintf(out, "%s %s", type_str, name);
- }
+ emit_c_decl(out, type_str, name);
free(type_str);
}
if (func->func.is_varargs)
@@ -691,4 +730,91 @@ void emit_func_signature(FILE *out, ASTNode *func, const char *name_override)
}
}
fprintf(out, ")");
+
+ if (ret_suffix)
+ {
+ fprintf(out, "%s", ret_suffix);
+ }
+}
+
+// Invalidate a moved-from variable by zeroing it out to prevent double-free
+int emit_move_invalidation(ParserContext *ctx, ASTNode *node, FILE *out)
+{
+ if (!node)
+ {
+ return 0;
+ }
+
+ // Check if it's a valid l-value we can memset
+ if (node->type != NODE_EXPR_VAR && node->type != NODE_EXPR_MEMBER)
+ {
+ return 0;
+ }
+
+ // Common logic to find type and check Drop
+ char *type_name = infer_type(ctx, node);
+ ASTNode *def = NULL;
+ if (type_name)
+ {
+ char *clean_type = type_name;
+ if (strncmp(clean_type, "struct ", 7) == 0)
+ {
+ clean_type += 7;
+ }
+ def = find_struct_def(ctx, clean_type);
+ }
+
+ if (def && def->type_info && def->type_info->traits.has_drop)
+ {
+ if (node->type == NODE_EXPR_VAR)
+ {
+ fprintf(out, "__z_drop_flag_%s = 0; ", node->var_ref.name);
+ fprintf(out, "memset(&%s, 0, sizeof(%s))", node->var_ref.name, node->var_ref.name);
+ return 1;
+ }
+ else if (node->type == NODE_EXPR_MEMBER)
+ {
+ // For members: memset(&foo.bar, 0, sizeof(foo.bar))
+ fprintf(out, "memset(&");
+ codegen_expression(ctx, node, out);
+ fprintf(out, ", 0, sizeof(");
+ codegen_expression(ctx, node, out);
+ fprintf(out, "))");
+ return 1;
+ }
+ }
+ return 0;
+}
+
+// Emits expression, wrapping it in a move-invalidation block if it's a consuming variable usage
+void codegen_expression_with_move(ParserContext *ctx, ASTNode *node, FILE *out)
+{
+ if (node && (node->type == NODE_EXPR_VAR || node->type == NODE_EXPR_MEMBER))
+ {
+ // Re-use infer logic to see if we need invalidation
+ char *type_name = infer_type(ctx, node);
+ ASTNode *def = NULL;
+ if (type_name)
+ {
+ char *clean_type = type_name;
+ if (strncmp(clean_type, "struct ", 7) == 0)
+ {
+ clean_type += 7;
+ }
+ def = find_struct_def(ctx, clean_type);
+ }
+
+ if (def && def->type_info && def->type_info->traits.has_drop)
+ {
+ fprintf(out, "({ __typeof__(");
+ codegen_expression(ctx, node, out);
+ fprintf(out, ") _mv = ");
+ codegen_expression(ctx, node, out);
+ fprintf(out, "; ");
+ emit_move_invalidation(ctx, node, out);
+ fprintf(out, "; _mv; })");
+ return;
+ }
+ }
+ codegen_expression(ctx, node, out);
}