aboutsummaryrefslogtreecommitdiff
path: root/src/Module.zig
diff options
context:
space:
mode:
Diffstat (limited to 'src/Module.zig')
-rw-r--r--src/Module.zig179
1 files changed, 88 insertions, 91 deletions
diff --git a/src/Module.zig b/src/Module.zig
index 3d0f22b46c..0601245db3 100644
--- a/src/Module.zig
+++ b/src/Module.zig
@@ -154,9 +154,7 @@ pub const DeclPlusEmitH = struct {
};
pub const Decl = struct {
- /// For declarations that have corresponding source code, this is identical to
- /// `getName().?`. For anonymous declarations this is allocated with Module's
- /// allocator.
+ /// Allocated with Module's allocator; outlives the ZIR code.
name: [*:0]const u8,
/// The most recent Type of the Decl after a successful semantic analysis.
/// Populated when `has_tv`.
@@ -270,13 +268,7 @@ pub const Decl = struct {
);
pub fn clearName(decl: *Decl, gpa: *Allocator) void {
- // name could be allocated in the ZIR or it could be owned by gpa.
- const file = decl.namespace.file_scope;
- const string_table_start = @ptrToInt(file.zir.string_bytes.ptr);
- const string_table_end = string_table_start + file.zir.string_bytes.len;
- if (@ptrToInt(decl.name) < string_table_start or @ptrToInt(decl.name) >= string_table_end) {
- gpa.free(mem.spanZ(decl.name));
- }
+ gpa.free(mem.spanZ(decl.name));
decl.name = undefined;
}
@@ -285,7 +277,7 @@ pub const Decl = struct {
log.debug("destroy {*} ({s})", .{ decl, decl.name });
decl.clearName(gpa);
if (decl.has_tv) {
- if (decl.val.getTypeNamespace()) |namespace| {
+ if (decl.getInnerNamespace()) |namespace| {
if (namespace.getDecl() == decl) {
namespace.clearDecls(module);
}
@@ -308,6 +300,9 @@ pub const Decl = struct {
func.deinit(gpa);
gpa.destroy(func);
}
+ if (decl.getVariable()) |variable| {
+ gpa.destroy(variable);
+ }
if (decl.value_arena) |arena_state| {
arena_state.promote(gpa).deinit();
decl.value_arena = null;
@@ -472,6 +467,47 @@ pub const Decl = struct {
return func;
}
+ pub fn getVariable(decl: *Decl) ?*Var {
+ if (!decl.has_tv) return null;
+ const variable = (decl.val.castTag(.variable) orelse return null).data;
+ if (variable.owner_decl != decl) return null;
+ return variable;
+ }
+
+ /// Gets the namespace that this Decl creates by being a struct, union,
+ /// enum, or opaque.
+ /// Only returns it if the Decl is the owner.
+ pub fn getInnerNamespace(decl: *Decl) ?*Scope.Namespace {
+ if (!decl.has_tv) return null;
+ const ty = (decl.val.castTag(.ty) orelse return null).data;
+ switch (ty.tag()) {
+ .@"struct" => {
+ const struct_obj = ty.castTag(.@"struct").?.data;
+ if (struct_obj.owner_decl != decl) return null;
+ return &struct_obj.namespace;
+ },
+ .enum_full => {
+ const enum_obj = ty.castTag(.enum_full).?.data;
+ if (enum_obj.owner_decl != decl) return null;
+ return &enum_obj.namespace;
+ },
+ .empty_struct => {
+ // design flaw, can't verify the owner is this decl
+ @panic("TODO can't implement getInnerNamespace for this type");
+ },
+ .@"opaque" => {
+ @panic("TODO opaque types");
+ },
+ .@"union", .union_tagged => {
+ const union_obj = ty.cast(Type.Payload.Union).?.data;
+ if (union_obj.owner_decl != decl) return null;
+ return &union_obj.namespace;
+ },
+
+ else => return null,
+ }
+ }
+
pub fn dump(decl: *Decl) void {
const loc = std.zig.findLineColumn(decl.scope.source.bytes, decl.src);
std.debug.print("{s}:{d}:{d} name={s} status={s}", .{
@@ -504,6 +540,23 @@ pub const Decl = struct {
fn removeDependency(decl: *Decl, other: *Decl) void {
decl.dependencies.removeAssertDiscard(other);
}
+
+ fn hasLinkAllocation(decl: Decl) bool {
+ return switch (decl.analysis) {
+ .unreferenced,
+ .in_progress,
+ .dependency_failure,
+ .sema_failure,
+ .sema_failure_retryable,
+ .codegen_failure,
+ .codegen_failure_retryable,
+ => false,
+
+ .complete,
+ .outdated,
+ => true,
+ };
+ }
};
/// This state is attached to every Decl when Module emit_h is non-null.
@@ -831,9 +884,8 @@ pub const Scope = struct {
/// Direct children of the namespace. Used during an update to detect
/// which decls have been added/removed from source.
/// Declaration order is preserved via entry order.
- /// Key memory references the string table of the containing `File` ZIR.
+ /// Key memory is owned by `decl.name`.
/// TODO save memory with https://github.com/ziglang/zig/issues/8619.
- /// Does not contain anonymous decls.
decls: std.StringArrayHashMapUnmanaged(*Decl) = .{},
pub fn deinit(ns: *Namespace, mod: *Module) void {
@@ -2468,8 +2520,6 @@ pub fn astGenFile(mod: *Module, file: *Scope.File, prog_node: *std.Progress.Node
/// * Decl.zir_index
/// * Fn.zir_body_inst
/// * Decl.zir_decl_index
-/// * Decl.name
-/// * Namespace.decl keys
fn updateZirRefs(gpa: *Allocator, file: *Scope.File, old_zir: Zir) !void {
const new_zir = file.zir;
@@ -2484,18 +2534,6 @@ fn updateZirRefs(gpa: *Allocator, file: *Scope.File, old_zir: Zir) !void {
try mapOldZirToNew(gpa, old_zir, new_zir, &inst_map, &extra_map);
- // Build string table for new ZIR.
- var string_table: std.StringHashMapUnmanaged(u32) = .{};
- defer string_table.deinit(gpa);
- {
- var i: usize = 2;
- while (i < new_zir.string_bytes.len) {
- const string = new_zir.nullTerminatedString(i);
- try string_table.put(gpa, string, @intCast(u32, i));
- i += string.len + 1;
- }
- }
-
// Walk the Decl graph, updating ZIR indexes, strings, and populating
// the deleted and outdated lists.
@@ -2523,12 +2561,6 @@ fn updateZirRefs(gpa: *Allocator, file: *Scope.File, old_zir: Zir) !void {
try file.deleted_decls.append(gpa, decl);
continue;
};
- const new_name_index = string_table.get(mem.spanZ(decl.name)) orelse {
- try file.deleted_decls.append(gpa, decl);
- continue;
- };
- decl.name = new_zir.nullTerminatedString(new_name_index).ptr;
-
const new_hash = decl.contentsHashZir(new_zir);
if (!std.zig.srcHashEql(old_hash, new_hash)) {
try file.outdated_decls.append(gpa, decl);
@@ -2558,16 +2590,9 @@ fn updateZirRefs(gpa: *Allocator, file: *Scope.File, old_zir: Zir) !void {
};
}
- if (decl.val.getTypeNamespace()) |namespace| {
+ if (decl.getInnerNamespace()) |namespace| {
for (namespace.decls.items()) |*entry| {
const sub_decl = entry.value;
- if (sub_decl.zir_decl_index != 0) {
- const new_key_index = string_table.get(entry.key) orelse {
- try file.deleted_decls.append(gpa, sub_decl);
- continue;
- };
- entry.key = new_zir.nullTerminatedString(new_key_index);
- }
try decl_stack.append(gpa, sub_decl);
}
}
@@ -2936,46 +2961,14 @@ fn semaDecl(mod: *Module, decl: *Decl) !bool {
}
return type_changed or is_inline != prev_is_inline;
} else {
- const is_mutable = decl_tv.val.tag() == .variable;
-
- var is_threadlocal = false; // TODO implement threadlocal variables
- var is_extern = false; // TODO implement extern variables
-
- if (is_mutable and !decl_tv.ty.isValidVarType(is_extern)) {
- return mod.fail(
- &block_scope.base,
- src, // TODO point at the mut token
- "variable of type '{}' must be const",
- .{decl_tv.ty},
- );
- }
-
var type_changed = true;
if (decl.has_tv) {
type_changed = !decl.ty.eql(decl_tv.ty);
decl.clearValues(gpa);
}
- const copied_val = try decl_tv.val.copy(&decl_arena.allocator);
- const is_extern_fn = copied_val.tag() == .extern_fn;
-
- // TODO: also avoid allocating this Var structure if `!is_mutable`.
- // I think this will require adjusting Sema to copy the value or something
- // like that; otherwise it causes use of undefined value when freeing resources.
- const decl_val: Value = if (is_extern_fn) copied_val else blk: {
- const new_variable = try decl_arena.allocator.create(Var);
- new_variable.* = .{
- .owner_decl = decl,
- .init = copied_val,
- .is_extern = is_extern,
- .is_mutable = is_mutable,
- .is_threadlocal = is_threadlocal,
- };
- break :blk try Value.Tag.variable.create(&decl_arena.allocator, new_variable);
- };
-
decl.ty = try decl_tv.ty.copy(&decl_arena.allocator);
- decl.val = decl_val;
+ decl.val = try decl_tv.val.copy(&decl_arena.allocator);
decl.align_val = try align_val.copy(&decl_arena.allocator);
decl.linksection_val = try linksection_val.copy(&decl_arena.allocator);
decl.has_tv = true;
@@ -3211,7 +3204,8 @@ fn scanDecl(iter: *ScanDeclIter, decl_sub_index: usize, flags: u4) InnerError!vo
const decl_node = iter.parent_decl.relativeToNodeIndex(decl_block_inst_data.src_node);
// Every Decl needs a name.
- const raw_decl_name: [:0]const u8 = switch (decl_name_index) {
+ var is_named_test = false;
+ const decl_name: [:0]const u8 = switch (decl_name_index) {
0 => name: {
if (is_exported) {
const i = iter.usingnamespace_index;
@@ -3228,24 +3222,28 @@ fn scanDecl(iter: *ScanDeclIter, decl_sub_index: usize, flags: u4) InnerError!vo
iter.unnamed_test_index += 1;
break :name try std.fmt.allocPrintZ(gpa, "test_{d}", .{i});
},
- else => zir.nullTerminatedString(decl_name_index),
- };
- const decl_name = if (raw_decl_name.len != 0) raw_decl_name else name: {
- const test_name = zir.nullTerminatedString(decl_name_index + 1);
- break :name try std.fmt.allocPrintZ(gpa, "test.{s}", .{test_name});
+ else => name: {
+ const raw_name = zir.nullTerminatedString(decl_name_index);
+ if (raw_name.len == 0) {
+ is_named_test = true;
+ const test_name = zir.nullTerminatedString(decl_name_index + 1);
+ break :name try std.fmt.allocPrintZ(gpa, "test.{s}", .{test_name});
+ } else {
+ break :name try gpa.dupeZ(u8, raw_name);
+ }
+ },
};
// We create a Decl for it regardless of analysis status.
const gop = try namespace.decls.getOrPut(gpa, decl_name);
if (!gop.found_existing) {
const new_decl = try mod.allocateNewDecl(namespace, decl_node);
- log.debug("scan new decl {*} ({s}) into {*}", .{ new_decl, decl_name, namespace });
+ log.debug("scan new {*} ({s}) into {*}", .{ new_decl, decl_name, namespace });
new_decl.src_line = line;
new_decl.name = decl_name;
gop.entry.value = new_decl;
// Exported decls, comptime decls, usingnamespace decls, and
// test decls if in test mode, get analyzed.
- const is_named_test = raw_decl_name.len == 0;
const want_analysis = is_exported or switch (decl_name_index) {
0 => true, // comptime decl
1 => mod.comp.bin_file.options.is_test, // test decl
@@ -3261,17 +3259,15 @@ fn scanDecl(iter: *ScanDeclIter, decl_sub_index: usize, flags: u4) InnerError!vo
new_decl.zir_decl_index = @intCast(u32, decl_sub_index);
return;
}
+ gpa.free(decl_name);
const decl = gop.entry.value;
- log.debug("scan existing decl {*} ({s}) of {*}", .{ decl, decl_name, namespace });
+ log.debug("scan existing {*} ({s}) of {*}", .{ decl, decl_name, namespace });
// Update the AST node of the decl; even if its contents are unchanged, it may
// have been re-ordered.
const prev_src_node = decl.src_node;
decl.src_node = decl_node;
decl.src_line = line;
- decl.clearName(gpa);
- decl.name = decl_name;
-
decl.is_pub = is_pub;
decl.is_exported = is_exported;
decl.has_align = has_align;
@@ -3305,14 +3301,13 @@ pub fn deleteDecl(
const tracy = trace(@src());
defer tracy.end();
- log.debug("deleting decl '{s}'", .{decl.name});
+ log.debug("deleting {*} ({s})", .{ decl, decl.name });
if (outdated_decls) |map| {
_ = map.swapRemove(decl);
- try map.ensureCapacity(map.count() + decl.dependants.count());
+ try map.ensureUnusedCapacity(decl.dependants.count());
}
- try mod.deletion_set.ensureCapacity(mod.gpa, mod.deletion_set.count() +
- decl.dependencies.count());
+ try mod.deletion_set.ensureUnusedCapacity(mod.gpa, decl.dependencies.count());
// Remove from the namespace it resides in.
decl.namespace.removeDecl(decl);
@@ -3354,7 +3349,9 @@ pub fn deleteDecl(
}
_ = mod.compile_log_decls.swapRemove(decl);
mod.deleteDeclExports(decl);
- mod.comp.bin_file.freeDecl(decl);
+ if (decl.hasLinkAllocation()) {
+ mod.comp.bin_file.freeDecl(decl);
+ }
decl.destroy(mod);
}