| author | |
| committer | |
| log | f87156e33c688effcf00b8fa9c2542391423ee78 |
| tree | 6b209218f51ca1a9e70bf246e364be7534cfd3fa |
| parent | 2ed9288246821c39ae75fa21998a53b34e713cd4 |
5 files changed, 893 insertions(+), 219 deletions(-)
lib/std/Thread/Mutex.zig+97-80| ... | @@ -33,17 +33,29 @@ const testing = std.testing; | ... | @@ -33,17 +33,29 @@ const testing = std.testing; |
| 33 | const StaticResetEvent = std.thread.StaticResetEvent; | 33 | const StaticResetEvent = std.thread.StaticResetEvent; |
| 34 | 34 | ||
| 35 | /// Try to acquire the mutex without blocking. Returns `null` if the mutex is | 35 | /// Try to acquire the mutex without blocking. Returns `null` if the mutex is |
| 36 | /// unavailable. Otherwise returns `Held`. Call `release` on `Held`. | 36 | /// unavailable. Otherwise returns `Held`. Call `release` on `Held`, or use |
| 37 | pub fn tryAcquire(m: *Mutex) ?Impl.Held { | 37 | /// releaseDirect(). |
| 38 | pub fn tryAcquire(m: *Mutex) ?Held { | ||
| 38 | return m.impl.tryAcquire(); | 39 | return m.impl.tryAcquire(); |
| 39 | } | 40 | } |
| 40 | 41 | ||
| 41 | /// Acquire the mutex. Deadlocks if the mutex is already | 42 | /// Acquire the mutex. Deadlocks if the mutex is already |
| 42 | /// held by the calling thread. | 43 | /// held by the calling thread. |
| 43 | pub fn acquire(m: *Mutex) Impl.Held { | 44 | pub fn acquire(m: *Mutex) Held { |
| 44 | return m.impl.acquire(); | 45 | return m.impl.acquire(); |
| 45 | } | 46 | } |
| 46 | 47 | ||
| 48 | /// Release the mutex. Prefer Held.release() if available. | ||
| 49 | pub fn releaseDirect(m: *Mutex) void { | ||
| 50 | return m.impl.releaseDirect(); | ||
| 51 | } | ||
| 52 | |||
| 53 | /// A held mutex handle. Call release to allow other threads to | ||
| 54 | /// take the mutex. Do not call release() more than once. | ||
| 55 | /// For more complex scenarios, this handle can be discarded | ||
| 56 | /// and Mutex.releaseDirect can be called instead. | ||
| 57 | pub const Held = Impl.Held; | ||
| 58 | |||
| 47 | const Impl = if (builtin.single_threaded) | 59 | const Impl = if (builtin.single_threaded) |
| 48 | Dummy | 60 | Dummy |
| 49 | else if (builtin.os.tag == .windows) | 61 | else if (builtin.os.tag == .windows) |
| ... | @@ -53,6 +65,32 @@ else if (std.Thread.use_pthreads) | ... | @@ -53,6 +65,32 @@ else if (std.Thread.use_pthreads) |
| 53 | else | 65 | else |
| 54 | AtomicMutex; | 66 | AtomicMutex; |
| 55 | 67 | ||
| 68 | fn HeldInterface(comptime MutexType: type) type { | ||
| 69 | return struct { | ||
| 70 | const Mixin = @This(); | ||
| 71 | pub const Held = struct { | ||
| 72 | mutex: *MutexType, | ||
| 73 | |||
| 74 | pub fn release(held: Mixin.Held) void { | ||
| 75 | held.mutex.releaseDirect(); | ||
| 76 | } | ||
| 77 | }; | ||
| 78 | |||
| 79 | pub fn tryAcquire(m: *MutexType) ?Mixin.Held { | ||
| 80 | if (m.tryAcquireDirect()) { | ||
| 81 | return Mixin.Held{ .mutex = m }; | ||
| 82 | } else { | ||
| 83 | return null; | ||
| 84 | } | ||
| 85 | } | ||
| 86 | |||
| 87 | pub fn acquire(m: *MutexType) Mixin.Held { | ||
| 88 | m.acquireDirect(); | ||
| 89 | return Mixin.Held{ .mutex = m }; | ||
| 90 | } | ||
| 91 | }; | ||
| 92 | } | ||
| 93 | |||
| 56 | pub const AtomicMutex = struct { | 94 | pub const AtomicMutex = struct { |
| 57 | state: State = .unlocked, | 95 | state: State = .unlocked, |
| 58 | 96 | ||
| ... | @@ -62,39 +100,32 @@ pub const AtomicMutex = struct { | ... | @@ -62,39 +100,32 @@ pub const AtomicMutex = struct { |
| 62 | waiting, | 100 | waiting, |
| 63 | }; | 101 | }; |
| 64 | 102 | ||
| 65 | pub const Held = struct { | 103 | pub usingnamespace HeldInterface(@This()); |
| 66 | mutex: *AtomicMutex, | ||
| 67 | 104 | ||
| 68 | pub fn release(held: Held) void { | 105 | fn tryAcquireDirect(m: *AtomicMutex) bool { |
| 69 | switch (@atomicRmw(State, &held.mutex.state, .Xchg, .unlocked, .Release)) { | 106 | return @cmpxchgStrong( |
| 70 | .unlocked => unreachable, | ||
| 71 | .locked => {}, | ||
| 72 | .waiting => held.mutex.unlockSlow(), | ||
| 73 | } | ||
| 74 | } | ||
| 75 | }; | ||
| 76 | |||
| 77 | pub fn tryAcquire(m: *AtomicMutex) ?Held { | ||
| 78 | if (@cmpxchgStrong( | ||
| 79 | State, | 107 | State, |
| 80 | &m.state, | 108 | &m.state, |
| 81 | .unlocked, | 109 | .unlocked, |
| 82 | .locked, | 110 | .locked, |
| 83 | .Acquire, | 111 | .Acquire, |
| 84 | .Monotonic, | 112 | .Monotonic, |
| 85 | ) == null) { | 113 | ) == null; |
| 86 | return Held{ .mutex = m }; | ||
| 87 | } else { | ||
| 88 | return null; | ||
| 89 | } | ||
| 90 | } | 114 | } |
| 91 | 115 | ||
| 92 | pub fn acquire(m: *AtomicMutex) Held { | 116 | fn acquireDirect(m: *AtomicMutex) void { |
| 93 | switch (@atomicRmw(State, &m.state, .Xchg, .locked, .Acquire)) { | 117 | switch (@atomicRmw(State, &m.state, .Xchg, .locked, .Acquire)) { |
| 94 | .unlocked => {}, | 118 | .unlocked => {}, |
| 95 | else => |s| m.lockSlow(s), | 119 | else => |s| m.lockSlow(s), |
| 96 | } | 120 | } |
| 97 | return Held{ .mutex = m }; | 121 | } |
| 122 | |||
| 123 | fn releaseDirect(m: *AtomicMutex) void { | ||
| 124 | switch (@atomicRmw(State, &m.state, .Xchg, .unlocked, .Release)) { | ||
| 125 | .unlocked => unreachable, | ||
| 126 | .locked => {}, | ||
| 127 | .waiting => m.unlockSlow(), | ||
| 128 | } | ||
| 98 | } | 129 | } |
| 99 | 130 | ||
| 100 | fn lockSlow(m: *AtomicMutex, current_state: State) void { | 131 | fn lockSlow(m: *AtomicMutex, current_state: State) void { |
| ... | @@ -171,36 +202,20 @@ pub const AtomicMutex = struct { | ... | @@ -171,36 +202,20 @@ pub const AtomicMutex = struct { |
| 171 | pub const PthreadMutex = struct { | 202 | pub const PthreadMutex = struct { |
| 172 | pthread_mutex: std.c.pthread_mutex_t = .{}, | 203 | pthread_mutex: std.c.pthread_mutex_t = .{}, |
| 173 | 204 | ||
| 174 | pub const Held = struct { | 205 | pub usingnamespace HeldInterface(@This()); |
| 175 | mutex: *PthreadMutex, | ||
| 176 | |||
| 177 | pub fn release(held: Held) void { | ||
| 178 | switch (std.c.pthread_mutex_unlock(&held.mutex.pthread_mutex)) { | ||
| 179 | .SUCCESS => return, | ||
| 180 | .INVAL => unreachable, | ||
| 181 | .AGAIN => unreachable, | ||
| 182 | .PERM => unreachable, | ||
| 183 | else => unreachable, | ||
| 184 | } | ||
| 185 | } | ||
| 186 | }; | ||
| 187 | 206 | ||
| 188 | /// Try to acquire the mutex without blocking. Returns null if | 207 | /// Try to acquire the mutex without blocking. Returns true if |
| 189 | /// the mutex is unavailable. Otherwise returns Held. Call | 208 | /// the mutex is unavailable. Otherwise returns false. Call |
| 190 | /// release on Held. | 209 | /// release when done. |
| 191 | pub fn tryAcquire(m: *PthreadMutex) ?Held { | 210 | fn tryAcquireDirect(m: *PthreadMutex) bool { |
| 192 | if (std.c.pthread_mutex_trylock(&m.pthread_mutex) == .SUCCESS) { | 211 | return std.c.pthread_mutex_trylock(&m.pthread_mutex) == .SUCCESS; |
| 193 | return Held{ .mutex = m }; | ||
| 194 | } else { | ||
| 195 | return null; | ||
| 196 | } | ||
| 197 | } | 212 | } |
| 198 | 213 | ||
| 199 | /// Acquire the mutex. Will deadlock if the mutex is already | 214 | /// Acquire the mutex. Will deadlock if the mutex is already |
| 200 | /// held by the calling thread. | 215 | /// held by the calling thread. |
| 201 | pub fn acquire(m: *PthreadMutex) Held { | 216 | fn acquireDirect(m: *PthreadMutex) void { |
| 202 | switch (std.c.pthread_mutex_lock(&m.pthread_mutex)) { | 217 | switch (std.c.pthread_mutex_lock(&m.pthread_mutex)) { |
| 203 | .SUCCESS => return Held{ .mutex = m }, | 218 | .SUCCESS => {}, |
| 204 | .INVAL => unreachable, | 219 | .INVAL => unreachable, |
| 205 | .BUSY => unreachable, | 220 | .BUSY => unreachable, |
| 206 | .AGAIN => unreachable, | 221 | .AGAIN => unreachable, |
| ... | @@ -209,6 +224,16 @@ pub const PthreadMutex = struct { | ... | @@ -209,6 +224,16 @@ pub const PthreadMutex = struct { |
| 209 | else => unreachable, | 224 | else => unreachable, |
| 210 | } | 225 | } |
| 211 | } | 226 | } |
| 227 | |||
| 228 | fn releaseDirect(m: *PthreadMutex) void { | ||
| 229 | switch (std.c.pthread_mutex_unlock(&m.pthread_mutex)) { | ||
| 230 | .SUCCESS => return, | ||
| 231 | .INVAL => unreachable, | ||
| 232 | .AGAIN => unreachable, | ||
| 233 | .PERM => unreachable, | ||
| 234 | else => unreachable, | ||
| 235 | } | ||
| 236 | } | ||
| 212 | }; | 237 | }; |
| 213 | 238 | ||
| 214 | /// This has the sematics as `Mutex`, however it does not actually do any | 239 | /// This has the sematics as `Mutex`, however it does not actually do any |
| ... | @@ -216,58 +241,50 @@ pub const PthreadMutex = struct { | ... | @@ -216,58 +241,50 @@ pub const PthreadMutex = struct { |
| 216 | pub const Dummy = struct { | 241 | pub const Dummy = struct { |
| 217 | lock: @TypeOf(lock_init) = lock_init, | 242 | lock: @TypeOf(lock_init) = lock_init, |
| 218 | 243 | ||
| 219 | const lock_init = if (std.debug.runtime_safety) false else {}; | 244 | pub usingnamespace HeldInterface(@This()); |
| 220 | |||
| 221 | pub const Held = struct { | ||
| 222 | mutex: *Dummy, | ||
| 223 | 245 | ||
| 224 | pub fn release(held: Held) void { | 246 | const lock_init = if (std.debug.runtime_safety) false else {}; |
| 225 | if (std.debug.runtime_safety) { | ||
| 226 | held.mutex.lock = false; | ||
| 227 | } | ||
| 228 | } | ||
| 229 | }; | ||
| 230 | 247 | ||
| 231 | /// Try to acquire the mutex without blocking. Returns null if | 248 | /// Try to acquire the mutex without blocking. Returns false if |
| 232 | /// the mutex is unavailable. Otherwise returns Held. Call | 249 | /// the mutex is unavailable. Otherwise returns true. |
| 233 | /// release on Held. | 250 | fn tryAcquireDirect(m: *Dummy) bool { |
| 234 | pub fn tryAcquire(m: *Dummy) ?Held { | ||
| 235 | if (std.debug.runtime_safety) { | 251 | if (std.debug.runtime_safety) { |
| 236 | if (m.lock) return null; | 252 | if (m.lock) return false; |
| 237 | m.lock = true; | 253 | m.lock = true; |
| 238 | } | 254 | } |
| 239 | return Held{ .mutex = m }; | 255 | return true; |
| 240 | } | 256 | } |
| 241 | 257 | ||
| 242 | /// Acquire the mutex. Will deadlock if the mutex is already | 258 | /// Acquire the mutex. Will deadlock if the mutex is already |
| 243 | /// held by the calling thread. | 259 | /// held by the calling thread. |
| 244 | pub fn acquire(m: *Dummy) Held { | 260 | fn acquireDirect(m: *Dummy) void { |
| 245 | return m.tryAcquire() orelse @panic("deadlock detected"); | 261 | if (!m.tryAcquireDirect()) { |
| 262 | @panic("deadlock detected"); | ||
| 263 | } | ||
| 264 | } | ||
| 265 | |||
| 266 | fn releaseDirect(m: *Dummy) void { | ||
| 267 | if (std.debug.runtime_safety) { | ||
| 268 | m.lock = false; | ||
| 269 | } | ||
| 246 | } | 270 | } |
| 247 | }; | 271 | }; |
| 248 | 272 | ||
| 249 | const WindowsMutex = struct { | 273 | const WindowsMutex = struct { |
| 250 | srwlock: windows.SRWLOCK = windows.SRWLOCK_INIT, | 274 | srwlock: windows.SRWLOCK = windows.SRWLOCK_INIT, |
| 251 | 275 | ||
| 252 | pub const Held = struct { | 276 | pub usingnamespace HeldInterface(@This()); |
| 253 | mutex: *WindowsMutex, | ||
| 254 | 277 | ||
| 255 | pub fn release(held: Held) void { | 278 | fn tryAcquireDirect(m: *WindowsMutex) bool { |
| 256 | windows.kernel32.ReleaseSRWLockExclusive(&held.mutex.srwlock); | 279 | return windows.kernel32.TryAcquireSRWLockExclusive(&m.srwlock) != windows.FALSE; |
| 257 | } | ||
| 258 | }; | ||
| 259 | |||
| 260 | pub fn tryAcquire(m: *WindowsMutex) ?Held { | ||
| 261 | if (windows.kernel32.TryAcquireSRWLockExclusive(&m.srwlock) != windows.FALSE) { | ||
| 262 | return Held{ .mutex = m }; | ||
| 263 | } else { | ||
| 264 | return null; | ||
| 265 | } | ||
| 266 | } | 280 | } |
| 267 | 281 | ||
| 268 | pub fn acquire(m: *WindowsMutex) Held { | 282 | fn acquireDirect(m: *WindowsMutex) void { |
| 269 | windows.kernel32.AcquireSRWLockExclusive(&m.srwlock); | 283 | windows.kernel32.AcquireSRWLockExclusive(&m.srwlock); |
| 270 | return Held{ .mutex = m }; | 284 | } |
| 285 | |||
| 286 | fn releaseDirect(m: *WindowsMutex) void { | ||
| 287 | windows.kernel32.ReleaseSRWLockExclusive(&m.srwlock); | ||
| 271 | } | 288 | } |
| 272 | }; | 289 | }; |
| 273 | 290 |
src/Sema.zig+6| ... | @@ -90,6 +90,7 @@ const LazySrcLoc = Module.LazySrcLoc; | ... | @@ -90,6 +90,7 @@ const LazySrcLoc = Module.LazySrcLoc; |
| 90 | const RangeSet = @import("RangeSet.zig"); | 90 | const RangeSet = @import("RangeSet.zig"); |
| 91 | const target_util = @import("target.zig"); | 91 | const target_util = @import("target.zig"); |
| 92 | const Package = @import("Package.zig"); | 92 | const Package = @import("Package.zig"); |
| 93 | const crash_report = @import("crash_report.zig"); | ||
| 93 | 94 | ||
| 94 | pub const InstMap = std.AutoHashMapUnmanaged(Zir.Inst.Index, Air.Inst.Ref); | 95 | pub const InstMap = std.AutoHashMapUnmanaged(Zir.Inst.Index, Air.Inst.Ref); |
| 95 | 96 | ||
| ... | @@ -153,11 +154,16 @@ pub fn analyzeBody( | ... | @@ -153,11 +154,16 @@ pub fn analyzeBody( |
| 153 | 154 | ||
| 154 | var orig_captures: usize = parent_capture_scope.captures.count(); | 155 | var orig_captures: usize = parent_capture_scope.captures.count(); |
| 155 | 156 | ||
| 157 | var crash_info = crash_report.prepAnalyzeBody(sema, block, body); | ||
| 158 | crash_info.push(); | ||
| 159 | defer crash_info.pop(); | ||
| 160 | |||
| 156 | // We use a while(true) loop here to avoid a redundant way of breaking out of | 161 | // We use a while(true) loop here to avoid a redundant way of breaking out of |
| 157 | // the loop. The only way to break out of the loop is with a `noreturn` | 162 | // the loop. The only way to break out of the loop is with a `noreturn` |
| 158 | // instruction. | 163 | // instruction. |
| 159 | var i: usize = 0; | 164 | var i: usize = 0; |
| 160 | const result = while (true) { | 165 | const result = while (true) { |
| 166 | crash_info.setBodyIndex(i); | ||
| 161 | const inst = body[i]; | 167 | const inst = body[i]; |
| 162 | const air_inst: Air.Inst.Ref = switch (tags[inst]) { | 168 | const air_inst: Air.Inst.Ref = switch (tags[inst]) { |
| 163 | // zig fmt: off | 169 | // zig fmt: off |
src/crash_report.zig created+581| ... | @@ -0,0 +1,581 @@ | ||
| 1 | const std = @import("std"); | ||
| 2 | const builtin = @import("builtin"); | ||
| 3 | const debug = std.debug; | ||
| 4 | const os = std.os; | ||
| 5 | const io = std.io; | ||
| 6 | const print_zir = @import("print_zir.zig"); | ||
| 7 | |||
| 8 | const Module = @import("Module.zig"); | ||
| 9 | const Sema = @import("Sema.zig"); | ||
| 10 | const Zir = @import("Zir.zig"); | ||
| 11 | |||
| 12 | pub const is_enabled = builtin.mode == .Debug; | ||
| 13 | |||
| 14 | /// To use these crash report diagnostics, publish these symbols in your main file. | ||
| 15 | /// You will also need to call initialize() on startup, preferably as the very first operation in your program. | ||
| 16 | pub const root_decls = struct { | ||
| 17 | pub const panic = if (is_enabled) compilerPanic else std.builtin.default_panic; | ||
| 18 | pub const enable_segfault_handler = if (is_enabled) false else debug.default_enable_segfault_handler; | ||
| 19 | }; | ||
| 20 | |||
| 21 | /// Install signal handlers to identify crashes and report diagnostics. | ||
| 22 | pub fn initialize() void { | ||
| 23 | if (is_enabled and debug.have_segfault_handling_support) { | ||
| 24 | attachSegfaultHandler(); | ||
| 25 | } | ||
| 26 | } | ||
| 27 | |||
| 28 | fn En(comptime T: type) type { | ||
| 29 | return if (is_enabled) T else void; | ||
| 30 | } | ||
| 31 | |||
| 32 | fn en(val: anytype) En(@TypeOf(val)) { | ||
| 33 | return if (is_enabled) val else {}; | ||
| 34 | } | ||
| 35 | |||
| 36 | pub const AnalyzeBody = struct { | ||
| 37 | parent: if (is_enabled) ?*AnalyzeBody else void, | ||
| 38 | sema: En(*Sema), | ||
| 39 | block: En(*Module.Scope.Block), | ||
| 40 | body: En([]const Zir.Inst.Index), | ||
| 41 | body_index: En(usize), | ||
| 42 | |||
| 43 | pub fn push(self: *@This()) void { | ||
| 44 | if (!is_enabled) return; | ||
| 45 | const head = &zir_state; | ||
| 46 | debug.assert(self.parent == null); | ||
| 47 | self.parent = head.*; | ||
| 48 | head.* = self; | ||
| 49 | } | ||
| 50 | |||
| 51 | pub fn pop(self: *@This()) void { | ||
| 52 | if (!is_enabled) return; | ||
| 53 | const head = &zir_state; | ||
| 54 | const old = head.*.?; | ||
| 55 | debug.assert(old == self); | ||
| 56 | head.* = old.parent; | ||
| 57 | } | ||
| 58 | |||
| 59 | pub fn setBodyIndex(self: *@This(), index: usize) void { | ||
| 60 | if (!is_enabled) return; | ||
| 61 | self.body_index = index; | ||
| 62 | } | ||
| 63 | }; | ||
| 64 | |||
| 65 | threadlocal var zir_state: ?*AnalyzeBody = if (is_enabled) null else @compileError("Cannot use zir_state if crash_report is disabled."); | ||
| 66 | |||
| 67 | pub fn prepAnalyzeBody(sema: *Sema, block: *Module.Scope.Block, body: []const Zir.Inst.Index) AnalyzeBody { | ||
| 68 | if (is_enabled) { | ||
| 69 | return .{ | ||
| 70 | .parent = null, | ||
| 71 | .sema = sema, | ||
| 72 | .block = block, | ||
| 73 | .body = body, | ||
| 74 | .body_index = 0, | ||
| 75 | }; | ||
| 76 | } else { | ||
| 77 | if (@sizeOf(AnalyzeBody) != 0) | ||
| 78 | @compileError("AnalyzeBody must have zero size when crash reports are disabled"); | ||
| 79 | return undefined; | ||
| 80 | } | ||
| 81 | } | ||
| 82 | |||
| 83 | fn dumpStatusReport() !void { | ||
| 84 | const anal = zir_state orelse return; | ||
| 85 | // Note: We have the panic mutex here, so we can safely use the global crash heap. | ||
| 86 | var fba = std.heap.FixedBufferAllocator.init(&crash_heap); | ||
| 87 | const allocator = &fba.allocator; | ||
| 88 | |||
| 89 | const stderr = io.getStdErr().writer(); | ||
| 90 | const block: *Scope.Block = anal.block; | ||
| 91 | |||
| 92 | try stderr.writeAll("Analyzing "); | ||
| 93 | try writeFullyQualifiedDeclWithFile(block.src_decl, stderr); | ||
| 94 | try stderr.writeAll("\n"); | ||
| 95 | |||
| 96 | print_zir.renderInstructionContext( | ||
| 97 | allocator, | ||
| 98 | anal.body, | ||
| 99 | anal.body_index, | ||
| 100 | block.src_decl.getFileScope(), | ||
| 101 | block.src_decl.src_node, | ||
| 102 | 6, // indent | ||
| 103 | stderr, | ||
| 104 | ) catch |err| switch (err) { | ||
| 105 | error.OutOfMemory => try stderr.writeAll(" <out of memory dumping zir>\n"), | ||
| 106 | else => |e| return e, | ||
| 107 | }; | ||
| 108 | try stderr.writeAll(" For full context, use the command\n zig ast-check -t "); | ||
| 109 | try writeFilePath(block.src_decl.getFileScope(), stderr); | ||
| 110 | try stderr.writeAll("\n\n"); | ||
| 111 | |||
| 112 | var parent = anal.parent; | ||
| 113 | while (parent) |curr| { | ||
| 114 | fba.reset(); | ||
| 115 | try stderr.writeAll(" in "); | ||
| 116 | try writeFullyQualifiedDeclWithFile(curr.block.src_decl, stderr); | ||
| 117 | try stderr.writeAll("\n > "); | ||
| 118 | print_zir.renderSingleInstruction( | ||
| 119 | allocator, | ||
| 120 | curr.body[curr.body_index], | ||
| 121 | curr.block.src_decl.getFileScope(), | ||
| 122 | curr.block.src_decl.src_node, | ||
| 123 | 6, // indent | ||
| 124 | stderr, | ||
| 125 | ) catch |err| switch (err) { | ||
| 126 | error.OutOfMemory => try stderr.writeAll(" <out of memory dumping zir>\n"), | ||
| 127 | else => |e| return e, | ||
| 128 | }; | ||
| 129 | try stderr.writeAll("\n"); | ||
| 130 | |||
| 131 | parent = curr.parent; | ||
| 132 | } | ||
| 133 | |||
| 134 | try stderr.writeAll("\n"); | ||
| 135 | } | ||
| 136 | |||
| 137 | const Scope = Module.Scope; | ||
| 138 | const Decl = Module.Decl; | ||
| 139 | |||
| 140 | var crash_heap: [16 * 4096]u8 = undefined; | ||
| 141 | |||
| 142 | fn writeFilePath(file: *Scope.File, stream: anytype) !void { | ||
| 143 | if (file.pkg.root_src_directory.path) |path| { | ||
| 144 | try stream.writeAll(path); | ||
| 145 | try stream.writeAll(std.fs.path.sep_str); | ||
| 146 | } | ||
| 147 | try stream.writeAll(file.sub_file_path); | ||
| 148 | } | ||
| 149 | |||
| 150 | fn writeFullyQualifiedDeclWithFile(decl: *Decl, stream: anytype) !void { | ||
| 151 | try writeFilePath(decl.getFileScope(), stream); | ||
| 152 | try stream.writeAll(": "); | ||
| 153 | try decl.namespace.renderFullyQualifiedName(std.mem.sliceTo(decl.name, 0), stream); | ||
| 154 | } | ||
| 155 | |||
| 156 | fn compilerPanic(msg: []const u8, error_return_trace: ?*std.builtin.StackTrace) noreturn { | ||
| 157 | PanicSwitch.preDispatch(); | ||
| 158 | @setCold(true); | ||
| 159 | const ret_addr = @returnAddress(); | ||
| 160 | const stack_ctx: StackContext = .{ .current = .{ .ret_addr = ret_addr } }; | ||
| 161 | PanicSwitch.dispatch(error_return_trace, stack_ctx, msg); | ||
| 162 | } | ||
| 163 | |||
| 164 | /// Attaches a global SIGSEGV handler | ||
| 165 | pub fn attachSegfaultHandler() void { | ||
| 166 | if (!debug.have_segfault_handling_support) { | ||
| 167 | @compileError("segfault handler not supported for this target"); | ||
| 168 | } | ||
| 169 | if (builtin.os.tag == .windows) { | ||
| 170 | _ = os.windows.kernel32.AddVectoredExceptionHandler(0, handleSegfaultWindows); | ||
| 171 | return; | ||
| 172 | } | ||
| 173 | var act = os.Sigaction{ | ||
| 174 | .handler = .{ .sigaction = handleSegfaultLinux }, | ||
| 175 | .mask = os.empty_sigset, | ||
| 176 | .flags = (os.SA.SIGINFO | os.SA.RESTART | os.SA.RESETHAND), | ||
| 177 | }; | ||
| 178 | |||
| 179 | os.sigaction(os.SIG.SEGV, &act, null); | ||
| 180 | os.sigaction(os.SIG.ILL, &act, null); | ||
| 181 | os.sigaction(os.SIG.BUS, &act, null); | ||
| 182 | } | ||
| 183 | |||
| 184 | fn handleSegfaultLinux(sig: i32, info: *const os.siginfo_t, ctx_ptr: ?*const c_void) callconv(.C) noreturn { | ||
| 185 | // TODO: use alarm() here to prevent infinite loops | ||
| 186 | PanicSwitch.preDispatch(); | ||
| 187 | |||
| 188 | const addr = switch (builtin.os.tag) { | ||
| 189 | .linux => @ptrToInt(info.fields.sigfault.addr), | ||
| 190 | .freebsd => @ptrToInt(info.addr), | ||
| 191 | .netbsd => @ptrToInt(info.info.reason.fault.addr), | ||
| 192 | .openbsd => @ptrToInt(info.data.fault.addr), | ||
| 193 | .solaris => @ptrToInt(info.reason.fault.addr), | ||
| 194 | else => @compileError("TODO implement handleSegfaultLinux for new linux OS"), | ||
| 195 | }; | ||
| 196 | |||
| 197 | var err_buffer: [128]u8 = undefined; | ||
| 198 | const error_msg = switch (sig) { | ||
| 199 | os.SIG.SEGV => std.fmt.bufPrint(&err_buffer, "Segmentation fault at address 0x{x}", .{addr}) catch "Segmentation fault", | ||
| 200 | os.SIG.ILL => std.fmt.bufPrint(&err_buffer, "Illegal instruction at address 0x{x}", .{addr}) catch "Illegal instruction", | ||
| 201 | os.SIG.BUS => std.fmt.bufPrint(&err_buffer, "Bus error at address 0x{x}", .{addr}) catch "Bus error", | ||
| 202 | else => std.fmt.bufPrint(&err_buffer, "Unknown error (signal {}) at address 0x{x}", .{ sig, addr }) catch "Unknown error", | ||
| 203 | }; | ||
| 204 | |||
| 205 | const stack_ctx: StackContext = switch (builtin.cpu.arch) { | ||
| 206 | .i386 => ctx: { | ||
| 207 | const ctx = @ptrCast(*const os.ucontext_t, @alignCast(@alignOf(os.ucontext_t), ctx_ptr)); | ||
| 208 | const ip = @intCast(usize, ctx.mcontext.gregs[os.REG.EIP]); | ||
| 209 | const bp = @intCast(usize, ctx.mcontext.gregs[os.REG.EBP]); | ||
| 210 | break :ctx StackContext{ .exception = .{ .bp = bp, .ip = ip } }; | ||
| 211 | }, | ||
| 212 | .x86_64 => ctx: { | ||
| 213 | const ctx = @ptrCast(*const os.ucontext_t, @alignCast(@alignOf(os.ucontext_t), ctx_ptr)); | ||
| 214 | const ip = switch (builtin.os.tag) { | ||
| 215 | .linux, .netbsd, .solaris => @intCast(usize, ctx.mcontext.gregs[os.REG.RIP]), | ||
| 216 | .freebsd => @intCast(usize, ctx.mcontext.rip), | ||
| 217 | .openbsd => @intCast(usize, ctx.sc_rip), | ||
| 218 | else => unreachable, | ||
| 219 | }; | ||
| 220 | const bp = switch (builtin.os.tag) { | ||
| 221 | .linux, .netbsd, .solaris => @intCast(usize, ctx.mcontext.gregs[os.REG.RBP]), | ||
| 222 | .openbsd => @intCast(usize, ctx.sc_rbp), | ||
| 223 | .freebsd => @intCast(usize, ctx.mcontext.rbp), | ||
| 224 | else => unreachable, | ||
| 225 | }; | ||
| 226 | break :ctx StackContext{ .exception = .{ .bp = bp, .ip = ip } }; | ||
| 227 | }, | ||
| 228 | .arm => ctx: { | ||
| 229 | const ctx = @ptrCast(*const os.ucontext_t, @alignCast(@alignOf(os.ucontext_t), ctx_ptr)); | ||
| 230 | const ip = @intCast(usize, ctx.mcontext.arm_pc); | ||
| 231 | const bp = @intCast(usize, ctx.mcontext.arm_fp); | ||
| 232 | break :ctx StackContext{ .exception = .{ .bp = bp, .ip = ip } }; | ||
| 233 | }, | ||
| 234 | .aarch64 => ctx: { | ||
| 235 | const ctx = @ptrCast(*const os.ucontext_t, @alignCast(@alignOf(os.ucontext_t), ctx_ptr)); | ||
| 236 | const ip = @intCast(usize, ctx.mcontext.pc); | ||
| 237 | // x29 is the ABI-designated frame pointer | ||
| 238 | const bp = @intCast(usize, ctx.mcontext.regs[29]); | ||
| 239 | break :ctx StackContext{ .exception = .{ .bp = bp, .ip = ip } }; | ||
| 240 | }, | ||
| 241 | else => .not_supported, | ||
| 242 | }; | ||
| 243 | |||
| 244 | PanicSwitch.dispatch(null, stack_ctx, error_msg); | ||
| 245 | } | ||
| 246 | |||
| 247 | const WindowsSegfaultMessage = union(enum) { | ||
| 248 | literal: []const u8, | ||
| 249 | segfault: void, | ||
| 250 | illegal_instruction: void, | ||
| 251 | }; | ||
| 252 | |||
| 253 | fn handleSegfaultWindows(info: *os.windows.EXCEPTION_POINTERS) callconv(os.windows.WINAPI) c_long { | ||
| 254 | switch (info.ExceptionRecord.ExceptionCode) { | ||
| 255 | os.windows.EXCEPTION_DATATYPE_MISALIGNMENT => handleSegfaultWindowsExtra(info, .{ .literal = "Unaligned Memory Access" }), | ||
| 256 | os.windows.EXCEPTION_ACCESS_VIOLATION => handleSegfaultWindowsExtra(info, .segfault), | ||
| 257 | os.windows.EXCEPTION_ILLEGAL_INSTRUCTION => handleSegfaultWindowsExtra(info, .illegal_instruction), | ||
| 258 | os.windows.EXCEPTION_STACK_OVERFLOW => handleSegfaultWindowsExtra(info, .{ .literal = "Stack Overflow" }), | ||
| 259 | else => return os.windows.EXCEPTION_CONTINUE_SEARCH, | ||
| 260 | } | ||
| 261 | } | ||
| 262 | |||
| 263 | fn handleSegfaultWindowsExtra(info: *os.windows.EXCEPTION_POINTERS, comptime msg: WindowsSegfaultMessage) noreturn { | ||
| 264 | PanicSwitch.preDispatch(); | ||
| 265 | |||
| 266 | const stack_ctx = if (@hasDecl(os.windows, "CONTEXT")) ctx: { | ||
| 267 | const regs = info.ContextRecord.getRegs(); | ||
| 268 | break :ctx StackContext{ .exception = .{ .bp = regs.bp, .ip = regs.ip } }; | ||
| 269 | } else ctx: { | ||
| 270 | const addr = @ptrToInt(info.ExceptionRecord.ExceptionAddress); | ||
| 271 | break :ctx StackContext{ .current = .{ .ret_addr = addr } }; | ||
| 272 | }; | ||
| 273 | |||
| 274 | switch (msg) { | ||
| 275 | .literal => |err| PanicSwitch.dispatch(null, stack_ctx, err), | ||
| 276 | .segfault => { | ||
| 277 | const format_item = "Segmentation fault at address 0x{x}"; | ||
| 278 | var buf: [format_item.len + 32]u8 = undefined; // 32 is arbitrary, but sufficiently large | ||
| 279 | const to_print = std.fmt.bufPrint(&buf, format_item, .{info.ExceptionRecord.ExceptionInformation[1]}) catch unreachable; | ||
| 280 | PanicSwitch.dispatch(null, stack_ctx, to_print); | ||
| 281 | }, | ||
| 282 | .illegal_instruction => { | ||
| 283 | const ip: ?usize = switch (stack_ctx) { | ||
| 284 | .exception => |ex| ex.ip, | ||
| 285 | .current => |cur| cur.ret_addr, | ||
| 286 | .not_supported => null, | ||
| 287 | }; | ||
| 288 | |||
| 289 | if (ip) |addr| { | ||
| 290 | const format_item = "Illegal instruction at address 0x{x}"; | ||
| 291 | var buf: [format_item.len + 32]u8 = undefined; // 32 is arbitrary, but sufficiently large | ||
| 292 | const to_print = std.fmt.bufPrint(&buf, format_item, .{addr}) catch unreachable; | ||
| 293 | PanicSwitch.dispatch(null, stack_ctx, to_print); | ||
| 294 | } else { | ||
| 295 | PanicSwitch.dispatch(null, stack_ctx, "Illegal Instruction"); | ||
| 296 | } | ||
| 297 | }, | ||
| 298 | } | ||
| 299 | } | ||
| 300 | |||
| 301 | const StackContext = union(enum) { | ||
| 302 | current: struct { | ||
| 303 | ret_addr: ?usize, | ||
| 304 | }, | ||
| 305 | exception: struct { | ||
| 306 | bp: usize, | ||
| 307 | ip: usize, | ||
| 308 | }, | ||
| 309 | not_supported: void, | ||
| 310 | |||
| 311 | pub fn dumpStackTrace(ctx: @This()) void { | ||
| 312 | switch (ctx) { | ||
| 313 | .current => |ct| { | ||
| 314 | debug.dumpCurrentStackTrace(ct.ret_addr); | ||
| 315 | }, | ||
| 316 | .exception => |ex| { | ||
| 317 | debug.dumpStackTraceFromBase(ex.bp, ex.ip); | ||
| 318 | }, | ||
| 319 | .not_supported => { | ||
| 320 | const stderr = io.getStdErr().writer(); | ||
| 321 | stderr.writeAll("Stack trace not supported on this platform.\n") catch {}; | ||
| 322 | }, | ||
| 323 | } | ||
| 324 | } | ||
| 325 | }; | ||
| 326 | |||
| 327 | const PanicSwitch = struct { | ||
| 328 | const RecoverStage = enum { | ||
| 329 | initialize, | ||
| 330 | report_stack, | ||
| 331 | release_mutex, | ||
| 332 | release_ref_count, | ||
| 333 | abort, | ||
| 334 | silent_abort, | ||
| 335 | }; | ||
| 336 | |||
| 337 | const RecoverVerbosity = enum { | ||
| 338 | message_and_stack, | ||
| 339 | message_only, | ||
| 340 | silent, | ||
| 341 | }; | ||
| 342 | |||
| 343 | const PanicState = struct { | ||
| 344 | recover_stage: RecoverStage = .initialize, | ||
| 345 | recover_verbosity: RecoverVerbosity = .message_and_stack, | ||
| 346 | panic_ctx: StackContext = undefined, | ||
| 347 | panic_trace: ?*const std.builtin.StackTrace = null, | ||
| 348 | awaiting_dispatch: bool = false, | ||
| 349 | }; | ||
| 350 | |||
| 351 | /// Counter for the number of threads currently panicking. | ||
| 352 | /// Updated atomically before taking the panic_mutex. | ||
| 353 | /// In recoverable cases, the program will not abort | ||
| 354 | /// until all panicking threads have dumped their traces. | ||
| 355 | var panicking: u8 = 0; | ||
| 356 | |||
| 357 | // Locked to avoid interleaving panic messages from multiple threads. | ||
| 358 | var panic_mutex = std.Thread.Mutex{}; | ||
| 359 | |||
| 360 | /// Tracks the state of the current panic. If the code within the | ||
| 361 | /// panic triggers a secondary panic, this allows us to recover. | ||
| 362 | threadlocal var panic_state_raw: PanicState = .{}; | ||
| 363 | |||
| 364 | /// The segfault handlers above need to do some work before they can dispatch | ||
| 365 | /// this switch. Calling preDispatch() first makes that work fault tolerant. | ||
| 366 | pub fn preDispatch() void { | ||
| 367 | // TODO: We want segfaults to trigger the panic recursively here, | ||
| 368 | // but if there is a segfault accessing this TLS slot it will cause an | ||
| 369 | // infinite loop. We should use `alarm()` to prevent the infinite | ||
| 370 | // loop and maybe also use a non-thread-local global to detect if | ||
| 371 | // it's happening and print a message. | ||
| 372 | var panic_state: *volatile PanicState = &panic_state_raw; | ||
| 373 | if (panic_state.awaiting_dispatch) { | ||
| 374 | dispatch(null, .{ .current = .{ .ret_addr = null } }, "Panic while preparing callstack"); | ||
| 375 | } | ||
| 376 | panic_state.awaiting_dispatch = true; | ||
| 377 | } | ||
| 378 | |||
| 379 | /// This is the entry point to a panic-tolerant panic handler. | ||
| 380 | /// preDispatch() *MUST* be called exactly once before calling this. | ||
| 381 | /// A threadlocal "recover_stage" is updated throughout the process. | ||
| 382 | /// If a panic happens during the panic, the recover_stage will be | ||
| 383 | /// used to select a recover* function to call to resume the panic. | ||
| 384 | /// The recover_verbosity field is used to handle panics while reporting | ||
| 385 | /// panics within panics. If the panic handler triggers a panic, it will | ||
| 386 | /// attempt to log an additional stack trace for the secondary panic. If | ||
| 387 | /// that panics, it will fall back to just logging the panic message. If | ||
| 388 | /// it can't even do that witout panicing, it will recover without logging | ||
| 389 | /// anything about the internal panic. Depending on the state, "recover" | ||
| 390 | /// here may just mean "call abort". | ||
| 391 | pub fn dispatch( | ||
| 392 | trace: ?*const std.builtin.StackTrace, | ||
| 393 | stack_ctx: StackContext, | ||
| 394 | msg: []const u8, | ||
| 395 | ) noreturn { | ||
| 396 | var panic_state: *volatile PanicState = &panic_state_raw; | ||
| 397 | debug.assert(panic_state.awaiting_dispatch); | ||
| 398 | panic_state.awaiting_dispatch = false; | ||
| 399 | nosuspend switch (panic_state.recover_stage) { | ||
| 400 | .initialize => goTo(initPanic, .{ panic_state, trace, stack_ctx, msg }), | ||
| 401 | .report_stack => goTo(recoverReportStack, .{ panic_state, trace, stack_ctx, msg }), | ||
| 402 | .release_mutex => goTo(recoverReleaseMutex, .{ panic_state, trace, stack_ctx, msg }), | ||
| 403 | .release_ref_count => goTo(recoverReleaseRefCount, .{ panic_state, trace, stack_ctx, msg }), | ||
| 404 | .abort => goTo(recoverAbort, .{ panic_state, trace, stack_ctx, msg }), | ||
| 405 | .silent_abort => goTo(abort, .{}), | ||
| 406 | }; | ||
| 407 | } | ||
| 408 | |||
| 409 | noinline fn initPanic( | ||
| 410 | state: *volatile PanicState, | ||
| 411 | trace: ?*const std.builtin.StackTrace, | ||
| 412 | stack: StackContext, | ||
| 413 | msg: []const u8, | ||
| 414 | ) noreturn { | ||
| 415 | // use a temporary so there's only one volatile store | ||
| 416 | const new_state = PanicState{ | ||
| 417 | .recover_stage = .abort, | ||
| 418 | .panic_ctx = stack, | ||
| 419 | .panic_trace = trace, | ||
| 420 | }; | ||
| 421 | state.* = new_state; | ||
| 422 | |||
| 423 | _ = @atomicRmw(u8, &panicking, .Add, 1, .SeqCst); | ||
| 424 | |||
| 425 | state.recover_stage = .release_ref_count; | ||
| 426 | |||
| 427 | _ = panic_mutex.acquire(); | ||
| 428 | |||
| 429 | state.recover_stage = .release_mutex; | ||
| 430 | |||
| 431 | const stderr = io.getStdErr().writer(); | ||
| 432 | if (builtin.single_threaded) { | ||
| 433 | stderr.print("panic: ", .{}) catch goTo(releaseMutex, .{state}); | ||
| 434 | } else { | ||
| 435 | const current_thread_id = std.Thread.getCurrentId(); | ||
| 436 | stderr.print("thread {} panic: ", .{current_thread_id}) catch goTo(releaseMutex, .{state}); | ||
| 437 | } | ||
| 438 | stderr.print("{s}\n", .{msg}) catch goTo(releaseMutex, .{state}); | ||
| 439 | |||
| 440 | state.recover_stage = .report_stack; | ||
| 441 | |||
| 442 | dumpStatusReport() catch |err| { | ||
| 443 | stderr.print("\nIntercepted error.{} while dumping current state. Continuing...\n", .{err}) catch {}; | ||
| 444 | }; | ||
| 445 | |||
| 446 | goTo(reportStack, .{state}); | ||
| 447 | } | ||
| 448 | |||
| 449 | noinline fn recoverReportStack( | ||
| 450 | state: *volatile PanicState, | ||
| 451 | trace: ?*const std.builtin.StackTrace, | ||
| 452 | stack: StackContext, | ||
| 453 | msg: []const u8, | ||
| 454 | ) noreturn { | ||
| 455 | recover(state, trace, stack, msg); | ||
| 456 | |||
| 457 | state.recover_stage = .release_mutex; | ||
| 458 | const stderr = io.getStdErr().writer(); | ||
| 459 | stderr.writeAll("\nOriginal Error:\n") catch {}; | ||
| 460 | goTo(reportStack, .{state}); | ||
| 461 | } | ||
| 462 | |||
| 463 | noinline fn reportStack(state: *volatile PanicState) noreturn { | ||
| 464 | state.recover_stage = .release_mutex; | ||
| 465 | |||
| 466 | if (state.panic_trace) |t| { | ||
| 467 | debug.dumpStackTrace(t.*); | ||
| 468 | } | ||
| 469 | state.panic_ctx.dumpStackTrace(); | ||
| 470 | |||
| 471 | goTo(releaseMutex, .{state}); | ||
| 472 | } | ||
| 473 | |||
| 474 | noinline fn recoverReleaseMutex( | ||
| 475 | state: *volatile PanicState, | ||
| 476 | trace: ?*const std.builtin.StackTrace, | ||
| 477 | stack: StackContext, | ||
| 478 | msg: []const u8, | ||
| 479 | ) noreturn { | ||
| 480 | recover(state, trace, stack, msg); | ||
| 481 | goTo(releaseMutex, .{state}); | ||
| 482 | } | ||
| 483 | |||
| 484 | noinline fn releaseMutex(state: *volatile PanicState) noreturn { | ||
| 485 | state.recover_stage = .abort; | ||
| 486 | |||
| 487 | panic_mutex.releaseDirect(); | ||
| 488 | |||
| 489 | goTo(releaseRefCount, .{state}); | ||
| 490 | } | ||
| 491 | |||
| 492 | noinline fn recoverReleaseRefCount( | ||
| 493 | state: *volatile PanicState, | ||
| 494 | trace: ?*const std.builtin.StackTrace, | ||
| 495 | stack: StackContext, | ||
| 496 | msg: []const u8, | ||
| 497 | ) noreturn { | ||
| 498 | recover(state, trace, stack, msg); | ||
| 499 | goTo(releaseRefCount, .{state}); | ||
| 500 | } | ||
| 501 | |||
| 502 | noinline fn releaseRefCount(state: *volatile PanicState) noreturn { | ||
| 503 | state.recover_stage = .abort; | ||
| 504 | |||
| 505 | if (@atomicRmw(u8, &panicking, .Sub, 1, .SeqCst) != 1) { | ||
| 506 | // Another thread is panicking, wait for the last one to finish | ||
| 507 | // and call abort() | ||
| 508 | |||
| 509 | // Sleep forever without hammering the CPU | ||
| 510 | var event: std.Thread.StaticResetEvent = .{}; | ||
| 511 | event.wait(); | ||
| 512 | // This should be unreachable, recurse into recoverAbort. | ||
| 513 | @panic("event.wait() returned"); | ||
| 514 | } | ||
| 515 | |||
| 516 | goTo(abort, .{}); | ||
| 517 | } | ||
| 518 | |||
| 519 | noinline fn recoverAbort( | ||
| 520 | state: *volatile PanicState, | ||
| 521 | trace: ?*const std.builtin.StackTrace, | ||
| 522 | stack: StackContext, | ||
| 523 | msg: []const u8, | ||
| 524 | ) noreturn { | ||
| 525 | recover(state, trace, stack, msg); | ||
| 526 | |||
| 527 | state.recover_stage = .silent_abort; | ||
| 528 | const stderr = io.getStdErr().writer(); | ||
| 529 | stderr.writeAll("Aborting...\n") catch {}; | ||
| 530 | goTo(abort, .{}); | ||
| 531 | } | ||
| 532 | |||
| 533 | noinline fn abort() noreturn { | ||
| 534 | os.abort(); | ||
| 535 | } | ||
| 536 | |||
| 537 | inline fn goTo(comptime func: anytype, args: anytype) noreturn { | ||
| 538 | // TODO: Tailcall is broken right now, but eventually this should be used | ||
| 539 | // to avoid blowing up the stack. It's ok for now though, there are no | ||
| 540 | // cycles in the state machine so the max stack usage is bounded. | ||
| 541 | //@call(.{.modifier = .always_tail}, func, args); | ||
| 542 | @call(.{}, func, args); | ||
| 543 | } | ||
| 544 | |||
| 545 | fn recover( | ||
| 546 | state: *volatile PanicState, | ||
| 547 | trace: ?*const std.builtin.StackTrace, | ||
| 548 | stack: StackContext, | ||
| 549 | msg: []const u8, | ||
| 550 | ) void { | ||
| 551 | switch (state.recover_verbosity) { | ||
| 552 | .message_and_stack => { | ||
| 553 | // lower the verbosity, and restore it at the end if we don't panic. | ||
| 554 | state.recover_verbosity = .message_only; | ||
| 555 | |||
| 556 | const stderr = io.getStdErr().writer(); | ||
| 557 | stderr.writeAll("\nPanicked during a panic: ") catch {}; | ||
| 558 | stderr.writeAll(msg) catch {}; | ||
| 559 | stderr.writeAll("\nInner panic stack:\n") catch {}; | ||
| 560 | if (trace) |t| { | ||
| 561 | debug.dumpStackTrace(t.*); | ||
| 562 | } | ||
| 563 | stack.dumpStackTrace(); | ||
| 564 | |||
| 565 | state.recover_verbosity = .message_and_stack; | ||
| 566 | }, | ||
| 567 | .message_only => { | ||
| 568 | state.recover_verbosity = .silent; | ||
| 569 | |||
| 570 | const stderr = io.getStdErr().writer(); | ||
| 571 | stderr.writeAll("\nPanicked while dumping inner panic stack: ") catch {}; | ||
| 572 | stderr.writeAll(msg) catch {}; | ||
| 573 | stderr.writeAll("\n") catch {}; | ||
| 574 | |||
| 575 | // If we succeed, restore all the way to dumping the stack. | ||
| 576 | state.recover_verbosity = .message_and_stack; | ||
| 577 | }, | ||
| 578 | .silent => {}, | ||
| 579 | } | ||
| 580 | } | ||
| 581 | }; | ||
src/main.zig+6| ... | @@ -20,6 +20,10 @@ const translate_c = @import("translate_c.zig"); | ... | @@ -20,6 +20,10 @@ const translate_c = @import("translate_c.zig"); |
| 20 | const Cache = @import("Cache.zig"); | 20 | const Cache = @import("Cache.zig"); |
| 21 | const target_util = @import("target.zig"); | 21 | const target_util = @import("target.zig"); |
| 22 | const ThreadPool = @import("ThreadPool.zig"); | 22 | const ThreadPool = @import("ThreadPool.zig"); |
| 23 | const crash_report = @import("crash_report.zig"); | ||
| 24 | |||
| 25 | // Crash report needs to override the panic handler and other root decls | ||
| 26 | pub usingnamespace crash_report.root_decls; | ||
| 23 | 27 | ||
| 24 | pub fn fatal(comptime format: []const u8, args: anytype) noreturn { | 28 | pub fn fatal(comptime format: []const u8, args: anytype) noreturn { |
| 25 | std.log.emerg(format, args); | 29 | std.log.emerg(format, args); |
| ... | @@ -134,6 +138,8 @@ var general_purpose_allocator = std.heap.GeneralPurposeAllocator(.{ | ... | @@ -134,6 +138,8 @@ var general_purpose_allocator = std.heap.GeneralPurposeAllocator(.{ |
| 134 | }){}; | 138 | }){}; |
| 135 | 139 | ||
| 136 | pub fn main() anyerror!void { | 140 | pub fn main() anyerror!void { |
| 141 | crash_report.initialize(); | ||
| 142 | |||
| 137 | var gpa_need_deinit = false; | 143 | var gpa_need_deinit = false; |
| 138 | const gpa = gpa: { | 144 | const gpa = gpa: { |
| 139 | if (!std.builtin.link_libc) { | 145 | if (!std.builtin.link_libc) { |
src/print_zir.zig+203-139| ... | @@ -24,15 +24,20 @@ pub fn renderAsTextToFile( | ... | @@ -24,15 +24,20 @@ pub fn renderAsTextToFile( |
| 24 | .code = scope_file.zir, | 24 | .code = scope_file.zir, |
| 25 | .indent = 0, | 25 | .indent = 0, |
| 26 | .parent_decl_node = 0, | 26 | .parent_decl_node = 0, |
| 27 | .recurse_decls = true, | ||
| 28 | .recurse_blocks = true, | ||
| 27 | }; | 29 | }; |
| 28 | 30 | ||
| 31 | var raw_stream = std.io.bufferedWriter(fs_file.writer()); | ||
| 32 | const stream = raw_stream.writer(); | ||
| 33 | |||
| 29 | const main_struct_inst = Zir.main_struct_inst; | 34 | const main_struct_inst = Zir.main_struct_inst; |
| 30 | try fs_file.writer().print("%{d} ", .{main_struct_inst}); | 35 | try stream.print("%{d} ", .{main_struct_inst}); |
| 31 | try writer.writeInstToStream(fs_file.writer(), main_struct_inst); | 36 | try writer.writeInstToStream(stream, main_struct_inst); |
| 32 | try fs_file.writeAll("\n"); | 37 | try stream.writeAll("\n"); |
| 33 | const imports_index = scope_file.zir.extra[@enumToInt(Zir.ExtraIndex.imports)]; | 38 | const imports_index = scope_file.zir.extra[@enumToInt(Zir.ExtraIndex.imports)]; |
| 34 | if (imports_index != 0) { | 39 | if (imports_index != 0) { |
| 35 | try fs_file.writeAll("Imports:\n"); | 40 | try stream.writeAll("Imports:\n"); |
| 36 | 41 | ||
| 37 | const extra = scope_file.zir.extraData(Zir.Inst.Imports, imports_index); | 42 | const extra = scope_file.zir.extraData(Zir.Inst.Imports, imports_index); |
| 38 | var import_i: u32 = 0; | 43 | var import_i: u32 = 0; |
| ... | @@ -44,13 +49,74 @@ pub fn renderAsTextToFile( | ... | @@ -44,13 +49,74 @@ pub fn renderAsTextToFile( |
| 44 | 49 | ||
| 45 | const src: LazySrcLoc = .{ .token_abs = item.data.token }; | 50 | const src: LazySrcLoc = .{ .token_abs = item.data.token }; |
| 46 | const import_path = scope_file.zir.nullTerminatedString(item.data.name); | 51 | const import_path = scope_file.zir.nullTerminatedString(item.data.name); |
| 47 | try fs_file.writer().print(" @import(\"{}\") ", .{ | 52 | try stream.print(" @import(\"{}\") ", .{ |
| 48 | std.zig.fmtEscapes(import_path), | 53 | std.zig.fmtEscapes(import_path), |
| 49 | }); | 54 | }); |
| 50 | try writer.writeSrc(fs_file.writer(), src); | 55 | try writer.writeSrc(stream, src); |
| 51 | try fs_file.writer().writeAll("\n"); | 56 | try stream.writeAll("\n"); |
| 52 | } | 57 | } |
| 53 | } | 58 | } |
| 59 | |||
| 60 | try raw_stream.flush(); | ||
| 61 | } | ||
| 62 | |||
| 63 | pub fn renderInstructionContext( | ||
| 64 | gpa: *Allocator, | ||
| 65 | block: []const Zir.Inst.Index, | ||
| 66 | block_index: usize, | ||
| 67 | scope_file: *Module.Scope.File, | ||
| 68 | parent_decl_node: Ast.Node.Index, | ||
| 69 | indent: u32, | ||
| 70 | stream: anytype, | ||
| 71 | ) !void { | ||
| 72 | var arena = std.heap.ArenaAllocator.init(gpa); | ||
| 73 | defer arena.deinit(); | ||
| 74 | |||
| 75 | var writer: Writer = .{ | ||
| 76 | .gpa = gpa, | ||
| 77 | .arena = &arena.allocator, | ||
| 78 | .file = scope_file, | ||
| 79 | .code = scope_file.zir, | ||
| 80 | .indent = if (indent < 2) 2 else indent, | ||
| 81 | .parent_decl_node = parent_decl_node, | ||
| 82 | .recurse_decls = false, | ||
| 83 | .recurse_blocks = true, | ||
| 84 | }; | ||
| 85 | |||
| 86 | try writer.writeBody(stream, block[0..block_index]); | ||
| 87 | try stream.writeByteNTimes(' ', writer.indent - 2); | ||
| 88 | try stream.print("> %{d} ", .{block[block_index]}); | ||
| 89 | try writer.writeInstToStream(stream, block[block_index]); | ||
| 90 | try stream.writeByte('\n'); | ||
| 91 | if (block_index + 1 < block.len) { | ||
| 92 | try writer.writeBody(stream, block[block_index + 1 ..]); | ||
| 93 | } | ||
| 94 | } | ||
| 95 | |||
| 96 | pub fn renderSingleInstruction( | ||
| 97 | gpa: *Allocator, | ||
| 98 | inst: Zir.Inst.Index, | ||
| 99 | scope_file: *Module.Scope.File, | ||
| 100 | parent_decl_node: Ast.Node.Index, | ||
| 101 | indent: u32, | ||
| 102 | stream: anytype, | ||
| 103 | ) !void { | ||
| 104 | var arena = std.heap.ArenaAllocator.init(gpa); | ||
| 105 | defer arena.deinit(); | ||
| 106 | |||
| 107 | var writer: Writer = .{ | ||
| 108 | .gpa = gpa, | ||
| 109 | .arena = &arena.allocator, | ||
| 110 | .file = scope_file, | ||
| 111 | .code = scope_file.zir, | ||
| 112 | .indent = indent, | ||
| 113 | .parent_decl_node = parent_decl_node, | ||
| 114 | .recurse_decls = false, | ||
| 115 | .recurse_blocks = false, | ||
| 116 | }; | ||
| 117 | |||
| 118 | try stream.print("%{d} ", .{inst}); | ||
| 119 | try writer.writeInstToStream(stream, inst); | ||
| 54 | } | 120 | } |
| 55 | 121 | ||
| 56 | const Writer = struct { | 122 | const Writer = struct { |
| ... | @@ -59,7 +125,9 @@ const Writer = struct { | ... | @@ -59,7 +125,9 @@ const Writer = struct { |
| 59 | file: *Module.Scope.File, | 125 | file: *Module.Scope.File, |
| 60 | code: Zir, | 126 | code: Zir, |
| 61 | indent: u32, | 127 | indent: u32, |
| 62 | parent_decl_node: u32, | 128 | parent_decl_node: Ast.Node.Index, |
| 129 | recurse_decls: bool, | ||
| 130 | recurse_blocks: bool, | ||
| 63 | 131 | ||
| 64 | fn relativeToNodeIndex(self: *Writer, offset: i32) Ast.Node.Index { | 132 | fn relativeToNodeIndex(self: *Writer, offset: i32) Ast.Node.Index { |
| 65 | return @bitCast(Ast.Node.Index, offset + @bitCast(i32, self.parent_decl_node)); | 133 | return @bitCast(Ast.Node.Index, offset + @bitCast(i32, self.parent_decl_node)); |
| ... | @@ -567,12 +635,8 @@ const Writer = struct { | ... | @@ -567,12 +635,8 @@ const Writer = struct { |
| 567 | try stream.print("\"{}\", ", .{ | 635 | try stream.print("\"{}\", ", .{ |
| 568 | std.zig.fmtEscapes(self.code.nullTerminatedString(extra.data.name)), | 636 | std.zig.fmtEscapes(self.code.nullTerminatedString(extra.data.name)), |
| 569 | }); | 637 | }); |
| 570 | try stream.writeAll("{\n"); | 638 | try self.writeBracedBody(stream, body); |
| 571 | self.indent += 2; | 639 | try stream.writeAll(") "); |
| 572 | try self.writeBody(stream, body); | ||
| 573 | self.indent -= 2; | ||
| 574 | try stream.writeByteNTimes(' ', self.indent); | ||
| 575 | try stream.writeAll("}) "); | ||
| 576 | try self.writeSrc(stream, inst_data.src()); | 640 | try self.writeSrc(stream, inst_data.src()); |
| 577 | } | 641 | } |
| 578 | 642 | ||
| ... | @@ -881,12 +945,8 @@ const Writer = struct { | ... | @@ -881,12 +945,8 @@ const Writer = struct { |
| 881 | const inst_data = self.code.instructions.items(.data)[inst].pl_node; | 945 | const inst_data = self.code.instructions.items(.data)[inst].pl_node; |
| 882 | const extra = self.code.extraData(Zir.Inst.Block, inst_data.payload_index); | 946 | const extra = self.code.extraData(Zir.Inst.Block, inst_data.payload_index); |
| 883 | const body = self.code.extra[extra.end..][0..extra.data.body_len]; | 947 | const body = self.code.extra[extra.end..][0..extra.data.body_len]; |
| 884 | try stream.writeAll("{\n"); | 948 | try self.writeBracedBody(stream, body); |
| 885 | self.indent += 2; | 949 | try stream.writeAll(") "); |
| 886 | try self.writeBody(stream, body); | ||
| 887 | self.indent -= 2; | ||
| 888 | try stream.writeByteNTimes(' ', self.indent); | ||
| 889 | try stream.writeAll("}) "); | ||
| 890 | } | 950 | } |
| 891 | 951 | ||
| 892 | fn writePlNodeCondBr(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void { | 952 | fn writePlNodeCondBr(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void { |
| ... | @@ -895,17 +955,11 @@ const Writer = struct { | ... | @@ -895,17 +955,11 @@ const Writer = struct { |
| 895 | const then_body = self.code.extra[extra.end..][0..extra.data.then_body_len]; | 955 | const then_body = self.code.extra[extra.end..][0..extra.data.then_body_len]; |
| 896 | const else_body = self.code.extra[extra.end + then_body.len ..][0..extra.data.else_body_len]; | 956 | const else_body = self.code.extra[extra.end + then_body.len ..][0..extra.data.else_body_len]; |
| 897 | try self.writeInstRef(stream, extra.data.condition); | 957 | try self.writeInstRef(stream, extra.data.condition); |
| 898 | try stream.writeAll(", {\n"); | 958 | try stream.writeAll(", "); |
| 899 | self.indent += 2; | 959 | try self.writeBracedBody(stream, then_body); |
| 900 | try self.writeBody(stream, then_body); | 960 | try stream.writeAll(", "); |
| 901 | self.indent -= 2; | 961 | try self.writeBracedBody(stream, else_body); |
| 902 | try stream.writeByteNTimes(' ', self.indent); | 962 | try stream.writeAll(") "); |
| 903 | try stream.writeAll("}, {\n"); | ||
| 904 | self.indent += 2; | ||
| 905 | try self.writeBody(stream, else_body); | ||
| 906 | self.indent -= 2; | ||
| 907 | try stream.writeByteNTimes(' ', self.indent); | ||
| 908 | try stream.writeAll("}) "); | ||
| 909 | try self.writeSrc(stream, inst_data.src()); | 963 | try self.writeSrc(stream, inst_data.src()); |
| 910 | } | 964 | } |
| 911 | 965 | ||
| ... | @@ -963,17 +1017,10 @@ const Writer = struct { | ... | @@ -963,17 +1017,10 @@ const Writer = struct { |
| 963 | } else { | 1017 | } else { |
| 964 | const prev_parent_decl_node = self.parent_decl_node; | 1018 | const prev_parent_decl_node = self.parent_decl_node; |
| 965 | if (src_node) |off| self.parent_decl_node = self.relativeToNodeIndex(off); | 1019 | if (src_node) |off| self.parent_decl_node = self.relativeToNodeIndex(off); |
| 966 | self.indent += 2; | 1020 | try self.writeBracedDecl(stream, body); |
| 967 | if (body.len == 0) { | 1021 | try stream.writeAll(", {\n"); |
| 968 | try stream.writeAll("{}, {\n"); | ||
| 969 | } else { | ||
| 970 | try stream.writeAll("{\n"); | ||
| 971 | try self.writeBody(stream, body); | ||
| 972 | |||
| 973 | try stream.writeByteNTimes(' ', self.indent - 2); | ||
| 974 | try stream.writeAll("}, {\n"); | ||
| 975 | } | ||
| 976 | 1022 | ||
| 1023 | self.indent += 2; | ||
| 977 | const bits_per_field = 4; | 1024 | const bits_per_field = 4; |
| 978 | const fields_per_u32 = 32 / bits_per_field; | 1025 | const fields_per_u32 = 32 / bits_per_field; |
| 979 | const bit_bags_count = std.math.divCeil(usize, fields_len, fields_per_u32) catch unreachable; | 1026 | const bit_bags_count = std.math.divCeil(usize, fields_len, fields_per_u32) catch unreachable; |
| ... | @@ -1096,17 +1143,10 @@ const Writer = struct { | ... | @@ -1096,17 +1143,10 @@ const Writer = struct { |
| 1096 | 1143 | ||
| 1097 | const prev_parent_decl_node = self.parent_decl_node; | 1144 | const prev_parent_decl_node = self.parent_decl_node; |
| 1098 | if (src_node) |off| self.parent_decl_node = self.relativeToNodeIndex(off); | 1145 | if (src_node) |off| self.parent_decl_node = self.relativeToNodeIndex(off); |
| 1099 | self.indent += 2; | 1146 | try self.writeBracedDecl(stream, body); |
| 1100 | if (body.len == 0) { | 1147 | try stream.writeAll(", {\n"); |
| 1101 | try stream.writeAll("{}, {\n"); | ||
| 1102 | } else { | ||
| 1103 | try stream.writeAll("{\n"); | ||
| 1104 | try self.writeBody(stream, body); | ||
| 1105 | |||
| 1106 | try stream.writeByteNTimes(' ', self.indent - 2); | ||
| 1107 | try stream.writeAll("}, {\n"); | ||
| 1108 | } | ||
| 1109 | 1148 | ||
| 1149 | self.indent += 2; | ||
| 1110 | const bits_per_field = 4; | 1150 | const bits_per_field = 4; |
| 1111 | const fields_per_u32 = 32 / bits_per_field; | 1151 | const fields_per_u32 = 32 / bits_per_field; |
| 1112 | const bit_bags_count = std.math.divCeil(usize, fields_len, fields_per_u32) catch unreachable; | 1152 | const bit_bags_count = std.math.divCeil(usize, fields_len, fields_per_u32) catch unreachable; |
| ... | @@ -1251,18 +1291,25 @@ const Writer = struct { | ... | @@ -1251,18 +1291,25 @@ const Writer = struct { |
| 1251 | try stream.writeAll(")"); | 1291 | try stream.writeAll(")"); |
| 1252 | } | 1292 | } |
| 1253 | } | 1293 | } |
| 1254 | const tag = self.code.instructions.items(.tag)[decl_index]; | ||
| 1255 | try stream.print(" line({d}) hash({}): %{d} = {s}(", .{ | ||
| 1256 | line, std.fmt.fmtSliceHexLower(&hash_bytes), decl_index, @tagName(tag), | ||
| 1257 | }); | ||
| 1258 | 1294 | ||
| 1259 | const decl_block_inst_data = self.code.instructions.items(.data)[decl_index].pl_node; | 1295 | if (self.recurse_decls) { |
| 1260 | const sub_decl_node_off = decl_block_inst_data.src_node; | 1296 | const tag = self.code.instructions.items(.tag)[decl_index]; |
| 1261 | self.parent_decl_node = self.relativeToNodeIndex(sub_decl_node_off); | 1297 | try stream.print(" line({d}) hash({}): %{d} = {s}(", .{ |
| 1262 | try self.writePlNodeBlockWithoutSrc(stream, decl_index); | 1298 | line, std.fmt.fmtSliceHexLower(&hash_bytes), decl_index, @tagName(tag), |
| 1263 | self.parent_decl_node = parent_decl_node; | 1299 | }); |
| 1264 | try self.writeSrc(stream, decl_block_inst_data.src()); | 1300 | |
| 1265 | try stream.writeAll("\n"); | 1301 | const decl_block_inst_data = self.code.instructions.items(.data)[decl_index].pl_node; |
| 1302 | const sub_decl_node_off = decl_block_inst_data.src_node; | ||
| 1303 | self.parent_decl_node = self.relativeToNodeIndex(sub_decl_node_off); | ||
| 1304 | try self.writePlNodeBlockWithoutSrc(stream, decl_index); | ||
| 1305 | self.parent_decl_node = parent_decl_node; | ||
| 1306 | try self.writeSrc(stream, decl_block_inst_data.src()); | ||
| 1307 | try stream.writeAll("\n"); | ||
| 1308 | } else { | ||
| 1309 | try stream.print(" line({d}) hash({}): %{d} = ...\n", .{ | ||
| 1310 | line, std.fmt.fmtSliceHexLower(&hash_bytes), decl_index, | ||
| 1311 | }); | ||
| 1312 | } | ||
| 1266 | } | 1313 | } |
| 1267 | return extra_index; | 1314 | return extra_index; |
| 1268 | } | 1315 | } |
| ... | @@ -1329,17 +1376,10 @@ const Writer = struct { | ... | @@ -1329,17 +1376,10 @@ const Writer = struct { |
| 1329 | } else { | 1376 | } else { |
| 1330 | const prev_parent_decl_node = self.parent_decl_node; | 1377 | const prev_parent_decl_node = self.parent_decl_node; |
| 1331 | if (src_node) |off| self.parent_decl_node = self.relativeToNodeIndex(off); | 1378 | if (src_node) |off| self.parent_decl_node = self.relativeToNodeIndex(off); |
| 1332 | self.indent += 2; | 1379 | try self.writeBracedDecl(stream, body); |
| 1333 | if (body.len == 0) { | 1380 | try stream.writeAll(", {\n"); |
| 1334 | try stream.writeAll("{}, {\n"); | ||
| 1335 | } else { | ||
| 1336 | try stream.writeAll("{\n"); | ||
| 1337 | try self.writeBody(stream, body); | ||
| 1338 | |||
| 1339 | try stream.writeByteNTimes(' ', self.indent - 2); | ||
| 1340 | try stream.writeAll("}, {\n"); | ||
| 1341 | } | ||
| 1342 | 1381 | ||
| 1382 | self.indent += 2; | ||
| 1343 | const bit_bags_count = std.math.divCeil(usize, fields_len, 32) catch unreachable; | 1383 | const bit_bags_count = std.math.divCeil(usize, fields_len, 32) catch unreachable; |
| 1344 | const body_end = extra_index; | 1384 | const body_end = extra_index; |
| 1345 | extra_index += bit_bags_count; | 1385 | extra_index += bit_bags_count; |
| ... | @@ -1463,18 +1503,18 @@ const Writer = struct { | ... | @@ -1463,18 +1503,18 @@ const Writer = struct { |
| 1463 | 1503 | ||
| 1464 | try self.writeInstRef(stream, extra.data.operand); | 1504 | try self.writeInstRef(stream, extra.data.operand); |
| 1465 | 1505 | ||
| 1506 | self.indent += 2; | ||
| 1507 | |||
| 1466 | if (special.body.len != 0) { | 1508 | if (special.body.len != 0) { |
| 1467 | const prong_name = switch (special_prong) { | 1509 | const prong_name = switch (special_prong) { |
| 1468 | .@"else" => "else", | 1510 | .@"else" => "else", |
| 1469 | .under => "_", | 1511 | .under => "_", |
| 1470 | else => unreachable, | 1512 | else => unreachable, |
| 1471 | }; | 1513 | }; |
| 1472 | try stream.print(", {s} => {{\n", .{prong_name}); | 1514 | try stream.writeAll(",\n"); |
| 1473 | self.indent += 2; | ||
| 1474 | try self.writeBody(stream, special.body); | ||
| 1475 | self.indent -= 2; | ||
| 1476 | try stream.writeByteNTimes(' ', self.indent); | 1515 | try stream.writeByteNTimes(' ', self.indent); |
| 1477 | try stream.writeAll("}"); | 1516 | try stream.print("{s} => ", .{prong_name}); |
| 1517 | try self.writeBracedBody(stream, special.body); | ||
| 1478 | } | 1518 | } |
| 1479 | 1519 | ||
| 1480 | var extra_index: usize = special.end; | 1520 | var extra_index: usize = special.end; |
| ... | @@ -1488,16 +1528,16 @@ const Writer = struct { | ... | @@ -1488,16 +1528,16 @@ const Writer = struct { |
| 1488 | const body = self.code.extra[extra_index..][0..body_len]; | 1528 | const body = self.code.extra[extra_index..][0..body_len]; |
| 1489 | extra_index += body_len; | 1529 | extra_index += body_len; |
| 1490 | 1530 | ||
| 1491 | try stream.writeAll(", "); | 1531 | try stream.writeAll(",\n"); |
| 1492 | try self.writeInstRef(stream, item_ref); | ||
| 1493 | try stream.writeAll(" => {\n"); | ||
| 1494 | self.indent += 2; | ||
| 1495 | try self.writeBody(stream, body); | ||
| 1496 | self.indent -= 2; | ||
| 1497 | try stream.writeByteNTimes(' ', self.indent); | 1532 | try stream.writeByteNTimes(' ', self.indent); |
| 1498 | try stream.writeAll("}"); | 1533 | try self.writeInstRef(stream, item_ref); |
| 1534 | try stream.writeAll(" => "); | ||
| 1535 | try self.writeBracedBody(stream, body); | ||
| 1499 | } | 1536 | } |
| 1500 | } | 1537 | } |
| 1538 | |||
| 1539 | self.indent -= 2; | ||
| 1540 | |||
| 1501 | try stream.writeAll(") "); | 1541 | try stream.writeAll(") "); |
| 1502 | try self.writeSrc(stream, inst_data.src()); | 1542 | try self.writeSrc(stream, inst_data.src()); |
| 1503 | } | 1543 | } |
| ... | @@ -1527,18 +1567,18 @@ const Writer = struct { | ... | @@ -1527,18 +1567,18 @@ const Writer = struct { |
| 1527 | 1567 | ||
| 1528 | try self.writeInstRef(stream, extra.data.operand); | 1568 | try self.writeInstRef(stream, extra.data.operand); |
| 1529 | 1569 | ||
| 1570 | self.indent += 2; | ||
| 1571 | |||
| 1530 | if (special.body.len != 0) { | 1572 | if (special.body.len != 0) { |
| 1531 | const prong_name = switch (special_prong) { | 1573 | const prong_name = switch (special_prong) { |
| 1532 | .@"else" => "else", | 1574 | .@"else" => "else", |
| 1533 | .under => "_", | 1575 | .under => "_", |
| 1534 | else => unreachable, | 1576 | else => unreachable, |
| 1535 | }; | 1577 | }; |
| 1536 | try stream.print(", {s} => {{\n", .{prong_name}); | 1578 | try stream.writeAll(",\n"); |
| 1537 | self.indent += 2; | ||
| 1538 | try self.writeBody(stream, special.body); | ||
| 1539 | self.indent -= 2; | ||
| 1540 | try stream.writeByteNTimes(' ', self.indent); | 1579 | try stream.writeByteNTimes(' ', self.indent); |
| 1541 | try stream.writeAll("}"); | 1580 | try stream.print("{s} => ", .{prong_name}); |
| 1581 | try self.writeBracedBody(stream, special.body); | ||
| 1542 | } | 1582 | } |
| 1543 | 1583 | ||
| 1544 | var extra_index: usize = special.end; | 1584 | var extra_index: usize = special.end; |
| ... | @@ -1552,14 +1592,11 @@ const Writer = struct { | ... | @@ -1552,14 +1592,11 @@ const Writer = struct { |
| 1552 | const body = self.code.extra[extra_index..][0..body_len]; | 1592 | const body = self.code.extra[extra_index..][0..body_len]; |
| 1553 | extra_index += body_len; | 1593 | extra_index += body_len; |
| 1554 | 1594 | ||
| 1555 | try stream.writeAll(", "); | 1595 | try stream.writeAll(",\n"); |
| 1556 | try self.writeInstRef(stream, item_ref); | ||
| 1557 | try stream.writeAll(" => {\n"); | ||
| 1558 | self.indent += 2; | ||
| 1559 | try self.writeBody(stream, body); | ||
| 1560 | self.indent -= 2; | ||
| 1561 | try stream.writeByteNTimes(' ', self.indent); | 1596 | try stream.writeByteNTimes(' ', self.indent); |
| 1562 | try stream.writeAll("}"); | 1597 | try self.writeInstRef(stream, item_ref); |
| 1598 | try stream.writeAll(" => "); | ||
| 1599 | try self.writeBracedBody(stream, body); | ||
| 1563 | } | 1600 | } |
| 1564 | } | 1601 | } |
| 1565 | { | 1602 | { |
| ... | @@ -1574,8 +1611,11 @@ const Writer = struct { | ... | @@ -1574,8 +1611,11 @@ const Writer = struct { |
| 1574 | const items = self.code.refSlice(extra_index, items_len); | 1611 | const items = self.code.refSlice(extra_index, items_len); |
| 1575 | extra_index += items_len; | 1612 | extra_index += items_len; |
| 1576 | 1613 | ||
| 1577 | for (items) |item_ref| { | 1614 | try stream.writeAll(",\n"); |
| 1578 | try stream.writeAll(", "); | 1615 | try stream.writeByteNTimes(' ', self.indent); |
| 1616 | |||
| 1617 | for (items) |item_ref, item_i| { | ||
| 1618 | if (item_i != 0) try stream.writeAll(", "); | ||
| 1579 | try self.writeInstRef(stream, item_ref); | 1619 | try self.writeInstRef(stream, item_ref); |
| 1580 | } | 1620 | } |
| 1581 | 1621 | ||
| ... | @@ -1586,7 +1626,9 @@ const Writer = struct { | ... | @@ -1586,7 +1626,9 @@ const Writer = struct { |
| 1586 | const item_last = @intToEnum(Zir.Inst.Ref, self.code.extra[extra_index]); | 1626 | const item_last = @intToEnum(Zir.Inst.Ref, self.code.extra[extra_index]); |
| 1587 | extra_index += 1; | 1627 | extra_index += 1; |
| 1588 | 1628 | ||
| 1589 | try stream.writeAll(", "); | 1629 | if (range_i != 0 or items.len != 0) { |
| 1630 | try stream.writeAll(", "); | ||
| 1631 | } | ||
| 1590 | try self.writeInstRef(stream, item_first); | 1632 | try self.writeInstRef(stream, item_first); |
| 1591 | try stream.writeAll("..."); | 1633 | try stream.writeAll("..."); |
| 1592 | try self.writeInstRef(stream, item_last); | 1634 | try self.writeInstRef(stream, item_last); |
| ... | @@ -1594,14 +1636,13 @@ const Writer = struct { | ... | @@ -1594,14 +1636,13 @@ const Writer = struct { |
| 1594 | 1636 | ||
| 1595 | const body = self.code.extra[extra_index..][0..body_len]; | 1637 | const body = self.code.extra[extra_index..][0..body_len]; |
| 1596 | extra_index += body_len; | 1638 | extra_index += body_len; |
| 1597 | try stream.writeAll(" => {\n"); | 1639 | try stream.writeAll(" => "); |
| 1598 | self.indent += 2; | 1640 | try self.writeBracedBody(stream, body); |
| 1599 | try self.writeBody(stream, body); | ||
| 1600 | self.indent -= 2; | ||
| 1601 | try stream.writeByteNTimes(' ', self.indent); | ||
| 1602 | try stream.writeAll("}"); | ||
| 1603 | } | 1641 | } |
| 1604 | } | 1642 | } |
| 1643 | |||
| 1644 | self.indent -= 2; | ||
| 1645 | |||
| 1605 | try stream.writeAll(") "); | 1646 | try stream.writeAll(") "); |
| 1606 | try self.writeSrc(stream, inst_data.src()); | 1647 | try self.writeSrc(stream, inst_data.src()); |
| 1607 | } | 1648 | } |
| ... | @@ -1796,12 +1837,8 @@ const Writer = struct { | ... | @@ -1796,12 +1837,8 @@ const Writer = struct { |
| 1796 | const extra = self.code.extraData(Zir.Inst.Block, inst_data.payload_index); | 1837 | const extra = self.code.extraData(Zir.Inst.Block, inst_data.payload_index); |
| 1797 | const body = self.code.extra[extra.end..][0..extra.data.body_len]; | 1838 | const body = self.code.extra[extra.end..][0..extra.data.body_len]; |
| 1798 | try self.writeInstRef(stream, inst_data.lhs); | 1839 | try self.writeInstRef(stream, inst_data.lhs); |
| 1799 | try stream.writeAll(", {\n"); | 1840 | try stream.writeAll(", "); |
| 1800 | self.indent += 2; | 1841 | try self.writeBracedBody(stream, body); |
| 1801 | try self.writeBody(stream, body); | ||
| 1802 | self.indent -= 2; | ||
| 1803 | try stream.writeByteNTimes(' ', self.indent); | ||
| 1804 | try stream.writeAll("})"); | ||
| 1805 | } | 1842 | } |
| 1806 | 1843 | ||
| 1807 | fn writeIntType(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void { | 1844 | fn writeIntType(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void { |
| ... | @@ -1846,12 +1883,8 @@ const Writer = struct { | ... | @@ -1846,12 +1883,8 @@ const Writer = struct { |
| 1846 | if (ret_ty_body.len == 0) { | 1883 | if (ret_ty_body.len == 0) { |
| 1847 | try stream.writeAll("ret_ty=void"); | 1884 | try stream.writeAll("ret_ty=void"); |
| 1848 | } else { | 1885 | } else { |
| 1849 | try stream.writeAll("ret_ty={\n"); | 1886 | try stream.writeAll("ret_ty="); |
| 1850 | self.indent += 2; | 1887 | try self.writeBracedBody(stream, ret_ty_body); |
| 1851 | try self.writeBody(stream, ret_ty_body); | ||
| 1852 | self.indent -= 2; | ||
| 1853 | try stream.writeByteNTimes(' ', self.indent); | ||
| 1854 | try stream.writeAll("}"); | ||
| 1855 | } | 1888 | } |
| 1856 | 1889 | ||
| 1857 | try self.writeOptionalInstRef(stream, ", cc=", cc); | 1890 | try self.writeOptionalInstRef(stream, ", cc=", cc); |
| ... | @@ -1860,16 +1893,9 @@ const Writer = struct { | ... | @@ -1860,16 +1893,9 @@ const Writer = struct { |
| 1860 | try self.writeFlag(stream, ", extern", is_extern); | 1893 | try self.writeFlag(stream, ", extern", is_extern); |
| 1861 | try self.writeFlag(stream, ", inferror", inferred_error_set); | 1894 | try self.writeFlag(stream, ", inferror", inferred_error_set); |
| 1862 | 1895 | ||
| 1863 | if (body.len == 0) { | 1896 | try stream.writeAll(", body="); |
| 1864 | try stream.writeAll(", body={}) "); | 1897 | try self.writeBracedBody(stream, body); |
| 1865 | } else { | 1898 | try stream.writeAll(") "); |
| 1866 | try stream.writeAll(", body={\n"); | ||
| 1867 | self.indent += 2; | ||
| 1868 | try self.writeBody(stream, body); | ||
| 1869 | self.indent -= 2; | ||
| 1870 | try stream.writeByteNTimes(' ', self.indent); | ||
| 1871 | try stream.writeAll("}) "); | ||
| 1872 | } | ||
| 1873 | if (body.len != 0) { | 1899 | if (body.len != 0) { |
| 1874 | try stream.print("(lbrace={d}:{d},rbrace={d}:{d}) ", .{ | 1900 | try stream.print("(lbrace={d}:{d},rbrace={d}:{d}) ", .{ |
| 1875 | src_locs.lbrace_line, @truncate(u16, src_locs.columns), | 1901 | src_locs.lbrace_line, @truncate(u16, src_locs.columns), |
| ... | @@ -1929,18 +1955,19 @@ const Writer = struct { | ... | @@ -1929,18 +1955,19 @@ const Writer = struct { |
| 1929 | } | 1955 | } |
| 1930 | 1956 | ||
| 1931 | fn writeSrc(self: *Writer, stream: anytype, src: LazySrcLoc) !void { | 1957 | fn writeSrc(self: *Writer, stream: anytype, src: LazySrcLoc) !void { |
| 1932 | const tree = self.file.tree; | 1958 | if (self.file.tree_loaded) { |
| 1933 | const src_loc: Module.SrcLoc = .{ | 1959 | const tree = self.file.tree; |
| 1934 | .file_scope = self.file, | 1960 | const src_loc: Module.SrcLoc = .{ |
| 1935 | .parent_decl_node = self.parent_decl_node, | 1961 | .file_scope = self.file, |
| 1936 | .lazy = src, | 1962 | .parent_decl_node = self.parent_decl_node, |
| 1937 | }; | 1963 | .lazy = src, |
| 1938 | // Caller must ensure AST tree is loaded. | 1964 | }; |
| 1939 | const abs_byte_off = src_loc.byteOffset(self.gpa) catch unreachable; | 1965 | const abs_byte_off = src_loc.byteOffset(self.gpa) catch unreachable; |
| 1940 | const delta_line = std.zig.findLineColumn(tree.source, abs_byte_off); | 1966 | const delta_line = std.zig.findLineColumn(tree.source, abs_byte_off); |
| 1941 | try stream.print("{s}:{d}:{d}", .{ | 1967 | try stream.print("{s}:{d}:{d}", .{ |
| 1942 | @tagName(src), delta_line.line + 1, delta_line.column + 1, | 1968 | @tagName(src), delta_line.line + 1, delta_line.column + 1, |
| 1943 | }); | 1969 | }); |
| 1970 | } | ||
| 1944 | } | 1971 | } |
| 1945 | 1972 | ||
| 1946 | fn writeSrcNode(self: *Writer, stream: anytype, src_node: ?i32) !void { | 1973 | fn writeSrcNode(self: *Writer, stream: anytype, src_node: ?i32) !void { |
| ... | @@ -1950,6 +1977,43 @@ const Writer = struct { | ... | @@ -1950,6 +1977,43 @@ const Writer = struct { |
| 1950 | return self.writeSrc(stream, src); | 1977 | return self.writeSrc(stream, src); |
| 1951 | } | 1978 | } |
| 1952 | 1979 | ||
| 1980 | fn writeBracedDecl(self: *Writer, stream: anytype, body: []const Zir.Inst.Index) !void { | ||
| 1981 | try self.writeBracedBodyConditional(stream, body, self.recurse_decls); | ||
| 1982 | } | ||
| 1983 | |||
| 1984 | fn writeBracedBody(self: *Writer, stream: anytype, body: []const Zir.Inst.Index) !void { | ||
| 1985 | try self.writeBracedBodyConditional(stream, body, self.recurse_blocks); | ||
| 1986 | } | ||
| 1987 | |||
| 1988 | fn writeBracedBodyConditional(self: *Writer, stream: anytype, body: []const Zir.Inst.Index, enabled: bool) !void { | ||
| 1989 | if (body.len == 0) { | ||
| 1990 | try stream.writeAll("{}"); | ||
| 1991 | } else if (enabled) { | ||
| 1992 | try stream.writeAll("{\n"); | ||
| 1993 | self.indent += 2; | ||
| 1994 | try self.writeBody(stream, body); | ||
| 1995 | self.indent -= 2; | ||
| 1996 | try stream.writeByteNTimes(' ', self.indent); | ||
| 1997 | try stream.writeAll("}"); | ||
| 1998 | } else if (body.len == 1) { | ||
| 1999 | try stream.writeByte('{'); | ||
| 2000 | try self.writeInstIndex(stream, body[0]); | ||
| 2001 | try stream.writeByte('}'); | ||
| 2002 | } else if (body.len == 2) { | ||
| 2003 | try stream.writeByte('{'); | ||
| 2004 | try self.writeInstIndex(stream, body[0]); | ||
| 2005 | try stream.writeAll(", "); | ||
| 2006 | try self.writeInstIndex(stream, body[1]); | ||
| 2007 | try stream.writeByte('}'); | ||
| 2008 | } else { | ||
| 2009 | try stream.writeByte('{'); | ||
| 2010 | try self.writeInstIndex(stream, body[0]); | ||
| 2011 | try stream.writeAll(".."); | ||
| 2012 | try self.writeInstIndex(stream, body[body.len - 1]); | ||
| 2013 | try stream.writeByte('}'); | ||
| 2014 | } | ||
| 2015 | } | ||
| 2016 | |||
| 1953 | fn writeBody(self: *Writer, stream: anytype, body: []const Zir.Inst.Index) !void { | 2017 | fn writeBody(self: *Writer, stream: anytype, body: []const Zir.Inst.Index) !void { |
| 1954 | for (body) |inst| { | 2018 | for (body) |inst| { |
| 1955 | try stream.writeByteNTimes(' ', self.indent); | 2019 | try stream.writeByteNTimes(' ', self.indent); |