Revert "wip"

This reverts commit 9e068a08e9.
This commit is contained in:
Richard Feldman 2025-11-19 15:16:40 -05:00
parent 9e068a08e9
commit 3656bbeea9
No known key found for this signature in database
2 changed files with 9 additions and 208 deletions

View file

@ -31,12 +31,9 @@ const layout_mod = @import("layout");
fn comptimeRocAlloc(alloc_args: *RocAlloc, env: *anyopaque) callconv(.c) void {
const evaluator: *ComptimeEvaluator = @ptrCast(@alignCast(env));
// We need to store a usize for the length, so allocate with at least usize alignment
const actual_alignment = @max(alloc_args.alignment, @alignOf(usize));
const align_enum = std.mem.Alignment.fromByteUnits(@as(usize, @intCast(actual_alignment)));
const align_enum = std.mem.Alignment.fromByteUnits(@as(usize, @intCast(alloc_args.alignment)));
// Store user data size in header (we'll use this in realloc to know how much to copy)
// IMPORTANT: size_storage_bytes must be aligned to actual_alignment so user data starts at correct alignment
const size_storage_bytes = std.mem.alignForward(usize, @sizeOf(usize), actual_alignment);
const size_storage_bytes = @max(alloc_args.alignment, @alignOf(usize));
const total_size = alloc_args.length + size_storage_bytes;
const result = evaluator.allocator.rawAlloc(total_size, align_enum, @returnAddress());
const base_ptr = result orelse {
@ -69,10 +66,9 @@ fn comptimeRocRealloc(realloc_args: *RocRealloc, env: *anyopaque) callconv(.c) v
// Allocate new memory and copy old data, but don't free old memory
// Old memory is leaked to avoid double-free issues with refcounting
const evaluator: *ComptimeEvaluator = @ptrCast(@alignCast(env));
const actual_alignment = @max(realloc_args.alignment, @alignOf(usize));
const size_storage_bytes = std.mem.alignForward(usize, @sizeOf(usize), actual_alignment);
const size_storage_bytes = @max(realloc_args.alignment, @alignOf(usize));
const new_total_size = realloc_args.new_length + size_storage_bytes;
const align_enum = std.mem.Alignment.fromByteUnits(@as(usize, @intCast(actual_alignment)));
const align_enum = std.mem.Alignment.fromByteUnits(@as(usize, @intCast(realloc_args.alignment)));
// Get old user data length (we now store the user length, not total size)
const old_user_length_ptr: *const usize = @ptrFromInt(@intFromPtr(realloc_args.answer) - @sizeOf(usize));

View file

@ -258,36 +258,15 @@ test "e_low_level_lambda - List.concat with two empty lists" {
}
test "e_low_level_lambda - List.concat preserves order" {
// Test concat + len (this works with evalModuleAndGetInt)
if (true) return error.SkipZigTest; // TODO: fix double-free issue
const src =
\\x = List.concat([10, 20], [30, 40, 50])
\\len = List.len(x)
\\first = List.first(x)
;
const len_value = try evalModuleAndGetInt(src, 1);
try testing.expectEqual(@as(i128, 5), len_value);
// TODO: The full test would call List.first(x) and render the Result value,
// but that still crashes with "Invalid free" after the list is allocated.
//
// What we know:
// - Creating a list works (can call List.len, List.concat, etc.)
// - Creating a Result works (Ok(42) can be rendered)
// - Creating a list THEN a Result works (x=[...]; y=Ok(42) works)
// - But calling List.first(x) or List.get(x, 0) crashes with "Invalid free"
//
// The crash happens when computing the runtime layout for the Result return type,
// specifically when trying to insert "payload" into the ident interner. This suggests
// that the list allocation is somehow corrupting memory that the interner later uses.
//
// The issue is NOT:
// - Alignment (fixed by using std.mem.alignForward)
// - Refcounting (no-op dealloc is intentional)
// - Header storage (length is correctly stored 8 bytes before user data)
//
// Remaining investigation needed:
// - Is the list allocation asking for the wrong size?
// - Is native Roc code writing past the end of allocated buffers?
// - Is there an interaction between Roc stack values and heap allocations?
const first_value = try evalModuleAndGetString(src, 1, testing.allocator);
defer testing.allocator.free(first_value);
try testing.expectEqualStrings("Ok 10", first_value);
}
test "e_low_level_lambda - List.concat with strings (refcounted elements)" {
@ -319,177 +298,3 @@ test "e_low_level_lambda - List.concat with empty string list" {
const len_value = try evalModuleAndGetInt(src, 1);
try testing.expectEqual(@as(i128, 3), len_value);
}
test "e_low_level_lambda - Debug: evalModuleAndGetString with two int declarations" {
// This test is to debug the memory corruption issue with evalModuleAndGetString
// evalModuleAndGetInt works fine with multiple declarations, but evalModuleAndGetString crashes
const src =
\\x = 42
\\y = 99
;
const y_value = try evalModuleAndGetString(src, 1, testing.allocator);
defer testing.allocator.free(y_value);
try testing.expectEqualStrings("99", y_value);
}
test "e_low_level_lambda - Debug: List with evalModuleAndGetInt" {
// Test that we can evaluate a list and get its length (doesn't call renderValueRocWithType)
const src =
\\y = [10, 20, 30]
\\len = List.len(y)
;
const len_value = try evalModuleAndGetInt(src, 1);
try testing.expectEqual(@as(i128, 3), len_value);
}
test "e_low_level_lambda - Debug: Render single list" {
// Test rendering a single list after fixing alignment
// NOTE: Lists currently render as "<unsupported>" - this is a known limitation
const src =
\\y = [10, 20, 30]
;
const y_value = try evalModuleAndGetString(src, 0, testing.allocator);
defer testing.allocator.free(y_value);
try testing.expectEqualStrings("<unsupported>", y_value);
}
test "e_low_level_lambda - Debug: Two lists" {
// Test evaluating two lists in sequence
const src =
\\a = [10, 20]
\\b = [30, 40]
;
const b_value = try evalModuleAndGetString(src, 1, testing.allocator);
defer testing.allocator.free(b_value);
try testing.expectEqualStrings("<unsupported>", b_value);
}
test "e_low_level_lambda - Debug: Simple Ok Result" {
// Test rendering a Result value
const src =
\\x = 42
\\y = Ok(10)
;
const y_value = try evalModuleAndGetString(src, 1, testing.allocator);
defer testing.allocator.free(y_value);
try testing.expectEqualStrings("Ok(10)", y_value);
}
test "e_low_level_lambda - Debug: List.len twice" {
// Test calling List.len twice
const src =
\\x = [10, 20, 30]
\\len1 = List.len(x)
\\len2 = List.len(x)
;
const len2_value = try evalModuleAndGetInt(src, 2);
try testing.expectEqual(@as(i128, 3), len2_value);
}
// Skipping when expression tests - minimal evaluator doesn't support when expressions yet
// test "e_low_level_lambda - Debug: When expression with list" {
// // Test a when expression that pattern matches
// const src =
// \\x = [10, 20, 30]
// \\y = when List.len(x) is
// \\ 3 -> 99
// \\ _ -> 0
// ;
//
// const y_value = try evalModuleAndGetInt(src, 1);
// try testing.expectEqual(@as(i128, 99), y_value);
// }
//
// test "e_low_level_lambda - Debug: Simple when expression" {
// // Test a when expression without lists
// const src =
// \\x = 3
// \\y = when x is
// \\ 3 -> 99
// \\ _ -> 0
// ;
//
// const y_value = try evalModuleAndGetInt(src, 1);
// try testing.expectEqual(@as(i128, 99), y_value);
// }
test "e_low_level_lambda - Debug: Simple Result without list" {
// Test creating a Result without any lists
const src =
\\x = Ok(42)
\\y = 99
;
const y_value = try evalModuleAndGetString(src, 1, testing.allocator);
defer testing.allocator.free(y_value);
try testing.expectEqualStrings("99", y_value);
}
test "e_low_level_lambda - Debug: List then Result" {
// Test creating a list, then a Result (without List.get)
const src =
\\x = [10, 20, 30]
\\y = Ok(42)
;
const y_value = try evalModuleAndGetString(src, 1, testing.allocator);
defer testing.allocator.free(y_value);
try testing.expectEqualStrings("Ok(42)", y_value);
}
// Disabled - still investigating memory corruption
// test "e_low_level_lambda - Debug: Minimal list with List.first" {
// // Test List.first with a single-element list
// // This test uses arena allocator to isolate Roc allocations
// const src =
// \\x = [42]
// \\y = List.first(x)
// ;
//
// const y_value = try evalModuleAndGetString(src, 1, testing.allocator);
// defer testing.allocator.free(y_value);
// try testing.expectEqualStrings("Ok(42)", y_value);
// }
// Skipping - this triggers the same memory corruption as List.get
// test "e_low_level_lambda - Debug: List.first attempt" {
// // Test List.first which also returns a Result
// const src =
// \\x = [10, 20, 30]
// \\y = List.first(x)
// ;
//
// const y_value = try evalModuleAndGetString(src, 1, testing.allocator);
// defer testing.allocator.free(y_value);
// try testing.expectEqualStrings("Ok(10)", y_value);
// }
// test "e_low_level_lambda - Debug: List.get attempt" {
// // Test List.get with evalModuleAndGetString - this was crashing
// const src =
// \\x = [10, 20, 30]
// \\y = List.get(x, 0)
// ;
//
// const y_value = try evalModuleAndGetString(src, 1, testing.allocator);
// defer testing.allocator.free(y_value);
// try testing.expectEqualStrings("Ok(10)", y_value);
// }
test "e_low_level_lambda - Debug: List.len after List.concat" {
// Test a multi-step operation: concat then len
const src =
\\x = List.concat([10, 20], [30, 40])
\\y = List.len(x)
;
const y_value = try evalModuleAndGetInt(src, 1);
try testing.expectEqual(@as(i128, 4), y_value);
}