Commit 65c27e8e66

Matthew Borkowski <matthew.h.borkowski@gmail.com>
2021-10-20 08:34:51
astgen.zig: delay adding closure_capture instructions to preserve GenZir nesting. Only containers create Namespaces, so the declaring_gz is always the GenZir passed to containerDecl, and containerDecl will always add exactly one instruction (an extended *_decl) to that GenZir. Thus, closure_capture instructions are always lined up immediately after a container decl instruction, so rather than adding them at the point of first mention, where we're nested arbitrarily deep, simply walk through the Namespace captures hash map at the end of each containerDecl branch and add them then.
1 parent 92d2aa1
Changed files (1)
src/AstGen.zig
@@ -3850,6 +3850,7 @@ fn structDeclInner(
     astgen.extra.appendSliceAssumeCapacity(block_scope.instructions.items);
     astgen.extra.appendSliceAssumeCapacity(fields_slice);
 
+    try gz.addNamespaceCaptures(&namespace);
     return indexToRef(decl_inst);
 }
 
@@ -3993,6 +3994,7 @@ fn unionDeclInner(
     astgen.extra.appendSliceAssumeCapacity(block_scope.instructions.items);
     astgen.extra.appendSliceAssumeCapacity(fields_slice);
 
+    try gz.addNamespaceCaptures(&namespace);
     return indexToRef(decl_inst);
 }
 
@@ -4234,6 +4236,7 @@ fn containerDecl(
             astgen.extra.appendSliceAssumeCapacity(block_scope.instructions.items);
             astgen.extra.appendSliceAssumeCapacity(fields_slice);
 
+            try gz.addNamespaceCaptures(&namespace);
             return rvalue(gz, rl, indexToRef(decl_inst), node);
         },
         .keyword_opaque => {
@@ -4268,6 +4271,7 @@ fn containerDecl(
             try astgen.extra.ensureUnusedCapacity(gpa, decls_slice.len);
             astgen.extra.appendSliceAssumeCapacity(decls_slice);
 
+            try gz.addNamespaceCaptures(&namespace);
             return rvalue(gz, rl, indexToRef(decl_inst), node);
         },
         else => unreachable,
@@ -6108,9 +6112,15 @@ fn tunnelThroughClosure(
     // already has one for this value.
     const gop = try ns.?.captures.getOrPut(gpa, refToIndex(value).?);
     if (!gop.found_existing) {
-        // Make a new capture for this value
-        const capture_ref = try ns.?.declaring_gz.?.addUnTok(.closure_capture, value, token);
-        gop.value_ptr.* = refToIndex(capture_ref).?;
+        // Make a new capture for this value but don't add it to the declaring_gz yet
+        try gz.astgen.instructions.append(gz.astgen.gpa, .{
+            .tag = .closure_capture,
+            .data = .{ .un_tok = .{
+                .operand = value,
+                .src_tok = ns.?.declaring_gz.?.tokenIndexToRelative(token),
+            } },
+        });
+        gop.value_ptr.* = @intCast(Zir.Inst.Index, gz.astgen.instructions.len - 1);
     }
 
     // Add an instruction to get the value from the closure into
@@ -8736,7 +8746,7 @@ const Scope = struct {
 
         /// Map from the raw captured value to the instruction
         /// ref of the capture for decls in this namespace
-        captures: std.AutoHashMapUnmanaged(Zir.Inst.Index, Zir.Inst.Index) = .{},
+        captures: std.AutoArrayHashMapUnmanaged(Zir.Inst.Index, Zir.Inst.Index) = .{},
 
         pub fn deinit(self: *Namespace, gpa: *Allocator) void {
             self.decls.deinit(gpa);
@@ -9876,6 +9886,15 @@ const GenZir = struct {
             else => unreachable,
         }
     }
+
+    fn addNamespaceCaptures(gz: *GenZir, namespace: *Scope.Namespace) !void {
+        if (namespace.captures.count() > 0) {
+            try gz.instructions.ensureUnusedCapacity(gz.astgen.gpa, namespace.captures.count());
+            for (namespace.captures.values()) |capture| {
+                gz.instructions.appendAssumeCapacity(capture);
+            }
+        }
+    }
 };
 
 /// This can only be for short-lived references; the memory becomes invalidated