fix infinite recursion bug (again)

This commit is contained in:
Luke Boswell 2025-12-15 15:18:49 +11:00
parent 0016bb1a04
commit 4e4eef2d04
No known key found for this signature in database
GPG key ID: 54A7324B1B975757
5 changed files with 704 additions and 510 deletions

View file

@ -1,9 +1,11 @@
app [main] { pf: platform "./platform/main.roc" }
import pf.Simple
Model : { value: I64 }
main = {
init: |{}| { value: 0 },
update: |m, delta| { value: m.value + delta },
render: |m| m.value,
render: |_m| Simple.leaf("hello"),
}

View file

@ -127,18 +127,15 @@ fn platform_main() !void {
try stdout.print("\x1b[32mSUCCESS\x1b[0m: init completed!\n", .{});
success_count += 1;
// Test 2: render takes Box(model), returns I64
try stdout.print("\n=== Test 2: render(Box(model)) -> I64 ===\n", .{});
var render_result: i64 = undefined;
// Test 2: render takes Box(model), returns Simple(Model) - an opaque type
// Simple(Model) is a tag union, so it has a discriminant + payload
// For now, just treat it as a blob and check we can call without crashing
try stdout.print("\n=== Test 2: render(Box(model)) -> Simple(Model) ===\n", .{});
var render_result: [64]u8 = undefined; // Buffer for opaque result
roc__render(&roc_ops, @as(*anyopaque, @ptrCast(&render_result)), @as(*anyopaque, @ptrCast(&boxed_model)));
try stdout.print("render returned: {}\n", .{render_result});
if (render_result == 0) {
try stdout.print("\x1b[32mSUCCESS\x1b[0m: render returned expected value 0!\n", .{});
success_count += 1;
} else {
try stdout.print("\x1b[31mFAIL\x1b[0m: render returned {} but expected 0!\n", .{render_result});
}
try stdout.print("render completed without crash\n", .{});
try stdout.print("\x1b[32mSUCCESS\x1b[0m: render returned Simple(Model)!\n", .{});
success_count += 1;
// Test 3: update takes (Box(model), I64), returns Box(model)
try stdout.print("\n=== Test 3: update(Box(model), 42) -> Box(model) ===\n", .{});
@ -151,17 +148,12 @@ fn platform_main() !void {
success_count += 1;
// Test 4: render the updated model
try stdout.print("\n=== Test 4: render(updated Box(model)) -> I64 ===\n", .{});
var final_result: i64 = undefined;
try stdout.print("\n=== Test 4: render(updated Box(model)) -> Simple(Model) ===\n", .{});
var final_result: [64]u8 = undefined;
roc__render(&roc_ops, @as(*anyopaque, @ptrCast(&final_result)), @as(*anyopaque, @ptrCast(&new_boxed_model)));
try stdout.print("render returned: {}\n", .{final_result});
if (final_result == 42) {
try stdout.print("\x1b[32mSUCCESS\x1b[0m: render returned expected value 42!\n", .{});
success_count += 1;
} else {
try stdout.print("\x1b[31mFAIL\x1b[0m: render returned {} but expected 42!\n", .{final_result});
}
try stdout.print("render completed without crash\n", .{});
try stdout.print("\x1b[32mSUCCESS\x1b[0m: render returned Simple(Model)!\n", .{});
success_count += 1;
// Final summary
try stdout.print("\n=== FINAL RESULT ===\n", .{});

View file

@ -3,10 +3,10 @@ platform ""
[Model : model] for main : {
init : {} -> model,
update : model, I64 -> model,
render : model -> I64
render : model -> Simple(model)
}
}
exposes []
exposes [Simple]
packages {}
provides { init_for_host: "init", update_for_host: "update", render_for_host: "render" }
targets: {
@ -21,6 +21,8 @@ platform ""
}
}
import Simple exposing [Simple]
# Explicit type annotations for host-facing functions
# Note: Use uppercase Model here - it's a type alias introduced by the for-clause [Model : model]
# that gets unified with the app's concrete type during type checking.
@ -38,7 +40,8 @@ update_for_host = |boxed_model, value| {
Box.box(update_fn(m, value))
}
render_for_host : Box(Model) -> I64
# This now returns Simple(Model) - an opaque type from an imported module
render_for_host : Box(Model) -> Simple(Model)
render_for_host = |boxed_model| {
m = Box.unbox(boxed_model)
render_fn = main.render