diff --git a/build.zig b/build.zig index 2f71a165d4..6d61a73b78 100644 --- a/build.zig +++ b/build.zig @@ -996,6 +996,8 @@ fn createTestPlatformHostLib( target: ResolvedTarget, optimize: OptimizeMode, roc_modules: modules.RocModules, + strip: bool, + omit_frame_pointer: ?bool, ) *Step.Compile { const lib = b.addLibrary(.{ .name = name, @@ -1004,7 +1006,8 @@ fn createTestPlatformHostLib( .root_source_file = b.path(host_path), .target = target, .optimize = optimize, - .strip = optimize != .Debug, + .strip = strip, + .omit_frame_pointer = omit_frame_pointer, .pic = true, // Enable Position Independent Code for PIE compatibility }), }); @@ -1026,6 +1029,8 @@ fn buildAndCopyTestPlatformHostLib( target_name: []const u8, optimize: OptimizeMode, roc_modules: modules.RocModules, + strip: bool, + omit_frame_pointer: ?bool, ) *Step.UpdateSourceFiles { const lib = createTestPlatformHostLib( b, @@ -1034,6 +1039,8 @@ fn buildAndCopyTestPlatformHostLib( target, optimize, roc_modules, + strip, + omit_frame_pointer, ); // Use correct filename for target platform @@ -1162,6 +1169,8 @@ fn setupTestPlatforms( optimize: OptimizeMode, roc_modules: modules.RocModules, test_platforms_step: *Step, + strip: bool, + omit_frame_pointer: ?bool, ) void { // Clear the Roc cache when test platforms are rebuilt to ensure stale cached hosts aren't used const clear_cache_step = createClearCacheStep(b); @@ -1176,6 +1185,8 @@ fn setupTestPlatforms( native_target_name, optimize, roc_modules, + strip, + omit_frame_pointer, ); clear_cache_step.dependOn(©_step.step); } @@ -1192,6 +1203,8 @@ fn setupTestPlatforms( cross_target.name, optimize, roc_modules, + strip, + omit_frame_pointer, ); clear_cache_step.dependOn(©_step.step); } @@ -1207,6 +1220,8 @@ fn setupTestPlatforms( "wasm32", optimize, roc_modules, + strip, + omit_frame_pointer, ); clear_cache_step.dependOn(©_step.step); } @@ -1247,7 +1262,7 @@ pub fn build(b: *std.Build) void { break :blk b.standardTargetOptions(.{ .default_target = default_target_query }); }; const optimize = b.standardOptimizeOption(.{}); - const strip = b.option(bool, "strip", "Omit debug information"); + const strip_flag = b.option(bool, "strip", "Omit debug information"); const no_bin = b.option(bool, "no-bin", "Skip emitting binaries (important for fast incremental compilation)") orelse false; const trace_eval = b.option(bool, "trace-eval", "Enable detailed evaluation tracing for debugging") orelse (optimize == .Debug); const trace_refcount = b.option(bool, "trace-refcount", "Enable detailed refcount tracing for debugging memory issues") orelse false; @@ -1284,15 +1299,36 @@ pub fn build(b: *std.Build) void { build_options.addOption(bool, "trace_eval", trace_eval); build_options.addOption(bool, "trace_refcount", trace_refcount); build_options.addOption([]const u8, "compiler_version", getCompilerVersion(b, optimize)); - if (target.result.os.tag == .macos and flag_tracy_callstack) { - std.log.warn("Tracy callstack does not work on MacOS, disabling.", .{}); - build_options.addOption(bool, "enable_tracy_callstack", false); - } else { - build_options.addOption(bool, "enable_tracy_callstack", flag_tracy_callstack); - } + build_options.addOption(bool, "enable_tracy_callstack", flag_tracy_callstack); build_options.addOption(bool, "enable_tracy_allocation", flag_tracy_allocation); build_options.addOption(u32, "tracy_callstack_depth", flag_tracy_callstack_depth); + // Calculate effective strip value + // - If strip is explicitly set by user, use that (warn if tracy_callstack is also set) + // - Otherwise, default to stripping if not debug, unless tracy_callstack is enabled + const strip: bool = blk: { + if (strip_flag) |strip_bool| { + // User explicitly set strip + if (strip_bool and flag_tracy_callstack) { + std.log.warn("Both -Dstrip and -Dtracy-callstack are enabled. " ++ + "Stripping will remove callstack information needed by Tracy.", .{}); + } + break :blk strip_bool; + } else { + // User did not set strip - use defaults + if (flag_tracy_callstack) { + // Don't strip when tracy callstack is enabled (preserves debug info) + break :blk false; + } else { + // Default: strip in release modes + break :blk optimize != .Debug; + } + } + }; + + // Don't omit frame pointer when tracy callstack is enabled (needed for callstack capture) + const omit_frame_pointer: ?bool = if (flag_tracy_callstack) false else null; + const target_is_native = // `query.isNative()` becomes false as soon as users override CPU features (e.g. -Dcpu=x86_64_v3), // but we still want to treat those builds as native so macOS can link against real FSEvents. @@ -1386,9 +1422,9 @@ pub fn build(b: *std.Build) void { roc_modules.eval.addImport("compiled_builtins", compiled_builtins_module); // Setup test platform host libraries - setupTestPlatforms(b, target, optimize, roc_modules, test_platforms_step); + setupTestPlatforms(b, target, optimize, roc_modules, test_platforms_step, strip, omit_frame_pointer); - const roc_exe = addMainExe(b, roc_modules, target, optimize, strip, enable_llvm, use_system_llvm, user_llvm_path, flag_enable_tracy, zstd, compiled_builtins_module, write_compiled_builtins) orelse return; + const roc_exe = addMainExe(b, roc_modules, target, optimize, strip, omit_frame_pointer, enable_llvm, use_system_llvm, user_llvm_path, flag_enable_tracy, zstd, compiled_builtins_module, write_compiled_builtins, flag_enable_tracy) orelse return; roc_modules.addAll(roc_exe); install_and_run(b, no_bin, roc_exe, roc_step, run_step, run_args); @@ -1797,6 +1833,8 @@ pub fn build(b: *std.Build) void { fx_host_target, optimize, roc_modules, + strip, + omit_frame_pointer, ); // Copy the fx test platform host library to the source directory @@ -1992,7 +2030,8 @@ fn addMainExe( roc_modules: modules.RocModules, target: ResolvedTarget, optimize: OptimizeMode, - strip: ?bool, + strip: bool, + omit_frame_pointer: ?bool, enable_llvm: bool, use_system_llvm: bool, user_llvm_path: ?[]const u8, @@ -2000,6 +2039,7 @@ fn addMainExe( zstd: *Dependency, compiled_builtins_module: *std.Build.Module, write_compiled_builtins: *Step.WriteFile, + flag_enable_tracy: ?[]const u8, ) ?*Step.Compile { const exe = b.addExecutable(.{ .name = "roc", @@ -2008,6 +2048,7 @@ fn addMainExe( .target = target, .optimize = optimize, .strip = strip, + .omit_frame_pointer = omit_frame_pointer, .link_libc = true, }), }); @@ -2026,6 +2067,8 @@ fn addMainExe( native_target_name, optimize, roc_modules, + strip, + omit_frame_pointer, ); b.getInstallStep().dependOn(©_step.step); } @@ -2042,6 +2085,8 @@ fn addMainExe( cross_target.name, optimize, roc_modules, + strip, + omit_frame_pointer, ); b.getInstallStep().dependOn(©_step.step); } @@ -2063,6 +2108,7 @@ fn addMainExe( .target = target, .optimize = optimize, .strip = strip, + .omit_frame_pointer = omit_frame_pointer, .pic = true, // Enable Position Independent Code for PIE compatibility }), }); @@ -2077,7 +2123,8 @@ fn addMainExe( .root_source_file = b.path("src/interpreter_shim/main.zig"), .target = target, .optimize = optimize, - .strip = optimize != .Debug, + .strip = strip, + .omit_frame_pointer = omit_frame_pointer, .pic = true, // Enable Position Independent Code for PIE compatibility }), .linkage = .static, @@ -2103,6 +2150,9 @@ fn addMainExe( copy_shim.addCopyFileToSource(shim_lib.getEmittedBin(), b.pathJoin(&.{ "src/cli", interpreter_shim_filename })); exe.step.dependOn(©_shim.step); + // Add tracy support (required by parse/can/check modules) + add_tracy(b, roc_modules.build_options, shim_lib, b.graph.host, false, flag_enable_tracy); + // Cross-compile interpreter shim for all supported targets // This allows `roc build --target=X` to work for cross-compilation const cross_compile_shim_targets = [_]struct { name: []const u8, query: std.Target.Query }{ @@ -2123,7 +2173,8 @@ fn addMainExe( .root_source_file = b.path("src/builtins/static_lib.zig"), .target = cross_resolved_target, .optimize = optimize, - .strip = optimize != .Debug, + .strip = strip, + .omit_frame_pointer = omit_frame_pointer, .pic = true, }), }); @@ -2136,7 +2187,8 @@ fn addMainExe( .root_source_file = b.path("src/interpreter_shim/main.zig"), .target = cross_resolved_target, .optimize = optimize, - .strip = optimize != .Debug, + .strip = strip, + .omit_frame_pointer = omit_frame_pointer, .pic = true, }), .linkage = .static, diff --git a/src/build/glibc_stub.zig b/src/build/glibc_stub.zig index e1d389fc58..ab190fc61f 100644 --- a/src/build/glibc_stub.zig +++ b/src/build/glibc_stub.zig @@ -39,6 +39,13 @@ pub fn generateComprehensiveStub( "munmap", "mremap", "msync", + // Used by Zig's CAllocator + "malloc", + "calloc", + "realloc", + "free", + "posix_memalign", + "malloc_usable_size", // File I/O "close", "read", diff --git a/src/build/modules.zig b/src/build/modules.zig index bf251cc7f7..f6ecdabe31 100644 --- a/src/build/modules.zig +++ b/src/build/modules.zig @@ -303,19 +303,19 @@ pub const ModuleType = enum { pub fn getDependencies(self: ModuleType) []const ModuleType { return switch (self) { .build_options => &.{}, - .builtins => &.{}, + .builtins => &.{.tracy}, .fs => &.{}, - .tracy => &.{ .build_options, .builtins }, + .tracy => &.{.build_options}, .collections => &.{}, .base => &.{ .collections, .builtins }, .roc_src => &.{}, - .types => &.{ .base, .collections }, + .types => &.{ .tracy, .base, .collections }, .reporting => &.{ .collections, .base }, .parse => &.{ .tracy, .collections, .base, .reporting }, .can => &.{ .tracy, .builtins, .collections, .types, .base, .parse, .reporting }, .check => &.{ .tracy, .builtins, .collections, .base, .parse, .types, .can, .reporting }, - .layout => &.{ .collections, .base, .types, .builtins, .can }, - .eval => &.{ .collections, .base, .types, .builtins, .parse, .can, .check, .layout, .build_options, .reporting }, + .layout => &.{ .tracy, .collections, .base, .types, .builtins, .can }, + .eval => &.{ .tracy, .collections, .base, .types, .builtins, .parse, .can, .check, .layout, .build_options, .reporting }, .compile => &.{ .tracy, .build_options, .fs, .builtins, .collections, .base, .types, .parse, .can, .check, .reporting, .layout, .eval, .unbundle }, .ipc => &.{}, .repl => &.{ .base, .collections, .compile, .parse, .types, .can, .check, .builtins, .layout, .eval }, diff --git a/src/build/tracy.zig b/src/build/tracy.zig index be8483db1e..291a111bb7 100644 --- a/src/build/tracy.zig +++ b/src/build/tracy.zig @@ -133,6 +133,9 @@ pub fn TracyAllocator(comptime name: ?[:0]const u8) type { } fn allocFn(ptr: *anyopaque, len: usize, ptr_align: std.mem.Alignment, ret_addr: usize) ?[*]u8 { + const zone = traceNamed(@src(), "alloc"); + defer zone.end(); + const self: *Self = @ptrCast(@alignCast(ptr)); const result = self.parent_allocator.rawAlloc(len, ptr_align, ret_addr); if (result) |data| { @@ -150,6 +153,9 @@ pub fn TracyAllocator(comptime name: ?[:0]const u8) type { } fn resizeFn(ptr: *anyopaque, buf: []u8, buf_align: std.mem.Alignment, new_len: usize, ret_addr: usize) bool { + const zone = traceNamed(@src(), "resize"); + defer zone.end(); + const self: *Self = @ptrCast(@alignCast(ptr)); if (self.parent_allocator.rawResize(buf, buf_align, new_len, ret_addr)) { if (name) |n| { @@ -169,6 +175,9 @@ pub fn TracyAllocator(comptime name: ?[:0]const u8) type { } fn remapFn(ptr: *anyopaque, buf: []u8, buf_align: std.mem.Alignment, new_len: usize, ret_addr: usize) ?[*]u8 { + const zone = traceNamed(@src(), "remap"); + defer zone.end(); + const self: *Self = @ptrCast(@alignCast(ptr)); if (self.parent_allocator.rawRemap(buf, buf_align, new_len, ret_addr)) |remapped| { if (name) |n| { @@ -188,6 +197,9 @@ pub fn TracyAllocator(comptime name: ?[:0]const u8) type { } fn freeFn(ptr: *anyopaque, buf: []u8, buf_align: std.mem.Alignment, ret_addr: usize) void { + const zone = traceNamed(@src(), "free"); + defer zone.end(); + const self: *Self = @ptrCast(@alignCast(ptr)); self.parent_allocator.rawFree(buf, buf_align, ret_addr); // this condition is to handle free being called on an empty slice that was never even allocated @@ -270,7 +282,9 @@ inline fn frameMarkEnd(comptime name: [:0]const u8) void { extern fn ___tracy_emit_frame_mark_start(name: [*:0]const u8) void; extern fn ___tracy_emit_frame_mark_end(name: [*:0]const u8) void; -inline fn alloc(ptr: [*]u8, len: usize) void { +/// Records a memory allocation with Tracy's memory profiler. +/// Call this after allocating memory to track it in Tracy's memory view. +pub inline fn alloc(ptr: [*]u8, len: usize) void { if (!enable) return; if (enable_callstack) { @@ -290,7 +304,9 @@ inline fn allocNamed(ptr: [*]u8, len: usize, comptime name: [:0]const u8) void { } } -inline fn free(ptr: [*]u8) void { +/// Records a memory deallocation with Tracy's memory profiler. +/// Call this before freeing memory to track it in Tracy's memory view. +pub inline fn free(ptr: [*]u8) void { if (!enable) return; if (enable_callstack) { diff --git a/src/builtins/host_abi.zig b/src/builtins/host_abi.zig index 5cb6d580b9..6782fd0198 100644 --- a/src/builtins/host_abi.zig +++ b/src/builtins/host_abi.zig @@ -9,6 +9,8 @@ //! This design makes Roc's ABI very simple; the calling convention is just "Ops pointer, //! return pointer, args pointers". +const tracy = @import("tracy"); + /// todo: describe RocCall pub const RocCall = fn ( /// Function pointers that the Roc program uses, e.g. alloc, dealloc, etc. @@ -70,6 +72,9 @@ pub const RocOps = extern struct { /// Helper function to crash the Roc program, returns control to the host. pub fn crash(self: *RocOps, msg: []const u8) void { + const trace = tracy.trace(@src()); + defer trace.end(); + const roc_crashed_args = RocCrashed{ .utf8_bytes = @constCast(msg.ptr), .len = msg.len, @@ -79,6 +84,9 @@ pub const RocOps = extern struct { /// Helper function to send debug output to the host. pub fn dbg(self: *RocOps, msg: []const u8) void { + const trace = tracy.trace(@src()); + defer trace.end(); + const roc_dbg_args = RocDbg{ .utf8_bytes = @constCast(msg.ptr), .len = msg.len, @@ -87,16 +95,31 @@ pub const RocOps = extern struct { } pub fn alloc(self: *RocOps, alignment: usize, length: usize) *anyopaque { + const trace = tracy.trace(@src()); + defer trace.end(); + var roc_alloc_args = RocAlloc{ .alignment = alignment, .length = length, .answer = self.env, }; self.roc_alloc(&roc_alloc_args, self.env); + + if (tracy.enable_allocation) { + tracy.alloc(@ptrCast(roc_alloc_args.answer), length); + } + return roc_alloc_args.answer; } pub fn dealloc(self: *RocOps, ptr: *anyopaque, alignment: usize) void { + const trace = tracy.trace(@src()); + defer trace.end(); + + if (tracy.enable_allocation) { + tracy.free(@ptrCast(ptr)); + } + var roc_dealloc_args = RocDealloc{ .alignment = alignment, .ptr = ptr, diff --git a/src/canonicalize/ModuleEnv.zig b/src/canonicalize/ModuleEnv.zig index 49b01c8c8e..8d766ba005 100644 --- a/src/canonicalize/ModuleEnv.zig +++ b/src/canonicalize/ModuleEnv.zig @@ -1134,7 +1134,18 @@ pub fn diagnosticToReport(self: *Self, diagnostic: CIR.Diagnostic, allocator: st try report.document.addAnnotatedText(owned_feature, .emphasized); try report.document.addLineBreak(); try report.document.addLineBreak(); + const owned_filename = try report.addOwnedString(filename); + const region_info = self.calcRegionInfo(data.region); + try report.document.addSourceRegion( + region_info, + .error_highlight, + owned_filename, + self.getSourceAll(), + self.getLineStartsAll(), + ); + try report.document.addLineBreak(); try report.document.addReflowingText("This error doesn't have a proper diagnostic report yet. Let us know if you want to help improve Roc's error messages!"); + try report.document.addLineBreak(); break :blk report; }, .malformed_type_annotation => |data| blk: { diff --git a/src/cli/linker.zig b/src/cli/linker.zig index 21202737f2..ecdc4d17f1 100644 --- a/src/cli/linker.zig +++ b/src/cli/linker.zig @@ -4,6 +4,7 @@ const std = @import("std"); const builtin = @import("builtin"); +const build_options = @import("build_options"); const Allocator = std.mem.Allocator; const base = @import("base"); const Allocators = base.Allocators; @@ -164,6 +165,11 @@ fn buildLinkArgs(allocs: *Allocators, config: LinkConfig) LinkError!std.array_li // Link against system libraries on macOS try args.append("-lSystem"); + + // Link C++ standard library if Tracy is enabled + if (build_options.enable_tracy) { + try args.append("-lc++"); + } }, .linux => { // Add linker name for Linux @@ -218,6 +224,11 @@ fn buildLinkArgs(allocs: *Allocators, config: LinkConfig) LinkError!std.array_li // Otherwise, dynamic linker is set via extra_args from caller }, } + + // Link C++ standard library if Tracy is enabled + if (build_options.enable_tracy) { + try args.append("-lstdc++"); + } }, .windows => { // Add linker name for Windows COFF @@ -275,6 +286,11 @@ fn buildLinkArgs(allocs: *Allocators, config: LinkConfig) LinkError!std.array_li // Suppress warnings using Windows style try args.append("/ignore:4217"); // Ignore locally defined symbol imported warnings try args.append("/ignore:4049"); // Ignore locally defined symbol imported warnings + + // Link C++ standard library if Tracy is enabled + if (build_options.enable_tracy) { + try args.append("/defaultlib:msvcprt"); + } }, .freestanding => { // WebAssembly linker (wasm-ld) for freestanding wasm32 target diff --git a/src/eval/interpreter.zig b/src/eval/interpreter.zig index 6ed5cc1424..00defd8e25 100644 --- a/src/eval/interpreter.zig +++ b/src/eval/interpreter.zig @@ -3,6 +3,7 @@ const std = @import("std"); const builtin = @import("builtin"); const build_options = @import("build_options"); +const tracy = @import("tracy"); /// Stack size for the interpreter. WASM targets use a smaller stack to avoid /// memory pressure from repeated allocations that can't be efficiently coalesced. @@ -664,6 +665,9 @@ pub const Interpreter = struct { /// Evaluates a Roc expression and returns the result. pub fn eval(self: *Interpreter, expr_idx: can.CIR.Expr.Idx, roc_ops: *RocOps) Error!StackValue { + const trace = tracy.trace(@src()); + defer trace.end(); + // Clear flex_type_context at the start of each top-level evaluation. // This prevents stale type mappings from previous evaluations from // interfering with polymorphic function instantiation. @@ -692,6 +696,9 @@ pub const Interpreter = struct { roc_ops: *RocOps, arg_ptr: ?*anyopaque, ) Error!void { + const trace = tracy.trace(@src()); + defer trace.end(); + if (arg_ptr) |args_ptr| { const func_val = try self.eval(expr_idx, roc_ops); defer func_val.decref(&self.runtime_layout_store, roc_ops); @@ -1005,6 +1012,9 @@ pub const Interpreter = struct { roc_ops: *RocOps, work_stack: *WorkStack, ) !SortWithResult { + const trace = tracy.trace(@src()); + defer trace.end(); + var saved_rigid_subst = saved_rigid_subst_in; std.debug.assert(list_arg.layout.tag == .list or list_arg.layout.tag == .list_of_zst); @@ -1152,6 +1162,9 @@ pub const Interpreter = struct { roc_ops: *RocOps, return_rt_var: types.Var, ) !StackValue { + const trace = tracy.trace(@src()); + defer trace.end(); + // Validate index is within bounds if (hosted_fn_index >= roc_ops.hosted_fns.count) { self.triggerCrash("Hosted function index out of bounds", false, roc_ops); @@ -1216,6 +1229,9 @@ pub const Interpreter = struct { /// Version of callLowLevelBuiltin that also accepts a target type for operations like num_from_numeral pub fn callLowLevelBuiltinWithTargetType(self: *Interpreter, op: can.CIR.Expr.LowLevel, args: []StackValue, roc_ops: *RocOps, return_rt_var: ?types.Var, target_type_var: ?types.Var) !StackValue { + const trace = tracy.trace(@src()); + defer trace.end(); + // For num_from_numeral, we need to pass the target type through a different mechanism // since the standard handler extracts it from the return type which has a generic parameter. // Store the target type temporarily so the handler can use it. @@ -1226,6 +1242,9 @@ pub const Interpreter = struct { } pub fn callLowLevelBuiltin(self: *Interpreter, op: can.CIR.Expr.LowLevel, args: []StackValue, roc_ops: *RocOps, return_rt_var: ?types.Var) !StackValue { + const trace = tracy.trace(@src()); + defer trace.end(); + switch (op) { .str_is_empty => { // Str.is_empty : Str -> Bool @@ -6325,6 +6344,9 @@ pub const Interpreter = struct { _: types.Var, // rhs_var unused roc_ops: *RocOps, ) StructuralEqError!bool { + const trace = tracy.trace(@src()); + defer trace.end(); + // Handle scalar comparisons (numbers, strings) directly. if (lhs.layout.tag == .scalar and rhs.layout.tag == .scalar) { const lhs_scalar = lhs.layout.data.scalar; @@ -7485,6 +7507,8 @@ pub const Interpreter = struct { out_binds: *std.array_list.AlignedManaged(Binding, null), expr_idx: ?can.CIR.Expr.Idx, ) !bool { + const trace = tracy.trace(@src()); + defer trace.end(); const pat = self.env.store.getPattern(pattern_idx); switch (pat) { .assign => |_| { @@ -7971,6 +7995,9 @@ pub const Interpreter = struct { roc_ops: *RocOps, receiver_rt_var: ?types.Var, ) Error!StackValue { + const trace = tracy.trace(@src()); + defer trace.end(); + // Check method resolution cache first const cache_key = MethodResolutionKey{ .origin_module = origin_module, @@ -8393,6 +8420,9 @@ pub const Interpreter = struct { /// Get the layout for a runtime type var using the O(1) biased slot array. pub fn getRuntimeLayout(self: *Interpreter, type_var: types.Var) !layout.Layout { + const trace = tracy.trace(@src()); + defer trace.end(); + var resolved = self.runtime_types.resolveVar(type_var); // Apply rigid variable substitution if this is a rigid variable @@ -8800,6 +8830,9 @@ pub const Interpreter = struct { /// Handles most structural types: tag unions, tuples, records, functions, and nominal types. /// Uses caching to handle recursive types and avoid duplicate work. pub fn translateTypeVar(self: *Interpreter, module: *can.ModuleEnv, compile_var: types.Var) Error!types.Var { + const trace = tracy.trace(@src()); + defer trace.end(); + const resolved = module.types.resolveVar(compile_var); const key = ModuleVarKey{ .module = module, .var_ = resolved.var_ }; @@ -8850,6 +8883,9 @@ pub const Interpreter = struct { .structure => |flat| { switch (flat) { .tag_union => |tu| { + const tu_trace = tracy.traceNamed(@src(), "translateTypeVar.tag_union"); + defer tu_trace.end(); + var rt_tag_args = try std.ArrayList(types.Var).initCapacity(self.allocator, 8); defer rt_tag_args.deinit(self.allocator); @@ -8905,6 +8941,9 @@ pub const Interpreter = struct { break :blk try self.runtime_types.freshFromContent(.{ .structure = .empty_tag_union }); }, .tuple => |t| { + const tup_trace = tracy.traceNamed(@src(), "translateTypeVar.tuple"); + defer tup_trace.end(); + const ct_elems = module.types.sliceVars(t.elems); var buf = try self.allocator.alloc(types.Var, ct_elems.len); defer self.allocator.free(buf); @@ -8915,6 +8954,9 @@ pub const Interpreter = struct { break :blk try self.runtime_types.freshFromContent(.{ .structure = .{ .tuple = .{ .elems = range } } }); }, .record => |rec| { + const rec_trace = tracy.traceNamed(@src(), "translateTypeVar.record"); + defer rec_trace.end(); + var acc = try FieldAccumulator.init(self.allocator); defer acc.deinit(); var visited = std.AutoHashMap(types.Var, void).init(self.allocator); @@ -8947,6 +8989,9 @@ pub const Interpreter = struct { break :blk try self.runtime_types.freshFromContent(.{ .structure = .{ .record = .{ .fields = rt_fields, .ext = rt_ext } } }); }, .record_unbound => |fields_range| { + const rub_trace = tracy.traceNamed(@src(), "translateTypeVar.record_unbound"); + defer rub_trace.end(); + // record_unbound has no extension - it's a complete set of fields const ct_fields = module.types.getRecordFieldsSlice(fields_range); var runtime_fields = try self.allocator.alloc(types.RecordField, ct_fields.len); @@ -8970,6 +9015,9 @@ pub const Interpreter = struct { break :blk try self.runtime_types.freshFromContent(.{ .structure = .empty_record }); }, .fn_pure => |f| { + const fnp_trace = tracy.traceNamed(@src(), "translateTypeVar.fn_pure"); + defer fnp_trace.end(); + const ct_args = module.types.sliceVars(f.args); var buf = try self.allocator.alloc(types.Var, ct_args.len); defer self.allocator.free(buf); @@ -8981,6 +9029,9 @@ pub const Interpreter = struct { break :blk try self.runtime_types.register(.{ .content = content, .rank = types.Rank.top_level, .mark = types.Mark.none }); }, .fn_effectful => |f| { + const fne_trace = tracy.traceNamed(@src(), "translateTypeVar.fn_effectful"); + defer fne_trace.end(); + const ct_args = module.types.sliceVars(f.args); var buf = try self.allocator.alloc(types.Var, ct_args.len); defer self.allocator.free(buf); @@ -8992,6 +9043,9 @@ pub const Interpreter = struct { break :blk try self.runtime_types.register(.{ .content = content, .rank = types.Rank.top_level, .mark = types.Mark.none }); }, .fn_unbound => |f| { + const fnu_trace = tracy.traceNamed(@src(), "translateTypeVar.fn_unbound"); + defer fnu_trace.end(); + const ct_args = module.types.sliceVars(f.args); var buf = try self.allocator.alloc(types.Var, ct_args.len); defer self.allocator.free(buf); @@ -9003,6 +9057,9 @@ pub const Interpreter = struct { break :blk try self.runtime_types.register(.{ .content = content, .rank = types.Rank.top_level, .mark = types.Mark.none }); }, .nominal_type => |nom| { + const nom_trace = tracy.traceNamed(@src(), "translateTypeVar.nominal_type"); + defer nom_trace.end(); + const ct_backing = module.types.getNominalBackingVar(nom); const ct_args = module.types.sliceNominalArgs(nom); @@ -9266,6 +9323,9 @@ pub const Interpreter = struct { /// Uses the standard Instantiator, filtering its output to only rigid->flex mappings /// (the Instantiator maps all types, but layout computation only needs rigids). fn instantiateType(self: *Interpreter, type_var: types.Var, subst_map: *std.AutoHashMap(types.Var, types.Var)) Error!types.Var { + const trace = tracy.trace(@src()); + defer trace.end(); + self.instantiate_scratch.clearRetainingCapacity(); // IMPORTANT: Use runtime_layout_store.env's ident store, NOT self.env. @@ -9303,6 +9363,9 @@ pub const Interpreter = struct { module: *can.ModuleEnv, tag_union: types.TagUnion, ) std.mem.Allocator.Error!std.ArrayList(types.Tag) { + const gt_trace = tracy.traceNamed(@src(), "gatherTags"); + defer gt_trace.end(); + var scratch_tags = try std.ArrayList(types.Tag).initCapacity(ctx.allocator, 8); const tag_slice = module.types.getTagsSlice(tag_union.tags); @@ -9438,6 +9501,9 @@ pub const Interpreter = struct { /// Prepare a call using a known runtime function type var. /// Builds and inserts a cache entry on miss using the function's declared return var. pub fn prepareCallWithFuncVar(self: *Interpreter, module_id: u32, func_id: u32, func_type_var: types.Var, args: []const types.Var) !PolyEntry { + const trace = tracy.trace(@src()); + defer trace.end(); + if (self.polyLookup(module_id, func_id, args)) |found| return found; const func_resolved = self.runtime_types.resolveVar(func_type_var); @@ -10228,6 +10294,9 @@ pub const Interpreter = struct { roc_ops: *RocOps, expected_rt_var: ?types.Var, ) Error!StackValue { + const trace = tracy.trace(@src()); + defer trace.end(); + var work_stack = try WorkStack.init(self.allocator); defer work_stack.deinit(); @@ -10467,6 +10536,9 @@ pub const Interpreter = struct { expected_rt_var: ?types.Var, roc_ops: *RocOps, ) Error!void { + const trace = tracy.trace(@src()); + defer trace.end(); + const expr = self.env.store.getExpr(expr_idx); switch (expr) { @@ -10980,6 +11052,8 @@ pub const Interpreter = struct { // Conditionals .e_if => |if_expr| { + const sched_trace = tracy.traceNamed(@src(), "sched.if"); + defer sched_trace.end(); const branches = self.env.store.sliceIfBranches(if_expr.branches); if (branches.len > 0) { // Get first branch @@ -11008,6 +11082,8 @@ pub const Interpreter = struct { // Blocks .e_block => |blk| { + const sched_trace = tracy.traceNamed(@src(), "sched.block"); + defer sched_trace.end(); const stmts = self.env.store.sliceStatements(blk.stmts); const bindings_start = self.bindings.items.len; @@ -11043,6 +11119,8 @@ pub const Interpreter = struct { // Tuples .e_tuple => |tup| { + const sched_trace = tracy.traceNamed(@src(), "sched.tuple"); + defer sched_trace.end(); const elems = self.env.store.sliceExpr(tup.elems); if (elems.len == 0) { // Empty tuple - create immediately @@ -11068,6 +11146,8 @@ pub const Interpreter = struct { // Lists .e_list => |list_expr| { + const sched_trace = tracy.traceNamed(@src(), "sched.list"); + defer sched_trace.end(); const elems = self.env.store.sliceExpr(list_expr.elems); // Get list type variable @@ -11108,6 +11188,8 @@ pub const Interpreter = struct { // Records .e_record => |rec| { + const sched_trace = tracy.traceNamed(@src(), "sched.record"); + defer sched_trace.end(); const ct_var = can.ModuleEnv.varFrom(expr_idx); const rt_var = try self.translateTypeVar(self.env, ct_var); const fields = self.env.store.sliceRecordFields(rec.fields); @@ -11286,6 +11368,8 @@ pub const Interpreter = struct { }, .e_return => |ret| { + const sched_trace = tracy.traceNamed(@src(), "sched.return"); + defer sched_trace.end(); // Schedule the early return continuation after evaluating the inner expression const inner_ct_var = can.ModuleEnv.varFrom(ret.expr); const inner_rt_var = try self.translateTypeVar(self.env, inner_ct_var); @@ -11299,6 +11383,8 @@ pub const Interpreter = struct { // Tag unions with payloads .e_tag => |tag| { + const sched_trace = tracy.traceNamed(@src(), "sched.tag"); + defer sched_trace.end(); // Determine runtime type and tag index. // Use expected_rt_var if it's resolved to something concrete (structure or alias). // If expected_rt_var is flex (unresolved), fall back to ct_var translation. @@ -11444,6 +11530,8 @@ pub const Interpreter = struct { // Pattern matching .e_match => |m| { + const sched_trace = tracy.traceNamed(@src(), "sched.match"); + defer sched_trace.end(); // Get type info for scrutinee and result const scrutinee_ct_var = can.ModuleEnv.varFrom(m.cond); const scrutinee_rt_var = try self.translateTypeVar(self.env, scrutinee_ct_var); @@ -11496,6 +11584,8 @@ pub const Interpreter = struct { }, .e_for => |for_expr| { + const sched_trace = tracy.traceNamed(@src(), "sched.for"); + defer sched_trace.end(); // For expression: first evaluate the list, then set up iteration const expr_ct_var = can.ModuleEnv.varFrom(for_expr.expr); const expr_rt_var = try self.translateTypeVar(self.env, expr_ct_var); @@ -11533,12 +11623,17 @@ pub const Interpreter = struct { // Function calls .e_call => |call| { + const sched_trace = tracy.traceNamed(@src(), "sched.call"); + defer sched_trace.end(); const func_idx = call.func; const arg_indices = self.env.store.sliceExpr(call.args); // Check if the function is an anno-only lookup that will crash const func_expr_check = self.env.store.getExpr(func_idx); if (func_expr_check == .e_lookup_local) { + const anno_trace = tracy.traceNamed(@src(), "sched.call.anno_check"); + defer anno_trace.end(); + const lookup = func_expr_check.e_lookup_local; const all_defs = self.env.store.sliceDefs(self.env.all_defs); for (all_defs) |def_idx| { @@ -11609,6 +11704,8 @@ pub const Interpreter = struct { var saved_rigid_subst: ?std.AutoHashMap(types.Var, types.Var) = null; if (should_instantiate) { + const clone_trace = tracy.traceNamed(@src(), "sched.call.rigid_clone"); + defer clone_trace.end(); saved_rigid_subst = try self.rigid_subst.clone(); } errdefer { @@ -11624,6 +11721,9 @@ pub const Interpreter = struct { // If we instantiated, update rigid_subst and empty_scope (will be restored in cleanup) if (should_instantiate) { + const setup_trace = tracy.traceNamed(@src(), "sched.call.instantiate_setup"); + defer setup_trace.end(); + // Ensure we have at least one scope level if (self.empty_scope.scopes.items.len == 0) { try self.empty_scope.scopes.append(types.VarMap.init(self.allocator)); @@ -11831,6 +11931,9 @@ pub const Interpreter = struct { expected_rt_var: ?types.Var, num_lit: @TypeOf(@as(can.CIR.Expr, undefined).e_num), ) Error!StackValue { + const trace = tracy.trace(@src()); + defer trace.end(); + // Get the layout type variable - use expected_rt_var if provided for layout determination const layout_rt_var = expected_rt_var orelse blk: { const ct_var = can.ModuleEnv.varFrom(expr_idx); @@ -12179,6 +12282,9 @@ pub const Interpreter = struct { zero: @TypeOf(@as(can.CIR.Expr, undefined).e_zero_argument_tag), roc_ops: *RocOps, ) Error!StackValue { + const trace = tracy.trace(@src()); + defer trace.end(); + var rt_var = expected_rt_var orelse blk: { const ct_var = can.ModuleEnv.varFrom(expr_idx); break :blk try self.translateTypeVar(self.env, ct_var); @@ -12388,6 +12494,9 @@ pub const Interpreter = struct { lam: @TypeOf(@as(can.CIR.Expr, undefined).e_lambda), roc_ops: *RocOps, ) Error!StackValue { + const trace = tracy.trace(@src()); + defer trace.end(); + // Build a closure value with empty captures using the runtime layout for the lambda's type const rt_var = if (expected_rt_var) |provided_var| provided_var @@ -12489,6 +12598,9 @@ pub const Interpreter = struct { cls: @TypeOf(@as(can.CIR.Expr, undefined).e_closure), roc_ops: *RocOps, ) Error!StackValue { + const trace = tracy.trace(@src()); + defer trace.end(); + const lam_expr = self.env.store.getExpr(cls.lambda_idx); if (lam_expr != .e_lambda) { self.triggerCrash("e_closure: lambda_idx does not point to e_lambda", false, roc_ops); @@ -12662,6 +12774,9 @@ pub const Interpreter = struct { expected_rt_var: ?types.Var, roc_ops: *RocOps, ) Error!StackValue { + const trace = tracy.trace(@src()); + defer trace.end(); + // Search bindings in reverse var i: usize = self.bindings.items.len; while (i > 0) { @@ -12786,6 +12901,9 @@ pub const Interpreter = struct { expected_rt_var: ?types.Var, roc_ops: *RocOps, ) Error!StackValue { + const trace = tracy.trace(@src()); + defer trace.end(); + const other_env = self.import_envs.get(lookup.module_idx) orelse { self.triggerCrash("e_lookup_external: import_envs missing entry for module", false, roc_ops); return error.Crash; @@ -13144,22 +13262,35 @@ pub const Interpreter = struct { cont: Continuation, roc_ops: *RocOps, ) Error!bool { + // Increased quota needed: 40+ tracy.traceNamed() calls generate comptime structs + @setEvalBranchQuota(5000); + const trace = tracy.trace(@src()); + defer trace.end(); + switch (cont) { .return_result => { + const cont_trace = tracy.traceNamed(@src(), "cont.return_result"); + defer cont_trace.end(); // Signal to exit the main loop - the result is on the value stack return false; }, .decref_value => |dv| { + const cont_trace = tracy.traceNamed(@src(), "cont.decref_value"); + defer cont_trace.end(); // Decrement reference count of the value dv.value.decref(&self.runtime_layout_store, roc_ops); return true; }, .trim_bindings => |tb| { + const cont_trace = tracy.traceNamed(@src(), "cont.trim_bindings"); + defer cont_trace.end(); // Restore bindings to a previous length self.trimBindingList(&self.bindings, tb.target_len, roc_ops); return true; }, .and_short_circuit => |sc| { + const cont_trace = tracy.traceNamed(@src(), "cont.and_short_circuit"); + defer cont_trace.end(); // Pop LHS value from stack const lhs = value_stack.pop() orelse return error.Crash; defer lhs.decref(&self.runtime_layout_store, roc_ops); @@ -13178,6 +13309,8 @@ pub const Interpreter = struct { return true; }, .or_short_circuit => |sc| { + const cont_trace = tracy.traceNamed(@src(), "cont.or_short_circuit"); + defer cont_trace.end(); // Pop LHS value from stack const lhs = value_stack.pop() orelse return error.Crash; defer lhs.decref(&self.runtime_layout_store, roc_ops); @@ -13196,6 +13329,8 @@ pub const Interpreter = struct { return true; }, .if_branch => |ib| { + const cont_trace = tracy.traceNamed(@src(), "cont.if_branch"); + defer cont_trace.end(); // Pop condition value from stack const cond = value_stack.pop() orelse return error.Crash; defer cond.decref(&self.runtime_layout_store, roc_ops); @@ -13233,6 +13368,8 @@ pub const Interpreter = struct { return true; }, .block_continue => |bc| { + const cont_trace = tracy.traceNamed(@src(), "cont.block_continue"); + defer cont_trace.end(); // For s_expr statements, we need to pop and discard the value // Only pop if should_discard_value is set (meaning this was scheduled after an s_expr) if (bc.should_discard_value) { @@ -13254,6 +13391,8 @@ pub const Interpreter = struct { return true; }, .bind_decl => |bd| { + const cont_trace = tracy.traceNamed(@src(), "cont.bind_decl"); + defer cont_trace.end(); // Pop evaluated value from stack const val = value_stack.pop() orelse return error.Crash; if (comptime trace_refcount and builtin.os.tag != .freestanding) { @@ -13322,6 +13461,8 @@ pub const Interpreter = struct { return true; }, .tuple_collect => |tc| { + const cont_trace = tracy.traceNamed(@src(), "cont.tuple_collect"); + defer cont_trace.end(); // Tuple collection works by evaluating elements one at a time // and tracking how many we've collected if (tc.remaining_elems.len > 0) { @@ -13351,6 +13492,7 @@ pub const Interpreter = struct { try value_stack.push(tuple_val); } else { // Gather layouts and values + const alloc_trace = tracy.traceNamed(@src(), "tuple_collect.alloc_temps"); var elem_layouts = try self.allocator.alloc(Layout, total_count); defer self.allocator.free(elem_layouts); @@ -13362,6 +13504,7 @@ pub const Interpreter = struct { // Collect element rt_vars for constructing tuple type var elem_rt_vars = try self.allocator.alloc(types.Var, total_count); defer self.allocator.free(elem_rt_vars); + alloc_trace.end(); // Pop values in reverse order (last evaluated is on top) var i: usize = total_count; @@ -13391,8 +13534,12 @@ pub const Interpreter = struct { } // Decref temporary values after they've been copied into the tuple - for (values) |val| { - val.decref(&self.runtime_layout_store, roc_ops); + { + const decref_trace = tracy.traceNamed(@src(), "tuple_collect.decref_elements"); + defer decref_trace.end(); + for (values) |val| { + val.decref(&self.runtime_layout_store, roc_ops); + } } try value_stack.push(dest); @@ -13401,6 +13548,8 @@ pub const Interpreter = struct { return true; }, .list_collect => |lc| { + const cont_trace = tracy.traceNamed(@src(), "cont.list_collect"); + defer cont_trace.end(); // List collection works by evaluating elements one at a time // and tracking how many we've collected if (lc.remaining_elems.len > 0) { @@ -13441,8 +13590,10 @@ pub const Interpreter = struct { try value_stack.push(dest); } else { // Pop all collected values from the value stack + const alloc_trace = tracy.traceNamed(@src(), "list_collect.alloc_temps"); var values = try self.allocator.alloc(StackValue, total_count); defer self.allocator.free(values); + alloc_trace.end(); // Pop values in reverse order (last evaluated is on top) var i: usize = total_count; @@ -13496,8 +13647,12 @@ pub const Interpreter = struct { header.* = runtime_list; // Decref temporary values after they've been copied into the list - for (values) |val| { - val.decref(&self.runtime_layout_store, roc_ops); + { + const decref_trace = tracy.traceNamed(@src(), "list_collect.decref_elements"); + defer decref_trace.end(); + for (values) |val| { + val.decref(&self.runtime_layout_store, roc_ops); + } } // Set the runtime type variable so method dispatch works correctly. @@ -13521,6 +13676,8 @@ pub const Interpreter = struct { return true; }, .record_collect => |rc| { + const cont_trace = tracy.traceNamed(@src(), "cont.record_collect"); + defer cont_trace.end(); // Record collection: evaluate extension (if any), then fields in order if (rc.remaining_fields.len > 0) { // More fields to evaluate - schedule next one @@ -13546,6 +13703,7 @@ pub const Interpreter = struct { const total_field_values = rc.collected_count; // Build layout info from collected values + const alloc_trace = tracy.traceNamed(@src(), "record_collect.alloc_temps"); var union_names = std.array_list.AlignedManaged(base_pkg.Ident.Idx, null).init(self.allocator); defer union_names.deinit(); var union_layouts = std.array_list.AlignedManaged(layout.Layout, null).init(self.allocator); @@ -13556,6 +13714,7 @@ pub const Interpreter = struct { // Pop field values from stack (in reverse order since last evaluated is on top) var field_values = try self.allocator.alloc(StackValue, total_field_values); defer self.allocator.free(field_values); + alloc_trace.end(); var i: usize = total_field_values; while (i > 0) { @@ -13664,11 +13823,15 @@ pub const Interpreter = struct { } // Decref base value and field values after they've been copied - if (base_value_opt) |base_value| { - base_value.decref(&self.runtime_layout_store, roc_ops); - } - for (field_values) |val| { - val.decref(&self.runtime_layout_store, roc_ops); + { + const decref_trace = tracy.traceNamed(@src(), "record_collect.decref_fields"); + defer decref_trace.end(); + if (base_value_opt) |base_value| { + base_value.decref(&self.runtime_layout_store, roc_ops); + } + for (field_values) |val| { + val.decref(&self.runtime_layout_store, roc_ops); + } } try value_stack.push(dest); @@ -13676,6 +13839,8 @@ pub const Interpreter = struct { return true; }, .early_return => { + const cont_trace = tracy.traceNamed(@src(), "cont.early_return"); + defer cont_trace.end(); // Pop the evaluated value and signal early return const return_value = value_stack.pop() orelse return error.Crash; self.early_return_value = return_value; @@ -13789,6 +13954,8 @@ pub const Interpreter = struct { return true; }, .tag_collect => |tc| { + const cont_trace = tracy.traceNamed(@src(), "cont.tag_collect"); + defer cont_trace.end(); // Tag payload collection: evaluate each argument, then finalize tag if (tc.remaining_args.len > 0) { // More arguments to evaluate @@ -14080,6 +14247,8 @@ pub const Interpreter = struct { return true; }, .match_branches => |mb| { + const cont_trace = tracy.traceNamed(@src(), "cont.match_branches"); + defer cont_trace.end(); // Scrutinee is on value stack - get it but keep it there for potential later use const scrutinee_temp = value_stack.pop() orelse return error.Crash; // Make a copy to protect from corruption @@ -14162,6 +14331,8 @@ pub const Interpreter = struct { return error.Crash; }, .match_guard => |mg| { + const cont_trace = tracy.traceNamed(@src(), "cont.match_guard"); + defer cont_trace.end(); // Guard result is on value stack const guard_val = value_stack.pop() orelse return error.Crash; defer guard_val.decref(&self.runtime_layout_store, roc_ops); @@ -14205,11 +14376,15 @@ pub const Interpreter = struct { return true; }, .match_cleanup => |mc| { + const cont_trace = tracy.traceNamed(@src(), "cont.match_cleanup"); + defer cont_trace.end(); // Result is on value stack - leave it there, just trim bindings self.trimBindingList(&self.bindings, mc.bindings_start, roc_ops); return true; }, .expect_check => |ec| { + const cont_trace = tracy.traceNamed(@src(), "cont.expect_check"); + defer cont_trace.end(); // Pop condition value from stack const cond_val = value_stack.pop() orelse return error.Crash; const succeeded = self.boolValueEquals(true, cond_val); @@ -14227,6 +14402,8 @@ pub const Interpreter = struct { return error.Crash; }, .dbg_print => |dp| { + const cont_trace = tracy.traceNamed(@src(), "cont.dbg_print"); + defer cont_trace.end(); // Pop evaluated value from stack const value = value_stack.pop() orelse return error.Crash; defer value.decref(&self.runtime_layout_store, roc_ops); @@ -14242,6 +14419,8 @@ pub const Interpreter = struct { return true; }, .str_collect => |sc| { + const cont_trace = tracy.traceNamed(@src(), "cont.str_collect"); + defer cont_trace.end(); // State machine for string interpolation: // 1. If needs_conversion, convert top of value stack to string // 2. If remaining segments, process next one @@ -14378,6 +14557,8 @@ pub const Interpreter = struct { return true; }, .call_collect_args => |cc| { + const cont_trace = tracy.traceNamed(@src(), "cont.call_collect_args"); + defer cont_trace.end(); // Function call: collect arguments one by one if (cc.remaining_args.len > 0) { // More arguments to evaluate @@ -14400,6 +14581,8 @@ pub const Interpreter = struct { return true; }, .call_invoke_closure => |ci| { + const cont_trace = tracy.traceNamed(@src(), "cont.call_invoke_closure"); + defer cont_trace.end(); // All arguments collected - pop them and the function, then invoke // Stack state: [func_val, arg0, arg1, ...] (func at bottom, args on top) var saved_rigid_subst = ci.saved_rigid_subst; @@ -14677,6 +14860,8 @@ pub const Interpreter = struct { return error.Crash; }, .call_cleanup => |cleanup| { + const cont_trace = tracy.traceNamed(@src(), "cont.call_cleanup"); + defer cont_trace.end(); // Function body evaluated - cleanup and return result // Check for early return if (self.early_return_value) |return_val_in| { @@ -14761,6 +14946,8 @@ pub const Interpreter = struct { return true; }, .unary_op_apply => |ua| { + const cont_trace = tracy.traceNamed(@src(), "cont.unary_op_apply"); + defer cont_trace.end(); // Unary operation: operand is on stack, apply method const operand = value_stack.pop() orelse return error.Crash; defer operand.decref(&self.runtime_layout_store, roc_ops); @@ -14873,6 +15060,8 @@ pub const Interpreter = struct { return true; }, .binop_eval_rhs => |be| { + const cont_trace = tracy.traceNamed(@src(), "cont.binop_eval_rhs"); + defer cont_trace.end(); // Binary operation: LHS is on stack, now evaluate RHS // We keep LHS on stack, push continuation to apply method after RHS is evaluated try work_stack.push(.{ .apply_continuation = .{ .binop_apply = .{ @@ -14888,6 +15077,8 @@ pub const Interpreter = struct { return true; }, .binop_apply => |ba| { + const cont_trace = tracy.traceNamed(@src(), "cont.binop_apply"); + defer cont_trace.end(); // Binary operation: both operands on stack, apply method // Stack: [lhs, rhs] - RHS on top const rhs = value_stack.pop() orelse return error.Crash; @@ -15316,6 +15507,8 @@ pub const Interpreter = struct { return true; }, .dot_access_await_receiver => |da| { + const cont_trace = tracy.traceNamed(@src(), "cont.dot_access_await_receiver"); + defer cont_trace.end(); // Pop the receiver from value stack (pushed by eval_expr for the receiver) const receiver_value = value_stack.pop() orelse return error.Crash; @@ -15350,6 +15543,8 @@ pub const Interpreter = struct { return true; }, .dot_access_resolve => |da| { + const cont_trace = tracy.traceNamed(@src(), "cont.dot_access_resolve"); + defer cont_trace.end(); // Dot access: receiver is carried in continuation to avoid value stack interleaving const receiver_value = da.receiver_value; @@ -15761,6 +15956,8 @@ pub const Interpreter = struct { return true; }, .dot_access_collect_args => |dac| { + const cont_trace = tracy.traceNamed(@src(), "cont.dot_access_collect_args"); + defer cont_trace.end(); // Dot access method call: collecting arguments // Stack: [receiver, method_func, arg0, arg1, ...] if (dac.remaining_args.len > 1) { @@ -16079,6 +16276,8 @@ pub const Interpreter = struct { return true; }, .type_var_dispatch_collect_args => |tvdc| { + const cont_trace = tracy.traceNamed(@src(), "cont.type_var_dispatch_collect_args"); + defer cont_trace.end(); // Type var dispatch: collecting arguments // Stack: [method_func, arg0, arg1, ...] if (tvdc.remaining_args.len > 0) { @@ -16102,6 +16301,8 @@ pub const Interpreter = struct { return true; }, .type_var_dispatch_invoke => |tvdi| { + const cont_trace = tracy.traceNamed(@src(), "cont.type_var_dispatch_invoke"); + defer cont_trace.end(); // Type var dispatch: all arguments collected, invoke the method // Stack: [method_func, arg0, arg1, ...] @@ -16253,6 +16454,8 @@ pub const Interpreter = struct { } }, .for_iterate => |fl_in| { + const cont_trace = tracy.traceNamed(@src(), "cont.for_iterate"); + defer cont_trace.end(); // For loop/expression iteration: list has been evaluated, start iterating const list_value = value_stack.pop() orelse { self.triggerCrash("for_iterate: value_stack empty", false, roc_ops); @@ -16357,6 +16560,8 @@ pub const Interpreter = struct { return true; }, .for_body_done => |fl| { + const cont_trace = tracy.traceNamed(@src(), "cont.for_body_done"); + defer cont_trace.end(); // For loop/expression body completed, clean up and continue to next iteration const body_result = value_stack.pop() orelse { self.triggerCrash("for_body_done: value_stack empty", false, roc_ops); @@ -16426,6 +16631,8 @@ pub const Interpreter = struct { return true; }, .while_loop_check => |wl| { + const cont_trace = tracy.traceNamed(@src(), "cont.while_loop_check"); + defer cont_trace.end(); // While loop: condition has been evaluated const cond_value = value_stack.pop() orelse return error.Crash; const cond_is_true = self.boolValueEquals(true, cond_value); @@ -16461,6 +16668,8 @@ pub const Interpreter = struct { return true; }, .while_loop_body_done => |wl| { + const cont_trace = tracy.traceNamed(@src(), "cont.while_loop_body_done"); + defer cont_trace.end(); // While loop body completed, check condition again const body_result = value_stack.pop() orelse return error.Crash; body_result.decref(&self.runtime_layout_store, roc_ops); @@ -16484,6 +16693,8 @@ pub const Interpreter = struct { return true; }, .expect_check_stmt => |ec| { + const cont_trace = tracy.traceNamed(@src(), "cont.expect_check_stmt"); + defer cont_trace.end(); // Expect statement: check condition result const cond_val = value_stack.pop() orelse return error.Crash; const is_true = self.boolValueEquals(true, cond_val); @@ -16504,6 +16715,8 @@ pub const Interpreter = struct { return true; }, .reassign_value => |rv| { + const cont_trace = tracy.traceNamed(@src(), "cont.reassign_value"); + defer cont_trace.end(); // Reassign statement: update binding const new_val = value_stack.pop() orelse { self.triggerCrash("reassign_value: value_stack empty", false, roc_ops); @@ -16532,6 +16745,8 @@ pub const Interpreter = struct { return true; }, .dbg_print_stmt => |dp| { + const cont_trace = tracy.traceNamed(@src(), "cont.dbg_print_stmt"); + defer cont_trace.end(); // Dbg statement: print value const value = value_stack.pop() orelse return error.Crash; defer value.decref(&self.runtime_layout_store, roc_ops); @@ -16551,6 +16766,8 @@ pub const Interpreter = struct { return true; }, .sort_compare_result => |sc_in| { + const cont_trace = tracy.traceNamed(@src(), "cont.sort_compare_result"); + defer cont_trace.end(); var sc = sc_in; var saved_rigid_subst = sc.saved_rigid_subst; defer { @@ -16789,6 +17006,8 @@ pub const Interpreter = struct { return true; }, .negate_bool => { + const cont_trace = tracy.traceNamed(@src(), "cont.negate_bool"); + defer cont_trace.end(); // Negate the boolean result on top of value stack (for != operator) var result = value_stack.pop() orelse { self.triggerCrash("negate_bool: expected value on stack", false, roc_ops); @@ -16801,6 +17020,8 @@ pub const Interpreter = struct { return true; }, .nominal_wrap => |nw| { + const cont_trace = tracy.traceNamed(@src(), "cont.nominal_wrap"); + defer cont_trace.end(); // Wrap the backing expression result with the nominal type's rt_var. // This ensures method dispatch can find methods defined on the nominal type. var result = value_stack.pop() orelse { diff --git a/src/interpreter_shim/main.zig b/src/interpreter_shim/main.zig index 1b97f6691c..7a5e530c3a 100644 --- a/src/interpreter_shim/main.zig +++ b/src/interpreter_shim/main.zig @@ -14,6 +14,7 @@ const types = @import("types"); const collections = @import("collections"); const import_mapping_mod = types.import_mapping; const eval = @import("eval"); +const tracy = @import("tracy"); // Platform detection const is_wasm32 = builtin.cpu.arch == .wasm32; @@ -33,8 +34,23 @@ const ipc = if (is_wasm32) struct { }; } else @import("ipc"); -// Allocator: wasm32 uses a simple arena, native uses page_allocator -const default_allocator = if (is_wasm32) wasm_allocator else std.heap.page_allocator; +// Debug allocator for native platforms (not wasm32) - provides leak detection in Debug/ReleaseSafe builds +var debug_allocator: if (is_wasm32) void else std.heap.DebugAllocator(.{}) = + if (is_wasm32) {} else .{ .backing_allocator = std.heap.c_allocator }; + +// Get the base allocator based on platform and build mode +fn getBaseAllocator() std.mem.Allocator { + if (is_wasm32) return wasm_allocator; + return switch (builtin.mode) { + .Debug, .ReleaseSafe => debug_allocator.allocator(), + .ReleaseFast, .ReleaseSmall => std.heap.c_allocator, + }; +} + +// TracyAllocator wrapping for allocation profiling +var tracy_allocator: tracy.TracyAllocator(null) = undefined; +var wrapped_allocator: std.mem.Allocator = undefined; +var allocator_initialized: bool = false; // Wasm32 allocator - uses roc_alloc from host const wasm_allocator = if (is_wasm32) std.mem.Allocator{ @@ -73,7 +89,15 @@ extern fn roc_realloc(ptr: *anyopaque, new_size: usize, old_size: usize, alignme extern fn roc_dealloc(ptr: *anyopaque, alignment: u32) callconv(.c) void; // Static empty import mapping for shim (no type name resolution needed) -var shim_import_mapping = import_mapping_mod.ImportMapping.init(default_allocator); +// Lazy-initialized to use the properly wrapped allocator +var shim_import_mapping: ?import_mapping_mod.ImportMapping = null; + +fn getShimImportMapping() *import_mapping_mod.ImportMapping { + if (shim_import_mapping == null) { + shim_import_mapping = import_mapping_mod.ImportMapping.init(wrapped_allocator); + } + return &shim_import_mapping.?; +} const SharedMemoryAllocator = if (is_wasm32) struct {} else ipc.SharedMemoryAllocator; @@ -195,6 +219,9 @@ const ShimError = error{ /// Returns a RocStr to the caller /// 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 { + const trace = tracy.trace(@src()); + defer trace.end(); + evaluateFromSharedMemory(entry_idx, ops, ret_ptr, arg_ptr) catch |err| { // Only show this generic error if we haven't already crashed with a more specific message // (errors like Crash and StackOverflow already triggered roc_crashed with details) @@ -208,6 +235,9 @@ export fn roc_entrypoint(entry_idx: u32, ops: *builtins.host_abi.RocOps, ret_ptr /// Initialize shared memory and ModuleEnv once per process fn initializeOnce(roc_ops: *RocOps) ShimError!void { + const trace = tracy.trace(@src()); + defer trace.end(); + // Fast path: if already initialized, return immediately if (shared_memory_initialized.isSet()) return; @@ -218,7 +248,19 @@ fn initializeOnce(roc_ops: *RocOps) ShimError!void { // Check again in case another thread initialized while we were waiting if (shared_memory_initialized.isSet()) return; - const allocator = default_allocator; + // Set up allocator with optional TracyAllocator wrapping before any allocations + if (!allocator_initialized) { + const base_allocator = getBaseAllocator(); + if (tracy.enable_allocation) { + tracy_allocator = tracy.tracyAllocator(base_allocator); + wrapped_allocator = tracy_allocator.allocator(); + } else { + wrapped_allocator = base_allocator; + } + allocator_initialized = true; + } + + const allocator = wrapped_allocator; var buf: [256]u8 = undefined; // IPC path only available on native platforms (not wasm32) @@ -276,6 +318,9 @@ fn initializeOnce(roc_ops: *RocOps) ShimError!void { /// Cross-platform evaluation (works for both IPC and embedded modes) fn evaluateFromSharedMemory(entry_idx: u32, roc_ops: *RocOps, ret_ptr: *anyopaque, arg_ptr: ?*anyopaque) ShimError!void { + const trace = tracy.trace(@src()); + defer trace.end(); + // Initialize shared memory once per process try initializeOnce(roc_ops); @@ -346,9 +391,12 @@ const SetupResult = struct { /// Works for both IPC mode (roc run) and embedded mode (roc build) /// Detects portable serialized format (cross-architecture) via magic number fn setupModuleEnv(roc_ops: *RocOps) ShimError!SetupResult { + const trace = tracy.trace(@src()); + defer trace.end(); + var buf: [256]u8 = undefined; const base_ptr = roc__serialized_base_ptr.?; - const allocator = default_allocator; + const allocator = wrapped_allocator; // Check for portable serialized format by looking at first 4 bytes // The magic number is at the very start of the buffer (no FIRST_ALLOC_OFFSET for portable format) @@ -504,6 +552,9 @@ fn setupModuleEnv(roc_ops: *RocOps) ShimError!SetupResult { /// Set up ModuleEnv from portable serialized format (cross-architecture builds) /// This format uses ModuleEnv.Serialized with fixed-size types fn setupModuleEnvFromSerialized(roc_ops: *RocOps, base_ptr: [*]align(1) u8, allocator: std.mem.Allocator) ShimError!SetupResult { + const trace = tracy.trace(@src()); + defer trace.end(); + var buf: [256]u8 = undefined; // Read the serialized header (use unaligned reads since embedded data may not be aligned) @@ -616,7 +667,10 @@ fn setupModuleEnvFromSerialized(roc_ops: *RocOps, base_ptr: [*]align(1) u8, allo /// Create and initialize interpreter with heap-allocated stable objects fn createInterpreter(env_ptr: *ModuleEnv, app_env: ?*ModuleEnv, builtin_modules: *const eval.BuiltinModules, roc_ops: *RocOps) ShimError!Interpreter { - const allocator = default_allocator; + const trace = tracy.trace(@src()); + defer trace.end(); + + const allocator = wrapped_allocator; // Use builtin types from the loaded builtin modules // This provides the actual definitions of plus, minus, times, etc. @@ -688,7 +742,7 @@ fn createInterpreter(env_ptr: *ModuleEnv, app_env: ?*ModuleEnv, builtin_modules: }; } - var interpreter = eval.Interpreter.init(allocator, env_ptr, builtin_types, builtin_module_env, imported_envs, &shim_import_mapping, app_env) catch { + var interpreter = eval.Interpreter.init(allocator, env_ptr, builtin_types, builtin_module_env, imported_envs, getShimImportMapping(), app_env) catch { roc_ops.crash("INTERPRETER SHIM: Interpreter initialization failed"); return error.InterpreterSetupFailed; }; diff --git a/src/layout/store.zig b/src/layout/store.zig index e4ee40b182..fe42a785ac 100644 --- a/src/layout/store.zig +++ b/src/layout/store.zig @@ -2,6 +2,7 @@ const std = @import("std"); const builtin = @import("builtin"); +const tracy = @import("tracy"); const base = @import("base"); const types = @import("types"); const collections = @import("collections"); @@ -260,6 +261,9 @@ pub const Store = struct { field_layouts: []const Layout, field_names: []const Ident.Idx, ) std.mem.Allocator.Error!Idx { + const trace = tracy.traceNamed(@src(), "layoutStore.putRecord"); + defer trace.end(); + var temp_fields = std.ArrayList(RecordField).empty; defer temp_fields.deinit(self.env.gpa); @@ -338,6 +342,9 @@ pub const Store = struct { /// Insert a tuple layout from concrete element layouts pub fn putTuple(self: *Self, element_layouts: []const Layout) std.mem.Allocator.Error!Idx { + const trace = tracy.traceNamed(@src(), "layoutStore.putTuple"); + defer trace.end(); + // Collect fields var temp_fields = std.ArrayList(TupleField).empty; defer temp_fields.deinit(self.env.gpa); @@ -1765,6 +1772,9 @@ pub const Store = struct { } pub fn insertLayout(self: *Self, layout: Layout) std.mem.Allocator.Error!Idx { + const trace = tracy.traceNamed(@src(), "layoutStore.insertLayout"); + defer trace.end(); + // For scalar types, return the appropriate sentinel value instead of inserting if (layout.tag == .scalar) { const result = idxFromScalar(layout.data.scalar); diff --git a/src/snapshot_tool/main.zig b/src/snapshot_tool/main.zig index 5a51ea1e18..8b389f3427 100644 --- a/src/snapshot_tool/main.zig +++ b/src/snapshot_tool/main.zig @@ -21,6 +21,7 @@ const repl = @import("repl"); const eval_mod = @import("eval"); const collections = @import("collections"); const compiled_builtins = @import("compiled_builtins"); +const tracy = @import("tracy"); const Repl = repl.Repl; const CrashContext = eval_mod.CrashContext; @@ -655,12 +656,19 @@ var debug_allocator: std.heap.DebugAllocator(.{}) = .{ /// cli entrypoint for snapshot tool pub fn main() !void { // Always use the debug allocator with the snapshot tool to help find allocation bugs. - const gpa = debug_allocator.allocator(); + var gpa_tracy: tracy.TracyAllocator(null) = undefined; + var gpa = debug_allocator.allocator(); defer { const mem_state = debug_allocator.deinit(); std.debug.assert(mem_state == .ok); } + // Wrap with Tracy for allocation profiling when enabled + if (tracy.enable_allocation) { + gpa_tracy = tracy.tracyAllocator(gpa); + gpa = gpa_tracy.allocator(); + } + const args = try std.process.argsAlloc(gpa); defer std.process.argsFree(gpa, args); diff --git a/src/types/store.zig b/src/types/store.zig index 2a8e6bc38a..f17e18808e 100644 --- a/src/types/store.zig +++ b/src/types/store.zig @@ -2,6 +2,7 @@ //! Contains both Slot & Descriptor stores const std = @import("std"); +const tracy = @import("tracy"); const base = @import("base"); const collections = @import("collections"); const serialization = @import("serialization"); @@ -143,6 +144,8 @@ pub const Store = struct { /// Create a new unbound, flexible type variable without a name /// Used in canonicalization when creating type slots pub fn fresh(self: *Self) std.mem.Allocator.Error!Var { + const trace = tracy.traceNamed(@src(), "typesStore.fresh"); + defer trace.end(); return try self.freshFromContent(Content{ .flex = Flex.init() }); } @@ -155,6 +158,8 @@ pub const Store = struct { /// Create a new variable with the provided desc /// Used in tests pub fn freshFromContent(self: *Self, content: Content) std.mem.Allocator.Error!Var { + const trace = tracy.traceNamed(@src(), "typesStore.freshFromContent"); + defer trace.end(); const desc_idx = try self.descs.insert(self.gpa, .{ .content = content, .rank = Rank.top_level, .mark = Mark.none }); const slot_idx = try self.slots.insert(self.gpa, .{ .root = desc_idx }); return Self.slotIdxToVar(slot_idx); @@ -468,6 +473,8 @@ pub const Store = struct { /// Append a var to the backing list, returning the idx pub fn appendVars(self: *Self, s: []const Var) std.mem.Allocator.Error!VarSafeList.Range { + const trace = tracy.traceNamed(@src(), "typesStore.appendVars"); + defer trace.end(); return try self.vars.appendSlice(self.gpa, s); } @@ -620,6 +627,8 @@ pub const Store = struct { /// Given a type var, follow all redirects until finding the root descriptor pub fn resolveVar(self: *const Self, initial_var: Var) ResolvedVarDesc { + const trace = tracy.traceNamed(@src(), "typesStore.resolveVar"); + defer trace.end(); var redirected_slot_idx = Self.varToSlotIdx(initial_var); var redirected_slot: Slot = self.slots.get(redirected_slot_idx); diff --git a/test/snapshots/binop_omnibus__single__no_spaces.md b/test/snapshots/binop_omnibus__single__no_spaces.md index 789c3b97a2..afc15fab9a 100644 --- a/test/snapshots/binop_omnibus__single__no_spaces.md +++ b/test/snapshots/binop_omnibus__single__no_spaces.md @@ -9,7 +9,7 @@ Err(foo)??12>5*5 or 13+2<5 and 10-1>=16 or 12<=3/5 ~~~ # EXPECTED UNDEFINED VARIABLE - binop_omnibus__single__no_spaces.md:1:5:1:8 -NOT IMPLEMENTED - :0:0:0:0 +NOT IMPLEMENTED - binop_omnibus__single__no_spaces.md:1:1:1:13 MISSING METHOD - binop_omnibus__single__no_spaces.md:1:32:1:34 # PROBLEMS **UNDEFINED VARIABLE** @@ -26,8 +26,15 @@ Err(foo)??12>5*5 or 13+2<5 and 10-1>=16 or 12<=3/5 **NOT IMPLEMENTED** This feature is not yet implemented: unsupported operator +**binop_omnibus__single__no_spaces.md:1:1:1:13:** +```roc +Err(foo)??12>5*5 or 13+2<5 and 10-1>=16 or 12<=3/5 +``` +^^^^^^^^^^^^ + This error doesn't have a proper diagnostic report yet. Let us know if you want to help improve Roc's error messages! + **MISSING METHOD** This **from_numeral** method is being called on a value whose type doesn't have that method: **binop_omnibus__single__no_spaces.md:1:32:1:34:** diff --git a/test/snapshots/binop_omnibus__singleline.md b/test/snapshots/binop_omnibus__singleline.md index 71e5f9cbca..2bd6be491f 100644 --- a/test/snapshots/binop_omnibus__singleline.md +++ b/test/snapshots/binop_omnibus__singleline.md @@ -9,7 +9,7 @@ Err(foo) ?? 12 > 5 * 5 or 13 + 2 < 5 and 10 - 1 >= 16 or 12 <= 3 / 5 ~~~ # EXPECTED UNDEFINED VARIABLE - binop_omnibus__singleline.md:1:5:1:8 -NOT IMPLEMENTED - :0:0:0:0 +NOT IMPLEMENTED - binop_omnibus__singleline.md:1:1:1:15 # PROBLEMS **UNDEFINED VARIABLE** Nothing is named `foo` in this scope. @@ -25,8 +25,15 @@ Err(foo) ?? 12 > 5 * 5 or 13 + 2 < 5 and 10 - 1 >= 16 or 12 <= 3 / 5 **NOT IMPLEMENTED** This feature is not yet implemented: unsupported operator +**binop_omnibus__singleline.md:1:1:1:15:** +```roc +Err(foo) ?? 12 > 5 * 5 or 13 + 2 < 5 and 10 - 1 >= 16 or 12 <= 3 / 5 +``` +^^^^^^^^^^^^^^ + This error doesn't have a proper diagnostic report yet. Let us know if you want to help improve Roc's error messages! + # TOKENS ~~~zig UpperIdent,NoSpaceOpenRound,LowerIdent,CloseRound,OpDoubleQuestion,Int,OpGreaterThan,Int,OpStar,Int,OpOr,Int,OpPlus,Int,OpLessThan,Int,OpAnd,Int,OpBinaryMinus,Int,OpGreaterThanOrEq,Int,OpOr,Int,OpLessThanOrEq,Int,OpSlash,Int, diff --git a/test/snapshots/binops.md b/test/snapshots/binops.md index feaae3429d..08c3181063 100644 --- a/test/snapshots/binops.md +++ b/test/snapshots/binops.md @@ -24,13 +24,20 @@ type=expr ) ~~~ # EXPECTED -NOT IMPLEMENTED - :0:0:0:0 +NOT IMPLEMENTED - binops.md:16:5:16:14 # PROBLEMS **NOT IMPLEMENTED** This feature is not yet implemented: unsupported operator +**binops.md:16:5:16:14:** +```roc + None ?? 0, +``` + ^^^^^^^^^ + This error doesn't have a proper diagnostic report yet. Let us know if you want to help improve Roc's error messages! + # TOKENS ~~~zig OpenRound, diff --git a/test/snapshots/expr/double_question_binop.md b/test/snapshots/expr/double_question_binop.md index ff43ba669e..bf39b0b36a 100644 --- a/test/snapshots/expr/double_question_binop.md +++ b/test/snapshots/expr/double_question_binop.md @@ -9,7 +9,7 @@ get_name!({}) ?? "Bob" ~~~ # EXPECTED UNDEFINED VARIABLE - double_question_binop.md:1:1:1:10 -NOT IMPLEMENTED - :0:0:0:0 +NOT IMPLEMENTED - double_question_binop.md:1:1:1:23 # PROBLEMS **UNDEFINED VARIABLE** Nothing is named `get_name!` in this scope. @@ -25,8 +25,15 @@ get_name!({}) ?? "Bob" **NOT IMPLEMENTED** This feature is not yet implemented: unsupported operator +**double_question_binop.md:1:1:1:23:** +```roc +get_name!({}) ?? "Bob" +``` +^^^^^^^^^^^^^^^^^^^^^^ + This error doesn't have a proper diagnostic report yet. Let us know if you want to help improve Roc's error messages! + # TOKENS ~~~zig LowerIdent,NoSpaceOpenRound,OpenCurly,CloseCurly,CloseRound,OpDoubleQuestion,StringStart,StringPart,StringEnd, diff --git a/test/snapshots/fuzz_crash/fuzz_crash_019.md b/test/snapshots/fuzz_crash/fuzz_crash_019.md index ee5acc9616..e2f0ddd706 100644 --- a/test/snapshots/fuzz_crash/fuzz_crash_019.md +++ b/test/snapshots/fuzz_crash/fuzz_crash_019.md @@ -174,7 +174,7 @@ UNDEFINED VARIABLE - fuzz_crash_019.md:100:11:100:14 UNDEFINED VARIABLE - fuzz_crash_019.md:102:4:102:6 UNDEFINED VARIABLE - fuzz_crash_019.md:102:8:102:13 UNDEFINED VARIABLE - fuzz_crash_019.md:105:2:105:3 -NOT IMPLEMENTED - :0:0:0:0 +NOT IMPLEMENTED - fuzz_crash_019.md:105:2:105:8 UNDEFINED VARIABLE - fuzz_crash_019.md:105:55:105:59 UNDEFINED VARIABLE - fuzz_crash_019.md:105:60:105:64 UNDEFINED VARIABLE - fuzz_crash_019.md:108:4:108:5 @@ -726,8 +726,15 @@ Is there an `import` or `exposing` missing up-top? **NOT IMPLEMENTED** This feature is not yet implemented: unsupported operator +**fuzz_crash_019.md:105:2:105:8:** +```roc + b?? 12 > 5 or 13 + 2 < 5 and 10 - 1 >= 16 or 12 <= 3 e_fn(arg1)?.od()?.ned()?.recd? +``` + ^^^^^^ + This error doesn't have a proper diagnostic report yet. Let us know if you want to help improve Roc's error messages! + **UNDEFINED VARIABLE** Nothing is named `e_fn` in this scope. Is there an `import` or `exposing` missing up-top? diff --git a/test/snapshots/fuzz_crash/fuzz_crash_020.md b/test/snapshots/fuzz_crash/fuzz_crash_020.md index 5cb92e7cc3..2e37b15dcb 100644 --- a/test/snapshots/fuzz_crash/fuzz_crash_020.md +++ b/test/snapshots/fuzz_crash/fuzz_crash_020.md @@ -175,7 +175,7 @@ UNDEFINED VARIABLE - fuzz_crash_020.md:100:11:100:14 UNDEFINED VARIABLE - fuzz_crash_020.md:102:4:102:6 UNDEFINED VARIABLE - fuzz_crash_020.md:102:8:102:13 UNDEFINED VARIABLE - fuzz_crash_020.md:105:2:105:3 -NOT IMPLEMENTED - :0:0:0:0 +NOT IMPLEMENTED - fuzz_crash_020.md:105:2:105:8 UNDEFINED VARIABLE - fuzz_crash_020.md:105:55:105:59 UNDEFINED VARIABLE - fuzz_crash_020.md:105:60:105:64 UNDEFINED VARIABLE - fuzz_crash_020.md:108:4:108:5 @@ -737,8 +737,15 @@ Is there an `import` or `exposing` missing up-top? **NOT IMPLEMENTED** This feature is not yet implemented: unsupported operator +**fuzz_crash_020.md:105:2:105:8:** +```roc + b?? 12 > 5 or 13 + 2 < 5 and 10 - 1 >= 16 or 12 <= 3 e_fn(arg1)?.od()?.ned()?.recd? +``` + ^^^^^^ + This error doesn't have a proper diagnostic report yet. Let us know if you want to help improve Roc's error messages! + **UNDEFINED VARIABLE** Nothing is named `e_fn` in this scope. Is there an `import` or `exposing` missing up-top? diff --git a/test/snapshots/fuzz_crash/fuzz_crash_023.md b/test/snapshots/fuzz_crash/fuzz_crash_023.md index 9532d661aa..af69947bd9 100644 --- a/test/snapshots/fuzz_crash/fuzz_crash_023.md +++ b/test/snapshots/fuzz_crash/fuzz_crash_023.md @@ -229,22 +229,22 @@ UNDECLARED TYPE - fuzz_crash_023.md:45:8:45:10 UNDECLARED TYPE - fuzz_crash_023.md:46:8:46:17 UNDECLARED TYPE - fuzz_crash_023.md:52:4:52:6 UNDECLARED TYPE - fuzz_crash_023.md:53:8:53:17 -NOT IMPLEMENTED - :0:0:0:0 +NOT IMPLEMENTED - fuzz_crash_023.md:6:1:12:4 MODULE NOT FOUND - fuzz_crash_023.md:16:1:16:27 MODULE NOT FOUND - fuzz_crash_023.md:17:1:20:20 UNDEFINED VARIABLE - fuzz_crash_023.md:72:4:72:13 UNUSED VARIABLE - fuzz_crash_023.md:97:3:97:8 UNUSED VARIABLE - fuzz_crash_023.md:1:1:1:1 -NOT IMPLEMENTED - :0:0:0:0 +NOT IMPLEMENTED - fuzz_crash_023.md:108:7:108:12 UNUSED VARIABLE - fuzz_crash_023.md:1:1:1:1 -NOT IMPLEMENTED - :0:0:0:0 +NOT IMPLEMENTED - fuzz_crash_023.md:111:4:111:9 UNUSED VARIABLE - fuzz_crash_023.md:1:1:1:1 -NOT IMPLEMENTED - :0:0:0:0 +NOT IMPLEMENTED - fuzz_crash_023.md:120:7:120:12 UNDEFINED VARIABLE - fuzz_crash_023.md:121:37:121:40 UNUSED VARIABLE - fuzz_crash_023.md:121:21:121:27 UNUSED VARIABLE - fuzz_crash_023.md:127:4:128:9 -NOT IMPLEMENTED - :0:0:0:0 -NOT IMPLEMENTED - :0:0:0:0 +NOT IMPLEMENTED - fuzz_crash_023.md:130:18:130:23 +NOT IMPLEMENTED - fuzz_crash_023.md:133:9:133:14 UNUSED VARIABLE - fuzz_crash_023.md:82:2:82:3 UNDEFINED VARIABLE - fuzz_crash_023.md:141:2:141:6 UNDECLARED TYPE - fuzz_crash_023.md:143:14:143:20 @@ -259,7 +259,7 @@ UNDEFINED VARIABLE - fuzz_crash_023.md:179:42:179:48 UNDEFINED VARIABLE - fuzz_crash_023.md:183:3:183:7 UNDEFINED VARIABLE - fuzz_crash_023.md:185:4:185:10 UNDEFINED VARIABLE - fuzz_crash_023.md:188:22:188:25 -NOT IMPLEMENTED - :0:0:0:0 +NOT IMPLEMENTED - fuzz_crash_023.md:188:18:188:32 UNDEFINED VARIABLE - fuzz_crash_023.md:189:26:189:33 UNDEFINED VARIABLE - fuzz_crash_023.md:189:34:189:38 UNDEFINED VARIABLE - fuzz_crash_023.md:190:2:190:14 @@ -449,8 +449,20 @@ This type is referenced here: **NOT IMPLEMENTED** This feature is not yet implemented: malformed import module name contains invalid control characters +**fuzz_crash_023.md:6:1:12:4:** +```roc +import # Comment after import keyword + pf # Comment after qualifier + .StdoutMultiline # Comment after ident + exposing [ # Comment after exposing open + line!, # Comment after exposed item + write!, # Another after exposed item + ] # Comment after exposing close +``` + This error doesn't have a proper diagnostic report yet. Let us know if you want to help improve Roc's error messages! + **MODULE NOT FOUND** The module `BadName` was not found in this Roc project. @@ -513,8 +525,15 @@ The unused variable is declared here: **NOT IMPLEMENTED** This feature is not yet implemented: alternatives pattern outside match expression +**fuzz_crash_023.md:108:7:108:12:** +```roc + [1, 2 | 5, 3, .. as rest] => 123 +``` + ^^^^^ + This error doesn't have a proper diagnostic report yet. Let us know if you want to help improve Roc's error messages! + **UNUSED VARIABLE** Variable `rest` is not used anywhere in your code. @@ -530,8 +549,15 @@ The unused variable is declared here: **NOT IMPLEMENTED** This feature is not yet implemented: alternatives pattern outside match expression +**fuzz_crash_023.md:111:4:111:9:** +```roc + 2 | 5, +``` + ^^^^^ + This error doesn't have a proper diagnostic report yet. Let us know if you want to help improve Roc's error messages! + **UNUSED VARIABLE** Variable `rest` is not used anywhere in your code. @@ -547,8 +573,15 @@ The unused variable is declared here: **NOT IMPLEMENTED** This feature is not yet implemented: alternatives pattern outside match expression +**fuzz_crash_023.md:120:7:120:12:** +```roc + (1, 2 | 5, 3) => 123 +``` + ^^^^^ + This error doesn't have a proper diagnostic report yet. Let us know if you want to help improve Roc's error messages! + **UNDEFINED VARIABLE** Nothing is named `add` in this scope. Is there an `import` or `exposing` missing up-top? @@ -587,13 +620,27 @@ The unused variable is declared here: **NOT IMPLEMENTED** This feature is not yet implemented: alternatives pattern outside match expression +**fuzz_crash_023.md:130:18:130:23:** +```roc + { foo: 1, bar: 2 | 7 } => 12 +``` + ^^^^^ + This error doesn't have a proper diagnostic report yet. Let us know if you want to help improve Roc's error messages! + **NOT IMPLEMENTED** This feature is not yet implemented: alternatives pattern outside match expression +**fuzz_crash_023.md:133:9:133:14:** +```roc + bar: 2 | 7, # After last record field +``` + ^^^^^ + This error doesn't have a proper diagnostic report yet. Let us know if you want to help improve Roc's error messages! + **UNUSED VARIABLE** Variable `b` is not used anywhere in your code. @@ -751,8 +798,15 @@ Is there an `import` or `exposing` missing up-top? **NOT IMPLEMENTED** This feature is not yet implemented: unsupported operator +**fuzz_crash_023.md:188:18:188:32:** +```roc + bin_op_result = Err(foo) ?? 12 > 5 * 5 or 13 + 2 < 5 and 10 - 1 >= 16 or 12 <= 3 / 5 +``` + ^^^^^^^^^^^^^^ + This error doesn't have a proper diagnostic report yet. Let us know if you want to help improve Roc's error messages! + **UNDEFINED VARIABLE** Nothing is named `some_fn` in this scope. Is there an `import` or `exposing` missing up-top? diff --git a/test/snapshots/fuzz_crash/fuzz_crash_027.md b/test/snapshots/fuzz_crash/fuzz_crash_027.md index 0abf37b3e4..ef7f16d462 100644 --- a/test/snapshots/fuzz_crash/fuzz_crash_027.md +++ b/test/snapshots/fuzz_crash/fuzz_crash_027.md @@ -193,14 +193,14 @@ UNDEFINED VARIABLE - fuzz_crash_027.md:65:6:65:7 UNUSED VARIABLE - fuzz_crash_027.md:64:11:64:14 UNDEFINED VARIABLE - fuzz_crash_027.md:71:7:71:11 UNUSED VARIABLE - fuzz_crash_027.md:1:1:1:1 -NOT IMPLEMENTED - :0:0:0:0 +NOT IMPLEMENTED - fuzz_crash_027.md:74:7:74:12 UNUSED VARIABLE - fuzz_crash_027.md:1:1:1:1 UNUSED VARIABLE - fuzz_crash_027.md:76:1:76:4 -NOT IMPLEMENTED - :0:0:0:0 +NOT IMPLEMENTED - fuzz_crash_027.md:81:7:81:12 UNDEFINED VARIABLE - fuzz_crash_027.md:82:37:82:40 UNUSED VARIABLE - fuzz_crash_027.md:82:21:82:27 -NOT IMPLEMENTED - :0:0:0:0 -NOT IMPLEMENTED - :0:0:0:0 +NOT IMPLEMENTED - fuzz_crash_027.md:88:4:88:6 +NOT IMPLEMENTED - fuzz_crash_027.md:89:18:89:23 UNUSED VARIABLE - fuzz_crash_027.md:62:2:62:3 UNDEFINED VARIABLE - fuzz_crash_027.md:97:2:97:6 UNDECLARED TYPE - fuzz_crash_027.md:99:14:99:20 @@ -212,7 +212,7 @@ UNDEFINED VARIABLE - fuzz_crash_027.md:132:42:132:48 UNDEFINED VARIABLE - fuzz_crash_027.md:136:3:136:7 UNDEFINED VARIABLE - fuzz_crash_027.md:138:4:138:10 UNDEFINED VARIABLE - fuzz_crash_027.md:141:14:141:17 -NOT IMPLEMENTED - :0:0:0:0 +NOT IMPLEMENTED - fuzz_crash_027.md:141:10:141:24 UNDEFINED VARIABLE - fuzz_crash_027.md:142:10:142:17 UNDEFINED VARIABLE - fuzz_crash_027.md:142:18:142:22 DOES NOT EXIST - fuzz_crash_027.md:145:4:145:13 @@ -544,8 +544,15 @@ The unused variable is declared here: **NOT IMPLEMENTED** This feature is not yet implemented: alternatives pattern outside match expression +**fuzz_crash_027.md:74:7:74:12:** +```roc + [1, 2 | 5, 3, .. as rest] => 123 +``` + ^^^^^ + This error doesn't have a proper diagnostic report yet. Let us know if you want to help improve Roc's error messages! + **UNUSED VARIABLE** Variable `rest` is not used anywhere in your code. @@ -573,8 +580,15 @@ ist **NOT IMPLEMENTED** This feature is not yet implemented: alternatives pattern outside match expression +**fuzz_crash_027.md:81:7:81:12:** +```roc + (1, 2 | 5, 3) => 123 +``` + ^^^^^ + This error doesn't have a proper diagnostic report yet. Let us know if you want to help improve Roc's error messages! + **UNDEFINED VARIABLE** Nothing is named `add` in this scope. Is there an `import` or `exposing` missing up-top? @@ -601,13 +615,27 @@ The unused variable is declared here: **NOT IMPLEMENTED** This feature is not yet implemented: report an error when unable to resolve field identifier +**fuzz_crash_027.md:88:4:88:6:** +```roc + ..} => 12 +``` + ^^ + This error doesn't have a proper diagnostic report yet. Let us know if you want to help improve Roc's error messages! + **NOT IMPLEMENTED** This feature is not yet implemented: alternatives pattern outside match expression +**fuzz_crash_027.md:89:18:89:23:** +```roc + { foo: 1, bar: 2 | 7 } => 12 +``` + ^^^^^ + This error doesn't have a proper diagnostic report yet. Let us know if you want to help improve Roc's error messages! + **UNUSED VARIABLE** Variable `b` is not used anywhere in your code. @@ -733,8 +761,15 @@ Is there an `import` or `exposing` missing up-top? **NOT IMPLEMENTED** This feature is not yet implemented: unsupported operator +**fuzz_crash_027.md:141:10:141:24:** +```roc + bsult = Err(foo) ?? 12 > 5 * 5 or 13 + 2 < 5 and 10 - 1 >= 16 or 12 <= 3 / 5 +``` + ^^^^^^^^^^^^^^ + This error doesn't have a proper diagnostic report yet. Let us know if you want to help improve Roc's error messages! + **UNDEFINED VARIABLE** Nothing is named `some_fn` in this scope. Is there an `import` or `exposing` missing up-top? diff --git a/test/snapshots/fuzz_crash/fuzz_crash_028.md b/test/snapshots/fuzz_crash/fuzz_crash_028.md index 6da0e9bf4f..d6a9878acf 100644 Binary files a/test/snapshots/fuzz_crash/fuzz_crash_028.md and b/test/snapshots/fuzz_crash/fuzz_crash_028.md differ diff --git a/test/snapshots/fuzz_crash/fuzz_crash_049.md b/test/snapshots/fuzz_crash/fuzz_crash_049.md index 2f830b2272..3509c527cd 100644 Binary files a/test/snapshots/fuzz_crash/fuzz_crash_049.md and b/test/snapshots/fuzz_crash/fuzz_crash_049.md differ diff --git a/test/snapshots/syntax_grab_bag.md b/test/snapshots/syntax_grab_bag.md index a019d395cd..699b055fd4 100644 --- a/test/snapshots/syntax_grab_bag.md +++ b/test/snapshots/syntax_grab_bag.md @@ -224,22 +224,22 @@ UNDECLARED TYPE - syntax_grab_bag.md:45:8:45:10 UNDECLARED TYPE - syntax_grab_bag.md:46:8:46:17 UNDECLARED TYPE - syntax_grab_bag.md:52:4:52:6 UNDECLARED TYPE - syntax_grab_bag.md:53:8:53:17 -NOT IMPLEMENTED - :0:0:0:0 +NOT IMPLEMENTED - syntax_grab_bag.md:6:1:12:4 MODULE NOT FOUND - syntax_grab_bag.md:16:1:16:27 MODULE NOT FOUND - syntax_grab_bag.md:17:1:20:20 UNDEFINED VARIABLE - syntax_grab_bag.md:72:4:72:13 UNUSED VARIABLE - syntax_grab_bag.md:97:3:97:8 UNUSED VARIABLE - syntax_grab_bag.md:1:1:1:1 -NOT IMPLEMENTED - :0:0:0:0 +NOT IMPLEMENTED - syntax_grab_bag.md:108:7:108:12 UNUSED VARIABLE - syntax_grab_bag.md:1:1:1:1 -NOT IMPLEMENTED - :0:0:0:0 +NOT IMPLEMENTED - syntax_grab_bag.md:111:4:111:9 UNUSED VARIABLE - syntax_grab_bag.md:1:1:1:1 -NOT IMPLEMENTED - :0:0:0:0 +NOT IMPLEMENTED - syntax_grab_bag.md:120:7:120:12 UNDEFINED VARIABLE - syntax_grab_bag.md:121:37:121:40 UNUSED VARIABLE - syntax_grab_bag.md:121:21:121:27 UNUSED VARIABLE - syntax_grab_bag.md:127:4:128:9 -NOT IMPLEMENTED - :0:0:0:0 -NOT IMPLEMENTED - :0:0:0:0 +NOT IMPLEMENTED - syntax_grab_bag.md:130:18:130:23 +NOT IMPLEMENTED - syntax_grab_bag.md:133:9:133:14 UNUSED VARIABLE - syntax_grab_bag.md:82:2:82:3 UNDEFINED VARIABLE - syntax_grab_bag.md:141:2:141:6 UNDECLARED TYPE - syntax_grab_bag.md:143:14:143:20 @@ -251,7 +251,7 @@ UNDEFINED VARIABLE - syntax_grab_bag.md:179:42:179:48 UNDEFINED VARIABLE - syntax_grab_bag.md:183:3:183:7 UNDEFINED VARIABLE - syntax_grab_bag.md:185:4:185:10 UNDEFINED VARIABLE - syntax_grab_bag.md:188:22:188:25 -NOT IMPLEMENTED - :0:0:0:0 +NOT IMPLEMENTED - syntax_grab_bag.md:188:18:188:32 UNDEFINED VARIABLE - syntax_grab_bag.md:189:26:189:33 UNDEFINED VARIABLE - syntax_grab_bag.md:189:34:189:38 UNDEFINED VARIABLE - syntax_grab_bag.md:190:2:190:14 @@ -384,8 +384,20 @@ This type is referenced here: **NOT IMPLEMENTED** This feature is not yet implemented: malformed import module name contains invalid control characters +**syntax_grab_bag.md:6:1:12:4:** +```roc +import # Comment after import keyword + pf # Comment after qualifier + .StdoutMultiline # Comment after ident + exposing [ # Comment after exposing open + line!, # Comment after exposed item + write!, # Another after exposed item + ] # Comment after exposing close +``` + This error doesn't have a proper diagnostic report yet. Let us know if you want to help improve Roc's error messages! + **MODULE NOT FOUND** The module `BadName` was not found in this Roc project. @@ -448,8 +460,15 @@ The unused variable is declared here: **NOT IMPLEMENTED** This feature is not yet implemented: alternatives pattern outside match expression +**syntax_grab_bag.md:108:7:108:12:** +```roc + [1, 2 | 5, 3, .. as rest] => 123 +``` + ^^^^^ + This error doesn't have a proper diagnostic report yet. Let us know if you want to help improve Roc's error messages! + **UNUSED VARIABLE** Variable `rest` is not used anywhere in your code. @@ -465,8 +484,15 @@ The unused variable is declared here: **NOT IMPLEMENTED** This feature is not yet implemented: alternatives pattern outside match expression +**syntax_grab_bag.md:111:4:111:9:** +```roc + 2 | 5, +``` + ^^^^^ + This error doesn't have a proper diagnostic report yet. Let us know if you want to help improve Roc's error messages! + **UNUSED VARIABLE** Variable `rest` is not used anywhere in your code. @@ -482,8 +508,15 @@ The unused variable is declared here: **NOT IMPLEMENTED** This feature is not yet implemented: alternatives pattern outside match expression +**syntax_grab_bag.md:120:7:120:12:** +```roc + (1, 2 | 5, 3) => 123 +``` + ^^^^^ + This error doesn't have a proper diagnostic report yet. Let us know if you want to help improve Roc's error messages! + **UNDEFINED VARIABLE** Nothing is named `add` in this scope. Is there an `import` or `exposing` missing up-top? @@ -522,13 +555,27 @@ The unused variable is declared here: **NOT IMPLEMENTED** This feature is not yet implemented: alternatives pattern outside match expression +**syntax_grab_bag.md:130:18:130:23:** +```roc + { foo: 1, bar: 2 | 7 } => 12 +``` + ^^^^^ + This error doesn't have a proper diagnostic report yet. Let us know if you want to help improve Roc's error messages! + **NOT IMPLEMENTED** This feature is not yet implemented: alternatives pattern outside match expression +**syntax_grab_bag.md:133:9:133:14:** +```roc + bar: 2 | 7, # After last record field +``` + ^^^^^ + This error doesn't have a proper diagnostic report yet. Let us know if you want to help improve Roc's error messages! + **UNUSED VARIABLE** Variable `b` is not used anywhere in your code. @@ -654,8 +701,15 @@ Is there an `import` or `exposing` missing up-top? **NOT IMPLEMENTED** This feature is not yet implemented: unsupported operator +**syntax_grab_bag.md:188:18:188:32:** +```roc + bin_op_result = Err(foo) ?? 12 > 5 * 5 or 13 + 2 < 5 and 10 - 1 >= 16 or 12 <= 3 / 5 +``` + ^^^^^^^^^^^^^^ + This error doesn't have a proper diagnostic report yet. Let us know if you want to help improve Roc's error messages! + **UNDEFINED VARIABLE** Nothing is named `some_fn` in this scope. Is there an `import` or `exposing` missing up-top?