mirror of
https://github.com/roc-lang/roc.git
synced 2025-12-23 08:48:03 +00:00
parent
9e068a08e9
commit
3656bbeea9
2 changed files with 9 additions and 208 deletions
|
|
@ -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));
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue