From a2e250d38c62f32296b92f0c9a2bdfc91d5fce2d Mon Sep 17 00:00:00 2001 From: Richard Feldman Date: Tue, 25 Nov 2025 09:33:28 -0500 Subject: [PATCH] Reproduce and fix `crash` bug --- src/canonicalize/Can.zig | 1 + src/cli/test/fx_platform_test.zig | 35 +++++++++++++++++++++++++++++++ src/eval/interpreter.zig | 10 +++++++++ src/interpreter_shim/main.zig | 10 ++++++--- 4 files changed, 53 insertions(+), 3 deletions(-) diff --git a/src/canonicalize/Can.zig b/src/canonicalize/Can.zig index 158a536256..43913a10f2 100644 --- a/src/canonicalize/Can.zig +++ b/src/canonicalize/Can.zig @@ -7803,6 +7803,7 @@ fn canonicalizeBlock(self: *Self, e: AST.Block) std.mem.Allocator.Error!Canonica const cir_stmt = self.env.store.getStatement(canonicailzed_stmt.idx); switch (cir_stmt) { .s_decl => |decl| try self.collectBoundVars(decl.pattern, &bound_vars), + .s_decl_gen => |decl| try self.collectBoundVars(decl.pattern, &bound_vars), .s_var => |var_stmt| try self.collectBoundVars(var_stmt.pattern_idx, &bound_vars), else => {}, } diff --git a/src/cli/test/fx_platform_test.zig b/src/cli/test/fx_platform_test.zig index c405a9456c..a5cf21fac8 100644 --- a/src/cli/test/fx_platform_test.zig +++ b/src/cli/test/fx_platform_test.zig @@ -264,3 +264,38 @@ test "fx platform expect with numeric literal" { try testing.expectEqualStrings("", run_result.stdout); try testing.expectEqualStrings("", run_result.stderr); } + +test "fx platform match with wildcard" { + const allocator = testing.allocator; + + try ensureRocBinary(allocator); + + // Run an app that uses a match expression with a wildcard pattern + // This tests that wildcard patterns in match expressions work correctly + const run_result = try std.process.Child.run(.{ + .allocator = allocator, + .argv = &[_][]const u8{ + "./zig-out/bin/roc", + "test/fx/match_with_wildcard.roc", + }, + }); + defer allocator.free(run_result.stdout); + defer allocator.free(run_result.stderr); + + switch (run_result.term) { + .Exited => |code| { + if (code != 0) { + std.debug.print("Run failed with exit code {}\n", .{code}); + std.debug.print("STDOUT: {s}\n", .{run_result.stdout}); + std.debug.print("STDERR: {s}\n", .{run_result.stderr}); + return error.RunFailed; + } + }, + else => { + std.debug.print("Run terminated abnormally: {}\n", .{run_result.term}); + std.debug.print("STDOUT: {s}\n", .{run_result.stdout}); + std.debug.print("STDERR: {s}\n", .{run_result.stderr}); + return error.RunFailed; + }, + } +} diff --git a/src/eval/interpreter.zig b/src/eval/interpreter.zig index d2277fd10f..fa5c9f9980 100644 --- a/src/eval/interpreter.zig +++ b/src/eval/interpreter.zig @@ -420,6 +420,16 @@ pub const Interpreter = struct { } const header: *const layout.Closure = @ptrCast(@alignCast(func_val.ptr.?)); + + // Switch to the closure's source module for correct expression evaluation. + // This is critical because pattern indices and expression indices in the closure + // are relative to the source module where the closure was defined, not the + // current module. Without this switch, bindings created in the closure body + // would have the wrong source_env and lookups would fail. + const saved_env = self.env; + self.env = @constCast(header.source_env); + defer self.env = saved_env; + const params = self.env.store.slicePatterns(header.params); try self.active_closures.append(func_val); diff --git a/src/interpreter_shim/main.zig b/src/interpreter_shim/main.zig index 479a69b63c..42e648315c 100644 --- a/src/interpreter_shim/main.zig +++ b/src/interpreter_shim/main.zig @@ -61,9 +61,13 @@ const ShimError = error{ /// Expected format in shared memory: [u64 parent_address][u32 entry_count][ModuleEnv data][u32[] def_indices] export fn roc_entrypoint(entry_idx: u32, ops: *builtins.host_abi.RocOps, ret_ptr: *anyopaque, arg_ptr: ?*anyopaque) callconv(.c) void { evaluateFromSharedMemory(entry_idx, ops, ret_ptr, arg_ptr) catch |err| { - var buf: [256]u8 = undefined; - const msg2 = std.fmt.bufPrint(&buf, "Error evaluating from shared memory: {s}", .{@errorName(err)}) catch "Error evaluating from shared memory"; - ops.crash(msg2); + // Only show this generic error if we haven't already crashed with a more specific message + // (errors like Crash already triggered roc_crashed with details) + if (err != error.Crash) { + var buf: [256]u8 = undefined; + const msg2 = std.fmt.bufPrint(&buf, "Error evaluating from shared memory: {s}", .{@errorName(err)}) catch "Error evaluating from shared memory"; + ops.crash(msg2); + } }; }