authorgravatar for goon.pri.low@gmail.comKendall Condon <goon.pri.low@gmail.com> 2026-04-20 17:44:52-04:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2026-05-25 18:37:24-07:00
logc805c7228978df4af34ee0d63948234fb0ab504e
tree83d7226098fc20c43d90bae9a96bde731594924a
parente1ce81eb546e10ae505b411b6882266d1834fce1

upgrade most uses of DebugAllocator to SafeAllocator


9 files changed, 74 insertions(+), 74 deletions(-)

build.zig+1-1
......@@ -183,7 +183,7 @@ pub fn build(b: *std.Build) !void {
183183 const tracy_callstack = b.option(bool, "tracy-callstack", "Include callstack information with Tracy data. Does nothing if -Dtracy is not provided") orelse (tracy != null);
184184 const tracy_allocation = b.option(bool, "tracy-allocation", "Include allocation information with Tracy data. Does nothing if -Dtracy is not provided") orelse (tracy != null);
185185 const tracy_callstack_depth: u32 = b.option(u32, "tracy-callstack-depth", "Declare callstack depth for Tracy data. Does nothing if -Dtracy_callstack is not provided") orelse 10;
186 const debug_gpa = b.option(bool, "debug-allocator", "Force the compiler to use DebugAllocator") orelse false;
186 const debug_gpa = b.option(bool, "debug-allocator", "Force the compiler to use SafeAllocator") orelse false;
187187 const link_libc = b.option(bool, "force-link-libc", "Force self-hosted compiler to link libc") orelse (enable_llvm or only_c);
188188 const sanitize_thread = b.option(bool, "sanitize-thread", "Enable thread-sanitization") orelse false;
189189 const strip = b.option(bool, "strip", "Omit debug information");
lib/compiler/build_runner.zig+3-3
......@@ -26,9 +26,9 @@ pub const std_options: std.Options = .{
2626pub fn main(init: process.Init.Minimal) !void {
2727 // The build runner is often short-lived, but thanks to `--watch` and `--webui`, that's not
2828 // always the case. So, we do need a true gpa for some things.
29 var debug_gpa_state: std.heap.DebugAllocator(.{}) = .init;
30 defer _ = debug_gpa_state.deinit();
31 const gpa = debug_gpa_state.allocator();
29 var safe_gpa_state: std.heap.SafeAllocator = .init(std.heap.page_allocator, .{});
30 defer _ = safe_gpa_state.deinit();
31 const gpa = safe_gpa_state.allocator();
3232
3333 var threaded: std.Io.Threaded = .init(gpa, .{
3434 .environ = init.environ,
lib/compiler/test_runner.zig+24-14
......@@ -91,8 +91,8 @@ fn mainServer(init: std.process.Init.Minimal) !void {
9191 return std.process.exit(0);
9292 },
9393 .query_test_metadata => {
94 testing.allocator_instance = .{};
95 defer if (testing.allocator_instance.deinit() == .leak) {
94 testing.allocator_instance = .init(std.heap.page_allocator, .{});
95 defer if (testing.allocator_instance.deinit() != 0) {
9696 @panic("internal test runner memory leak");
9797 };
9898
......@@ -123,7 +123,10 @@ fn mainServer(init: std.process.Init.Minimal) !void {
123123
124124 .run_test => {
125125 testing.environ = init.environ;
126 testing.allocator_instance = .{};
126 testing.allocator_instance = .init(std.heap.page_allocator, .{
127 .canary = 0xc3a701ba,
128 .check_write_after_free = true,
129 });
127130 testing.io_instance = .init(testing.allocator, .{
128131 .argv0 = .init(init.args),
129132 .environ = init.environ,
......@@ -150,8 +153,7 @@ fn mainServer(init: std.process.Init.Minimal) !void {
150153 },
151154 };
152155 testing.io_instance.deinit();
153 const leak_count = testing.allocator_instance.detectLeaks();
154 testing.allocator_instance.deinitWithoutLeakChecks();
156 const leak_count = testing.allocator_instance.deinit();
155157 try server.serveTestResults(.{
156158 .index = index,
157159 .flags = .{
......@@ -173,8 +175,8 @@ fn mainServer(init: std.process.Init.Minimal) !void {
173175 // since they are not present.
174176 if (!builtin.fuzz) unreachable;
175177
176 var gpa_instance: std.heap.DebugAllocator(.{}) = .init;
177 defer if (gpa_instance.deinit() == .leak) {
178 var gpa_instance: std.heap.SafeAllocator = .init(std.heap.page_allocator, .{});
179 defer if (gpa_instance.deinit() != 0) {
178180 @panic("internal test runner memory leak");
179181 };
180182 const gpa = gpa_instance.allocator();
......@@ -271,14 +273,17 @@ fn mainTerminal(init: std.process.Init.Minimal) void {
271273
272274 var leaks: usize = 0;
273275 for (test_fn_list, 0..) |test_fn, i| {
274 testing.allocator_instance = .{};
276 testing.allocator_instance = .init(std.heap.page_allocator, .{
277 .canary = 0xc3a701ba,
278 .check_write_after_free = true,
279 });
275280 testing.io_instance = .init(testing.allocator, .{
276281 .argv0 = .init(init.args),
277282 .environ = init.environ,
278283 });
279284 defer {
280285 testing.io_instance.deinit();
281 if (testing.allocator_instance.deinit() == .leak) leaks += 1;
286 if (testing.allocator_instance.deinit() != 0) leaks += 1;
282287 }
283288 testing.log_level = .warn;
284289 testing.environ = init.environ;
......@@ -430,8 +435,11 @@ var fuzz_runner: if (builtin.fuzz) struct {
430435 error.WriteFailed => panic("failed to write to stdout: {t}", .{stdout_writer.err.?}),
431436 };
432437
433 testing.allocator_instance = .{};
434 defer if (testing.allocator_instance.deinit() == .leak) std.process.exit(1);
438 testing.allocator_instance = .init(std.heap.page_allocator, .{
439 .canary = 0xc3a701ba,
440 .check_write_after_free = true,
441 });
442 defer if (testing.allocator_instance.deinit() != 0) std.process.exit(1);
435443 is_fuzz_test = false;
436444
437445 builtin.test_functions[fuzz_runner.indexes[i]].func() catch |err| switch (err) {
......@@ -554,8 +562,11 @@ pub fn fuzz(
554562
555563 fn test_one() callconv(.c) bool {
556564 @disableInstrumentation();
557 testing.allocator_instance = .{};
558 defer if (testing.allocator_instance.deinit() == .leak) std.process.exit(1);
565 testing.allocator_instance = .init(std.heap.page_allocator, .{
566 .canary = 0xcacce5e0,
567 .check_write_after_free = true,
568 });
569 defer if (testing.allocator_instance.deinit() != 0) std.process.exit(1);
559570 log_err_count = 0;
560571 testOne(ctx, @constCast(&testing.Smith{ .in = null })) catch |err| switch (err) {
561572 error.SkipZigTest => return true,
......@@ -582,7 +593,6 @@ pub fn fuzz(
582593 if (builtin.fuzz) {
583594 // Preserve the calling test's allocator state
584595 const prev_allocator_state = testing.allocator_instance;
585 testing.allocator_instance = .{};
586596 defer testing.allocator_instance = prev_allocator_state;
587597
588598 global.ctx = context;
lib/fuzzer.zig+3-3
......@@ -39,10 +39,10 @@ fn logOverride(
3939 fw.interface.flush() catch panic("failed to write to fuzzer log: {t}", .{fw.err.?});
4040}
4141
42var debug_allocator: std.heap.DebugAllocator(.{}) = .init;
42var safe_allocator: std.heap.SafeAllocator = .init(std.heap.page_allocator, .{});
4343const gpa = switch (builtin.mode) {
44 .Debug => debug_allocator.allocator(),
45 .ReleaseFast, .ReleaseSmall, .ReleaseSafe => std.heap.smp_allocator,
44 .Debug, .ReleaseSafe => safe_allocator.allocator(),
45 .ReleaseFast, .ReleaseSmall => std.heap.smp_allocator,
4646};
4747
4848// Seperate from `exec` to allow initialization before `exec` is.
lib/std/heap/ArenaAllocator.zig+3-5
......@@ -671,12 +671,10 @@ test "reset while retaining a buffer" {
671671
672672 // Create two internal buffers
673673 _ = try a.alloc(u8, 1);
674 _ = try a.alloc(u8, 1000);
675
676674 try std.testing.expect(arena_allocator.state.used_list != null);
677
678 // Check that we have at least two buffers
679 try std.testing.expect(arena_allocator.state.used_list.?.next != null);
675 while (arena_allocator.state.used_list.?.next == null) {
676 _ = try a.alloc(u8, 1000);
677 }
680678
681679 // This retains the first allocated buffer
682680 try std.testing.expect(arena_allocator.reset(.{ .retain_with_limit = 2 }));
lib/std/mem.zig+12-6
......@@ -201,9 +201,12 @@ test "Allocator.resize" {
201201 defer testing.allocator.free(values);
202202
203203 for (values, 0..) |*v, i| v.* = @as(T, @intCast(i));
204 if (!testing.allocator.resize(values, values.len + 10)) return error.OutOfMemory;
205 values = values.ptr[0 .. values.len + 10];
206 try testing.expect(values.len == 110);
204 if (testing.allocator.resize(values, values.len + 10)) {
205 values = values.ptr[0 .. values.len + 10];
206 try testing.expect(values.len == 110);
207 } else {
208 // `resize` is not guaranteed to succeed even if there is sufficient memory.
209 }
207210 }
208211
209212 const primitiveFloatTypes = .{
......@@ -217,9 +220,12 @@ test "Allocator.resize" {
217220 defer testing.allocator.free(values);
218221
219222 for (values, 0..) |*v, i| v.* = @as(T, @floatFromInt(i));
220 if (!testing.allocator.resize(values, values.len + 10)) return error.OutOfMemory;
221 values = values.ptr[0 .. values.len + 10];
222 try testing.expect(values.len == 110);
223 if (testing.allocator.resize(values, values.len + 10)) {
224 values = values.ptr[0 .. values.len + 10];
225 try testing.expect(values.len == 110);
226 } else {
227 // `resize` is not guaranteed to succeed even if there is sufficient memory.
228 }
223229 }
224230}
225231
lib/std/start.zig+7-8
......@@ -710,12 +710,11 @@ fn mainWithoutEnv(c_argc: c_int, c_argv: [*][*:0]c_char) callconv(.c) c_int {
710710/// General error message for a malformed return type
711711const bad_main_ret = "expected return type of main to be 'void', '!void', 'noreturn', 'u8', or '!u8'";
712712
713const use_debug_allocator = !is_wasm and switch (builtin.mode) {
714 .Debug => true,
715 .ReleaseSafe => !builtin.link_libc, // Not ideal, but the best we have for now.
713const use_safe_allocator = !is_wasm and switch (builtin.mode) {
714 .Debug, .ReleaseSafe => true,
716715 .ReleaseFast, .ReleaseSmall => !builtin.link_libc and builtin.single_threaded, // Also not ideal.
717716};
718var debug_allocator: std.heap.DebugAllocator(.{}) = .init;
717var safe_allocator: std.heap.SafeAllocator = .init(std.heap.page_allocator, .{});
719718
720719inline fn callMain(args: std.process.Args.Vector, environ: std.process.Environ.Block) u8 {
721720 const fn_info = @typeInfo(@TypeOf(root.main)).@"fn";
......@@ -725,8 +724,8 @@ inline fn callMain(args: std.process.Args.Vector, environ: std.process.Environ.B
725724 .environ = .{ .block = environ },
726725 }));
727726
728 const gpa = if (use_debug_allocator)
729 debug_allocator.allocator()
727 const gpa = if (use_safe_allocator)
728 safe_allocator.allocator()
730729 else if (builtin.link_libc)
731730 std.heap.c_allocator
732731 else if (is_wasm)
......@@ -736,8 +735,8 @@ inline fn callMain(args: std.process.Args.Vector, environ: std.process.Environ.B
736735 else
737736 comptime unreachable;
738737
739 defer if (use_debug_allocator) {
740 _ = debug_allocator.deinit(); // Leaks do not affect return code.
738 defer if (use_safe_allocator) {
739 _ = safe_allocator.deinit(); // Leaks do not affect return code.
741740 };
742741
743742 const arena_backing_allocator = if (is_wasm) gpa else std.heap.page_allocator;
lib/std/testing.zig+5-13
......@@ -17,19 +17,11 @@ var failing_allocator_instance = FailingAllocator.init(base_allocator_instance.a
1717});
1818var base_allocator_instance = std.heap.FixedBufferAllocator.init("");
1919
20/// This should only be used in temporary test programs.
21pub const allocator = allocator_instance.allocator();
22pub var allocator_instance: std.heap.DebugAllocator(.{
23 .stack_trace_frames = if (std.debug.sys_can_stack_trace) 10 else 0,
24 .resize_stack_traces = true,
25 // A unique value so that when a default-constructed
26 // DebugAllocator is incorrectly passed to testing allocator, or
27 // vice versa, panic occurs.
28 .canary = @truncate(0x2731e675c3a701ba),
29}) = b: {
30 if (!builtin.is_test) @compileError("testing allocator used when not testing");
31 break :b .init;
32};
20pub var allocator_instance: std.heap.SafeAllocator = undefined;
21pub const allocator = if (builtin.is_test)
22 allocator_instance.allocator()
23else
24 @compileError("not testing");
3325
3426pub var io_instance: Io.Threaded = undefined;
3527pub const io = if (builtin.is_test) io_instance.io() else @compileError("not testing");
src/main.zig+16-21
......@@ -157,34 +157,29 @@ pub fn log(
157157 std.log.defaultLog(level, scope, format, args);
158158}
159159
160const use_debug_allocator = build_options.debug_gpa or
160const use_safe_allocator = build_options.debug_gpa or
161161 (native_os != .wasi and !builtin.link_libc and switch (builtin.mode) {
162162 .Debug, .ReleaseSafe => true,
163163 .ReleaseFast, .ReleaseSmall => false,
164164 });
165165
166const RootAllocator = if (use_debug_allocator) std.heap.DebugAllocator(.{
166// TODO: The `align(@alignOf(std.heap.SafeAllocator))` can be removed the next time zig1.wasm is updated
167var safe_allocator: std.heap.SafeAllocator align(@alignOf(std.heap.SafeAllocator)) = .init(std.heap.page_allocator, .{
167168 .stack_trace_frames = build_options.mem_leak_frames,
168 .thread_safe = switch (build_options.io_mode) {
169 .threaded => true,
170 .evented => false,
171 },
172}) else struct {
173 pub const init: RootAllocator = .{};
174 pub fn allocator(_: RootAllocator) Allocator {
175 if (native_os == .wasi) return std.heap.wasm_allocator;
176 if (builtin.link_libc) return std.heap.c_allocator;
177 return std.heap.smp_allocator;
178 }
179 pub fn deinit(_: RootAllocator) std.heap.Check {
180 return .ok;
181 }
182};
169});
183170
184171pub fn main(init: std.process.Init.Minimal) anyerror!void {
185 var root_allocator: RootAllocator = .init;
186 defer _ = root_allocator.deinit();
187 const root_gpa = root_allocator.allocator();
172 const root_gpa = if (use_safe_allocator)
173 safe_allocator.allocator()
174 else if (native_os == .wasi)
175 std.heap.wasm_allocator
176 else if (builtin.link_libc)
177 std.heap.c_allocator
178 else
179 std.heap.smp_allocator;
180 defer if (use_safe_allocator) {
181 _ = safe_allocator.deinit();
182 };
188183 var io_impl: IoImpl = undefined;
189184 switch (build_options.io_mode) {
190185 .threaded => io_impl = .init(root_gpa, .{
......@@ -197,7 +192,7 @@ pub fn main(init: std.process.Init.Minimal) anyerror!void {
197192 .argv0 = .init(init.args),
198193 .environ = init.environ,
199194
200 .backing_allocator_needs_mutex = use_debug_allocator,
195 .backing_allocator_needs_mutex = false,
201196 }),
202197 }
203198 defer io_impl.deinit();