diff --git a/src/codegen/codegen_stmt.c b/src/codegen/codegen_stmt.c index b416438f..01540c6c 100644 --- a/src/codegen/codegen_stmt.c +++ b/src/codegen/codegen_stmt.c @@ -2500,8 +2500,37 @@ void codegen_node_single(ParserContext *ctx, ASTNode *node, FILE *out) break; } default: - codegen_expression(ctx, node, out); - fprintf(out, ";\n"); + { + // Check if expression returns a Drop type that needs cleanup. + // Without this, bare calls like `MyInt::new()` leak memory (issue #406). + int expr_has_drop = 0; + char *inferred = infer_type(ctx, node); + if (inferred) { + char *ct = inferred; + if (strncmp(ct, "struct ", 7) == 0) ct += 7; + ASTNode *def = find_struct_def(ctx, ct); + if (def && def->type_info) { + expr_has_drop = def->type_info->traits.has_drop; + } else if (node->type_info) { + TypeKind k = node->type_info->kind; + if (k == TYPE_STRUCT || k == TYPE_ENUM) + expr_has_drop = node->type_info->traits.has_drop; + } + free(inferred); + } else if (node->type_info) { + TypeKind k = node->type_info->kind; + if (k == TYPE_STRUCT || k == TYPE_ENUM) + expr_has_drop = node->type_info->traits.has_drop; + } + if (expr_has_drop) { + int id = tmp_counter++; + fprintf(out, " ZC_AUTO _z_tmp_%d = ", id); + codegen_expression(ctx, node, out); + fprintf(out, ";\n _z_drop(_z_tmp_%d);\n", id); + } else { + codegen_expression(ctx, node, out); + fprintf(out, ";\n"); + } if (node->type == NODE_EXPR_CALL && node->call.callee && pending_closure_free_count > 0) { int is_thread_spawn = 0; @@ -2524,6 +2553,8 @@ void codegen_node_single(ParserContext *ctx, ASTNode *node, FILE *out) } } emit_pending_closure_frees(out); + break; + } } } diff --git a/tests/runtime/memory/test_drop_unassigned.zc b/tests/runtime/memory/test_drop_unassigned.zc new file mode 100644 index 00000000..2e9da42d --- /dev/null +++ b/tests/runtime/memory/test_drop_unassigned.zc @@ -0,0 +1,38 @@ +// Regression test for issue #406: +// Bare expressions returning Drop types should still call drop. +// Before the fix, `MyResource::new();` would leak memory because +// the return value was neither assigned nor dropped. + +import "std/mem.zc" + +let DROP_CALLED = 0; + +struct MyResource { + id: int; +} + +impl Drop for MyResource { + fn drop(self) { + DROP_CALLED = DROP_CALLED + 1; + } +} + +impl MyResource { + fn new(id: int) -> MyResource { + return MyResource { id: id }; + } +} + +test "drop_unassigned_call" { + DROP_CALLED = 0; + + // Bare call — return value is not assigned. + // The generated code must store the result in a temp and call _z_drop. + MyResource::new(42); + + if (DROP_CALLED != 1) { + println "Error: Drop was not called for unassigned expression!"; + println " Expected DROP_CALLED=1, got {DROP_CALLED}"; + exit(1); + } +}