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 {...@@ -183,7 +183,7 @@ pub fn build(b: *std.Build) !void {
183 const tracy_callstack = b.option(bool, "tracy-callstack", "Include callstack information with Tracy data. Does nothing if -Dtracy is not provided") orelse (tracy != null);183 const tracy_callstack = b.option(bool, "tracy-callstack", "Include callstack information with Tracy data. Does nothing if -Dtracy is not provided") orelse (tracy != null);
184 const tracy_allocation = b.option(bool, "tracy-allocation", "Include allocation information with Tracy data. Does nothing if -Dtracy is not provided") orelse (tracy != null);184 const tracy_allocation = b.option(bool, "tracy-allocation", "Include allocation information with Tracy data. Does nothing if -Dtracy is not provided") orelse (tracy != null);
185 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;185 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;
187 const link_libc = b.option(bool, "force-link-libc", "Force self-hosted compiler to link libc") orelse (enable_llvm or only_c);187 const link_libc = b.option(bool, "force-link-libc", "Force self-hosted compiler to link libc") orelse (enable_llvm or only_c);
188 const sanitize_thread = b.option(bool, "sanitize-thread", "Enable thread-sanitization") orelse false;188 const sanitize_thread = b.option(bool, "sanitize-thread", "Enable thread-sanitization") orelse false;
189 const strip = b.option(bool, "strip", "Omit debug information");189 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 = .{...@@ -26,9 +26,9 @@ pub const std_options: std.Options = .{
26pub fn main(init: process.Init.Minimal) !void {26pub fn main(init: process.Init.Minimal) !void {
27 // The build runner is often short-lived, but thanks to `--watch` and `--webui`, that's not27 // The build runner is often short-lived, but thanks to `--watch` and `--webui`, that's not
28 // always the case. So, we do need a true gpa for some things.28 // always the case. So, we do need a true gpa for some things.
29 var debug_gpa_state: std.heap.DebugAllocator(.{}) = .init;29 var safe_gpa_state: std.heap.SafeAllocator = .init(std.heap.page_allocator, .{});
30 defer _ = debug_gpa_state.deinit();30 defer _ = safe_gpa_state.deinit();
31 const gpa = debug_gpa_state.allocator();31 const gpa = safe_gpa_state.allocator();
3232
33 var threaded: std.Io.Threaded = .init(gpa, .{33 var threaded: std.Io.Threaded = .init(gpa, .{
34 .environ = init.environ,34 .environ = init.environ,
lib/compiler/test_runner.zig+24-14
...@@ -91,8 +91,8 @@ fn mainServer(init: std.process.Init.Minimal) !void {...@@ -91,8 +91,8 @@ fn mainServer(init: std.process.Init.Minimal) !void {
91 return std.process.exit(0);91 return std.process.exit(0);
92 },92 },
93 .query_test_metadata => {93 .query_test_metadata => {
94 testing.allocator_instance = .{};94 testing.allocator_instance = .init(std.heap.page_allocator, .{});
95 defer if (testing.allocator_instance.deinit() == .leak) {95 defer if (testing.allocator_instance.deinit() != 0) {
96 @panic("internal test runner memory leak");96 @panic("internal test runner memory leak");
97 };97 };
9898
...@@ -123,7 +123,10 @@ fn mainServer(init: std.process.Init.Minimal) !void {...@@ -123,7 +123,10 @@ fn mainServer(init: std.process.Init.Minimal) !void {
123123
124 .run_test => {124 .run_test => {
125 testing.environ = init.environ;125 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 });
127 testing.io_instance = .init(testing.allocator, .{130 testing.io_instance = .init(testing.allocator, .{
128 .argv0 = .init(init.args),131 .argv0 = .init(init.args),
129 .environ = init.environ,132 .environ = init.environ,
...@@ -150,8 +153,7 @@ fn mainServer(init: std.process.Init.Minimal) !void {...@@ -150,8 +153,7 @@ fn mainServer(init: std.process.Init.Minimal) !void {
150 },153 },
151 };154 };
152 testing.io_instance.deinit();155 testing.io_instance.deinit();
153 const leak_count = testing.allocator_instance.detectLeaks();156 const leak_count = testing.allocator_instance.deinit();
154 testing.allocator_instance.deinitWithoutLeakChecks();
155 try server.serveTestResults(.{157 try server.serveTestResults(.{
156 .index = index,158 .index = index,
157 .flags = .{159 .flags = .{
...@@ -173,8 +175,8 @@ fn mainServer(init: std.process.Init.Minimal) !void {...@@ -173,8 +175,8 @@ fn mainServer(init: std.process.Init.Minimal) !void {
173 // since they are not present.175 // since they are not present.
174 if (!builtin.fuzz) unreachable;176 if (!builtin.fuzz) unreachable;
175177
176 var gpa_instance: std.heap.DebugAllocator(.{}) = .init;178 var gpa_instance: std.heap.SafeAllocator = .init(std.heap.page_allocator, .{});
177 defer if (gpa_instance.deinit() == .leak) {179 defer if (gpa_instance.deinit() != 0) {
178 @panic("internal test runner memory leak");180 @panic("internal test runner memory leak");
179 };181 };
180 const gpa = gpa_instance.allocator();182 const gpa = gpa_instance.allocator();
...@@ -271,14 +273,17 @@ fn mainTerminal(init: std.process.Init.Minimal) void {...@@ -271,14 +273,17 @@ fn mainTerminal(init: std.process.Init.Minimal) void {
271273
272 var leaks: usize = 0;274 var leaks: usize = 0;
273 for (test_fn_list, 0..) |test_fn, i| {275 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 });
275 testing.io_instance = .init(testing.allocator, .{280 testing.io_instance = .init(testing.allocator, .{
276 .argv0 = .init(init.args),281 .argv0 = .init(init.args),
277 .environ = init.environ,282 .environ = init.environ,
278 });283 });
279 defer {284 defer {
280 testing.io_instance.deinit();285 testing.io_instance.deinit();
281 if (testing.allocator_instance.deinit() == .leak) leaks += 1;286 if (testing.allocator_instance.deinit() != 0) leaks += 1;
282 }287 }
283 testing.log_level = .warn;288 testing.log_level = .warn;
284 testing.environ = init.environ;289 testing.environ = init.environ;
...@@ -430,8 +435,11 @@ var fuzz_runner: if (builtin.fuzz) struct {...@@ -430,8 +435,11 @@ var fuzz_runner: if (builtin.fuzz) struct {
430 error.WriteFailed => panic("failed to write to stdout: {t}", .{stdout_writer.err.?}),435 error.WriteFailed => panic("failed to write to stdout: {t}", .{stdout_writer.err.?}),
431 };436 };
432437
433 testing.allocator_instance = .{};438 testing.allocator_instance = .init(std.heap.page_allocator, .{
434 defer if (testing.allocator_instance.deinit() == .leak) std.process.exit(1);439 .canary = 0xc3a701ba,
440 .check_write_after_free = true,
441 });
442 defer if (testing.allocator_instance.deinit() != 0) std.process.exit(1);
435 is_fuzz_test = false;443 is_fuzz_test = false;
436444
437 builtin.test_functions[fuzz_runner.indexes[i]].func() catch |err| switch (err) {445 builtin.test_functions[fuzz_runner.indexes[i]].func() catch |err| switch (err) {
...@@ -554,8 +562,11 @@ pub fn fuzz(...@@ -554,8 +562,11 @@ pub fn fuzz(
554562
555 fn test_one() callconv(.c) bool {563 fn test_one() callconv(.c) bool {
556 @disableInstrumentation();564 @disableInstrumentation();
557 testing.allocator_instance = .{};565 testing.allocator_instance = .init(std.heap.page_allocator, .{
558 defer if (testing.allocator_instance.deinit() == .leak) std.process.exit(1);566 .canary = 0xcacce5e0,
567 .check_write_after_free = true,
568 });
569 defer if (testing.allocator_instance.deinit() != 0) std.process.exit(1);
559 log_err_count = 0;570 log_err_count = 0;
560 testOne(ctx, @constCast(&testing.Smith{ .in = null })) catch |err| switch (err) {571 testOne(ctx, @constCast(&testing.Smith{ .in = null })) catch |err| switch (err) {
561 error.SkipZigTest => return true,572 error.SkipZigTest => return true,
...@@ -582,7 +593,6 @@ pub fn fuzz(...@@ -582,7 +593,6 @@ pub fn fuzz(
582 if (builtin.fuzz) {593 if (builtin.fuzz) {
583 // Preserve the calling test's allocator state594 // Preserve the calling test's allocator state
584 const prev_allocator_state = testing.allocator_instance;595 const prev_allocator_state = testing.allocator_instance;
585 testing.allocator_instance = .{};
586 defer testing.allocator_instance = prev_allocator_state;596 defer testing.allocator_instance = prev_allocator_state;
587597
588 global.ctx = context;598 global.ctx = context;
lib/fuzzer.zig+3-3
...@@ -39,10 +39,10 @@ fn logOverride(...@@ -39,10 +39,10 @@ fn logOverride(
39 fw.interface.flush() catch panic("failed to write to fuzzer log: {t}", .{fw.err.?});39 fw.interface.flush() catch panic("failed to write to fuzzer log: {t}", .{fw.err.?});
40}40}
4141
42var debug_allocator: std.heap.DebugAllocator(.{}) = .init;42var safe_allocator: std.heap.SafeAllocator = .init(std.heap.page_allocator, .{});
43const gpa = switch (builtin.mode) {43const gpa = switch (builtin.mode) {
44 .Debug => debug_allocator.allocator(),44 .Debug, .ReleaseSafe => safe_allocator.allocator(),
45 .ReleaseFast, .ReleaseSmall, .ReleaseSafe => std.heap.smp_allocator,45 .ReleaseFast, .ReleaseSmall => std.heap.smp_allocator,
46};46};
4747
48// Seperate from `exec` to allow initialization before `exec` is.48// 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" {...@@ -671,12 +671,10 @@ test "reset while retaining a buffer" {
671671
672 // Create two internal buffers672 // Create two internal buffers
673 _ = try a.alloc(u8, 1);673 _ = try a.alloc(u8, 1);
674 _ = try a.alloc(u8, 1000);
675
676 try std.testing.expect(arena_allocator.state.used_list != null);674 try std.testing.expect(arena_allocator.state.used_list != null);
677675 while (arena_allocator.state.used_list.?.next == null) {
678 // Check that we have at least two buffers676 _ = try a.alloc(u8, 1000);
679 try std.testing.expect(arena_allocator.state.used_list.?.next != null);677 }
680678
681 // This retains the first allocated buffer679 // This retains the first allocated buffer
682 try std.testing.expect(arena_allocator.reset(.{ .retain_with_limit = 2 }));680 try std.testing.expect(arena_allocator.reset(.{ .retain_with_limit = 2 }));
lib/std/mem.zig+12-6
...@@ -201,9 +201,12 @@ test "Allocator.resize" {...@@ -201,9 +201,12 @@ test "Allocator.resize" {
201 defer testing.allocator.free(values);201 defer testing.allocator.free(values);
202202
203 for (values, 0..) |*v, i| v.* = @as(T, @intCast(i));203 for (values, 0..) |*v, i| v.* = @as(T, @intCast(i));
204 if (!testing.allocator.resize(values, values.len + 10)) return error.OutOfMemory;204 if (testing.allocator.resize(values, values.len + 10)) {
205 values = values.ptr[0 .. values.len + 10];205 values = values.ptr[0 .. values.len + 10];
206 try testing.expect(values.len == 110);206 try testing.expect(values.len == 110);
207 } else {
208 // `resize` is not guaranteed to succeed even if there is sufficient memory.
209 }
207 }210 }
208211
209 const primitiveFloatTypes = .{212 const primitiveFloatTypes = .{
...@@ -217,9 +220,12 @@ test "Allocator.resize" {...@@ -217,9 +220,12 @@ test "Allocator.resize" {
217 defer testing.allocator.free(values);220 defer testing.allocator.free(values);
218221
219 for (values, 0..) |*v, i| v.* = @as(T, @floatFromInt(i));222 for (values, 0..) |*v, i| v.* = @as(T, @floatFromInt(i));
220 if (!testing.allocator.resize(values, values.len + 10)) return error.OutOfMemory;223 if (testing.allocator.resize(values, values.len + 10)) {
221 values = values.ptr[0 .. values.len + 10];224 values = values.ptr[0 .. values.len + 10];
222 try testing.expect(values.len == 110);225 try testing.expect(values.len == 110);
226 } else {
227 // `resize` is not guaranteed to succeed even if there is sufficient memory.
228 }
223 }229 }
224}230}
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 {...@@ -710,12 +710,11 @@ fn mainWithoutEnv(c_argc: c_int, c_argv: [*][*:0]c_char) callconv(.c) c_int {
710/// General error message for a malformed return type710/// General error message for a malformed return type
711const bad_main_ret = "expected return type of main to be 'void', '!void', 'noreturn', 'u8', or '!u8'";711const 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) {713const use_safe_allocator = !is_wasm and switch (builtin.mode) {
714 .Debug => true,714 .Debug, .ReleaseSafe => true,
715 .ReleaseSafe => !builtin.link_libc, // Not ideal, but the best we have for now.
716 .ReleaseFast, .ReleaseSmall => !builtin.link_libc and builtin.single_threaded, // Also not ideal.715 .ReleaseFast, .ReleaseSmall => !builtin.link_libc and builtin.single_threaded, // Also not ideal.
717};716};
718var debug_allocator: std.heap.DebugAllocator(.{}) = .init;717var safe_allocator: std.heap.SafeAllocator = .init(std.heap.page_allocator, .{});
719718
720inline fn callMain(args: std.process.Args.Vector, environ: std.process.Environ.Block) u8 {719inline fn callMain(args: std.process.Args.Vector, environ: std.process.Environ.Block) u8 {
721 const fn_info = @typeInfo(@TypeOf(root.main)).@"fn";720 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...@@ -725,8 +724,8 @@ inline fn callMain(args: std.process.Args.Vector, environ: std.process.Environ.B
725 .environ = .{ .block = environ },724 .environ = .{ .block = environ },
726 }));725 }));
727726
728 const gpa = if (use_debug_allocator)727 const gpa = if (use_safe_allocator)
729 debug_allocator.allocator()728 safe_allocator.allocator()
730 else if (builtin.link_libc)729 else if (builtin.link_libc)
731 std.heap.c_allocator730 std.heap.c_allocator
732 else if (is_wasm)731 else if (is_wasm)
...@@ -736,8 +735,8 @@ inline fn callMain(args: std.process.Args.Vector, environ: std.process.Environ.B...@@ -736,8 +735,8 @@ inline fn callMain(args: std.process.Args.Vector, environ: std.process.Environ.B
736 else735 else
737 comptime unreachable;736 comptime unreachable;
738737
739 defer if (use_debug_allocator) {738 defer if (use_safe_allocator) {
740 _ = debug_allocator.deinit(); // Leaks do not affect return code.739 _ = safe_allocator.deinit(); // Leaks do not affect return code.
741 };740 };
742741
743 const arena_backing_allocator = if (is_wasm) gpa else std.heap.page_allocator;742 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...@@ -17,19 +17,11 @@ var failing_allocator_instance = FailingAllocator.init(base_allocator_instance.a
17});17});
18var base_allocator_instance = std.heap.FixedBufferAllocator.init("");18var base_allocator_instance = std.heap.FixedBufferAllocator.init("");
1919
20/// This should only be used in temporary test programs.20pub var allocator_instance: std.heap.SafeAllocator = undefined;
21pub const allocator = allocator_instance.allocator();21pub const allocator = if (builtin.is_test)
22pub var allocator_instance: std.heap.DebugAllocator(.{22 allocator_instance.allocator()
23 .stack_trace_frames = if (std.debug.sys_can_stack_trace) 10 else 0,23else
24 .resize_stack_traces = true,24 @compileError("not testing");
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};
3325
34pub var io_instance: Io.Threaded = undefined;26pub var io_instance: Io.Threaded = undefined;
35pub const io = if (builtin.is_test) io_instance.io() else @compileError("not testing");27pub 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(...@@ -157,34 +157,29 @@ pub fn log(
157 std.log.defaultLog(level, scope, format, args);157 std.log.defaultLog(level, scope, format, args);
158}158}
159159
160const use_debug_allocator = build_options.debug_gpa or160const use_safe_allocator = build_options.debug_gpa or
161 (native_os != .wasi and !builtin.link_libc and switch (builtin.mode) {161 (native_os != .wasi and !builtin.link_libc and switch (builtin.mode) {
162 .Debug, .ReleaseSafe => true,162 .Debug, .ReleaseSafe => true,
163 .ReleaseFast, .ReleaseSmall => false,163 .ReleaseFast, .ReleaseSmall => false,
164 });164 });
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, .{
167 .stack_trace_frames = build_options.mem_leak_frames,168 .stack_trace_frames = build_options.mem_leak_frames,
168 .thread_safe = switch (build_options.io_mode) {169});
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};
183170
184pub fn main(init: std.process.Init.Minimal) anyerror!void {171pub fn main(init: std.process.Init.Minimal) anyerror!void {
185 var root_allocator: RootAllocator = .init;172 const root_gpa = if (use_safe_allocator)
186 defer _ = root_allocator.deinit();173 safe_allocator.allocator()
187 const root_gpa = root_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 };
188 var io_impl: IoImpl = undefined;183 var io_impl: IoImpl = undefined;
189 switch (build_options.io_mode) {184 switch (build_options.io_mode) {
190 .threaded => io_impl = .init(root_gpa, .{185 .threaded => io_impl = .init(root_gpa, .{
...@@ -197,7 +192,7 @@ pub fn main(init: std.process.Init.Minimal) anyerror!void {...@@ -197,7 +192,7 @@ pub fn main(init: std.process.Init.Minimal) anyerror!void {
197 .argv0 = .init(init.args),192 .argv0 = .init(init.args),
198 .environ = init.environ,193 .environ = init.environ,
199194
200 .backing_allocator_needs_mutex = use_debug_allocator,195 .backing_allocator_needs_mutex = false,
201 }),196 }),
202 }197 }
203 defer io_impl.deinit();198 defer io_impl.deinit();