Skip to content
Open
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 28 additions & 2 deletions src/codegen/codegen_stmt.c
Original file line number Diff line number Diff line change
Expand Up @@ -2500,8 +2500,32 @@ 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);
expr_has_drop = (def && def->type_info) ? def->type_info->traits.has_drop
: (node->type_info ? node->type_info->traits.has_drop : 0);
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);
Comment on lines +2525 to +2529
Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Valid concern. For bare lvalue statements like x; or obj.field;, the current fix would copy into a temp and drop the temp, leaving the original intact. This is safe because the original still gets dropped at scope end via the normal variable-drop mechanism. There is no double-drop because the temp is a copy, not a move. However, if the type has non-trivial copy semantics (e.g. refcounted), this could be wasteful. A follow-up could check if the expression is an lvalue and skip the temp in that case.

} else {
codegen_expression(ctx, node, out);
fprintf(out, ";\n");
Comment on lines +2504 to +2532
Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch! Added regression test that verifies as a bare statement triggers drop cleanup. Test passes with the fix and would fail without it.

}
Comment on lines +2507 to +2533
Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point. The else branch already guards with k == TYPE_STRUCT || k == TYPE_ENUM, so TYPE_FUNCTION won't set expr_has_drop via that path. The primary branch uses find_struct_def which returns NULL for function types, so expr_has_drop stays 0. The risk is in the fallback: if a function type somehow has traits.has_drop=1 AND no struct def is found, the inferred-type path could incorrectly flag it. A defensive fix would be to add the same kind check in the inferred-type path. Will address in a follow-up.

if (node->type == NODE_EXPR_CALL && node->call.callee && pending_closure_free_count > 0)
{
int is_thread_spawn = 0;
Expand All @@ -2524,6 +2548,8 @@ void codegen_node_single(ParserContext *ctx, ASTNode *node, FILE *out)
}
}
emit_pending_closure_frees(out);
break;
}
}
}

Expand Down
Loading