authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-04-26 20:41:07-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-04-26 20:41:07-07:00
logbfded492f0c12be2778c566bad0c82dea6ee76cb
tree0942c1ca40f40fe371504fd4818535a9b73f90e5
parent91c317bb9aa906684104db3d73442ab1198a83f4

stage2: rewire the frontend driver to whole-file-zir

* Remove some unused imports in AstGen.zig. I think it would make sense to start decoupling AstGen from the rest of the compiler code, similar to how the tokenizer and parser are decoupled. * AstGen: For decls, move the block_inline instructions to the top of the function so that they get lower ZIR instruction indexes. With this, the block_inline instruction index combined with its corresponding break_inline instruction index can be used to form a ZIR instruction range. This is useful for allocating an array to map ZIR instructions to semantically analyzed instructions. * Module: extract emit-h functionality into a struct, and only allocate it when emit-h is activated. * Module: remove the `decl_table` field. This previously was a table of all Decls in the entire Module. A "name hash" strategy was used to find decls within a given namespace, using this global table. Now, each Namespace has its own map of name to children Decls. - Additionally, there were 3 places that relied on iterating over decl_table in order to function: - C backend and SPIR-V backend. These now have their own decl_table that they keep populated when `updateDecl` and `removeDecl` are called. - emit-h. A `decl_table` field has been added to the new GlobalEmitH struct which is only allocated when emit-h is activated. * Module: fix ZIR serialization/deserialization bug in debug mode having to do with the secret safety tag for untagged unions. There is still an open TODO to investigate a friendlier solution to this problem with the language. * Module: improve deserialization of ZIR to allocate only exactly as much capacity as length in the instructions array so as to not waste space. * Module: move `srcHashEql` to `std.zig` to live next to the definition of `SrcHash` itself. * Module: re-introduce the logic for scanning top level declarations within a namespace. * Compilation: add an `analyze_pkg` Job which is used to kick off the start of semantic analysis by doing the equivalent of `_ = @import("std");`. The `analyze_pkg` job is unconditionally added to the work queue on every update(), with pkg set to the std lib pkg. * Rename TZIR to AIR in a few places. A more comprehensive rename will come later.

8 files changed, 493 insertions(+), 790 deletions(-)

