authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-04-30 11:07:31-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-04-30 11:07:31-07:00
log2d8d681b5ee34663aa87f0583f7b1a012b17d5b4
treefefb28fbbdc9ab0c94c41a8609402077307ce024
parent5d696b0706de2ac267cb774ef39e0d81131d5c38

stage2: un-tangle memory management of Decl and Namespace

Before there was this "top_decl" and "tmp_namespace" stack values that were kludgy and buggy. Now Sema is slightly reworked so that files which are structs are analyzed with their own Decl and Namespace already set up. After this commit there are no memory leaks for a successful build-obj.

3 files changed, 138 insertions(+), 101 deletions(-)

src/Module.zig+83-72
......@@ -276,11 +276,16 @@ pub const Decl = struct {
276276
277277 pub fn destroy(decl: *Decl, module: *Module) void {
278278 const gpa = module.gpa;
279 log.debug("destroy Decl {s}", .{decl.name});
279280 decl.clearName(gpa);
280281 if (decl.has_tv) {
281282 if (decl.val.castTag(.function)) |payload| {
282283 const func = payload.data;
283284 func.deinit(gpa);
285 } else if (decl.val.getTypeNamespace()) |namespace| {
286 if (namespace.getDecl() == decl) {
287 namespace.clearDecls(module);
288 }
284289 }
285290 decl.clearValues(gpa);
286291 }
......@@ -303,6 +308,13 @@ pub const Decl = struct {
303308 }
304309 }
305310
311 pub fn finalizeNewArena(decl: *Decl, arena: *std.heap.ArenaAllocator) !void {
312 assert(decl.value_arena == null);
313 const arena_state = try arena.allocator.create(std.heap.ArenaAllocator.State);
314 arena_state.* = arena.state;
315 decl.value_arena = arena_state;
316 }
317
306318 /// This name is relative to the containing namespace of the decl.
307319 /// The memory is owned by the containing File ZIR.
308320 pub fn getName(decl: Decl) ?[:0]const u8 {
......@@ -719,13 +731,20 @@ pub const Scope = struct {
719731 decls: std.StringArrayHashMapUnmanaged(*Decl) = .{},
720732
721733 pub fn deinit(ns: *Namespace, mod: *Module) void {
734 ns.clearDecls(mod);
735 ns.* = undefined;
736 }
737
738 pub fn clearDecls(ns: *Namespace, mod: *Module) void {
722739 const gpa = mod.gpa;
723740
724 for (ns.decls.items()) |entry| {
741 var decls = ns.decls;
742 ns.decls = .{};
743
744 for (decls.items()) |entry| {
725745 entry.value.destroy(mod);
726746 }
727 ns.decls.deinit(gpa);
728 ns.* = undefined;
747 decls.deinit(gpa);
729748 }
730749
731750 pub fn removeDecl(ns: *Namespace, child: *Decl) void {
......@@ -775,14 +794,9 @@ pub const Scope = struct {
775794 /// Package that this file is a part of, managed externally.
776795 pkg: *Package,
777796 /// The namespace of the struct that represents this file.
778 /// Populated only when status is success.
797 /// Populated only when status is `success_air`.
779798 /// Owned by its owner Decl Value.
780799 namespace: *Namespace,
781 /// All namespaces that this file contains. This is here so that
782 /// when a file is updated, and new ZIR code is generated, the
783 /// old and new ZIR code can be compared side by side and references
784 /// to old ZIR updated to new ZIR, and a changelist generated.
785 namespace_set: std.AutoArrayHashMapUnmanaged(*Namespace, void) = .{},
786800
787801 pub fn unload(file: *File, gpa: *Allocator) void {
788802 file.unloadTree(gpa);
......@@ -813,6 +827,10 @@ pub const Scope = struct {
813827
814828 pub fn deinit(file: *File, mod: *Module) void {
815829 const gpa = mod.gpa;
830 log.debug("deinit File {s}", .{file.sub_file_path});
831 if (file.status == .success_air) {
832 file.namespace.getDecl().destroy(mod);
833 }
816834 gpa.free(file.sub_file_path);
817835 file.unload(gpa);
818836 file.* = undefined;
......@@ -866,6 +884,18 @@ pub const Scope = struct {
866884 gpa.destroy(file);
867885 }
868886
887 pub fn fullyQualifiedNameZ(file: File, gpa: *Allocator) ![:0]u8 {
888 // Convert all the slashes into dots and truncate the extension.
889 const ext = std.fs.path.extension(file.sub_file_path);
890 const noext = file.sub_file_path[0 .. file.sub_file_path.len - ext.len];
891 const duped = try gpa.dupeZ(u8, noext);
892 for (duped) |*byte| switch (byte.*) {
893 '/', '\\' => byte.* = '.',
894 else => continue,
895 };
896 return duped;
897 }
898
869899 pub fn dumpSrc(file: *File, src: LazySrcLoc) void {
870900 const loc = std.zig.findLineColumn(file.source.bytes, src);
871901 std.debug.print("{s}:{d}:{d}\n", .{ file.sub_file_path, loc.line + 1, loc.column + 1 });
......@@ -3297,48 +3327,49 @@ pub fn semaFile(mod: *Module, file: *Scope.File) InnerError!void {
32973327 assert(file.zir_loaded);
32983328
32993329 const gpa = mod.gpa;
3300 var decl_arena = std.heap.ArenaAllocator.init(gpa);
3301 defer decl_arena.deinit();
3302
3303 // We need a Decl to pass to Sema and collect dependencies. But ultimately we
3304 // want to pass them on to the Decl for the struct that represents the file.
3305 var tmp_namespace: Scope.Namespace = .{
3306 .parent = null,
3307 .file_scope = file,
3308 .ty = Type.initTag(.type),
3330 var new_decl_arena = std.heap.ArenaAllocator.init(gpa);
3331 errdefer new_decl_arena.deinit();
3332
3333 const struct_obj = try new_decl_arena.allocator.create(Module.Struct);
3334 const struct_ty = try Type.Tag.@"struct".create(&new_decl_arena.allocator, struct_obj);
3335 const struct_val = try Value.Tag.ty.create(&new_decl_arena.allocator, struct_ty);
3336 struct_obj.* = .{
3337 .owner_decl = undefined, // set below
3338 .fields = .{},
3339 .node_offset = 0, // it's the struct for the root file
3340 .namespace = .{
3341 .parent = null,
3342 .ty = struct_ty,
3343 .file_scope = file,
3344 },
33093345 };
3310 var top_decl: Decl = .{
3311 .name = "",
3312 .namespace = &tmp_namespace,
3313 .generation = mod.generation,
3314 .src_node = 0, // the root AST node for the file
3315 .analysis = .in_progress,
3316 .deletion_flag = false,
3317 .is_pub = true,
3318 .is_exported = false,
3319 .has_linksection = false,
3320 .has_align = false,
3321 .link = undefined, // don't try to codegen this
3322 .fn_link = undefined, // not a function
3323 .zir_decl_index = undefined,
3346 file.namespace = &struct_obj.namespace;
3347 const new_decl = try mod.allocateNewDecl(&struct_obj.namespace, 0);
3348 struct_obj.owner_decl = new_decl;
3349 new_decl.name = try file.fullyQualifiedNameZ(gpa);
3350 new_decl.is_pub = true;
3351 new_decl.is_exported = false;
3352 new_decl.has_align = false;
3353 new_decl.has_linksection = false;
3354 new_decl.zir_decl_index = undefined;
3355 new_decl.ty = struct_ty;
3356 new_decl.val = struct_val;
3357 new_decl.has_tv = true;
3358 new_decl.analysis = .complete;
3359 new_decl.generation = mod.generation;
33243360
3325 .has_tv = false,
3326 .ty = undefined,
3327 .val = undefined,
3328 .align_val = undefined,
3329 .linksection_val = undefined,
3330 };
3331 defer top_decl.dependencies.deinit(gpa);
3361 var sema_arena = std.heap.ArenaAllocator.init(gpa);
3362 defer sema_arena.deinit();
33323363
33333364 var sema: Sema = .{
33343365 .mod = mod,
33353366 .gpa = gpa,
3336 .arena = &decl_arena.allocator,
3367 .arena = &sema_arena.allocator,
33373368 .code = file.zir,
33383369 // TODO use a map because this array is too big
3339 .inst_map = try decl_arena.allocator.alloc(*ir.Inst, file.zir.instructions.len),
3340 .owner_decl = &top_decl,
3341 .namespace = &tmp_namespace,
3370 .inst_map = try sema_arena.allocator.alloc(*ir.Inst, file.zir.instructions.len),
3371 .owner_decl = new_decl,
3372 .namespace = &struct_obj.namespace,
33423373 .func = null,
33433374 .owner_func = null,
33443375 .param_inst_list = &.{},
......@@ -3346,7 +3377,7 @@ pub fn semaFile(mod: *Module, file: *Scope.File) InnerError!void {
33463377 var block_scope: Scope.Block = .{
33473378 .parent = null,
33483379 .sema = &sema,
3349 .src_decl = &top_decl,
3380 .src_decl = new_decl,
33503381 .instructions = .{},
33513382 .inlining = null,
33523383 .is_comptime = true,
......@@ -3355,23 +3386,9 @@ pub fn semaFile(mod: *Module, file: *Scope.File) InnerError!void {
33553386
33563387 const main_struct_inst = file.zir.extra[@enumToInt(Zir.ExtraIndex.main_struct)] -
33573388 @intCast(u32, Zir.Inst.Ref.typed_value_map.len);
3358 const air_inst = try sema.zirStructDecl(&block_scope, main_struct_inst, .Auto);
3359 assert(air_inst.ty.zigTypeTag() == .Type);
3360 const val = air_inst.value().?;
3361 const struct_ty = try val.toType(&decl_arena.allocator);
3362 const struct_decl = struct_ty.getOwnerDecl();
3363
3364 file.namespace = struct_ty.getNamespace().?;
3365 file.namespace.parent = null;
3366
3367 // Transfer the dependencies to `owner_decl`.
3368 assert(top_decl.dependants.count() == 0);
3369 for (top_decl.dependencies.items()) |entry| {
3370 const dep = entry.key;
3371 dep.removeDependant(&top_decl);
3372 if (dep == struct_decl) continue;
3373 _ = try mod.declareDeclDependency(struct_decl, dep);
3374 }
3389 try sema.analyzeStructDecl(&block_scope, &new_decl_arena, new_decl, main_struct_inst, .Auto, struct_obj);
3390 try new_decl.finalizeNewArena(&new_decl_arena);
3391
33753392 file.status = .success_air;
33763393}
33773394
......@@ -4319,23 +4336,17 @@ pub fn constIntBig(mod: *Module, arena: *Allocator, src: LazySrcLoc, ty: Type, b
43194336 }
43204337}
43214338
4322pub fn createAnonymousDecl(
4323 mod: *Module,
4324 scope: *Scope,
4325 decl_arena: *std.heap.ArenaAllocator,
4326 typed_value: TypedValue,
4327) !*Decl {
4339pub fn createAnonymousDecl(mod: *Module, scope: *Scope, typed_value: TypedValue) !*Decl {
43284340 const name_index = mod.getNextAnonNameIndex();
43294341 const scope_decl = scope.ownerDecl().?;
4342 const namespace = scope_decl.namespace;
4343 try namespace.decls.ensureCapacity(mod.gpa, namespace.decls.count() + 1);
43304344 const name = try std.fmt.allocPrintZ(mod.gpa, "{s}__anon_{d}", .{ scope_decl.name, name_index });
43314345 errdefer mod.gpa.free(name);
4332 const namespace = scope_decl.namespace;
43334346 const new_decl = try mod.allocateNewDecl(namespace, scope_decl.src_node);
4334 new_decl.name = name;
4347 namespace.decls.putAssumeCapacityNoClobber(name, new_decl);
43354348
4336 const decl_arena_state = try decl_arena.allocator.create(std.heap.ArenaAllocator.State);
4337
4338 decl_arena_state.* = decl_arena.state;
4349 new_decl.name = name;
43394350 new_decl.ty = typed_value.ty;
43404351 new_decl.val = typed_value.val;
43414352 new_decl.has_tv = true;
src/Sema.zig+47-29
......@@ -634,15 +634,19 @@ fn zirCoerceResultPtr(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) In
634634 return sema.mod.fail(&block.base, sema.src, "TODO implement zirCoerceResultPtr", .{});
635635}
636636
637pub fn zirStructDecl(
637pub fn analyzeStructDecl(
638638 sema: *Sema,
639639 block: *Scope.Block,
640 new_decl_arena: *std.heap.ArenaAllocator,
641 new_decl: *Decl,
640642 inst: Zir.Inst.Index,
641643 layout: std.builtin.TypeInfo.ContainerLayout,
642) InnerError!*Inst {
644 struct_obj: *Module.Struct,
645) InnerError!void {
643646 const tracy = trace(@src());
644647 defer tracy.end();
645648
649 const mod = sema.mod;
646650 const gpa = sema.gpa;
647651 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
648652 const src = inst_data.src();
......@@ -650,27 +654,7 @@ pub fn zirStructDecl(
650654 const fields_len = extra.data.fields_len;
651655 const decls_len = extra.data.decls_len;
652656
653 var new_decl_arena = std.heap.ArenaAllocator.init(gpa);
654
655 const struct_obj = try new_decl_arena.allocator.create(Module.Struct);
656 const struct_ty = try Type.Tag.@"struct".create(&new_decl_arena.allocator, struct_obj);
657 const struct_val = try Value.Tag.ty.create(&new_decl_arena.allocator, struct_ty);
658 const new_decl = try sema.mod.createAnonymousDecl(&block.base, &new_decl_arena, .{
659 .ty = Type.initTag(.type),
660 .val = struct_val,
661 });
662 struct_obj.* = .{
663 .owner_decl = sema.owner_decl,
664 .fields = .{},
665 .node_offset = inst_data.src_node,
666 .namespace = .{
667 .parent = sema.owner_decl.namespace,
668 .ty = struct_ty,
669 .file_scope = block.getFileScope(),
670 },
671 };
672
673 var extra_index: usize = try sema.mod.scanNamespace(
657 var extra_index: usize = try mod.scanNamespace(
674658 &struct_obj.namespace,
675659 extra.end,
676660 decls_len,
......@@ -680,7 +664,7 @@ pub fn zirStructDecl(
680664 const body = sema.code.extra[extra_index..][0..extra.data.body_len];
681665 if (fields_len == 0) {
682666 assert(body.len == 0);
683 return sema.analyzeDeclVal(block, src, new_decl);
667 return;
684668 }
685669
686670 try struct_obj.fields.ensureCapacity(&new_decl_arena.allocator, fields_len);
......@@ -691,7 +675,7 @@ pub fn zirStructDecl(
691675 // Within the field type, default value, and alignment expressions, the "owner decl"
692676 // should be the struct itself. Thus we need a new Sema.
693677 var struct_sema: Sema = .{
694 .mod = sema.mod,
678 .mod = mod,
695679 .gpa = gpa,
696680 .arena = &new_decl_arena.allocator,
697681 .code = sema.code,
......@@ -752,7 +736,7 @@ pub fn zirStructDecl(
752736 // This string needs to outlive the ZIR code.
753737 const field_name = try new_decl_arena.allocator.dupe(u8, field_name_zir);
754738 if (field_type_ref == .none) {
755 return sema.mod.fail(&block.base, src, "TODO: implement anytype struct field", .{});
739 return mod.fail(&block.base, src, "TODO: implement anytype struct field", .{});
756740 }
757741 const field_ty: Type = if (field_type_ref == .none)
758742 Type.initTag(.noreturn)
......@@ -788,7 +772,38 @@ pub fn zirStructDecl(
788772 gop.entry.value.default_val = (try sema.resolveInstConst(block, src, default_ref)).val;
789773 }
790774 }
775}
776
777fn zirStructDecl(
778 sema: *Sema,
779 block: *Scope.Block,
780 inst: Zir.Inst.Index,
781 layout: std.builtin.TypeInfo.ContainerLayout,
782) InnerError!*Inst {
783 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
784 const src = inst_data.src();
785
786 var new_decl_arena = std.heap.ArenaAllocator.init(sema.gpa);
791787
788 const struct_obj = try new_decl_arena.allocator.create(Module.Struct);
789 const struct_ty = try Type.Tag.@"struct".create(&new_decl_arena.allocator, struct_obj);
790 const struct_val = try Value.Tag.ty.create(&new_decl_arena.allocator, struct_ty);
791 const new_decl = try sema.mod.createAnonymousDecl(&block.base, .{
792 .ty = Type.initTag(.type),
793 .val = struct_val,
794 });
795 struct_obj.* = .{
796 .owner_decl = sema.owner_decl,
797 .fields = .{},
798 .node_offset = inst_data.src_node,
799 .namespace = .{
800 .parent = sema.owner_decl.namespace,
801 .ty = struct_ty,
802 .file_scope = block.getFileScope(),
803 },
804 };
805 try sema.analyzeStructDecl(block, &new_decl_arena, new_decl, inst, layout, struct_obj);
806 try new_decl.finalizeNewArena(&new_decl_arena);
792807 return sema.analyzeDeclVal(block, src, new_decl);
793808}
794809
......@@ -822,14 +837,14 @@ fn zirEnumDecl(
822837 };
823838
824839 const enum_obj = try new_decl_arena.allocator.create(Module.EnumFull);
825 const enum_ty_payload = try gpa.create(Type.Payload.EnumFull);
840 const enum_ty_payload = try new_decl_arena.allocator.create(Type.Payload.EnumFull);
826841 enum_ty_payload.* = .{
827842 .base = .{ .tag = if (nonexhaustive) .enum_nonexhaustive else .enum_full },
828843 .data = enum_obj,
829844 };
830845 const enum_ty = Type.initPayload(&enum_ty_payload.base);
831846 const enum_val = try Value.Tag.ty.create(&new_decl_arena.allocator, enum_ty);
832 const new_decl = try sema.mod.createAnonymousDecl(&block.base, &new_decl_arena, .{
847 const new_decl = try sema.mod.createAnonymousDecl(&block.base, .{
833848 .ty = Type.initTag(.type),
834849 .val = enum_val,
835850 });
......@@ -856,6 +871,7 @@ fn zirEnumDecl(
856871 const body = sema.code.extra[extra_index..][0..extra.data.body_len];
857872 if (fields_len == 0) {
858873 assert(body.len == 0);
874 try new_decl.finalizeNewArena(&new_decl_arena);
859875 return sema.analyzeDeclVal(block, src, new_decl);
860876 }
861877
......@@ -942,6 +958,7 @@ fn zirEnumDecl(
942958 }
943959 }
944960
961 try new_decl.finalizeNewArena(&new_decl_arena);
945962 return sema.analyzeDeclVal(block, src, new_decl);
946963}
947964
......@@ -1424,10 +1441,11 @@ fn zirStr(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*In
14241441 const decl_ty = try Type.Tag.array_u8_sentinel_0.create(&new_decl_arena.allocator, bytes.len);
14251442 const decl_val = try Value.Tag.bytes.create(&new_decl_arena.allocator, bytes);
14261443
1427 const new_decl = try sema.mod.createAnonymousDecl(&block.base, &new_decl_arena, .{
1444 const new_decl = try sema.mod.createAnonymousDecl(&block.base, .{
14281445 .ty = decl_ty,
14291446 .val = decl_val,
14301447 });
1448 try new_decl.finalizeNewArena(&new_decl_arena);
14311449 return sema.analyzeDeclRef(block, .unneeded, new_decl);
14321450}
14331451
src/value.zig+8
......@@ -593,6 +593,14 @@ pub const Value = extern union {
593593 unreachable;
594594 }
595595
596 /// Returns null if not a type or if the type has no namespace.
597 pub fn getTypeNamespace(self: Value) ?*Module.Scope.Namespace {
598 return switch (self.tag()) {
599 .ty => self.castTag(.ty).?.data.getNamespace(),
600 else => null,
601 };
602 }
603
596604 /// Asserts that the value is representable as a type.
597605 pub fn toType(self: Value, allocator: *Allocator) !Type {
598606 return switch (self.tag()) {