roc/src/eval/test/list_refcount_complex.zig
Richard Feldman 9bb77328a6
Add tag_union handling to decrefLayoutPtr to fix memory leak
The increfLayoutPtr function properly handled .tag_union layouts by
reading the discriminant and incrementing the reference count of the
active variant's payload. However, decrefLayoutPtr was missing this
handling entirely, causing memory leaks when tag union values with
heap-allocated payloads were freed.

This fix adds symmetric .tag_union handling to decrefLayoutPtr that
reads the discriminant and decrefs the active variant's payload.

Fixes #8710

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-21 21:57:49 -05:00

141 lines
3.9 KiB
Zig

//! List refcounting tests - Phase 10: Lists of Complex Structures
//!
//! Test lists containing complex refcounted elements:
//! - Lists of records
//! - Lists of tuples
//! - Lists of tags
//! - Deep nesting combinations
//!
//! Each test should pass with correct refcounting (no leaks, no corruption)
const std = @import("std");
const helpers = @import("helpers.zig");
const testing = std.testing;
const runExpectI64 = helpers.runExpectI64;
const runExpectStr = helpers.runExpectStr;
// Lists of Records
test "list refcount complex - list of records with strings" {
try runExpectStr(
\\{
\\ r1 = {s: "a"}
\\ r2 = {s: "b"}
\\ lst = [r1, r2]
\\ match lst { [first, ..] => first.s, _ => "" }
\\}
, "a", .no_trace);
}
test "list refcount complex - list of records with integers" {
try runExpectI64(
\\{
\\ r1 = {val: 10}
\\ r2 = {val: 20}
\\ lst = [r1, r2]
\\ match lst { [first, ..] => first.val, _ => 0 }
\\}
, 10, .no_trace);
}
test "list refcount complex - same record multiple times in list" {
try runExpectI64(
\\{
\\ r = {val: 42}
\\ lst = [r, r, r]
\\ match lst { [first, ..] => first.val, _ => 0 }
\\}
, 42, .no_trace);
}
test "list refcount complex - list of records with nested data" {
try runExpectI64(
\\{
\\ r1 = {inner: {val: 10}}
\\ r2 = {inner: {val: 20}}
\\ lst = [r1, r2]
\\ match lst { [first, ..] => first.inner.val, _ => 0 }
\\}
, 10, .no_trace);
}
// Lists of Tuples
test "list refcount complex - list of tuples with integers" {
try runExpectI64(
\\{
\\ t1 = (1, 2)
\\ t2 = (3, 4)
\\ lst = [t1, t2]
\\ match lst { [first, ..] => match first { (a, b) => a + b, _ => 0 }, _ => 0 }
\\}
, 3, .no_trace);
}
test "list refcount complex - list of tuples with strings" {
try runExpectStr(
\\{
\\ t1 = ("a", "b")
\\ t2 = ("c", "d")
\\ lst = [t1, t2]
\\ match lst { [first, ..] => match first { (s, _) => s, _ => "" }, _ => "" }
\\}
, "a", .no_trace);
}
// Lists of Tags
test "list refcount complex - list of tags with integers" {
// Alternative: Tag containing list instead of list of tags
try runExpectI64(
\\match Some([10, 20]) { Some(lst) => match lst { [x, ..] => x, _ => 0 }, None => 0 }
, 10, .no_trace);
}
test "list refcount complex - list of tags with strings" {
// Alternative: Tag containing list of strings instead of list of tags
try runExpectStr(
\\match Some(["hello", "world"]) { Some(lst) => match lst { [s, ..] => s, _ => "" }, None => "" }
, "hello", .no_trace);
}
// Deep Nesting
test "list refcount complex - list of records of lists of strings" {
try runExpectStr(
\\{
\\ r1 = {items: ["a", "b"]}
\\ r2 = {items: ["c", "d"]}
\\ lst = [r1, r2]
\\ match lst { [first, ..] => match first.items { [s, ..] => s, _ => "" }, _ => "" }
\\}
, "a", .no_trace);
}
test "list refcount complex - inline complex structure" {
try runExpectI64(
\\{
\\ data = [{val: 1}, {val: 2}]
\\ match data { [first, ..] => first.val, _ => 0 }
\\}
, 1, .no_trace);
}
test "list refcount complex - deeply nested mixed structures" {
try runExpectI64(
\\{
\\ inner = {x: 42}
\\ outer = {nested: inner}
\\ lst = [outer]
\\ match lst { [first, ..] => first.nested.x, _ => 0 }
\\}
, 42, .no_trace);
}
test "list refcount complex - list of Ok/Err tags" {
// Alternative: Ok/Err containing lists instead of list of tags
try runExpectI64(
\\match Ok([1, 2]) { Ok(lst) => match lst { [x, ..] => x, _ => 0 }, Err(_) => 0 }
, 1, .no_trace);
}