diff options
| author | Andrew Kelley <andrew@ziglang.org> | 2021-04-30 11:07:31 -0700 |
|---|---|---|
| committer | Andrew Kelley <andrew@ziglang.org> | 2021-04-30 11:07:31 -0700 |
| commit | 2d8d681b5ee34663aa87f0583f7b1a012b17d5b4 (patch) | |
| tree | fefb28fbbdc9ab0c94c41a8609402077307ce024 /src | |
| parent | 5d696b0706de2ac267cb774ef39e0d81131d5c38 (diff) | |
| download | zig-2d8d681b5ee34663aa87f0583f7b1a012b17d5b4.tar.gz zig-2d8d681b5ee34663aa87f0583f7b1a012b17d5b4.zip | |
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.
Diffstat (limited to 'src')
| -rw-r--r-- | src/Module.zig | 155 | ||||
| -rw-r--r-- | src/Sema.zig | 76 | ||||
| -rw-r--r-- | src/value.zig | 8 |
3 files changed, 138 insertions, 101 deletions
diff --git a/src/Module.zig b/src/Module.zig index 5ef0448849..f0810d00a5 100644 --- a/src/Module.zig +++ b/src/Module.zig @@ -276,11 +276,16 @@ pub const Decl = struct { pub fn destroy(decl: *Decl, module: *Module) void { const gpa = module.gpa; + log.debug("destroy Decl {s}", .{decl.name}); decl.clearName(gpa); if (decl.has_tv) { if (decl.val.castTag(.function)) |payload| { const func = payload.data; func.deinit(gpa); + } else if (decl.val.getTypeNamespace()) |namespace| { + if (namespace.getDecl() == decl) { + namespace.clearDecls(module); + } } decl.clearValues(gpa); } @@ -303,6 +308,13 @@ pub const Decl = struct { } } + pub fn finalizeNewArena(decl: *Decl, arena: *std.heap.ArenaAllocator) !void { + assert(decl.value_arena == null); + const arena_state = try arena.allocator.create(std.heap.ArenaAllocator.State); + arena_state.* = arena.state; + decl.value_arena = arena_state; + } + /// This name is relative to the containing namespace of the decl. /// The memory is owned by the containing File ZIR. pub fn getName(decl: Decl) ?[:0]const u8 { @@ -719,13 +731,20 @@ pub const Scope = struct { decls: std.StringArrayHashMapUnmanaged(*Decl) = .{}, pub fn deinit(ns: *Namespace, mod: *Module) void { + ns.clearDecls(mod); + ns.* = undefined; + } + + pub fn clearDecls(ns: *Namespace, mod: *Module) void { const gpa = mod.gpa; - for (ns.decls.items()) |entry| { + var decls = ns.decls; + ns.decls = .{}; + + for (decls.items()) |entry| { entry.value.destroy(mod); } - ns.decls.deinit(gpa); - ns.* = undefined; + decls.deinit(gpa); } pub fn removeDecl(ns: *Namespace, child: *Decl) void { @@ -775,14 +794,9 @@ pub const Scope = struct { /// Package that this file is a part of, managed externally. pkg: *Package, /// The namespace of the struct that represents this file. - /// Populated only when status is success. + /// Populated only when status is `success_air`. /// Owned by its owner Decl Value. namespace: *Namespace, - /// All namespaces that this file contains. This is here so that - /// when a file is updated, and new ZIR code is generated, the - /// old and new ZIR code can be compared side by side and references - /// to old ZIR updated to new ZIR, and a changelist generated. - namespace_set: std.AutoArrayHashMapUnmanaged(*Namespace, void) = .{}, pub fn unload(file: *File, gpa: *Allocator) void { file.unloadTree(gpa); @@ -813,6 +827,10 @@ pub const Scope = struct { pub fn deinit(file: *File, mod: *Module) void { const gpa = mod.gpa; + log.debug("deinit File {s}", .{file.sub_file_path}); + if (file.status == .success_air) { + file.namespace.getDecl().destroy(mod); + } gpa.free(file.sub_file_path); file.unload(gpa); file.* = undefined; @@ -866,6 +884,18 @@ pub const Scope = struct { gpa.destroy(file); } + pub fn fullyQualifiedNameZ(file: File, gpa: *Allocator) ![:0]u8 { + // Convert all the slashes into dots and truncate the extension. + const ext = std.fs.path.extension(file.sub_file_path); + const noext = file.sub_file_path[0 .. file.sub_file_path.len - ext.len]; + const duped = try gpa.dupeZ(u8, noext); + for (duped) |*byte| switch (byte.*) { + '/', '\\' => byte.* = '.', + else => continue, + }; + return duped; + } + pub fn dumpSrc(file: *File, src: LazySrcLoc) void { const loc = std.zig.findLineColumn(file.source.bytes, src); 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 { assert(file.zir_loaded); const gpa = mod.gpa; - var decl_arena = std.heap.ArenaAllocator.init(gpa); - defer decl_arena.deinit(); - - // We need a Decl to pass to Sema and collect dependencies. But ultimately we - // want to pass them on to the Decl for the struct that represents the file. - var tmp_namespace: Scope.Namespace = .{ - .parent = null, - .file_scope = file, - .ty = Type.initTag(.type), + var new_decl_arena = std.heap.ArenaAllocator.init(gpa); + errdefer new_decl_arena.deinit(); + + const struct_obj = try new_decl_arena.allocator.create(Module.Struct); + const struct_ty = try Type.Tag.@"struct".create(&new_decl_arena.allocator, struct_obj); + const struct_val = try Value.Tag.ty.create(&new_decl_arena.allocator, struct_ty); + struct_obj.* = .{ + .owner_decl = undefined, // set below + .fields = .{}, + .node_offset = 0, // it's the struct for the root file + .namespace = .{ + .parent = null, + .ty = struct_ty, + .file_scope = file, + }, }; - var top_decl: Decl = .{ - .name = "", - .namespace = &tmp_namespace, - .generation = mod.generation, - .src_node = 0, // the root AST node for the file - .analysis = .in_progress, - .deletion_flag = false, - .is_pub = true, - .is_exported = false, - .has_linksection = false, - .has_align = false, - .link = undefined, // don't try to codegen this - .fn_link = undefined, // not a function - .zir_decl_index = undefined, + file.namespace = &struct_obj.namespace; + const new_decl = try mod.allocateNewDecl(&struct_obj.namespace, 0); + struct_obj.owner_decl = new_decl; + new_decl.name = try file.fullyQualifiedNameZ(gpa); + new_decl.is_pub = true; + new_decl.is_exported = false; + new_decl.has_align = false; + new_decl.has_linksection = false; + new_decl.zir_decl_index = undefined; + new_decl.ty = struct_ty; + new_decl.val = struct_val; + new_decl.has_tv = true; + new_decl.analysis = .complete; + new_decl.generation = mod.generation; - .has_tv = false, - .ty = undefined, - .val = undefined, - .align_val = undefined, - .linksection_val = undefined, - }; - defer top_decl.dependencies.deinit(gpa); + var sema_arena = std.heap.ArenaAllocator.init(gpa); + defer sema_arena.deinit(); var sema: Sema = .{ .mod = mod, .gpa = gpa, - .arena = &decl_arena.allocator, + .arena = &sema_arena.allocator, .code = file.zir, // TODO use a map because this array is too big - .inst_map = try decl_arena.allocator.alloc(*ir.Inst, file.zir.instructions.len), - .owner_decl = &top_decl, - .namespace = &tmp_namespace, + .inst_map = try sema_arena.allocator.alloc(*ir.Inst, file.zir.instructions.len), + .owner_decl = new_decl, + .namespace = &struct_obj.namespace, .func = null, .owner_func = null, .param_inst_list = &.{}, @@ -3346,7 +3377,7 @@ pub fn semaFile(mod: *Module, file: *Scope.File) InnerError!void { var block_scope: Scope.Block = .{ .parent = null, .sema = &sema, - .src_decl = &top_decl, + .src_decl = new_decl, .instructions = .{}, .inlining = null, .is_comptime = true, @@ -3355,23 +3386,9 @@ pub fn semaFile(mod: *Module, file: *Scope.File) InnerError!void { const main_struct_inst = file.zir.extra[@enumToInt(Zir.ExtraIndex.main_struct)] - @intCast(u32, Zir.Inst.Ref.typed_value_map.len); - const air_inst = try sema.zirStructDecl(&block_scope, main_struct_inst, .Auto); - assert(air_inst.ty.zigTypeTag() == .Type); - const val = air_inst.value().?; - const struct_ty = try val.toType(&decl_arena.allocator); - const struct_decl = struct_ty.getOwnerDecl(); - - file.namespace = struct_ty.getNamespace().?; - file.namespace.parent = null; - - // Transfer the dependencies to `owner_decl`. - assert(top_decl.dependants.count() == 0); - for (top_decl.dependencies.items()) |entry| { - const dep = entry.key; - dep.removeDependant(&top_decl); - if (dep == struct_decl) continue; - _ = try mod.declareDeclDependency(struct_decl, dep); - } + try sema.analyzeStructDecl(&block_scope, &new_decl_arena, new_decl, main_struct_inst, .Auto, struct_obj); + try new_decl.finalizeNewArena(&new_decl_arena); + file.status = .success_air; } @@ -4319,23 +4336,17 @@ pub fn constIntBig(mod: *Module, arena: *Allocator, src: LazySrcLoc, ty: Type, b } } -pub fn createAnonymousDecl( - mod: *Module, - scope: *Scope, - decl_arena: *std.heap.ArenaAllocator, - typed_value: TypedValue, -) !*Decl { +pub fn createAnonymousDecl(mod: *Module, scope: *Scope, typed_value: TypedValue) !*Decl { const name_index = mod.getNextAnonNameIndex(); const scope_decl = scope.ownerDecl().?; + const namespace = scope_decl.namespace; + try namespace.decls.ensureCapacity(mod.gpa, namespace.decls.count() + 1); const name = try std.fmt.allocPrintZ(mod.gpa, "{s}__anon_{d}", .{ scope_decl.name, name_index }); errdefer mod.gpa.free(name); - const namespace = scope_decl.namespace; const new_decl = try mod.allocateNewDecl(namespace, scope_decl.src_node); - new_decl.name = name; + namespace.decls.putAssumeCapacityNoClobber(name, new_decl); - const decl_arena_state = try decl_arena.allocator.create(std.heap.ArenaAllocator.State); - - decl_arena_state.* = decl_arena.state; + new_decl.name = name; new_decl.ty = typed_value.ty; new_decl.val = typed_value.val; new_decl.has_tv = true; diff --git a/src/Sema.zig b/src/Sema.zig index d1893a82f9..bb9a608b7c 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -634,15 +634,19 @@ fn zirCoerceResultPtr(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) In return sema.mod.fail(&block.base, sema.src, "TODO implement zirCoerceResultPtr", .{}); } -pub fn zirStructDecl( +pub fn analyzeStructDecl( sema: *Sema, block: *Scope.Block, + new_decl_arena: *std.heap.ArenaAllocator, + new_decl: *Decl, inst: Zir.Inst.Index, layout: std.builtin.TypeInfo.ContainerLayout, -) InnerError!*Inst { + struct_obj: *Module.Struct, +) InnerError!void { const tracy = trace(@src()); defer tracy.end(); + const mod = sema.mod; const gpa = sema.gpa; const inst_data = sema.code.instructions.items(.data)[inst].pl_node; const src = inst_data.src(); @@ -650,27 +654,7 @@ pub fn zirStructDecl( const fields_len = extra.data.fields_len; const decls_len = extra.data.decls_len; - var new_decl_arena = std.heap.ArenaAllocator.init(gpa); - - const struct_obj = try new_decl_arena.allocator.create(Module.Struct); - const struct_ty = try Type.Tag.@"struct".create(&new_decl_arena.allocator, struct_obj); - const struct_val = try Value.Tag.ty.create(&new_decl_arena.allocator, struct_ty); - const new_decl = try sema.mod.createAnonymousDecl(&block.base, &new_decl_arena, .{ - .ty = Type.initTag(.type), - .val = struct_val, - }); - struct_obj.* = .{ - .owner_decl = sema.owner_decl, - .fields = .{}, - .node_offset = inst_data.src_node, - .namespace = .{ - .parent = sema.owner_decl.namespace, - .ty = struct_ty, - .file_scope = block.getFileScope(), - }, - }; - - var extra_index: usize = try sema.mod.scanNamespace( + var extra_index: usize = try mod.scanNamespace( &struct_obj.namespace, extra.end, decls_len, @@ -680,7 +664,7 @@ pub fn zirStructDecl( const body = sema.code.extra[extra_index..][0..extra.data.body_len]; if (fields_len == 0) { assert(body.len == 0); - return sema.analyzeDeclVal(block, src, new_decl); + return; } try struct_obj.fields.ensureCapacity(&new_decl_arena.allocator, fields_len); @@ -691,7 +675,7 @@ pub fn zirStructDecl( // Within the field type, default value, and alignment expressions, the "owner decl" // should be the struct itself. Thus we need a new Sema. var struct_sema: Sema = .{ - .mod = sema.mod, + .mod = mod, .gpa = gpa, .arena = &new_decl_arena.allocator, .code = sema.code, @@ -752,7 +736,7 @@ pub fn zirStructDecl( // This string needs to outlive the ZIR code. const field_name = try new_decl_arena.allocator.dupe(u8, field_name_zir); if (field_type_ref == .none) { - return sema.mod.fail(&block.base, src, "TODO: implement anytype struct field", .{}); + return mod.fail(&block.base, src, "TODO: implement anytype struct field", .{}); } const field_ty: Type = if (field_type_ref == .none) Type.initTag(.noreturn) @@ -788,7 +772,38 @@ pub fn zirStructDecl( gop.entry.value.default_val = (try sema.resolveInstConst(block, src, default_ref)).val; } } +} + +fn zirStructDecl( + sema: *Sema, + block: *Scope.Block, + inst: Zir.Inst.Index, + layout: std.builtin.TypeInfo.ContainerLayout, +) InnerError!*Inst { + const inst_data = sema.code.instructions.items(.data)[inst].pl_node; + const src = inst_data.src(); + + var new_decl_arena = std.heap.ArenaAllocator.init(sema.gpa); + const struct_obj = try new_decl_arena.allocator.create(Module.Struct); + const struct_ty = try Type.Tag.@"struct".create(&new_decl_arena.allocator, struct_obj); + const struct_val = try Value.Tag.ty.create(&new_decl_arena.allocator, struct_ty); + const new_decl = try sema.mod.createAnonymousDecl(&block.base, .{ + .ty = Type.initTag(.type), + .val = struct_val, + }); + struct_obj.* = .{ + .owner_decl = sema.owner_decl, + .fields = .{}, + .node_offset = inst_data.src_node, + .namespace = .{ + .parent = sema.owner_decl.namespace, + .ty = struct_ty, + .file_scope = block.getFileScope(), + }, + }; + try sema.analyzeStructDecl(block, &new_decl_arena, new_decl, inst, layout, struct_obj); + try new_decl.finalizeNewArena(&new_decl_arena); return sema.analyzeDeclVal(block, src, new_decl); } @@ -822,14 +837,14 @@ fn zirEnumDecl( }; const enum_obj = try new_decl_arena.allocator.create(Module.EnumFull); - const enum_ty_payload = try gpa.create(Type.Payload.EnumFull); + const enum_ty_payload = try new_decl_arena.allocator.create(Type.Payload.EnumFull); enum_ty_payload.* = .{ .base = .{ .tag = if (nonexhaustive) .enum_nonexhaustive else .enum_full }, .data = enum_obj, }; const enum_ty = Type.initPayload(&enum_ty_payload.base); const enum_val = try Value.Tag.ty.create(&new_decl_arena.allocator, enum_ty); - const new_decl = try sema.mod.createAnonymousDecl(&block.base, &new_decl_arena, .{ + const new_decl = try sema.mod.createAnonymousDecl(&block.base, .{ .ty = Type.initTag(.type), .val = enum_val, }); @@ -856,6 +871,7 @@ fn zirEnumDecl( const body = sema.code.extra[extra_index..][0..extra.data.body_len]; if (fields_len == 0) { assert(body.len == 0); + try new_decl.finalizeNewArena(&new_decl_arena); return sema.analyzeDeclVal(block, src, new_decl); } @@ -942,6 +958,7 @@ fn zirEnumDecl( } } + try new_decl.finalizeNewArena(&new_decl_arena); return sema.analyzeDeclVal(block, src, new_decl); } @@ -1424,10 +1441,11 @@ fn zirStr(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*In const decl_ty = try Type.Tag.array_u8_sentinel_0.create(&new_decl_arena.allocator, bytes.len); const decl_val = try Value.Tag.bytes.create(&new_decl_arena.allocator, bytes); - const new_decl = try sema.mod.createAnonymousDecl(&block.base, &new_decl_arena, .{ + const new_decl = try sema.mod.createAnonymousDecl(&block.base, .{ .ty = decl_ty, .val = decl_val, }); + try new_decl.finalizeNewArena(&new_decl_arena); return sema.analyzeDeclRef(block, .unneeded, new_decl); } diff --git a/src/value.zig b/src/value.zig index d2d7be3007..04608878c0 100644 --- a/src/value.zig +++ b/src/value.zig @@ -593,6 +593,14 @@ pub const Value = extern union { unreachable; } + /// Returns null if not a type or if the type has no namespace. + pub fn getTypeNamespace(self: Value) ?*Module.Scope.Namespace { + return switch (self.tag()) { + .ty => self.castTag(.ty).?.data.getNamespace(), + else => null, + }; + } + /// Asserts that the value is representable as a type. pub fn toType(self: Value, allocator: *Allocator) !Type { return switch (self.tag()) { |
