Skip to content

Commit fb4ad37

Browse files
committed
LLVM: fix memory leak of debug type names
This required adjusting `Type.nameAlloc` to be used with a general-purpose allocator and added `Type.nameAllocArena` for the arena use case (avoids allocation sometimes).
1 parent 874b51d commit fb4ad37

File tree

4 files changed

+80
-50
lines changed

4 files changed

+80
-50
lines changed

src/Sema.zig

+1-1
Original file line numberDiff line numberDiff line change
@@ -12405,7 +12405,7 @@ fn zirTypeName(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
1240512405
var anon_decl = try block.startAnonDecl(LazySrcLoc.unneeded);
1240612406
defer anon_decl.deinit();
1240712407

12408-
const bytes = try ty.nameAlloc(anon_decl.arena());
12408+
const bytes = try ty.nameAllocArena(anon_decl.arena());
1240912409

1241012410
const new_decl = try anon_decl.finish(
1241112411
try Type.Tag.array_u8_sentinel_0.create(anon_decl.arena(), bytes.len),

src/codegen/llvm.zig

+20-10
Original file line numberDiff line numberDiff line change
@@ -1922,7 +1922,8 @@ pub const DeclGen = struct {
19221922
.Int => {
19231923
const info = ty.intInfo(target);
19241924
assert(info.bits != 0);
1925-
const name = try ty.nameAlloc(gpa); // TODO this is a leak
1925+
const name = try ty.nameAlloc(gpa);
1926+
defer gpa.free(name);
19261927
const dwarf_encoding: c_uint = switch (info.signedness) {
19271928
.signed => DW.ATE.signed,
19281929
.unsigned => DW.ATE.unsigned,
@@ -1967,7 +1968,8 @@ pub const DeclGen = struct {
19671968
const di_file = try dg.object.getDIFile(gpa, owner_decl.src_namespace.file_scope);
19681969
const di_scope = try dg.namespaceToDebugScope(owner_decl.src_namespace);
19691970

1970-
const name = try ty.nameAlloc(gpa); // TODO this is a leak
1971+
const name = try ty.nameAlloc(gpa);
1972+
defer gpa.free(name);
19711973
var buffer: Type.Payload.Bits = undefined;
19721974
const int_ty = ty.intTagType(&buffer);
19731975

@@ -1989,7 +1991,8 @@ pub const DeclGen = struct {
19891991
},
19901992
.Float => {
19911993
const bits = ty.floatBits(target);
1992-
const name = try ty.nameAlloc(gpa); // TODO this is a leak
1994+
const name = try ty.nameAlloc(gpa);
1995+
defer gpa.free(name);
19931996
gop.value_ptr.* = dib.createBasicType(name, bits, DW.ATE.float);
19941997
return gop.value_ptr.*;
19951998
},
@@ -2039,7 +2042,8 @@ pub const DeclGen = struct {
20392042
const ptr_ty = ty.slicePtrFieldType(&buf);
20402043
const len_ty = Type.usize;
20412044

2042-
const name = try ty.nameAlloc(gpa); // TODO this is a leak
2045+
const name = try ty.nameAlloc(gpa);
2046+
defer gpa.free(name);
20432047
const di_file: ?*llvm.DIFile = null;
20442048
const line = 0;
20452049
const compile_unit_scope = dg.object.di_compile_unit.?.toScope();
@@ -2109,7 +2113,8 @@ pub const DeclGen = struct {
21092113
}
21102114

21112115
const elem_di_ty = try lowerDebugType(dg, ptr_info.pointee_type);
2112-
const name = try ty.nameAlloc(gpa); // TODO this is a leak
2116+
const name = try ty.nameAlloc(gpa);
2117+
defer gpa.free(name);
21132118
const ptr_di_ty = dib.createPointerType(
21142119
elem_di_ty,
21152120
target.cpu.arch.ptrBitWidth(),
@@ -2125,7 +2130,8 @@ pub const DeclGen = struct {
21252130
gop.value_ptr.* = dib.createBasicType("anyopaque", 0, DW.ATE.signed);
21262131
return gop.value_ptr.*;
21272132
}
2128-
const name = try ty.nameAlloc(gpa); // TODO this is a leak
2133+
const name = try ty.nameAlloc(gpa);
2134+
defer gpa.free(name);
21292135
const owner_decl = ty.getOwnerDecl();
21302136
const opaque_di_ty = dib.createForwardDeclType(
21312137
DW.TAG.structure_type,
@@ -2162,7 +2168,8 @@ pub const DeclGen = struct {
21622168
return vector_di_ty;
21632169
},
21642170
.Optional => {
2165-
const name = try ty.nameAlloc(gpa); // TODO this is a leak
2171+
const name = try ty.nameAlloc(gpa);
2172+
defer gpa.free(name);
21662173
var buf: Type.Payload.ElemType = undefined;
21672174
const child_ty = ty.optionalChild(&buf);
21682175
if (!child_ty.hasRuntimeBits()) {
@@ -2253,7 +2260,8 @@ pub const DeclGen = struct {
22532260
try dg.object.di_type_map.put(gpa, ty, err_set_di_ty);
22542261
return err_set_di_ty;
22552262
}
2256-
const name = try ty.nameAlloc(gpa); // TODO this is a leak
2263+
const name = try ty.nameAlloc(gpa);
2264+
defer gpa.free(name);
22572265
const di_file: ?*llvm.DIFile = null;
22582266
const line = 0;
22592267
const compile_unit_scope = dg.object.di_compile_unit.?.toScope();
@@ -2329,7 +2337,8 @@ pub const DeclGen = struct {
23292337
},
23302338
.Struct => {
23312339
const compile_unit_scope = dg.object.di_compile_unit.?.toScope();
2332-
const name = try ty.nameAlloc(gpa); // TODO this is a leak
2340+
const name = try ty.nameAlloc(gpa);
2341+
defer gpa.free(name);
23332342
const fwd_decl = dib.createReplaceableCompositeType(
23342343
DW.TAG.structure_type,
23352344
name.ptr,
@@ -2477,7 +2486,8 @@ pub const DeclGen = struct {
24772486
.Union => {
24782487
const owner_decl = ty.getOwnerDecl();
24792488

2480-
const name = try ty.nameAlloc(gpa); // TODO this is a leak
2489+
const name = try ty.nameAlloc(gpa);
2490+
defer gpa.free(name);
24812491
const fwd_decl = dib.createReplaceableCompositeType(
24822492
DW.TAG.structure_type,
24832493
name.ptr,

src/link/Dwarf.zig

+1-1
Original file line numberDiff line numberDiff line change
@@ -882,7 +882,7 @@ fn addDbgInfoType(
882882
const abi_size = ty.abiSize(target);
883883
try leb128.writeULEB128(dbg_info_buffer.writer(), abi_size);
884884
// DW.AT.name, DW.FORM.string
885-
const struct_name = try ty.nameAlloc(arena);
885+
const struct_name = try ty.nameAllocArena(arena);
886886
try dbg_info_buffer.ensureUnusedCapacity(struct_name.len + 1);
887887
dbg_info_buffer.appendSliceAssumeCapacity(struct_name);
888888
dbg_info_buffer.appendAssumeCapacity(0);

src/type.zig

+58-38
Original file line numberDiff line numberDiff line change
@@ -1766,8 +1766,20 @@ pub const Type = extern union {
17661766
}
17671767
}
17681768

1769+
pub fn nameAllocArena(ty: Type, arena: Allocator) Allocator.Error![:0]const u8 {
1770+
return nameAllocAdvanced(ty, arena, true);
1771+
}
1772+
1773+
pub fn nameAlloc(ty: Type, gpa: Allocator) Allocator.Error![:0]const u8 {
1774+
return nameAllocAdvanced(ty, gpa, false);
1775+
}
1776+
17691777
/// Returns a name suitable for `@typeName`.
1770-
pub fn nameAlloc(ty: Type, arena: Allocator) Allocator.Error![:0]const u8 {
1778+
pub fn nameAllocAdvanced(
1779+
ty: Type,
1780+
ally: Allocator,
1781+
is_arena: bool,
1782+
) Allocator.Error![:0]const u8 {
17711783
const t = ty.tag();
17721784
switch (t) {
17731785
.inferred_alloc_const => unreachable,
@@ -1812,71 +1824,79 @@ pub const Type = extern union {
18121824
.noreturn,
18131825
.var_args_param,
18141826
.bound_fn,
1815-
=> return @tagName(t),
1827+
=> return maybeDupe(@tagName(t), ally, is_arena),
18161828

1817-
.enum_literal => return "@Type(.EnumLiteral)",
1818-
.@"null" => return "@Type(.Null)",
1819-
.@"undefined" => return "@Type(.Undefined)",
1829+
.enum_literal => return maybeDupe("@Type(.EnumLiteral)", ally, is_arena),
1830+
.@"null" => return maybeDupe("@Type(.Null)", ally, is_arena),
1831+
.@"undefined" => return maybeDupe("@Type(.Undefined)", ally, is_arena),
18201832

1821-
.empty_struct, .empty_struct_literal => return "struct {}",
1833+
.empty_struct, .empty_struct_literal => return maybeDupe("struct {}", ally, is_arena),
18221834

18231835
.@"struct" => {
18241836
const struct_obj = ty.castTag(.@"struct").?.data;
1825-
return try arena.dupeZ(u8, std.mem.sliceTo(struct_obj.owner_decl.name, 0));
1837+
return try ally.dupeZ(u8, std.mem.sliceTo(struct_obj.owner_decl.name, 0));
18261838
},
18271839
.@"union", .union_tagged => {
18281840
const union_obj = ty.cast(Payload.Union).?.data;
1829-
return try arena.dupeZ(u8, std.mem.sliceTo(union_obj.owner_decl.name, 0));
1841+
return try ally.dupeZ(u8, std.mem.sliceTo(union_obj.owner_decl.name, 0));
18301842
},
18311843
.enum_full, .enum_nonexhaustive => {
18321844
const enum_full = ty.cast(Payload.EnumFull).?.data;
1833-
return try arena.dupeZ(u8, std.mem.sliceTo(enum_full.owner_decl.name, 0));
1845+
return try ally.dupeZ(u8, std.mem.sliceTo(enum_full.owner_decl.name, 0));
18341846
},
18351847
.enum_simple => {
18361848
const enum_simple = ty.castTag(.enum_simple).?.data;
1837-
return try arena.dupeZ(u8, std.mem.sliceTo(enum_simple.owner_decl.name, 0));
1849+
return try ally.dupeZ(u8, std.mem.sliceTo(enum_simple.owner_decl.name, 0));
18381850
},
18391851
.enum_numbered => {
18401852
const enum_numbered = ty.castTag(.enum_numbered).?.data;
1841-
return try arena.dupeZ(u8, std.mem.sliceTo(enum_numbered.owner_decl.name, 0));
1853+
return try ally.dupeZ(u8, std.mem.sliceTo(enum_numbered.owner_decl.name, 0));
18421854
},
18431855
.@"opaque" => {
1844-
// TODO use declaration name
1845-
return "opaque {}";
1846-
},
1847-
1848-
.anyerror_void_error_union => return "anyerror!void",
1849-
.const_slice_u8 => return "[]const u8",
1850-
.const_slice_u8_sentinel_0 => return "[:0]const u8",
1851-
.fn_noreturn_no_args => return "fn() noreturn",
1852-
.fn_void_no_args => return "fn() void",
1853-
.fn_naked_noreturn_no_args => return "fn() callconv(.Naked) noreturn",
1854-
.fn_ccc_void_no_args => return "fn() callconv(.C) void",
1855-
.single_const_pointer_to_comptime_int => return "*const comptime_int",
1856-
.manyptr_u8 => return "[*]u8",
1857-
.manyptr_const_u8 => return "[*]const u8",
1858-
.manyptr_const_u8_sentinel_0 => return "[*:0]const u8",
1859-
.atomic_order => return "AtomicOrder",
1860-
.atomic_rmw_op => return "AtomicRmwOp",
1861-
.calling_convention => return "CallingConvention",
1862-
.address_space => return "AddressSpace",
1863-
.float_mode => return "FloatMode",
1864-
.reduce_op => return "ReduceOp",
1865-
.call_options => return "CallOptions",
1866-
.prefetch_options => return "PrefetchOptions",
1867-
.export_options => return "ExportOptions",
1868-
.extern_options => return "ExternOptions",
1869-
.type_info => return "Type",
1856+
const opaque_obj = ty.cast(Payload.Opaque).?.data;
1857+
return try ally.dupeZ(u8, std.mem.sliceTo(opaque_obj.owner_decl.name, 0));
1858+
},
1859+
1860+
.anyerror_void_error_union => return maybeDupe("anyerror!void", ally, is_arena),
1861+
.const_slice_u8 => return maybeDupe("[]const u8", ally, is_arena),
1862+
.const_slice_u8_sentinel_0 => return maybeDupe("[:0]const u8", ally, is_arena),
1863+
.fn_noreturn_no_args => return maybeDupe("fn() noreturn", ally, is_arena),
1864+
.fn_void_no_args => return maybeDupe("fn() void", ally, is_arena),
1865+
.fn_naked_noreturn_no_args => return maybeDupe("fn() callconv(.Naked) noreturn", ally, is_arena),
1866+
.fn_ccc_void_no_args => return maybeDupe("fn() callconv(.C) void", ally, is_arena),
1867+
.single_const_pointer_to_comptime_int => return maybeDupe("*const comptime_int", ally, is_arena),
1868+
.manyptr_u8 => return maybeDupe("[*]u8", ally, is_arena),
1869+
.manyptr_const_u8 => return maybeDupe("[*]const u8", ally, is_arena),
1870+
.manyptr_const_u8_sentinel_0 => return maybeDupe("[*:0]const u8", ally, is_arena),
1871+
.atomic_order => return maybeDupe("AtomicOrder", ally, is_arena),
1872+
.atomic_rmw_op => return maybeDupe("AtomicRmwOp", ally, is_arena),
1873+
.calling_convention => return maybeDupe("CallingConvention", ally, is_arena),
1874+
.address_space => return maybeDupe("AddressSpace", ally, is_arena),
1875+
.float_mode => return maybeDupe("FloatMode", ally, is_arena),
1876+
.reduce_op => return maybeDupe("ReduceOp", ally, is_arena),
1877+
.call_options => return maybeDupe("CallOptions", ally, is_arena),
1878+
.prefetch_options => return maybeDupe("PrefetchOptions", ally, is_arena),
1879+
.export_options => return maybeDupe("ExportOptions", ally, is_arena),
1880+
.extern_options => return maybeDupe("ExternOptions", ally, is_arena),
1881+
.type_info => return maybeDupe("Type", ally, is_arena),
18701882

18711883
else => {
18721884
// TODO this is wasteful and also an incorrect implementation of `@typeName`
1873-
var buf = std.ArrayList(u8).init(arena);
1885+
var buf = std.ArrayList(u8).init(ally);
18741886
try buf.writer().print("{}", .{ty});
18751887
return try buf.toOwnedSliceSentinel(0);
18761888
},
18771889
}
18781890
}
18791891

1892+
fn maybeDupe(s: [:0]const u8, ally: Allocator, is_arena: bool) Allocator.Error![:0]const u8 {
1893+
if (is_arena) {
1894+
return s;
1895+
} else {
1896+
return try ally.dupeZ(u8, s);
1897+
}
1898+
}
1899+
18801900
pub fn toValue(self: Type, allocator: Allocator) Allocator.Error!Value {
18811901
switch (self.tag()) {
18821902
.u1 => return Value.initTag(.u1_type),

0 commit comments

Comments
 (0)