aboutsummaryrefslogtreecommitdiff
path: root/src/Module.zig
diff options
context:
space:
mode:
authorAndrew Kelley <andrew@ziglang.org>2021-04-30 11:07:31 -0700
committerAndrew Kelley <andrew@ziglang.org>2021-04-30 11:07:31 -0700
commit2d8d681b5ee34663aa87f0583f7b1a012b17d5b4 (patch)
treefefb28fbbdc9ab0c94c41a8609402077307ce024 /src/Module.zig
parent5d696b0706de2ac267cb774ef39e0d81131d5c38 (diff)
downloadzig-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/Module.zig')
-rw-r--r--src/Module.zig155
1 files changed, 83 insertions, 72 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;