BRANCH_TODO+55-304
......@@ -1,3 +1,6 @@
1 * reimplement semaDecl
2 * use a hash map for instructions because the array is too big
3
14 * keep track of file dependencies/dependants
25 * unload files from memory when a dependency is dropped
36
......@@ -5,6 +8,7 @@
58
69 * get rid of failed_root_src_file
710 * get rid of Scope.DeclRef
11 * get rid of NameHash
812 * handle decl collision with usingnamespace
913 * the decl doing the looking up needs to create a decl dependency
1014 on each usingnamespace decl
......@@ -35,58 +39,6 @@
3539 * AstGen: add result location pointers to function calls
3640 * nested function decl: how to refer to params?
3741
38 * detect when to put cached ZIR into the local cache instead of the global one
39
40 const container_name_hash: Scope.NameHash = if (found_pkg) |pkg|
41 pkg.namespace_hash
42 else
43 std.zig.hashName(cur_pkg.namespace_hash, "/", resolved_path);
44
45 file_scope.* = .{
46 .root_container = .{
47 .parent = null,
48 .file_scope = file_scope,
49 .decls = .{},
50 .ty = struct_ty,
51 .parent_name_hash = container_name_hash,
52 },
53 };
54 mod.analyzeContainer(&file_scope.root_container) catch |err| switch (err) {
55 error.AnalysisFail => {
56 assert(mod.comp.totalErrorCount() != 0);
57 },
58 else => |e| return e,
59 };
60 return file_scope;
61
62
63
64 // Until then we simulate a full cache miss. Source files could have been loaded
65 // for any reason; to force a refresh we unload now.
66 module.unloadFile(module.root_scope);
67 module.failed_root_src_file = null;
68 module.analyzeNamespace(&module.root_scope.root_container) catch |err| switch (err) {
69 error.AnalysisFail => {
70 assert(self.totalErrorCount() != 0);
71 },
72 error.OutOfMemory => return error.OutOfMemory,
73 else => |e| {
74 module.failed_root_src_file = e;
75 },
76 };
77
78 // TODO only analyze imports if they are still referenced
79 for (module.import_table.items()) |entry| {
80 module.unloadFile(entry.value);
81 module.analyzeNamespace(&entry.value.root_container) catch |err| switch (err) {
82 error.AnalysisFail => {
83 assert(self.totalErrorCount() != 0);
84 },
85 else => |e| return e,
86 };
87 }
88
89
9042pub fn createContainerDecl(
9143 mod: *Module,
9244 scope: *Scope,
......@@ -131,123 +83,6 @@ fn getAnonTypeName(mod: *Module, scope: *Scope, base_token: std.zig.ast.TokenInd
13183}
13284
13385
134 const parent_name_hash: Scope.NameHash = if (found_pkg) |pkg|
135 pkg.namespace_hash
136 else
137 std.zig.hashName(cur_pkg.namespace_hash, "/", resolved_path);
138
139 // We need a Decl to pass to AstGen and collect dependencies. But ultimately we
140 // want to pass them on to the Decl for the struct that represents the file.
141 var tmp_namespace: Scope.Namespace = .{
142 .parent = null,
143 .file_scope = new_file,
144 .parent_name_hash = parent_name_hash,
145 .ty = Type.initTag(.type),
146 };
147
148 const tree = try mod.getAstTree(new_file);
149
150
151 const top_decl = try mod.createNewDecl(
152 &tmp_namespace,
153 resolved_path,
154 0,
155 parent_name_hash,
156 std.zig.hashSrc(tree.source),
157 );
158 defer {
159 mod.decl_table.removeAssertDiscard(parent_name_hash);
160 top_decl.destroy(mod);
161 }
162
163 var gen_scope_arena = std.heap.ArenaAllocator.init(gpa);
164 defer gen_scope_arena.deinit();
165
166 var astgen = try AstGen.init(mod, top_decl, &gen_scope_arena.allocator);
167 defer astgen.deinit();
168
169 var gen_scope: Scope.GenZir = .{
170 .force_comptime = true,
171 .parent = &new_file.base,
172 .astgen = &astgen,
173 };
174 defer gen_scope.instructions.deinit(gpa);
175
176 const container_decl: ast.full.ContainerDecl = .{
177 .layout_token = null,
178 .ast = .{
179 .main_token = undefined,
180 .enum_token = null,
181 .members = tree.rootDecls(),
182 .arg = 0,
183 },
184 };
185
186 const struct_decl_ref = try AstGen.structDeclInner(
187 &gen_scope,
188 &gen_scope.base,
189 0,
190 container_decl,
191 .struct_decl,
192 );
193 _ = try gen_scope.addBreak(.break_inline, 0, struct_decl_ref);
194
195 var code = try gen_scope.finish();
196 defer code.deinit(gpa);
197 if (std.builtin.mode == .Debug and mod.comp.verbose_ir) {
198 code.dump(gpa, "import", &gen_scope.base, 0) catch {};
199 }
200
201 var sema: Sema = .{
202 .mod = mod,
203 .gpa = gpa,
204 .arena = &gen_scope_arena.allocator,
205 .code = code,
206 .inst_map = try gen_scope_arena.allocator.alloc(*ir.Inst, code.instructions.len),
207 .owner_decl = top_decl,
208 .namespace = top_decl.namespace,
209 .func = null,
210 .owner_func = null,
211 .param_inst_list = &.{},
212 };
213 var block_scope: Scope.Block = .{
214 .parent = null,
215 .sema = &sema,
216 .src_decl = top_decl,
217 .instructions = .{},
218 .inlining = null,
219 .is_comptime = true,
220 };
221 defer block_scope.instructions.deinit(gpa);
222
223 const init_inst_zir_ref = try sema.rootAsRef(&block_scope);
224 const analyzed_struct_inst = try sema.resolveInst(init_inst_zir_ref);
225 assert(analyzed_struct_inst.ty.zigTypeTag() == .Type);
226 const val = analyzed_struct_inst.value().?;
227 const struct_ty = try val.toType(&gen_scope_arena.allocator);
228 const struct_decl = struct_ty.getOwnerDecl();
229
230 struct_decl.contents_hash = top_decl.contents_hash;
231 new_file.namespace = struct_ty.getNamespace().?;
232 new_file.namespace.parent = null;
233 //new_file.namespace.parent_name_hash = tmp_namespace.parent_name_hash;
234
235 // Transfer the dependencies to `owner_decl`.
236 assert(top_decl.dependants.count() == 0);
237 for (top_decl.dependencies.items()) |entry| {
238 const dep = entry.key;
239 dep.removeDependant(top_decl);
240 if (dep == struct_decl) continue;
241 _ = try mod.declareDeclDependency(struct_decl, dep);
242 }
243
244 return new_file;
245
246
247
248
249
250
25186pub fn analyzeFile(mod: *Module, file: *Scope.File) !void {
25287 // We call `getAstTree` here so that `analyzeFile` has the error set that includes
25388 // file system operations, but `analyzeNamespace` does not.
......@@ -467,38 +302,6 @@ fn astgenAndSemaFn(
467302 }
468303 return type_changed or is_inline != prev_is_inline;
469304}
470
471fn astgenAndSemaVarDecl(
472 mod: *Module,
473 decl: *Decl,
474 tree: ast.Tree,
475 var_decl: ast.full.VarDecl,
476) !bool {
477 const token_tags = tree.tokens.items(.tag);
478
479}
480
481
482 /// Asserts the scope is a child of a File and has an AST tree and returns the tree.
483 pub fn tree(scope: *Scope) *const ast.Tree {
484 switch (scope.tag) {
485 .file => return &scope.cast(File).?.tree,
486 .block => return &scope.cast(Block).?.src_decl.namespace.file_scope.tree,
487 .gen_zir => return scope.cast(GenZir).?.tree(),
488 .local_val => return &scope.cast(LocalVal).?.gen_zir.astgen.decl.namespace.file_scope.tree,
489 .local_ptr => return &scope.cast(LocalPtr).?.gen_zir.astgen.decl.namespace.file_scope.tree,
490 .namespace => return &scope.cast(Namespace).?.file_scope.tree,
491 .decl_ref => return &scope.cast(DeclRef).?.decl.namespace.file_scope.tree,
492 }
493 }
494
495
496 error.FileNotFound => {
497 return mod.fail(&block.base, src, "unable to find '{s}'", .{operand});
498 },
499
500
501
502305 log.debug("extern fn symbol expected in lib '{s}'", .{lib_name_str});
503306 mod.comp.stage1AddLinkLib(lib_name_str) catch |err| {
504307 return mod.failTok(
......@@ -540,86 +343,6 @@ fn astgenAndSemaVarDecl(
540343 );
541344 }
542345
543 if (counts.values == 0 and counts.decls == 0 and arg_inst == .none) {
544 // No explicitly provided tag values and no top level declarations! In this case,
545 // we can construct the enum type in AstGen and it will be correctly shared by all
546 // generic function instantiations and comptime function calls.
547 var new_decl_arena = std.heap.ArenaAllocator.init(gpa);
548 errdefer new_decl_arena.deinit();
549 const arena = &new_decl_arena.allocator;
550
551 var fields_map: std.StringArrayHashMapUnmanaged(void) = .{};
552 try fields_map.ensureCapacity(arena, counts.total_fields);
553 for (container_decl.ast.members) |member_node| {
554 if (member_node == counts.nonexhaustive_node)
555 continue;
556 const member = switch (node_tags[member_node]) {
557 .container_field_init => tree.containerFieldInit(member_node),
558 .container_field_align => tree.containerFieldAlign(member_node),
559 .container_field => tree.containerField(member_node),
560 else => unreachable, // We checked earlier.
561 };
562 const name_token = member.ast.name_token;
563 const tag_name = try mod.identifierTokenStringTreeArena(
564 scope,
565 name_token,
566 tree,
567 arena,
568 );
569 const gop = fields_map.getOrPutAssumeCapacity(tag_name);
570 if (gop.found_existing) {
571 const msg = msg: {
572 const msg = try mod.errMsg(
573 scope,
574 gz.tokSrcLoc(name_token),
575 "duplicate enum tag",
576 .{},
577 );
578 errdefer msg.destroy(gpa);
579 // Iterate to find the other tag. We don't eagerly store it in a hash
580 // map because in the hot path there will be no compile error and we
581 // don't need to waste time with a hash map.
582 const bad_node = for (container_decl.ast.members) |other_member_node| {
583 const other_member = switch (node_tags[other_member_node]) {
584 .container_field_init => tree.containerFieldInit(other_member_node),
585 .container_field_align => tree.containerFieldAlign(other_member_node),
586 .container_field => tree.containerField(other_member_node),
587 else => unreachable, // We checked earlier.
588 };
589 const other_tag_name = try mod.identifierTokenStringTreeArena(
590 scope,
591 other_member.ast.name_token,
592 tree,
593 arena,
594 );
595 if (mem.eql(u8, tag_name, other_tag_name))
596 break other_member_node;
597 } else unreachable;
598 const other_src = gz.nodeSrcLoc(bad_node);
599 try mod.errNote(scope, other_src, msg, "other tag here", .{});
600 break :msg msg;
601 };
602 return mod.failWithOwnedErrorMsg(scope, msg);
603 }
604 }
605 const enum_simple = try arena.create(Module.EnumSimple);
606 enum_simple.* = .{
607 .owner_decl = astgen.decl,
608 .node_offset = astgen.decl.nodeIndexToRelative(node),
609 .fields = fields_map,
610 };
611 const enum_ty = try Type.Tag.enum_simple.create(arena, enum_simple);
612 const enum_val = try Value.Tag.ty.create(arena, enum_ty);
613 const new_decl = try mod.createAnonymousDecl(scope, &new_decl_arena, .{
614 .ty = Type.initTag(.type),
615 .val = enum_val,
616 });
617 const decl_index = try mod.declareDeclDependency(astgen.decl, new_decl);
618 const result = try gz.addDecl(.decl_val, decl_index, node);
619 return rvalue(gz, scope, rl, result, node);
620 }
621
622
623346 if (mod.lookupIdentifier(scope, ident_name)) |decl| {
624347 const msg = msg: {
625348 const msg = try mod.errMsg(
......@@ -687,29 +410,6 @@ fn astgenAndSemaVarDecl(
687410 }
688411 }
689412
690 fn writeFuncExtra(
691 self: *Writer,
692 stream: anytype,
693 inst: Inst.Index,
694 var_args: bool,
695 ) !void {
696 const inst_data = self.code.instructions.items(.data)[inst].pl_node;
697 const src = inst_data.src();
698 const extra = self.code.extraData(Inst.FuncExtra, inst_data.payload_index);
699 const param_types = self.code.refSlice(extra.end, extra.data.param_types_len);
700 const cc = extra.data.cc;
701 const body = self.code.extra[extra.end + param_types.len ..][0..extra.data.body_len];
702 return self.writeFuncCommon(
703 stream,
704 param_types,
705 extra.data.return_type,
706 var_args,
707 cc,
708 body,
709 src,
710 );
711 }
712
713413
714414 const error_set = try arena.create(Module.ErrorSet);
715415 error_set.* = .{
......@@ -732,3 +432,54 @@ fn astgenAndSemaVarDecl(
732432
733433 // when implementing this be sure to add test coverage for the asm return type
734434 // not resolving into a type (the node_offset_asm_ret_ty field of LazySrcLoc)
435
436
437
438pub fn analyzeNamespace(
439 mod: *Module,
440 namespace: *Scope.Namespace,
441 decls: []const ast.Node.Index,
442) InnerError!void {
443 for (decls) |decl_node| switch (node_tags[decl_node]) {
444 .@"comptime" => {
445 const name_index = mod.getNextAnonNameIndex();
446 const name = try std.fmt.allocPrint(mod.gpa, "__comptime_{d}", .{name_index});
447 defer mod.gpa.free(name);
448
449 const contents_hash = std.zig.hashSrc(tree.getNodeSource(decl_node));
450
451 const new_decl = try mod.createNewDecl(namespace, name, decl_node, name_hash, contents_hash);
452 namespace.decls.putAssumeCapacity(new_decl, {});
453 mod.comp.work_queue.writeItemAssumeCapacity(.{ .analyze_decl = new_decl });
454 },
455
456 // Container fields are handled in AstGen.
457 .container_field_init,
458 .container_field_align,
459 .container_field,
460 => continue,
461
462 .test_decl => {
463 if (mod.comp.bin_file.options.is_test) {
464 log.err("TODO: analyze test decl", .{});
465 }
466 },
467 .@"usingnamespace" => {
468 const name_index = mod.getNextAnonNameIndex();
469 const name = try std.fmt.allocPrint(mod.gpa, "__usingnamespace_{d}", .{name_index});
470 defer mod.gpa.free(name);
471
472 const contents_hash = std.zig.hashSrc(tree.getNodeSource(decl_node));
473
474 const new_decl = try mod.createNewDecl(namespace, name, decl_node, name_hash, contents_hash);
475 namespace.decls.putAssumeCapacity(new_decl, {});
476
477 mod.ensureDeclAnalyzed(new_decl) catch |err| switch (err) {
478 error.OutOfMemory => return error.OutOfMemory,
479 error.AnalysisFail => continue,
480 };
481 },
482 else => unreachable,
483 };
484}
485
lib/std/zig.zig+4
......@@ -24,6 +24,10 @@ pub fn hashSrc(src: []const u8) SrcHash {
2424 return out;
2525}
2626
27pub fn srcHashEql(a: SrcHash, b: SrcHash) bool {
28 return @bitCast(u128, a) == @bitCast(u128, b);
29}
30
2731pub fn hashName(parent_hash: SrcHash, sep: []const u8, name: []const u8) SrcHash {
2832 var out: SrcHash = undefined;
2933 var hasher = std.crypto.hash.Blake3.init(.{});
src/AstGen.zig+14-7
......@@ -12,9 +12,6 @@ const Allocator = std.mem.Allocator;
1212const assert = std.debug.assert;
1313const ArrayListUnmanaged = std.ArrayListUnmanaged;
1414
15const Value = @import("value.zig").Value;
16const Type = @import("type.zig").Type;
17const TypedValue = @import("TypedValue.zig");
1815const Zir = @import("Zir.zig");
1916const Module = @import("Module.zig");
2017const trace = @import("tracy.zig").trace;
......@@ -2648,6 +2645,10 @@ fn fnDecl(
26482645 const tree = &astgen.file.tree;
26492646 const token_tags = tree.tokens.items(.tag);
26502647
2648 // We insert this at the beginning so that its instruction index marks the
2649 // start of the top level declaration.
2650 const block_inst = try gz.addBlock(.block_inline, fn_proto.ast.proto_node);
2651
26512652 var decl_gz: GenZir = .{
26522653 .force_comptime = true,
26532654 .decl_node_index = fn_proto.ast.proto_node,
......@@ -2843,7 +2844,8 @@ fn fnDecl(
28432844 };
28442845 const fn_name_str_index = try decl_gz.identAsString(fn_name_token);
28452846
2846 const block_inst = try gz.addBlock(.block_inline, fn_proto.ast.proto_node);
2847 // We add this at the end so that its instruction index marks the end range
2848 // of the top level declaration.
28472849 _ = try decl_gz.addBreak(.break_inline, block_inst, func_inst);
28482850 try decl_gz.setBlockBody(block_inst);
28492851
......@@ -2875,6 +2877,12 @@ fn globalVarDecl(
28752877 const tree = &astgen.file.tree;
28762878 const token_tags = tree.tokens.items(.tag);
28772879
2880 const is_mutable = token_tags[var_decl.ast.mut_token] == .keyword_var;
2881 const tag: Zir.Inst.Tag = if (is_mutable) .block_inline_var else .block_inline;
2882 // We do this at the beginning so that the instruction index marks the range start
2883 // of the top level declaration.
2884 const block_inst = try gz.addBlock(tag, node);
2885
28782886 var block_scope: GenZir = .{
28792887 .parent = scope,
28802888 .decl_node_index = node,
......@@ -2900,7 +2908,6 @@ fn globalVarDecl(
29002908 };
29012909 try wip_decls.next(gpa, is_pub, is_export, align_inst != .none, section_inst != .none);
29022910
2903 const is_mutable = token_tags[var_decl.ast.mut_token] == .keyword_var;
29042911 const is_threadlocal = if (var_decl.threadlocal_token) |tok| blk: {
29052912 if (!is_mutable) {
29062913 return astgen.failTok(tok, "threadlocal variable cannot be constant", .{});
......@@ -2940,8 +2947,8 @@ fn globalVarDecl(
29402947 var_decl.ast.init_node,
29412948 );
29422949
2943 const tag: Zir.Inst.Tag = if (is_mutable) .block_inline_var else .block_inline;
2944 const block_inst = try gz.addBlock(tag, node);
2950 // We do this at the end so that the instruction index marks the end
2951 // range of a top level declaration.
29452952 _ = try block_scope.addBreak(.break_inline, block_inst, init_inst);
29462953 try block_scope.setBlockBody(block_inst);
29472954 break :vi block_inst;
src/Compilation.zig+48-15
......@@ -180,6 +180,8 @@ const Job = union(enum) {
180180 /// The source file containing the Decl has been updated, and so the
181181 /// Decl may need its line number information updated in the debug info.
182182 update_line_number: *Module.Decl,
183 /// The main source file for the package needs to be analyzed.
184 analyze_pkg: *Package,
183185
184186 /// one of the glibc static objects
185187 glibc_crt_file: glibc.CRTFile,
......@@ -278,6 +280,7 @@ pub const MiscTask = enum {
278280 compiler_rt,
279281 libssp,
280282 zig_libc,
283 analyze_pkg,
281284};
282285
283286pub const MiscError = struct {
......@@ -1155,6 +1158,13 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {
11551158 .path = try options.global_cache_directory.join(arena, &[_][]const u8{zir_sub_dir}),
11561159 };
11571160
1161 const emit_h: ?*Module.GlobalEmitH = if (options.emit_h) |loc| eh: {
1162 const eh = try gpa.create(Module.GlobalEmitH);
1163 eh.* = .{ .loc = loc };
1164 break :eh eh;
1165 } else null;
1166 errdefer if (emit_h) |eh| gpa.destroy(eh);
1167
11581168 // TODO when we implement serialization and deserialization of incremental
11591169 // compilation metadata, this is where we would load it. We have open a handle
11601170 // to the directory where the output either already is, or will be.
......@@ -1170,7 +1180,7 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {
11701180 .zig_cache_artifact_directory = zig_cache_artifact_directory,
11711181 .global_zir_cache = global_zir_cache,
11721182 .local_zir_cache = local_zir_cache,
1173 .emit_h = options.emit_h,
1183 .emit_h = emit_h,
11741184 .error_name_list = try std.ArrayListUnmanaged([]const u8).initCapacity(gpa, 1),
11751185 };
11761186 module.error_name_list.appendAssumeCapacity("(no error)");
......@@ -1595,6 +1605,8 @@ pub fn update(self: *Compilation) !void {
15951605 for (module.import_table.items()) |entry| {
15961606 self.astgen_work_queue.writeItemAssumeCapacity(entry.value);
15971607 }
1608
1609 try self.work_queue.writeItem(.{ .analyze_pkg = std_pkg });
15981610 }
15991611 }
16001612
......@@ -1672,11 +1684,13 @@ pub fn totalErrorCount(self: *Compilation) usize {
16721684 }
16731685 total += 1;
16741686 }
1675 for (module.emit_h_failed_decls.items()) |entry| {
1676 if (entry.key.namespace.file_scope.status == .parse_failure) {
1677 continue;
1687 if (module.emit_h) |emit_h| {
1688 for (emit_h.failed_decls.items()) |entry| {
1689 if (entry.key.namespace.file_scope.status == .parse_failure) {
1690 continue;
1691 }
1692 total += 1;
16781693 }
1679 total += 1;
16801694 }
16811695 }
16821696
......@@ -1743,13 +1757,15 @@ pub fn getAllErrorsAlloc(self: *Compilation) !AllErrors {
17431757 }
17441758 try AllErrors.add(module, &arena, &errors, entry.value.*);
17451759 }
1746 for (module.emit_h_failed_decls.items()) |entry| {
1747 if (entry.key.namespace.file_scope.status == .parse_failure) {
1748 // Skip errors for Decls within files that had a parse failure.
1749 // We'll try again once parsing succeeds.
1750 continue;
1760 if (module.emit_h) |emit_h| {
1761 for (emit_h.failed_decls.items()) |entry| {
1762 if (entry.key.namespace.file_scope.status == .parse_failure) {
1763 // Skip errors for Decls within files that had a parse failure.
1764 // We'll try again once parsing succeeds.
1765 continue;
1766 }
1767 try AllErrors.add(module, &arena, &errors, entry.value.*);
17511768 }
1752 try AllErrors.add(module, &arena, &errors, entry.value.*);
17531769 }
17541770 for (module.failed_exports.items()) |entry| {
17551771 try AllErrors.add(module, &arena, &errors, entry.value.*);
......@@ -1942,10 +1958,11 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor
19421958 if (build_options.omit_stage2)
19431959 @panic("sadly stage2 is omitted from this build to save memory on the CI server");
19441960 const module = self.bin_file.options.module.?;
1945 const emit_loc = module.emit_h.?;
1961 const emit_h = module.emit_h.?;
1962 _ = try emit_h.decl_table.getOrPut(module.gpa, decl);
19461963 const tv = decl.typed_value.most_recent.typed_value;
1947 const emit_h = decl.getEmitH(module);
1948 const fwd_decl = &emit_h.fwd_decl;
1964 const decl_emit_h = decl.getEmitH(module);
1965 const fwd_decl = &decl_emit_h.fwd_decl;
19491966 fwd_decl.shrinkRetainingCapacity(0);
19501967
19511968 var dg: c_codegen.DeclGen = .{
......@@ -1960,7 +1977,7 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor
19601977
19611978 c_codegen.genHeader(&dg) catch |err| switch (err) {
19621979 error.AnalysisFail => {
1963 try module.emit_h_failed_decls.put(module.gpa, decl, dg.error_msg.?);
1980 try emit_h.failed_decls.put(module.gpa, decl, dg.error_msg.?);
19641981 continue;
19651982 },
19661983 else => |e| return e,
......@@ -1994,6 +2011,22 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor
19942011 decl.analysis = .codegen_failure_retryable;
19952012 };
19962013 },
2014 .analyze_pkg => |pkg| {
2015 if (build_options.omit_stage2)
2016 @panic("sadly stage2 is omitted from this build to save memory on the CI server");
2017 const module = self.bin_file.options.module.?;
2018 module.semaPkg(pkg) catch |err| switch (err) {
2019 error.CurrentWorkingDirectoryUnlinked,
2020 error.Unexpected,
2021 => try self.setMiscFailure(
2022 .analyze_pkg,
2023 "unexpected problem analyzing package '{s}'",
2024 .{pkg.root_src_path},
2025 ),
2026 error.OutOfMemory => return error.OutOfMemory,
2027 error.AnalysisFail => continue,
2028 };
2029 },
19972030 .glibc_crt_file => |crt_file| {
19982031 glibc.buildCRTFile(self, crt_file) catch |err| {
19992032 // TODO Surface more error details.
src/Module.zig+315-388
......@@ -54,8 +54,6 @@ symbol_exports: std.StringArrayHashMapUnmanaged(*Export) = .{},
5454/// is performing the export of another Decl.
5555/// This table owns the Export memory.
5656export_owners: std.AutoArrayHashMapUnmanaged(*Decl, []*Export) = .{},
57/// Maps fully qualified namespaced names to the Decl struct for them.
58decl_table: std.ArrayHashMapUnmanaged(Scope.NameHash, *Decl, Scope.name_hash_hash, Scope.name_hash_eql, false) = .{},
5957/// The set of all the files in the Module. We keep track of this in order to iterate
6058/// over it and check which source files have been modified on the file system when
6159/// an update is requested, as well as to cache `@import` results.
......@@ -68,10 +66,6 @@ import_table: std.StringArrayHashMapUnmanaged(*Scope.File) = .{},
6866/// Note that a Decl can succeed but the Fn it represents can fail. In this case,
6967/// a Decl can have a failed_decls entry but have analysis status of success.
7068failed_decls: std.AutoArrayHashMapUnmanaged(*Decl, *ErrorMsg) = .{},
71/// When emit_h is non-null, each Decl gets one more compile error slot for
72/// emit-h failing for that Decl. This table is also how we tell if a Decl has
73/// failed emit-h or succeeded.
74emit_h_failed_decls: std.AutoArrayHashMapUnmanaged(*Decl, *ErrorMsg) = .{},
7569/// Keep track of one `@compileLog` callsite per owner Decl.
7670compile_log_decls: std.AutoArrayHashMapUnmanaged(*Decl, SrcLoc) = .{},
7771/// Using a map here for consistency with the other fields here.
......@@ -113,12 +107,24 @@ stage1_flags: packed struct {
113107 reserved: u2 = 0,
114108} = .{},
115109
116emit_h: ?Compilation.EmitLoc,
117
118110job_queued_update_builtin_zig: bool = true,
119111
120112compile_log_text: ArrayListUnmanaged(u8) = .{},
121113
114emit_h: ?*GlobalEmitH,
115
116/// A `Module` has zero or one of these depending on whether `-femit-h` is enabled.
117pub const GlobalEmitH = struct {
118 /// Where to put the output.
119 loc: Compilation.EmitLoc,
120 /// When emit_h is non-null, each Decl gets one more compile error slot for
121 /// emit-h failing for that Decl. This table is also how we tell if a Decl has
122 /// failed emit-h or succeeded.
123 failed_decls: std.AutoArrayHashMapUnmanaged(*Decl, *ErrorMsg) = .{},
124 /// Tracks all decls in order to iterate over them and emit .h code for them.
125 decl_table: std.AutoArrayHashMapUnmanaged(*Decl, void) = .{},
126};
127
122128pub const ErrorInt = u32;
123129
124130pub const Export = struct {
......@@ -293,10 +299,6 @@ pub const Decl = struct {
293299 return tree.tokens.items(.start)[decl.srcToken()];
294300 }
295301
296 pub fn fullyQualifiedNameHash(decl: Decl) Scope.NameHash {
297 return decl.namespace.fullyQualifiedNameHash(mem.spanZ(decl.name));
298 }
299
300302 pub fn renderFullyQualifiedName(decl: Decl, writer: anytype) !void {
301303 const unqualified_name = mem.spanZ(decl.name);
302304 return decl.namespace.renderFullyQualifiedName(unqualified_name, writer);
......@@ -318,6 +320,11 @@ pub const Decl = struct {
318320 return (try decl.typedValue()).val;
319321 }
320322
323 pub fn isFunction(decl: *Decl) !bool {
324 const tv = try decl.typedValue();
325 return tv.ty.zigTypeTag() == .Fn;
326 }
327
321328 pub fn dump(decl: *Decl) void {
322329 const loc = std.zig.findLineColumn(decl.scope.source.bytes, decl.src);
323330 std.debug.print("{s}:{d}:{d} name={s} status={s}", .{
......@@ -611,14 +618,6 @@ pub const Scope = struct {
611618 }
612619 }
613620
614 fn name_hash_hash(x: NameHash) u32 {
615 return @truncate(u32, @bitCast(u128, x));
616 }
617
618 fn name_hash_eql(a: NameHash, b: NameHash) bool {
619 return @bitCast(u128, a) == @bitCast(u128, b);
620 }
621
622621 pub const Tag = enum {
623622 /// .zig source code.
624623 file,
......@@ -643,28 +642,32 @@ pub const Scope = struct {
643642
644643 parent: ?*Namespace,
645644 file_scope: *Scope.File,
646 parent_name_hash: NameHash,
647645 /// Will be a struct, enum, union, or opaque.
648646 ty: Type,
649647 /// Direct children of the namespace. Used during an update to detect
650648 /// which decls have been added/removed from source.
651 decls: std.AutoArrayHashMapUnmanaged(*Decl, void) = .{},
652 usingnamespace_set: std.AutoHashMapUnmanaged(*Namespace, bool) = .{},
653
654 pub fn deinit(ns: *Namespace, gpa: *Allocator) void {
649 /// Declaration order is preserved via entry order.
650 /// Key memory references the string table of the containing `File` ZIR.
651 /// TODO save memory with https://github.com/ziglang/zig/issues/8619.
652 /// Does not contain anonymous decls.
653 decls: std.StringArrayHashMapUnmanaged(*Decl) = .{},
654 /// Names imported into the namespace via `usingnamespace`.
655 /// The key memory is owned by the ZIR of the `File` containing the `Namespace`.
656 usingnamespace_decls: std.StringArrayHashMapUnmanaged(*Namespace) = .{},
657
658 pub fn deinit(ns: *Namespace, mod: *Module) void {
659 const gpa = mod.gpa;
660
661 for (ns.decls.items()) |entry| {
662 entry.value.destroy(mod);
663 }
655664 ns.decls.deinit(gpa);
656665 ns.* = undefined;
657666 }
658667
659668 pub fn removeDecl(ns: *Namespace, child: *Decl) void {
660 _ = ns.decls.swapRemove(child);
661 }
662
663 /// Must generate unique bytes with no collisions with other decls.
664 /// The point of hashing here is only to limit the number of bytes of
665 /// the unique identifier to a fixed size (16 bytes).
666 pub fn fullyQualifiedNameHash(ns: Namespace, name: []const u8) NameHash {
667 return std.zig.hashName(ns.parent_name_hash, ".", name);
669 // Preserve declaration order.
670 _ = ns.decls.orderedRemove(mem.spanZ(child.name));
668671 }
669672
670673 pub fn renderFullyQualifiedName(ns: Namespace, name: []const u8, writer: anytype) !void {
......@@ -738,7 +741,9 @@ pub const Scope = struct {
738741 }
739742 }
740743
741 pub fn deinit(file: *File, gpa: *Allocator) void {
744 pub fn deinit(file: *File, mod: *Module) void {
745 const gpa = mod.gpa;
746 file.namespace.deinit(mod);
742747 gpa.free(file.sub_file_path);
743748 file.unload(gpa);
744749 file.* = undefined;
......@@ -786,8 +791,9 @@ pub const Scope = struct {
786791 return &file.tree;
787792 }
788793
789 pub fn destroy(file: *File, gpa: *Allocator) void {
790 file.deinit(gpa);
794 pub fn destroy(file: *File, mod: *Module) void {
795 const gpa = mod.gpa;
796 file.deinit(mod);
791797 gpa.destroy(file);
792798 }
793799
......@@ -798,7 +804,7 @@ pub const Scope = struct {
798804 };
799805
800806 /// This is the context needed to semantically analyze ZIR instructions and
801 /// produce TZIR instructions.
807 /// produce AIR instructions.
802808 /// This is a temporary structure stored on the stack; references to it are valid only
803809 /// during semantic analysis of the block.
804810 pub const Block = struct {
......@@ -818,7 +824,7 @@ pub const Scope = struct {
818824 is_comptime: bool,
819825
820826 /// This `Block` maps a block ZIR instruction to the corresponding
821 /// TZIR instruction for break instruction analysis.
827 /// AIR instruction for break instruction analysis.
822828 pub const Label = struct {
823829 zir_block: Zir.Inst.Index,
824830 merges: Merges,
......@@ -826,7 +832,7 @@ pub const Scope = struct {
826832
827833 /// This `Block` indicates that an inline function call is happening
828834 /// and return instructions should be analyzed as a break instruction
829 /// to this TZIR block instruction.
835 /// to this AIR block instruction.
830836 /// It is shared among all the blocks in an inline or comptime called
831837 /// function.
832838 pub const Inlining = struct {
......@@ -2632,20 +2638,19 @@ pub fn deinit(mod: *Module) void {
26322638
26332639 mod.deletion_set.deinit(gpa);
26342640
2635 for (mod.decl_table.items()) |entry| {
2636 entry.value.destroy(mod);
2637 }
2638 mod.decl_table.deinit(gpa);
2639
26402641 for (mod.failed_decls.items()) |entry| {
26412642 entry.value.destroy(gpa);
26422643 }
26432644 mod.failed_decls.deinit(gpa);
26442645
2645 for (mod.emit_h_failed_decls.items()) |entry| {
2646 entry.value.destroy(gpa);
2646 if (mod.emit_h) |emit_h| {
2647 for (emit_h.failed_decls.items()) |entry| {
2648 entry.value.destroy(gpa);
2649 }
2650 emit_h.failed_decls.deinit(gpa);
2651 emit_h.decl_table.deinit(gpa);
2652 gpa.destroy(emit_h);
26472653 }
2648 mod.emit_h_failed_decls.deinit(gpa);
26492654
26502655 for (mod.failed_files.items()) |entry| {
26512656 if (entry.value) |msg| msg.destroy(gpa);
......@@ -2682,7 +2687,7 @@ pub fn deinit(mod: *Module) void {
26822687
26832688 for (mod.import_table.items()) |entry| {
26842689 gpa.free(entry.key);
2685 entry.value.destroy(gpa);
2690 entry.value.destroy(mod);
26862691 }
26872692 mod.import_table.deinit(gpa);
26882693}
......@@ -2700,8 +2705,8 @@ const data_has_safety_tag = @sizeOf(Zir.Inst.Data) != 8;
27002705// We need a better language feature for initializing a union with
27012706// a runtime known tag.
27022707const Stage1DataLayout = extern struct {
2703 safety_tag: u8,
27042708 data: [8]u8 align(8),
2709 safety_tag: u8,
27052710};
27062711comptime {
27072712 if (data_has_safety_tag) {
......@@ -2783,12 +2788,15 @@ pub fn astGenFile(mod: *Module, file: *Scope.File, prog_node: *std.Progress.Node
27832788 log.debug("AstGen cache stale: {s}", .{file.sub_file_path});
27842789 break :cached;
27852790 }
2786 log.debug("AstGen cache hit: {s}", .{file.sub_file_path});
2791 log.debug("AstGen cache hit: {s} instructions_len={d}", .{
2792 file.sub_file_path, header.instructions_len,
2793 });
27872794
27882795 var instructions: std.MultiArrayList(Zir.Inst) = .{};
27892796 defer instructions.deinit(gpa);
27902797
2791 try instructions.resize(gpa, header.instructions_len);
2798 try instructions.setCapacity(gpa, header.instructions_len);
2799 instructions.len = header.instructions_len;
27922800
27932801 var zir: Zir = .{
27942802 .instructions = instructions.toOwnedSlice(),
......@@ -3126,6 +3134,88 @@ pub fn ensureDeclAnalyzed(mod: *Module, decl: *Decl) InnerError!void {
31263134 }
31273135}
31283136
3137pub fn semaPkg(mod: *Module, pkg: *Package) !void {
3138 const file = (try mod.importPkg(mod.root_pkg, pkg)).file;
3139 return mod.semaFile(file);
3140}
3141
3142pub fn semaFile(mod: *Module, file: *Scope.File) InnerError!void {
3143 const tracy = trace(@src());
3144 defer tracy.end();
3145
3146 assert(file.zir_loaded);
3147 assert(!file.zir.hasCompileErrors());
3148
3149 const gpa = mod.gpa;
3150 var decl_arena = std.heap.ArenaAllocator.init(gpa);
3151 defer decl_arena.deinit();
3152
3153 // We need a Decl to pass to Sema and collect dependencies. But ultimately we
3154 // want to pass them on to the Decl for the struct that represents the file.
3155 var tmp_namespace: Scope.Namespace = .{
3156 .parent = null,
3157 .file_scope = file,
3158 .ty = Type.initTag(.type),
3159 };
3160 var top_decl: Decl = .{
3161 .name = "",
3162 .namespace = &tmp_namespace,
3163 .generation = mod.generation,
3164 .src_node = 0, // the root AST node for the file
3165 .typed_value = .never_succeeded,
3166 .analysis = .in_progress,
3167 .deletion_flag = false,
3168 .is_pub = true,
3169 .link = undefined, // don't try to codegen this
3170 .fn_link = undefined, // not a function
3171 .contents_hash = undefined, // top-level struct has no contents hash
3172 };
3173 defer top_decl.dependencies.deinit(gpa);
3174
3175 var sema: Sema = .{
3176 .mod = mod,
3177 .gpa = gpa,
3178 .arena = &decl_arena.allocator,
3179 .code = file.zir,
3180 // TODO use a map because this array is too big
3181 .inst_map = try decl_arena.allocator.alloc(*ir.Inst, file.zir.instructions.len),
3182 .owner_decl = &top_decl,
3183 .namespace = &tmp_namespace,
3184 .func = null,
3185 .owner_func = null,
3186 .param_inst_list = &.{},
3187 };
3188 var block_scope: Scope.Block = .{
3189 .parent = null,
3190 .sema = &sema,
3191 .src_decl = &top_decl,
3192 .instructions = .{},
3193 .inlining = null,
3194 .is_comptime = true,
3195 };
3196 defer block_scope.instructions.deinit(gpa);
3197
3198 const main_struct_inst = file.zir.extra[@enumToInt(Zir.ExtraIndex.main_struct)] -
3199 @intCast(u32, Zir.Inst.Ref.typed_value_map.len);
3200 const air_inst = try sema.zirStructDecl(&block_scope, main_struct_inst, .Auto);
3201 assert(air_inst.ty.zigTypeTag() == .Type);
3202 const val = air_inst.value().?;
3203 const struct_ty = try val.toType(&decl_arena.allocator);
3204 const struct_decl = struct_ty.getOwnerDecl();
3205
3206 file.namespace = struct_ty.getNamespace().?;
3207 file.namespace.parent = null;
3208
3209 // Transfer the dependencies to `owner_decl`.
3210 assert(top_decl.dependants.count() == 0);
3211 for (top_decl.dependencies.items()) |entry| {
3212 const dep = entry.key;
3213 dep.removeDependant(&top_decl);
3214 if (dep == struct_decl) continue;
3215 _ = try mod.declareDeclDependency(struct_decl, dep);
3216 }
3217}
3218
31293219/// Returns `true` if the Decl type changed.
31303220/// Returns `true` if this is the first time analyzing the Decl.
31313221/// Returns `false` otherwise.
......@@ -3268,31 +3358,32 @@ pub fn importFile(
32683358 };
32693359}
32703360
3271pub fn analyzeNamespace(
3361pub fn scanNamespace(
32723362 mod: *Module,
32733363 namespace: *Scope.Namespace,
3274 decls: []const ast.Node.Index,
3275) InnerError!void {
3364 extra_start: usize,
3365 decls_len: u32,
3366 parent_decl: *Decl,
3367) InnerError!usize {
32763368 const tracy = trace(@src());
32773369 defer tracy.end();
32783370
3279 // We may be analyzing it for the first time, or this may be
3280 // an incremental update. This code handles both cases.
3281 assert(namespace.file_scope.tree_loaded); // Caller must ensure tree loaded.
3282 const tree: *const ast.Tree = &namespace.file_scope.tree;
3283 const node_tags = tree.nodes.items(.tag);
3284 const node_datas = tree.nodes.items(.data);
3371 const gpa = mod.gpa;
3372 const zir = namespace.file_scope.zir;
32853373
3286 try mod.comp.work_queue.ensureUnusedCapacity(decls.len);
3287 try namespace.decls.ensureCapacity(mod.gpa, decls.len);
3374 try mod.comp.work_queue.ensureUnusedCapacity(decls_len);
3375 try namespace.decls.ensureCapacity(gpa, decls_len);
32883376
32893377 // Keep track of the decls that we expect to see in this namespace so that
32903378 // we know which ones have been deleted.
3291 var deleted_decls = std.AutoArrayHashMap(*Decl, void).init(mod.gpa);
3379 var deleted_decls = std.AutoArrayHashMap(*Decl, void).init(gpa);
32923380 defer deleted_decls.deinit();
3293 try deleted_decls.ensureCapacity(namespace.decls.items().len);
3294 for (namespace.decls.items()) |entry| {
3295 deleted_decls.putAssumeCapacityNoClobber(entry.key, {});
3381 {
3382 const namespace_decls = namespace.decls.items();
3383 try deleted_decls.ensureCapacity(namespace_decls.len);
3384 for (namespace_decls) |entry| {
3385 deleted_decls.putAssumeCapacityNoClobber(entry.value, {});
3386 }
32963387 }
32973388
32983389 // Keep track of decls that are invalidated from the update. Ultimately,
......@@ -3300,177 +3391,61 @@ pub fn analyzeNamespace(
33003391 // the outdated decls, but we cannot queue up the tasks until after
33013392 // we find out which ones have been deleted, otherwise there would be
33023393 // deleted Decl pointers in the work queue.
3303 var outdated_decls = std.AutoArrayHashMap(*Decl, void).init(mod.gpa);
3394 var outdated_decls = std.AutoArrayHashMap(*Decl, void).init(gpa);
33043395 defer outdated_decls.deinit();
33053396
3306 for (decls) |decl_node| switch (node_tags[decl_node]) {
3307 .fn_decl => {
3308 const fn_proto = node_datas[decl_node].lhs;
3309 const body = node_datas[decl_node].rhs;
3310 switch (node_tags[fn_proto]) {
3311 .fn_proto_simple => {
3312 var params: [1]ast.Node.Index = undefined;
3313 try mod.semaContainerFn(
3314 namespace,
3315 &deleted_decls,
3316 &outdated_decls,
3317 decl_node,
3318 tree.*,
3319 body,
3320 tree.fnProtoSimple(&params, fn_proto),
3321 );
3322 },
3323 .fn_proto_multi => try mod.semaContainerFn(
3324 namespace,
3325 &deleted_decls,
3326 &outdated_decls,
3327 decl_node,
3328 tree.*,
3329 body,
3330 tree.fnProtoMulti(fn_proto),
3331 ),
3332 .fn_proto_one => {
3333 var params: [1]ast.Node.Index = undefined;
3334 try mod.semaContainerFn(
3335 namespace,
3336 &deleted_decls,
3337 &outdated_decls,
3338 decl_node,
3339 tree.*,
3340 body,
3341 tree.fnProtoOne(&params, fn_proto),
3342 );
3343 },
3344 .fn_proto => try mod.semaContainerFn(
3345 namespace,
3346 &deleted_decls,
3347 &outdated_decls,
3348 decl_node,
3349 tree.*,
3350 body,
3351 tree.fnProto(fn_proto),
3352 ),
3353 else => unreachable,
3354 }
3355 },
3356 .fn_proto_simple => {
3357 var params: [1]ast.Node.Index = undefined;
3358 try mod.semaContainerFn(
3359 namespace,
3360 &deleted_decls,
3361 &outdated_decls,
3362 decl_node,
3363 tree.*,
3364 0,
3365 tree.fnProtoSimple(&params, decl_node),
3366 );
3367 },
3368 .fn_proto_multi => try mod.semaContainerFn(
3369 namespace,
3370 &deleted_decls,
3371 &outdated_decls,
3372 decl_node,
3373 tree.*,
3374 0,
3375 tree.fnProtoMulti(decl_node),
3376 ),
3377 .fn_proto_one => {
3378 var params: [1]ast.Node.Index = undefined;
3379 try mod.semaContainerFn(
3380 namespace,
3381 &deleted_decls,
3382 &outdated_decls,
3383 decl_node,
3384 tree.*,
3385 0,
3386 tree.fnProtoOne(&params, decl_node),
3387 );
3388 },
3389 .fn_proto => try mod.semaContainerFn(
3390 namespace,
3391 &deleted_decls,
3392 &outdated_decls,
3393 decl_node,
3394 tree.*,
3395 0,
3396 tree.fnProto(decl_node),
3397 ),
3397 const bit_bags_count = std.math.divCeil(usize, decls_len, 8) catch unreachable;
3398 var extra_index = extra_start + bit_bags_count;
3399 var bit_bag_index: usize = extra_start;
3400 var cur_bit_bag: u32 = undefined;
3401 var decl_i: u32 = 0;
3402 while (decl_i < decls_len) : (decl_i += 1) {
3403 if (decl_i % 8 == 0) {
3404 cur_bit_bag = zir.extra[bit_bag_index];
3405 bit_bag_index += 1;
3406 }
3407 const is_pub = @truncate(u1, cur_bit_bag) != 0;
3408 cur_bit_bag >>= 1;
3409 const is_exported = @truncate(u1, cur_bit_bag) != 0;
3410 cur_bit_bag >>= 1;
3411 const has_align = @truncate(u1, cur_bit_bag) != 0;
3412 cur_bit_bag >>= 1;
3413 const has_section = @truncate(u1, cur_bit_bag) != 0;
3414 cur_bit_bag >>= 1;
3415
3416 const hash_u32s = zir.extra[extra_index..][0..4];
3417 extra_index += 4;
3418 const name_idx = zir.extra[extra_index];
3419 extra_index += 1;
3420 const decl_index = zir.extra[extra_index];
3421 extra_index += 1;
3422 const align_inst: Zir.Inst.Ref = if (!has_align) .none else inst: {
3423 const inst = @intToEnum(Zir.Inst.Ref, zir.extra[extra_index]);
3424 extra_index += 1;
3425 break :inst inst;
3426 };
3427 const section_inst: Zir.Inst.Ref = if (!has_section) .none else inst: {
3428 const inst = @intToEnum(Zir.Inst.Ref, zir.extra[extra_index]);
3429 extra_index += 1;
3430 break :inst inst;
3431 };
3432 const decl_name: ?[]const u8 = if (name_idx == 0) null else zir.nullTerminatedString(name_idx);
3433 const contents_hash = @bitCast(std.zig.SrcHash, hash_u32s.*);
33983434
3399 .global_var_decl => try mod.semaContainerVar(
3400 namespace,
3401 &deleted_decls,
3402 &outdated_decls,
3403 decl_node,
3404 tree.*,
3405 tree.globalVarDecl(decl_node),
3406 ),
3407 .local_var_decl => try mod.semaContainerVar(
3408 namespace,
3409 &deleted_decls,
3410 &outdated_decls,
3411 decl_node,
3412 tree.*,
3413 tree.localVarDecl(decl_node),
3414 ),
3415 .simple_var_decl => try mod.semaContainerVar(
3435 try mod.scanDecl(
34163436 namespace,
34173437 &deleted_decls,
34183438 &outdated_decls,
3419 decl_node,
3420 tree.*,
3421 tree.simpleVarDecl(decl_node),
3422 ),
3423 .aligned_var_decl => try mod.semaContainerVar(
3424 namespace,
3425 &deleted_decls,
3426 &outdated_decls,
3427 decl_node,
3428 tree.*,
3429 tree.alignedVarDecl(decl_node),
3430 ),
3431
3432 .@"comptime" => {
3433 const name_index = mod.getNextAnonNameIndex();
3434 const name = try std.fmt.allocPrint(mod.gpa, "__comptime_{d}", .{name_index});
3435 defer mod.gpa.free(name);
3436
3437 const name_hash = namespace.fullyQualifiedNameHash(name);
3438 const contents_hash = std.zig.hashSrc(tree.getNodeSource(decl_node));
3439
3440 const new_decl = try mod.createNewDecl(namespace, name, decl_node, name_hash, contents_hash);
3441 namespace.decls.putAssumeCapacity(new_decl, {});
3442 mod.comp.work_queue.writeItemAssumeCapacity(.{ .analyze_decl = new_decl });
3443 },
3444
3445 // Container fields are handled in AstGen.
3446 .container_field_init,
3447 .container_field_align,
3448 .container_field,
3449 => continue,
3450
3451 .test_decl => {
3452 if (mod.comp.bin_file.options.is_test) {
3453 log.err("TODO: analyze test decl", .{});
3454 }
3455 },
3456 .@"usingnamespace" => {
3457 const name_index = mod.getNextAnonNameIndex();
3458 const name = try std.fmt.allocPrint(mod.gpa, "__usingnamespace_{d}", .{name_index});
3459 defer mod.gpa.free(name);
3460
3461 const name_hash = namespace.fullyQualifiedNameHash(name);
3462 const contents_hash = std.zig.hashSrc(tree.getNodeSource(decl_node));
3463
3464 const new_decl = try mod.createNewDecl(namespace, name, decl_node, name_hash, contents_hash);
3465 namespace.decls.putAssumeCapacity(new_decl, {});
3466
3467 mod.ensureDeclAnalyzed(new_decl) catch |err| switch (err) {
3468 error.OutOfMemory => return error.OutOfMemory,
3469 error.AnalysisFail => continue,
3470 };
3471 },
3472 else => unreachable,
3473 };
3439 contents_hash,
3440 decl_name,
3441 decl_index,
3442 is_pub,
3443 is_exported,
3444 align_inst,
3445 section_inst,
3446 parent_decl,
3447 );
3448 }
34743449 // Handle explicitly deleted decls from the source code. This is one of two
34753450 // places that Decl deletions happen. The other is in `Compilation`, after
34763451 // `performAllTheWork`, where we iterate over `Module.deletion_set` and
......@@ -3493,133 +3468,98 @@ pub fn analyzeNamespace(
34933468 for (outdated_decls.items()) |entry| {
34943469 try mod.markOutdatedDecl(entry.key);
34953470 }
3471 return extra_index;
34963472}
34973473
3498fn semaContainerFn(
3474fn scanDecl(
34993475 mod: *Module,
35003476 namespace: *Scope.Namespace,
35013477 deleted_decls: *std.AutoArrayHashMap(*Decl, void),
35023478 outdated_decls: *std.AutoArrayHashMap(*Decl, void),
3503 decl_node: ast.Node.Index,
3504 tree: ast.Tree,
3505 body_node: ast.Node.Index,
3506 fn_proto: ast.full.FnProto,
3507) !void {
3479 contents_hash: std.zig.SrcHash,
3480 decl_name: ?[]const u8,
3481 decl_index: Zir.Inst.Index,
3482 is_pub: bool,
3483 is_exported: bool,
3484 align_inst: Zir.Inst.Ref,
3485 section_inst: Zir.Inst.Ref,
3486 parent_decl: *Decl,
3487) InnerError!void {
35083488 const tracy = trace(@src());
35093489 defer tracy.end();
35103490
3511 // We will create a Decl for it regardless of analysis status.
3512 const name_token = fn_proto.name_token orelse {
3513 // This problem will go away with #1717.
3514 @panic("TODO missing function name");
3515 };
3516 const name = tree.tokenSlice(name_token); // TODO use identifierTokenString
3517 const name_hash = namespace.fullyQualifiedNameHash(name);
3518 const contents_hash = std.zig.hashSrc(tree.getNodeSource(decl_node));
3519 if (mod.decl_table.get(name_hash)) |decl| {
3520 // Update the AST node of the decl; even if its contents are unchanged, it may
3521 // have been re-ordered.
3522 const prev_src_node = decl.src_node;
3523 decl.src_node = decl_node;
3524 if (deleted_decls.swapRemove(decl) == null) {
3525 decl.analysis = .sema_failure;
3526 const msg = try ErrorMsg.create(mod.gpa, .{
3527 .file_scope = namespace.file_scope,
3528 .parent_decl_node = 0,
3529 .lazy = .{ .token_abs = name_token },
3530 }, "redeclaration of '{s}'", .{decl.name});
3531 errdefer msg.destroy(mod.gpa);
3532 const other_src_loc: SrcLoc = .{
3533 .file_scope = namespace.file_scope,
3534 .parent_decl_node = 0,
3535 .lazy = .{ .node_abs = prev_src_node },
3536 };
3537 try mod.errNoteNonLazy(other_src_loc, msg, "previously declared here", .{});
3538 try mod.failed_decls.putNoClobber(mod.gpa, decl, msg);
3539 } else {
3540 if (!srcHashEql(decl.contents_hash, contents_hash)) {
3541 try outdated_decls.put(decl, {});
3542 decl.contents_hash = contents_hash;
3543 } else switch (mod.comp.bin_file.tag) {
3544 .coff => {
3545 // TODO Implement for COFF
3546 },
3547 .elf => if (decl.fn_link.elf.len != 0) {
3548 // TODO Look into detecting when this would be unnecessary by storing enough state
3549 // in `Decl` to notice that the line number did not change.
3550 mod.comp.work_queue.writeItemAssumeCapacity(.{ .update_line_number = decl });
3551 },
3552 .macho => if (decl.fn_link.macho.len != 0) {
3553 // TODO Look into detecting when this would be unnecessary by storing enough state
3554 // in `Decl` to notice that the line number did not change.
3555 mod.comp.work_queue.writeItemAssumeCapacity(.{ .update_line_number = decl });
3556 },
3557 .c, .wasm, .spirv => {},
3558 }
3491 const gpa = mod.gpa;
3492 const zir = namespace.file_scope.zir;
3493 const decl_block_inst_data = zir.instructions.items(.data)[decl_index].pl_node;
3494 const decl_node = parent_decl.relativeToNodeIndex(decl_block_inst_data.src_node);
3495
3496 // We create a Decl for it regardless of analysis status.
3497 // Decls that have names are keyed in the namespace by the name. Decls without
3498 // names are keyed by their contents hash. This way we can detect if, for example,
3499 // a comptime decl gets moved around in the file.
3500 const decl_key = decl_name orelse &contents_hash;
3501 const gop = try namespace.decls.getOrPut(gpa, decl_key);
3502 if (!gop.found_existing) {
3503 if (align_inst != .none) {
3504 return mod.fail(&namespace.base, .{ .node_abs = decl_node }, "TODO: implement decls with align()", .{});
35593505 }
3560 } else {
3561 const new_decl = try mod.createNewDecl(namespace, name, decl_node, name_hash, contents_hash);
3562 namespace.decls.putAssumeCapacity(new_decl, {});
3563 if (fn_proto.extern_export_token) |maybe_export_token| {
3564 const token_tags = tree.tokens.items(.tag);
3565 if (token_tags[maybe_export_token] == .keyword_export) {
3566 mod.comp.work_queue.writeItemAssumeCapacity(.{ .analyze_decl = new_decl });
3567 }
3506 if (section_inst != .none) {
3507 return mod.fail(&namespace.base, .{ .node_abs = decl_node }, "TODO: implement decls with linksection()", .{});
3508 }
3509 const new_decl = try mod.createNewDecl(namespace, decl_key, decl_node, contents_hash);
3510 // Update the key reference to the longer-lived memory.
3511 gop.entry.key = &new_decl.contents_hash;
3512 gop.entry.value = new_decl;
3513 // exported decls, comptime, test, and usingnamespace decls get analyzed.
3514 if (decl_name == null or is_exported) {
3515 mod.comp.work_queue.writeItemAssumeCapacity(.{ .analyze_decl = new_decl });
35683516 }
3569 new_decl.is_pub = fn_proto.visib_token != null;
3517 new_decl.is_pub = is_pub;
3518 return;
35703519 }
3571}
3572
3573fn semaContainerVar(
3574 mod: *Module,
3575 namespace: *Scope.Namespace,
3576 deleted_decls: *std.AutoArrayHashMap(*Decl, void),
3577 outdated_decls: *std.AutoArrayHashMap(*Decl, void),
3578 decl_node: ast.Node.Index,
3579 tree: ast.Tree,
3580 var_decl: ast.full.VarDecl,
3581) !void {
3582 const tracy = trace(@src());
3583 defer tracy.end();
3584
3585 const name_token = var_decl.ast.mut_token + 1;
3586 const name = tree.tokenSlice(name_token); // TODO identifierTokenString
3587 const name_hash = namespace.fullyQualifiedNameHash(name);
3588 const contents_hash = std.zig.hashSrc(tree.getNodeSource(decl_node));
3589 if (mod.decl_table.get(name_hash)) |decl| {
3590 // Update the AST Node index of the decl, even if its contents are unchanged, it may
3591 // have been re-ordered.
3592 const prev_src_node = decl.src_node;
3593 decl.src_node = decl_node;
3594 if (deleted_decls.swapRemove(decl) == null) {
3595 decl.analysis = .sema_failure;
3596 const msg = try ErrorMsg.create(mod.gpa, .{
3597 .file_scope = namespace.file_scope,
3598 .parent_decl_node = 0,
3599 .lazy = .{ .token_abs = name_token },
3600 }, "redeclaration of '{s}'", .{decl.name});
3601 errdefer msg.destroy(mod.gpa);
3602 const other_src_loc: SrcLoc = .{
3603 .file_scope = decl.namespace.file_scope,
3604 .parent_decl_node = 0,
3605 .lazy = .{ .node_abs = prev_src_node },
3606 };
3607 try mod.errNoteNonLazy(other_src_loc, msg, "previously declared here", .{});
3608 try mod.failed_decls.putNoClobber(mod.gpa, decl, msg);
3609 } else if (!srcHashEql(decl.contents_hash, contents_hash)) {
3610 try outdated_decls.put(decl, {});
3611 decl.contents_hash = contents_hash;
3520 const decl = gop.entry.value;
3521 // Update the AST node of the decl; even if its contents are unchanged, it may
3522 // have been re-ordered.
3523 const prev_src_node = decl.src_node;
3524 decl.src_node = decl_node;
3525 if (deleted_decls.swapRemove(decl) == null) {
3526 if (true) {
3527 @panic("TODO I think this code path is unreachable; should be caught by AstGen.");
36123528 }
3529 decl.analysis = .sema_failure;
3530 const msg = try ErrorMsg.create(gpa, .{
3531 .file_scope = namespace.file_scope,
3532 .parent_decl_node = 0,
3533 .lazy = .{ .token_abs = name_token },
3534 }, "redeclaration of '{s}'", .{decl.name});
3535 errdefer msg.destroy(gpa);
3536 const other_src_loc: SrcLoc = .{
3537 .file_scope = namespace.file_scope,
3538 .parent_decl_node = 0,
3539 .lazy = .{ .node_abs = prev_src_node },
3540 };
3541 try mod.errNoteNonLazy(other_src_loc, msg, "previously declared here", .{});
3542 try mod.failed_decls.putNoClobber(gpa, decl, msg);
36133543 } else {
3614 const new_decl = try mod.createNewDecl(namespace, name, decl_node, name_hash, contents_hash);
3615 namespace.decls.putAssumeCapacity(new_decl, {});
3616 if (var_decl.extern_export_token) |maybe_export_token| {
3617 const token_tags = tree.tokens.items(.tag);
3618 if (token_tags[maybe_export_token] == .keyword_export) {
3619 mod.comp.work_queue.writeItemAssumeCapacity(.{ .analyze_decl = new_decl });
3620 }
3621 }
3622 new_decl.is_pub = var_decl.visib_token != null;
3544 if (!std.zig.srcHashEql(decl.contents_hash, contents_hash)) {
3545 try outdated_decls.put(decl, {});
3546 decl.contents_hash = contents_hash;
3547 } else if (try decl.isFunction()) switch (mod.comp.bin_file.tag) {
3548 .coff => {
3549 // TODO Implement for COFF
3550 },
3551 .elf => if (decl.fn_link.elf.len != 0) {
3552 // TODO Look into detecting when this would be unnecessary by storing enough state
3553 // in `Decl` to notice that the line number did not change.
3554 mod.comp.work_queue.writeItemAssumeCapacity(.{ .update_line_number = decl });
3555 },
3556 .macho => if (decl.fn_link.macho.len != 0) {
3557 // TODO Look into detecting when this would be unnecessary by storing enough state
3558 // in `Decl` to notice that the line number did not change.
3559 mod.comp.work_queue.writeItemAssumeCapacity(.{ .update_line_number = decl });
3560 },
3561 .c, .wasm, .spirv => {},
3562 };
36233563 }
36243564}
36253565
......@@ -3644,8 +3584,6 @@ pub fn deleteDecl(
36443584 // not be present in the set, and this does nothing.
36453585 decl.namespace.removeDecl(decl);
36463586
3647 const name_hash = decl.fullyQualifiedNameHash();
3648 mod.decl_table.removeAssertDiscard(name_hash);
36493587 // Remove itself from its dependencies, because we are about to destroy the decl pointer.
36503588 for (decl.dependencies.items()) |entry| {
36513589 const dep = entry.key;
......@@ -3675,8 +3613,11 @@ pub fn deleteDecl(
36753613 if (mod.failed_decls.swapRemove(decl)) |entry| {
36763614 entry.value.destroy(mod.gpa);
36773615 }
3678 if (mod.emit_h_failed_decls.swapRemove(decl)) |entry| {
3679 entry.value.destroy(mod.gpa);
3616 if (mod.emit_h) |emit_h| {
3617 if (emit_h.failed_decls.swapRemove(decl)) |entry| {
3618 entry.value.destroy(mod.gpa);
3619 }
3620 emit_h.decl_table.removeAssertDiscard(decl);
36803621 }
36813622 _ = mod.compile_log_decls.swapRemove(decl);
36823623 mod.deleteDeclExports(decl);
......@@ -3776,7 +3717,7 @@ pub fn analyzeFnBody(mod: *Module, decl: *Decl, func: *Fn) !void {
37763717 };
37773718 defer inner_block.instructions.deinit(mod.gpa);
37783719
3779 // TZIR currently requires the arg parameters to be the first N instructions
3720 // AIR currently requires the arg parameters to be the first N instructions
37803721 try inner_block.instructions.appendSlice(mod.gpa, param_inst_list);
37813722
37823723 func.state = .in_progress;
......@@ -3796,8 +3737,10 @@ fn markOutdatedDecl(mod: *Module, decl: *Decl) !void {
37963737 if (mod.failed_decls.swapRemove(decl)) |entry| {
37973738 entry.value.destroy(mod.gpa);
37983739 }
3799 if (mod.emit_h_failed_decls.swapRemove(decl)) |entry| {
3800 entry.value.destroy(mod.gpa);
3740 if (mod.emit_h) |emit_h| {
3741 if (emit_h.failed_decls.swapRemove(decl)) |entry| {
3742 entry.value.destroy(mod.gpa);
3743 }
38013744 }
38023745 _ = mod.compile_log_decls.swapRemove(decl);
38033746 decl.analysis = .outdated;
......@@ -3854,18 +3797,11 @@ fn createNewDecl(
38543797 namespace: *Scope.Namespace,
38553798 decl_name: []const u8,
38563799 src_node: ast.Node.Index,
3857 name_hash: Scope.NameHash,
38583800 contents_hash: std.zig.SrcHash,
38593801) !*Decl {
3860 try mod.decl_table.ensureCapacity(mod.gpa, mod.decl_table.items().len + 1);
38613802 const new_decl = try mod.allocateNewDecl(namespace, src_node, contents_hash);
38623803 errdefer mod.gpa.destroy(new_decl);
38633804 new_decl.name = try mem.dupeZ(mod.gpa, u8, decl_name);
3864 log.debug("insert Decl {s} with hash {}", .{
3865 new_decl.name,
3866 std.fmt.fmtSliceHexLower(&name_hash),
3867 });
3868 mod.decl_table.putAssumeCapacityNoClobber(name_hash, new_decl);
38693805 return new_decl;
38703806}
38713807
......@@ -4074,9 +4010,8 @@ pub fn createAnonymousDecl(
40744010 const name = try std.fmt.allocPrint(mod.gpa, "{s}__anon_{d}", .{ scope_decl.name, name_index });
40754011 defer mod.gpa.free(name);
40764012 const namespace = scope_decl.namespace;
4077 const name_hash = namespace.fullyQualifiedNameHash(name);
40784013 const src_hash: std.zig.SrcHash = undefined;
4079 const new_decl = try mod.createNewDecl(namespace, name, scope_decl.src_node, name_hash, src_hash);
4014 const new_decl = try mod.createNewDecl(namespace, name, scope_decl.src_node, src_hash);
40804015 const decl_arena_state = try decl_arena.allocator.create(std.heap.ArenaAllocator.State);
40814016
40824017 decl_arena_state.* = decl_arena.state;
......@@ -4125,30 +4060,26 @@ pub fn lookupInNamespace(
41254060 ident_name: []const u8,
41264061 only_pub_usingnamespaces: bool,
41274062) ?*Decl {
4128 const name_hash = namespace.fullyQualifiedNameHash(ident_name);
4129 log.debug("lookup Decl {s} with hash {}", .{
4130 ident_name,
4131 std.fmt.fmtSliceHexLower(&name_hash),
4132 });
4133 // TODO handle decl collision with usingnamespace
4134 // TODO the decl doing the looking up needs to create a decl dependency
4135 // on each usingnamespace decl here.
4136 if (mod.decl_table.get(name_hash)) |decl| {
4137 return decl;
4138 }
4139 {
4140 var it = namespace.usingnamespace_set.iterator();
4141 while (it.next()) |entry| {
4142 const other_ns = entry.key;
4143 const other_is_pub = entry.value;
4144 if (only_pub_usingnamespaces and !other_is_pub) continue;
4145 // TODO handle cycles
4146 if (mod.lookupInNamespace(other_ns, ident_name, true)) |decl| {
4147 return decl;
4148 }
4149 }
4150 }
4151 return null;
4063 @panic("TODO lookupInNamespace");
4064 //// TODO handle decl collision with usingnamespace
4065 //// TODO the decl doing the looking up needs to create a decl dependency
4066 //// on each usingnamespace decl here.
4067 //if (mod.decl_table.get(name_hash)) |decl| {
4068 // return decl;
4069 //}
4070 //{
4071 // var it = namespace.usingnamespace_set.iterator();
4072 // while (it.next()) |entry| {
4073 // const other_ns = entry.key;
4074 // const other_is_pub = entry.value;
4075 // if (only_pub_usingnamespaces and !other_is_pub) continue;
4076 // // TODO handle cycles
4077 // if (mod.lookupInNamespace(other_ns, ident_name, true)) |decl| {
4078 // return decl;
4079 // }
4080 // }
4081 //}
4082 //return null;
41524083}
41534084
41544085pub fn makeIntType(arena: *Allocator, signedness: std.builtin.Signedness, bits: u16) !Type {
......@@ -4274,10 +4205,6 @@ pub fn failWithOwnedErrorMsg(mod: *Module, scope: *Scope, err_msg: *ErrorMsg) In
42744205 return error.AnalysisFail;
42754206}
42764207
4277fn srcHashEql(a: std.zig.SrcHash, b: std.zig.SrcHash) bool {
4278 return @bitCast(u128, a) == @bitCast(u128, b);
4279}
4280
42814208pub fn intAdd(allocator: *Allocator, lhs: Value, rhs: Value) !Value {
42824209 // TODO is this a performance issue? maybe we should try the operation without
42834210 // resorting to BigInt first.
src/Sema.zig+21-57
......@@ -655,7 +655,7 @@ fn zirCoerceResultPtr(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) In
655655 return sema.mod.fail(&block.base, sema.src, "TODO implement zirCoerceResultPtr", .{});
656656}
657657
658fn zirStructDecl(
658pub fn zirStructDecl(
659659 sema: *Sema,
660660 block: *Scope.Block,
661661 inst: Zir.Inst.Index,
......@@ -668,8 +668,8 @@ fn zirStructDecl(
668668 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
669669 const src = inst_data.src();
670670 const extra = sema.code.extraData(Zir.Inst.StructDecl, inst_data.payload_index);
671 const body = sema.code.extra[extra.end..][0..extra.data.body_len];
672671 const fields_len = extra.data.fields_len;
672 const decls_len = extra.data.decls_len;
673673
674674 var new_decl_arena = std.heap.ArenaAllocator.init(gpa);
675675
......@@ -686,37 +686,19 @@ fn zirStructDecl(
686686 .node_offset = inst_data.src_node,
687687 .namespace = .{
688688 .parent = sema.owner_decl.namespace,
689 .parent_name_hash = new_decl.fullyQualifiedNameHash(),
690689 .ty = struct_ty,
691690 .file_scope = block.getFileScope(),
692691 },
693692 };
694693
695 {
696 const ast = std.zig.ast;
697 const node = sema.owner_decl.relativeToNodeIndex(inst_data.src_node);
698 const tree: *const ast.Tree = &struct_obj.namespace.file_scope.tree;
699 const node_tags = tree.nodes.items(.tag);
700 var buf: [2]ast.Node.Index = undefined;
701 const members: []const ast.Node.Index = switch (node_tags[node]) {
702 .container_decl,
703 .container_decl_trailing,
704 => tree.containerDecl(node).ast.members,
705
706 .container_decl_two,
707 .container_decl_two_trailing,
708 => tree.containerDeclTwo(&buf, node).ast.members,
709
710 .container_decl_arg,
711 .container_decl_arg_trailing,
712 => tree.containerDeclArg(node).ast.members,
713
714 .root => tree.rootDecls(),
715 else => unreachable,
716 };
717 try sema.mod.analyzeNamespace(&struct_obj.namespace, members);
718 }
694 var extra_index: usize = try sema.mod.scanNamespace(
695 &struct_obj.namespace,
696 extra.end,
697 decls_len,
698 new_decl,
699 );
719700
701 const body = sema.code.extra[extra_index..][0..extra.data.body_len];
720702 if (fields_len == 0) {
721703 assert(body.len == 0);
722704 return sema.analyzeDeclVal(block, src, new_decl);
......@@ -760,8 +742,8 @@ fn zirStructDecl(
760742 sema.branch_quota = struct_sema.branch_quota;
761743 }
762744 const bit_bags_count = std.math.divCeil(usize, fields_len, 16) catch unreachable;
763 const body_end = extra.end + body.len;
764 var extra_index: usize = body_end + bit_bags_count;
745 const body_end = extra_index + body.len;
746 extra_index += bit_bags_count;
765747 var bit_bag_index: usize = body_end;
766748 var cur_bit_bag: u32 = undefined;
767749 var field_i: u32 = 0;
......@@ -829,8 +811,8 @@ fn zirEnumDecl(
829811 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
830812 const src = inst_data.src();
831813 const extra = sema.code.extraData(Zir.Inst.EnumDecl, inst_data.payload_index);
832 const body = sema.code.extra[extra.end..][0..extra.data.body_len];
833814 const fields_len = extra.data.fields_len;
815 const decls_len = extra.data.decls_len;
834816
835817 var new_decl_arena = std.heap.ArenaAllocator.init(gpa);
836818
......@@ -865,44 +847,27 @@ fn zirEnumDecl(
865847 .node_offset = inst_data.src_node,
866848 .namespace = .{
867849 .parent = sema.owner_decl.namespace,
868 .parent_name_hash = new_decl.fullyQualifiedNameHash(),
869850 .ty = enum_ty,
870851 .file_scope = block.getFileScope(),
871852 },
872853 };
873854
874 {
875 const ast = std.zig.ast;
876 const node = sema.owner_decl.relativeToNodeIndex(inst_data.src_node);
877 const tree: *const ast.Tree = &enum_obj.namespace.file_scope.tree;
878 const node_tags = tree.nodes.items(.tag);
879 var buf: [2]ast.Node.Index = undefined;
880 const members: []const ast.Node.Index = switch (node_tags[node]) {
881 .container_decl,
882 .container_decl_trailing,
883 => tree.containerDecl(node).ast.members,
884
885 .container_decl_two,
886 .container_decl_two_trailing,
887 => tree.containerDeclTwo(&buf, node).ast.members,
888
889 .container_decl_arg,
890 .container_decl_arg_trailing,
891 => tree.containerDeclArg(node).ast.members,
892
893 .root => tree.rootDecls(),
894 else => unreachable,
895 };
896 try sema.mod.analyzeNamespace(&enum_obj.namespace, members);
897 }
855 var extra_index: usize = try sema.mod.scanNamespace(
856 &enum_obj.namespace,
857 extra.end,
858 decls_len,
859 new_decl,
860 );
898861
862 const body = sema.code.extra[extra_index..][0..extra.data.body_len];
899863 if (fields_len == 0) {
900864 assert(body.len == 0);
901865 return sema.analyzeDeclVal(block, src, new_decl);
902866 }
903867
904868 const bit_bags_count = std.math.divCeil(usize, fields_len, 32) catch unreachable;
905 const body_end = extra.end + body.len;
869 const body_end = extra_index + body.len;
870 extra_index += bit_bags_count;
906871
907872 try enum_obj.fields.ensureCapacity(&new_decl_arena.allocator, fields_len);
908873 const any_values = for (sema.code.extra[body_end..][0..bit_bags_count]) |bag| {
......@@ -947,7 +912,6 @@ fn zirEnumDecl(
947912 sema.branch_count = enum_sema.branch_count;
948913 sema.branch_quota = enum_sema.branch_quota;
949914 }
950 var extra_index: usize = body_end + bit_bags_count;
951915 var bit_bag_index: usize = body_end;
952916 var cur_bit_bag: u32 = undefined;
953917 var field_i: u32 = 0;
src/link/C.zig+23-15
......@@ -15,6 +15,10 @@ pub const base_tag: link.File.Tag = .c;
1515pub const zig_h = @embedFile("C/zig.h");
1616
1717base: link.File,
18/// This linker backend does not try to incrementally link output C source code.
19/// Instead, it tracks all declarations in this table, and iterates over it
20/// in the flush function, stitching pre-rendered pieces of C code together.
21decl_table: std.AutoArrayHashMapUnmanaged(*Module.Decl, void) = .{},
1822
1923/// Per-declaration data. For functions this is the body, and
2024/// the forward declaration is stored in the FnBlock.
......@@ -66,10 +70,10 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio
6670}
6771
6872pub fn deinit(self: *C) void {
69 const module = self.base.options.module orelse return;
70 for (module.decl_table.items()) |entry| {
71 self.freeDecl(entry.value);
73 for (self.decl_table.items()) |entry| {
74 self.freeDecl(entry.key);
7275 }
76 self.decl_table.deinit(self.base.allocator);
7377}
7478
7579pub fn allocateDeclIndexes(self: *C, decl: *Module.Decl) !void {}
......@@ -88,6 +92,9 @@ pub fn updateDecl(self: *C, module: *Module, decl: *Module.Decl) !void {
8892 const tracy = trace(@src());
8993 defer tracy.end();
9094
95 // Keep track of all decls so we can iterate over them on flush().
96 _ = try self.decl_table.getOrPut(self.base.allocator, decl);
97
9198 const fwd_decl = &decl.fn_link.c.fwd_decl;
9299 const typedefs = &decl.fn_link.c.typedefs;
93100 const code = &decl.link.c.code;
......@@ -168,7 +175,7 @@ pub fn flushModule(self: *C, comp: *Compilation) !void {
168175 defer all_buffers.deinit();
169176
170177 // This is at least enough until we get to the function bodies without error handling.
171 try all_buffers.ensureCapacity(module.decl_table.count() + 2);
178 try all_buffers.ensureCapacity(self.decl_table.count() + 2);
172179
173180 var file_size: u64 = zig_h.len;
174181 all_buffers.appendAssumeCapacity(.{
......@@ -197,8 +204,8 @@ pub fn flushModule(self: *C, comp: *Compilation) !void {
197204 // Typedefs, forward decls and non-functions first.
198205 // TODO: performance investigation: would keeping a list of Decls that we should
199206 // generate, rather than querying here, be faster?
200 for (module.decl_table.items()) |kv| {
201 const decl = kv.value;
207 for (self.decl_table.items()) |kv| {
208 const decl = kv.key;
202209 switch (decl.typed_value) {
203210 .most_recent => |tvm| {
204211 const buf = buf: {
......@@ -237,8 +244,8 @@ pub fn flushModule(self: *C, comp: *Compilation) !void {
237244
238245 // Now the function bodies.
239246 try all_buffers.ensureCapacity(all_buffers.items.len + fn_count);
240 for (module.decl_table.items()) |kv| {
241 const decl = kv.value;
247 for (self.decl_table.items()) |kv| {
248 const decl = kv.key;
242249 switch (decl.typed_value) {
243250 .most_recent => |tvm| {
244251 if (tvm.typed_value.val.castTag(.function)) |_| {
......@@ -263,13 +270,13 @@ pub fn flushEmitH(module: *Module) !void {
263270 const tracy = trace(@src());
264271 defer tracy.end();
265272
266 const emit_h_loc = module.emit_h orelse return;
273 const emit_h = module.emit_h orelse return;
267274
268275 // We collect a list of buffers to write, and write them all at once with pwritev 😎
269276 var all_buffers = std.ArrayList(std.os.iovec_const).init(module.gpa);
270277 defer all_buffers.deinit();
271278
272 try all_buffers.ensureCapacity(module.decl_table.count() + 1);
279 try all_buffers.ensureCapacity(emit_h.decl_table.count() + 1);
273280
274281 var file_size: u64 = zig_h.len;
275282 all_buffers.appendAssumeCapacity(.{
......@@ -277,9 +284,10 @@ pub fn flushEmitH(module: *Module) !void {
277284 .iov_len = zig_h.len,
278285 });
279286
280 for (module.decl_table.items()) |kv| {
281 const emit_h = kv.value.getEmitH(module);
282 const buf = emit_h.fwd_decl.items;
287 for (emit_h.decl_table.items()) |kv| {
288 const decl = kv.key;
289 const decl_emit_h = decl.getEmitH(module);
290 const buf = decl_emit_h.fwd_decl.items;
283291 all_buffers.appendAssumeCapacity(.{
284292 .iov_base = buf.ptr,
285293 .iov_len = buf.len,
......@@ -287,8 +295,8 @@ pub fn flushEmitH(module: *Module) !void {
287295 file_size += buf.len;
288296 }
289297
290 const directory = emit_h_loc.directory orelse module.comp.local_cache_directory;
291 const file = try directory.handle.createFile(emit_h_loc.basename, .{
298 const directory = emit_h.loc.directory orelse module.comp.local_cache_directory;
299 const file = try directory.handle.createFile(emit_h.loc.basename, .{
292300 // We set the end position explicitly below; by not truncating the file, we possibly
293301 // make it easier on the file system by doing 1 reallocation instead of two.
294302 .truncate = false,
src/link/SpirV.zig+13-4
......@@ -37,9 +37,14 @@ pub const FnData = struct {
3737
3838base: link.File,
3939
40// TODO: Does this file need to support multiple independent modules?
40/// TODO: Does this file need to support multiple independent modules?
4141spirv_module: codegen.SPIRVModule,
4242
43/// This linker backend does not try to incrementally link output SPIR-V code.
44/// Instead, it tracks all declarations in this table, and iterates over it
45/// in the flush function.
46decl_table: std.AutoArrayHashMapUnmanaged(*Module.Decl, void) = .{},
47
4348pub fn createEmpty(gpa: *Allocator, options: link.Options) !*SpirV {
4449 const spirv = try gpa.create(SpirV);
4550 spirv.* = .{
......@@ -88,6 +93,7 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio
8893}
8994
9095pub fn deinit(self: *SpirV) void {
96 self.decl_table.deinit(self.base.allocator);
9197 self.spirv_module.deinit();
9298}
9399
......@@ -95,6 +101,9 @@ pub fn updateDecl(self: *SpirV, module: *Module, decl: *Module.Decl) !void {
95101 const tracy = trace(@src());
96102 defer tracy.end();
97103
104 // Keep track of all decls so we can iterate over them on flush().
105 _ = try self.decl_table.getOrPut(self.base.allocator, decl);
106
98107 const fn_data = &decl.fn_link.spirv;
99108 if (fn_data.id == null) {
100109 fn_data.id = self.spirv_module.allocId();
......@@ -164,12 +173,12 @@ pub fn flushModule(self: *SpirV, comp: *Compilation) !void {
164173 defer all_buffers.deinit();
165174
166175 // Pre-allocate enough for the binary info + all functions
167 try all_buffers.ensureCapacity(module.decl_table.count() + 1);
176 try all_buffers.ensureCapacity(self.decl_table.count() + 1);
168177
169178 all_buffers.appendAssumeCapacity(wordsToIovConst(binary.items));
170179
171 for (module.decl_table.items()) |entry| {
172 const decl = entry.value;
180 for (self.decl_table.items()) |entry| {
181 const decl = entry.key;
173182 switch (decl.typed_value) {
174183 .most_recent => |tvm| {
175184 const fn_data = &decl.fn_link.spirv;