authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-05-11 22:12:36-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-05-11 22:12:36-07:00
log1ab1a96f87279375e656bba35280a85b62973255
tree456a1c00ce806827214678f13ef721dfe704a970
parentd7567c06fd8bc1777f98e33abfe3070f698782ec

stage2: improve Decl lifetime management

* Compilation: iteration over the deletion_set only tries to delete the first one, relying on Decl destroy to remove itself from the deletion set. * link: `freeDecl` now has to handle the possibility of freeing a Decl that was never called with `allocateDeclIndexes`. * `deleteDecl` recursively iterates over a Decl's Namespace sub-Decl objects and calls `deleteDecl` on them. - Prevents Decl objects from being destroyed when they are still in `deletion_set`. * Sema: fix cleanup of anonymous Decl objects when an error occurs during semantic analysis. * tests: update test cases for fully qualified names

5 files changed, 67 insertions(+), 44 deletions(-)

src/Compilation.zig+3-5
...@@ -1616,14 +1616,12 @@ pub fn update(self: *Compilation) !void {...@@ -1616,14 +1616,12 @@ pub fn update(self: *Compilation) !void {
1616 // Process the deletion set. We use a while loop here because the1616 // Process the deletion set. We use a while loop here because the
1617 // deletion set may grow as we call `deleteDecl` within this loop,1617 // deletion set may grow as we call `deleteDecl` within this loop,
1618 // and more unreferenced Decls are revealed.1618 // and more unreferenced Decls are revealed.
1619 var entry_i: usize = 0;1619 while (module.deletion_set.entries.items.len != 0) {
1620 while (entry_i < module.deletion_set.entries.items.len) : (entry_i += 1) {1620 const decl = module.deletion_set.entries.items[0].key;
1621 const decl = module.deletion_set.entries.items[entry_i].key;
1622 assert(decl.deletion_flag);1621 assert(decl.deletion_flag);
1623 assert(decl.dependants.items().len == 0);1622 assert(decl.dependants.count() == 0);
1624 try module.deleteDecl(decl, null);1623 try module.deleteDecl(decl, null);
1625 }1624 }
1626 module.deletion_set.shrinkRetainingCapacity(0);
1627 }1625 }
1628 }1626 }
16291627
src/Module.zig+53-34
...@@ -283,7 +283,9 @@ pub const Decl = struct {...@@ -283,7 +283,9 @@ pub const Decl = struct {
283 pub fn destroy(decl: *Decl, module: *Module) void {283 pub fn destroy(decl: *Decl, module: *Module) void {
284 const gpa = module.gpa;284 const gpa = module.gpa;
285 log.debug("destroy {*} ({s})", .{ decl, decl.name });285 log.debug("destroy {*} ({s})", .{ decl, decl.name });
286 decl.clearName(gpa);286 if (decl.deletion_flag) {
287 module.deletion_set.swapRemoveAssertDiscard(decl);
288 }
287 if (decl.has_tv) {289 if (decl.has_tv) {
288 if (decl.getInnerNamespace()) |namespace| {290 if (decl.getInnerNamespace()) |namespace| {
289 namespace.clearDecls(module);291 namespace.clearDecls(module);
...@@ -292,6 +294,7 @@ pub const Decl = struct {...@@ -292,6 +294,7 @@ pub const Decl = struct {
292 }294 }
293 decl.dependants.deinit(gpa);295 decl.dependants.deinit(gpa);
294 decl.dependencies.deinit(gpa);296 decl.dependencies.deinit(gpa);
297 decl.clearName(gpa);
295 if (module.emit_h != null) {298 if (module.emit_h != null) {
296 const decl_plus_emit_h = @fieldParentPtr(DeclPlusEmitH, "decl", decl);299 const decl_plus_emit_h = @fieldParentPtr(DeclPlusEmitH, "decl", decl);
297 decl_plus_emit_h.emit_h.fwd_decl.deinit(gpa);300 decl_plus_emit_h.emit_h.fwd_decl.deinit(gpa);
...@@ -546,28 +549,6 @@ pub const Decl = struct {...@@ -546,28 +549,6 @@ pub const Decl = struct {
546 fn removeDependency(decl: *Decl, other: *Decl) void {549 fn removeDependency(decl: *Decl, other: *Decl) void {
547 decl.dependencies.removeAssertDiscard(other);550 decl.dependencies.removeAssertDiscard(other);
548 }551 }
549
550 fn hasLinkAllocation(decl: Decl) bool {
551 return switch (decl.analysis) {
552 .unreferenced,
553 .in_progress,
554 .dependency_failure,
555 .file_failure,
556 .sema_failure,
557 .sema_failure_retryable,
558 .codegen_failure,
559 .codegen_failure_retryable,
560 => false,
561
562 .complete,
563 .outdated,
564 => {
565 if (!decl.owns_tv)
566 return false;
567 return decl.ty.hasCodeGenBits();
568 },
569 };
570 }
571};552};
572553
573/// This state is attached to every Decl when Module emit_h is non-null.554/// This state is attached to every Decl when Module emit_h is non-null.
...@@ -929,6 +910,32 @@ pub const Scope = struct {...@@ -929,6 +910,32 @@ pub const Scope = struct {
929 anon_decls.deinit(gpa);910 anon_decls.deinit(gpa);
930 }911 }
931912
913 pub fn deleteAllDecls(
914 ns: *Namespace,
915 mod: *Module,
916 outdated_decls: ?*std.AutoArrayHashMap(*Decl, void),
917 ) !void {
918 const gpa = mod.gpa;
919
920 log.debug("deleteAllDecls {*}", .{ns});
921
922 while (ns.decls.count() != 0) {
923 const last_entry = ns.decls.entries.items[ns.decls.entries.items.len - 1];
924 const child_decl = last_entry.value;
925 try mod.deleteDecl(child_decl, outdated_decls);
926 }
927 ns.decls.deinit(gpa);
928 ns.decls = .{};
929
930 while (ns.anon_decls.count() != 0) {
931 const last_entry = ns.anon_decls.entries.items[ns.anon_decls.entries.items.len - 1];
932 const child_decl = last_entry.key;
933 try mod.deleteDecl(child_decl, outdated_decls);
934 }
935 ns.anon_decls.deinit(gpa);
936 ns.anon_decls = .{};
937 }
938
932 pub fn removeDecl(ns: *Namespace, child: *Decl) void {939 pub fn removeDecl(ns: *Namespace, child: *Decl) void {
933 if (child.zir_decl_index == 0) {940 if (child.zir_decl_index == 0) {
934 _ = ns.anon_decls.swapRemove(child);941 _ = ns.anon_decls.swapRemove(child);
...@@ -2646,7 +2653,7 @@ fn updateZirRefs(gpa: *Allocator, file: *Scope.File, old_zir: Zir) !void {...@@ -2646,7 +2653,7 @@ fn updateZirRefs(gpa: *Allocator, file: *Scope.File, old_zir: Zir) !void {
2646 }2653 }
2647 }2654 }
26482655
2649 if (!decl.has_tv) continue;2656 if (!decl.owns_tv) continue;
26502657
2651 if (decl.getStruct()) |struct_obj| {2658 if (decl.getStruct()) |struct_obj| {
2652 struct_obj.zir_index = inst_map.get(struct_obj.zir_index) orelse {2659 struct_obj.zir_index = inst_map.get(struct_obj.zir_index) orelse {
...@@ -3401,17 +3408,19 @@ pub fn deleteDecl(...@@ -3401,17 +3408,19 @@ pub fn deleteDecl(
3401 mod: *Module,3408 mod: *Module,
3402 decl: *Decl,3409 decl: *Decl,
3403 outdated_decls: ?*std.AutoArrayHashMap(*Decl, void),3410 outdated_decls: ?*std.AutoArrayHashMap(*Decl, void),
3404) !void {3411) Allocator.Error!void {
3405 const tracy = trace(@src());3412 const tracy = trace(@src());
3406 defer tracy.end();3413 defer tracy.end();
34073414
3408 log.debug("deleting {*} ({s})", .{ decl, decl.name });3415 log.debug("deleting {*} ({s})", .{ decl, decl.name });
34093416
3417 const gpa = mod.gpa;
3418 try mod.deletion_set.ensureUnusedCapacity(gpa, decl.dependencies.count());
3419
3410 if (outdated_decls) |map| {3420 if (outdated_decls) |map| {
3411 _ = map.swapRemove(decl);3421 _ = map.swapRemove(decl);
3412 try map.ensureUnusedCapacity(decl.dependants.count());3422 try map.ensureUnusedCapacity(decl.dependants.count());
3413 }3423 }
3414 try mod.deletion_set.ensureUnusedCapacity(mod.gpa, decl.dependencies.count());
34153424
3416 // Remove from the namespace it resides in.3425 // Remove from the namespace it resides in.
3417 decl.namespace.removeDecl(decl);3426 decl.namespace.removeDecl(decl);
...@@ -3443,18 +3452,23 @@ pub fn deleteDecl(...@@ -3443,18 +3452,23 @@ pub fn deleteDecl(
3443 }3452 }
3444 }3453 }
3445 if (mod.failed_decls.swapRemove(decl)) |entry| {3454 if (mod.failed_decls.swapRemove(decl)) |entry| {
3446 entry.value.destroy(mod.gpa);3455 entry.value.destroy(gpa);
3447 }3456 }
3448 if (mod.emit_h) |emit_h| {3457 if (mod.emit_h) |emit_h| {
3449 if (emit_h.failed_decls.swapRemove(decl)) |entry| {3458 if (emit_h.failed_decls.swapRemove(decl)) |entry| {
3450 entry.value.destroy(mod.gpa);3459 entry.value.destroy(gpa);
3451 }3460 }
3452 emit_h.decl_table.removeAssertDiscard(decl);3461 emit_h.decl_table.removeAssertDiscard(decl);
3453 }3462 }
3454 _ = mod.compile_log_decls.swapRemove(decl);3463 _ = mod.compile_log_decls.swapRemove(decl);
3455 mod.deleteDeclExports(decl);3464 mod.deleteDeclExports(decl);
3456 if (decl.hasLinkAllocation()) {3465 mod.comp.bin_file.freeDecl(decl);
3457 mod.comp.bin_file.freeDecl(decl);3466
3467 if (decl.has_tv) {
3468 if (decl.getInnerNamespace()) |namespace| {
3469 try namespace.deleteAllDecls(mod, outdated_decls);
3470 }
3471 decl.clearValues(gpa);
3458 }3472 }
34593473
3460 decl.destroy(mod);3474 decl.destroy(mod);
...@@ -3827,6 +3841,12 @@ pub fn constIntBig(mod: *Module, arena: *Allocator, src: LazySrcLoc, ty: Type, b...@@ -3827,6 +3841,12 @@ pub fn constIntBig(mod: *Module, arena: *Allocator, src: LazySrcLoc, ty: Type, b
3827 }3841 }
3828}3842}
38293843
3844pub fn deleteAnonDecl(mod: *Module, scope: *Scope, decl: *Decl) void {
3845 const scope_decl = scope.ownerDecl().?;
3846 scope_decl.namespace.anon_decls.swapRemoveAssertDiscard(decl);
3847 decl.destroy(mod);
3848}
3849
3830/// Takes ownership of `name` even if it returns an error.3850/// Takes ownership of `name` even if it returns an error.
3831pub fn createAnonymousDeclNamed(3851pub fn createAnonymousDeclNamed(
3832 mod: *Module,3852 mod: *Module,
...@@ -4814,6 +4834,8 @@ pub fn processOutdatedAndDeletedDecls(mod: *Module) !void {...@@ -4814,6 +4834,8 @@ pub fn processOutdatedAndDeletedDecls(mod: *Module) !void {
4814 for (file.outdated_decls.items) |decl| {4834 for (file.outdated_decls.items) |decl| {
4815 outdated_decls.putAssumeCapacity(decl, {});4835 outdated_decls.putAssumeCapacity(decl, {});
4816 }4836 }
4837 file.outdated_decls.clearRetainingCapacity();
4838
4817 // Handle explicitly deleted decls from the source code. This is one of two4839 // Handle explicitly deleted decls from the source code. This is one of two
4818 // places that Decl deletions happen. The other is in `Compilation`, after4840 // places that Decl deletions happen. The other is in `Compilation`, after
4819 // `performAllTheWork`, where we iterate over `Module.deletion_set` and4841 // `performAllTheWork`, where we iterate over `Module.deletion_set` and
...@@ -4824,12 +4846,9 @@ pub fn processOutdatedAndDeletedDecls(mod: *Module) !void {...@@ -4824,12 +4846,9 @@ pub fn processOutdatedAndDeletedDecls(mod: *Module) !void {
4824 // deletion set at this time.4846 // deletion set at this time.
4825 for (file.deleted_decls.items) |decl| {4847 for (file.deleted_decls.items) |decl| {
4826 log.debug("deleted from source: {*} ({s})", .{ decl, decl.name });4848 log.debug("deleted from source: {*} ({s})", .{ decl, decl.name });
4827 if (decl.deletion_flag) {
4828 log.debug("{*} ({s}) redundantly in deletion set; removing", .{ decl, decl.name });
4829 mod.deletion_set.removeAssertDiscard(decl);
4830 }
4831 try mod.deleteDecl(decl, &outdated_decls);4849 try mod.deleteDecl(decl, &outdated_decls);
4832 }4850 }
4851 file.deleted_decls.clearRetainingCapacity();
4833 }4852 }
4834 // Finally we can queue up re-analysis tasks after we have processed4853 // Finally we can queue up re-analysis tasks after we have processed
4835 // the deleted decls.4854 // the deleted decls.
src/Sema.zig+6
...@@ -725,6 +725,7 @@ fn zirStructDecl(...@@ -725,6 +725,7 @@ fn zirStructDecl(
725 .ty = Type.initTag(.type),725 .ty = Type.initTag(.type),
726 .val = struct_val,726 .val = struct_val,
727 }, type_name);727 }, type_name);
728 errdefer sema.mod.deleteAnonDecl(&block.base, new_decl);
728 struct_obj.* = .{729 struct_obj.* = .{
729 .owner_decl = new_decl,730 .owner_decl = new_decl,
730 .fields = .{},731 .fields = .{},
...@@ -842,6 +843,8 @@ fn zirEnumDecl(...@@ -842,6 +843,8 @@ fn zirEnumDecl(
842 .ty = Type.initTag(.type),843 .ty = Type.initTag(.type),
843 .val = enum_val,844 .val = enum_val,
844 }, type_name);845 }, type_name);
846 errdefer sema.mod.deleteAnonDecl(&block.base, new_decl);
847
845 enum_obj.* = .{848 enum_obj.* = .{
846 .owner_decl = new_decl,849 .owner_decl = new_decl,
847 .tag_ty = tag_ty,850 .tag_ty = tag_ty,
...@@ -1005,6 +1008,7 @@ fn zirUnionDecl(...@@ -1005,6 +1008,7 @@ fn zirUnionDecl(
1005 .ty = Type.initTag(.type),1008 .ty = Type.initTag(.type),
1006 .val = union_val,1009 .val = union_val,
1007 }, type_name);1010 }, type_name);
1011 errdefer sema.mod.deleteAnonDecl(&block.base, new_decl);
1008 union_obj.* = .{1012 union_obj.* = .{
1009 .owner_decl = new_decl,1013 .owner_decl = new_decl,
1010 .tag_ty = Type.initTag(.@"null"),1014 .tag_ty = Type.initTag(.@"null"),
...@@ -1071,6 +1075,7 @@ fn zirErrorSetDecl(...@@ -1071,6 +1075,7 @@ fn zirErrorSetDecl(
1071 .ty = Type.initTag(.type),1075 .ty = Type.initTag(.type),
1072 .val = error_set_val,1076 .val = error_set_val,
1073 }, type_name);1077 }, type_name);
1078 errdefer sema.mod.deleteAnonDecl(&block.base, new_decl);
1074 const names = try new_decl_arena.allocator.alloc([]const u8, fields.len);1079 const names = try new_decl_arena.allocator.alloc([]const u8, fields.len);
1075 for (fields) |str_index, i| {1080 for (fields) |str_index, i| {
1076 names[i] = try new_decl_arena.allocator.dupe(u8, sema.code.nullTerminatedString(str_index));1081 names[i] = try new_decl_arena.allocator.dupe(u8, sema.code.nullTerminatedString(str_index));
...@@ -1575,6 +1580,7 @@ fn zirStr(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*In...@@ -1575,6 +1580,7 @@ fn zirStr(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*In
1575 .ty = decl_ty,1580 .ty = decl_ty,
1576 .val = decl_val,1581 .val = decl_val,
1577 });1582 });
1583 errdefer sema.mod.deleteAnonDecl(&block.base, new_decl);
1578 try new_decl.finalizeNewArena(&new_decl_arena);1584 try new_decl.finalizeNewArena(&new_decl_arena);
1579 return sema.analyzeDeclRef(block, .unneeded, new_decl);1585 return sema.analyzeDeclRef(block, .unneeded, new_decl);
1580}1586}
src/link/C.zig+1-1
...@@ -79,7 +79,7 @@ pub fn deinit(self: *C) void {...@@ -79,7 +79,7 @@ pub fn deinit(self: *C) void {
79pub fn allocateDeclIndexes(self: *C, decl: *Module.Decl) !void {}79pub fn allocateDeclIndexes(self: *C, decl: *Module.Decl) !void {}
8080
81pub fn freeDecl(self: *C, decl: *Module.Decl) void {81pub fn freeDecl(self: *C, decl: *Module.Decl) void {
82 self.decl_table.removeAssertDiscard(decl);82 _ = self.decl_table.swapRemove(decl);
83 decl.link.c.code.deinit(self.base.allocator);83 decl.link.c.code.deinit(self.base.allocator);
84 decl.fn_link.c.fwd_decl.deinit(self.base.allocator);84 decl.fn_link.c.fwd_decl.deinit(self.base.allocator);
85 var it = decl.fn_link.c.typedefs.iterator();85 var it = decl.fn_link.c.typedefs.iterator();
test/stage2/cbe.zig+4-4
...@@ -708,7 +708,7 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -708,7 +708,7 @@ pub fn addCases(ctx: *TestContext) !void {
708 \\ const b = @intToEnum(E, 3);708 \\ const b = @intToEnum(E, 3);
709 \\}709 \\}
710 , &.{710 , &.{
711 ":3:15: error: enum 'E' has no tag with value 3",711 ":3:15: error: enum 'test_case.E' has no tag with value 3",
712 ":1:11: note: enum declared here",712 ":1:11: note: enum declared here",
713 });713 });
714714
...@@ -724,7 +724,7 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -724,7 +724,7 @@ pub fn addCases(ctx: *TestContext) !void {
724 , &.{724 , &.{
725 ":4:5: error: switch must handle all possibilities",725 ":4:5: error: switch must handle all possibilities",
726 ":4:5: note: unhandled enumeration value: 'b'",726 ":4:5: note: unhandled enumeration value: 'b'",
727 ":1:11: note: enum 'E' declared here",727 ":1:11: note: enum 'test_case.E' declared here",
728 });728 });
729729
730 case.addError(730 case.addError(
...@@ -779,7 +779,7 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -779,7 +779,7 @@ pub fn addCases(ctx: *TestContext) !void {
779 \\ var x = E.d;779 \\ var x = E.d;
780 \\}780 \\}
781 , &.{781 , &.{
782 ":3:14: error: enum 'E' has no member named 'd'",782 ":3:14: error: enum 'test_case.E' has no member named 'd'",
783 ":1:11: note: enum declared here",783 ":1:11: note: enum declared here",
784 });784 });
785785
...@@ -789,7 +789,7 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -789,7 +789,7 @@ pub fn addCases(ctx: *TestContext) !void {
789 \\ var x: E = .d;789 \\ var x: E = .d;
790 \\}790 \\}
791 , &.{791 , &.{
792 ":3:17: error: enum 'E' has no field named 'd'",792 ":3:17: error: enum 'test_case.E' has no field named 'd'",
793 ":1:11: note: enum declared here",793 ":1:11: note: enum declared here",
794 });794 });
795 }795 }