authorgravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2024-03-05 07:22:47+00:00
committergravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2024-03-06 21:26:38+00:00
log2c4ac44f25743f5b7ae9db6bc570ab71f15fd83b
tree5936a2c47c13ea1fcd5bd37ce523754517be38cf
parentd0c022f7347b5cda34751a986a535aee3b1f45dc
signaturelock-open Commit is signed but in an unrecognized format.

compiler: treat decl_val/decl_ref of potentially generic decls as captures

This fixes an issue with the implementation of #18816. Consider the following code: ```zig pub fn Wrap(comptime T: type) type { return struct { pub const T1 = T; inner: struct { x: T1 }, }; } ``` Previously, the type of `inner` was not considered to be "capturing" any value, as `T1` is a decl. However, since it is declared within a generic function, this decl reference depends on the context, and thus should be treated as a capture. AstGen has been augmented to tunnel references to decls through closure when the decl was declared in a potentially-generic context (i.e. within a function).

6 files changed, 194 insertions(+), 65 deletions(-)

lib/std/zig/AstGen.zig+90-26
...@@ -44,6 +44,9 @@ compile_errors: ArrayListUnmanaged(Zir.Inst.CompileErrors.Item) = .{},...@@ -44,6 +44,9 @@ compile_errors: ArrayListUnmanaged(Zir.Inst.CompileErrors.Item) = .{},
44/// The topmost block of the current function.44/// The topmost block of the current function.
45fn_block: ?*GenZir = null,45fn_block: ?*GenZir = null,
46fn_var_args: bool = false,46fn_var_args: bool = false,
47/// Whether we are somewhere within a function. If `true`, any container decls may be
48/// generic and thus must be tunneled through closure.
49within_fn: bool = false,
47/// The return type of the current function. This may be a trivial `Ref`, or50/// The return type of the current function. This may be a trivial `Ref`, or
48/// otherwise it refers to a `ret_type` instruction.51/// otherwise it refers to a `ret_type` instruction.
49fn_ret_ty: Zir.Inst.Ref = .none,52fn_ret_ty: Zir.Inst.Ref = .none,
...@@ -4050,6 +4053,11 @@ fn fnDecl(...@@ -4050,6 +4053,11 @@ fn fnDecl(
4050 };4053 };
4051 defer fn_gz.unstack();4054 defer fn_gz.unstack();
40524055
4056 // Set this now, since parameter types, return type, etc may be generic.
4057 const prev_within_fn = astgen.within_fn;
4058 defer astgen.within_fn = prev_within_fn;
4059 astgen.within_fn = true;
4060
4053 const is_pub = fn_proto.visib_token != null;4061 const is_pub = fn_proto.visib_token != null;
4054 const is_export = blk: {4062 const is_export = blk: {
4055 const maybe_export_token = fn_proto.extern_export_inline_token orelse break :blk false;4063 const maybe_export_token = fn_proto.extern_export_inline_token orelse break :blk false;
...@@ -4311,6 +4319,10 @@ fn fnDecl(...@@ -4311,6 +4319,10 @@ fn fnDecl(
43114319
4312 const prev_fn_block = astgen.fn_block;4320 const prev_fn_block = astgen.fn_block;
4313 const prev_fn_ret_ty = astgen.fn_ret_ty;4321 const prev_fn_ret_ty = astgen.fn_ret_ty;
4322 defer {
4323 astgen.fn_block = prev_fn_block;
4324 astgen.fn_ret_ty = prev_fn_ret_ty;
4325 }
4314 astgen.fn_block = &fn_gz;4326 astgen.fn_block = &fn_gz;
4315 astgen.fn_ret_ty = if (is_inferred_error or ret_ref.toIndex() != null) r: {4327 astgen.fn_ret_ty = if (is_inferred_error or ret_ref.toIndex() != null) r: {
4316 // We're essentially guaranteed to need the return type at some point,4328 // We're essentially guaranteed to need the return type at some point,
...@@ -4319,10 +4331,6 @@ fn fnDecl(...@@ -4319,10 +4331,6 @@ fn fnDecl(
4319 // return type now so the rest of the function can use it.4331 // return type now so the rest of the function can use it.
4320 break :r try fn_gz.addNode(.ret_type, decl_node);4332 break :r try fn_gz.addNode(.ret_type, decl_node);
4321 } else ret_ref;4333 } else ret_ref;
4322 defer {
4323 astgen.fn_block = prev_fn_block;
4324 astgen.fn_ret_ty = prev_fn_ret_ty;
4325 }
43264334
4327 const prev_var_args = astgen.fn_var_args;4335 const prev_var_args = astgen.fn_var_args;
4328 astgen.fn_var_args = is_var_args;4336 astgen.fn_var_args = is_var_args;
...@@ -4768,11 +4776,14 @@ fn testDecl(...@@ -4768,11 +4776,14 @@ fn testDecl(
4768 };4776 };
4769 defer fn_block.unstack();4777 defer fn_block.unstack();
47704778
4779 const prev_within_fn = astgen.within_fn;
4771 const prev_fn_block = astgen.fn_block;4780 const prev_fn_block = astgen.fn_block;
4772 const prev_fn_ret_ty = astgen.fn_ret_ty;4781 const prev_fn_ret_ty = astgen.fn_ret_ty;
4782 astgen.within_fn = true;
4773 astgen.fn_block = &fn_block;4783 astgen.fn_block = &fn_block;
4774 astgen.fn_ret_ty = .anyerror_void_error_union_type;4784 astgen.fn_ret_ty = .anyerror_void_error_union_type;
4775 defer {4785 defer {
4786 astgen.within_fn = prev_within_fn;
4776 astgen.fn_block = prev_fn_block;4787 astgen.fn_block = prev_fn_block;
4777 astgen.fn_ret_ty = prev_fn_ret_ty;4788 astgen.fn_ret_ty = prev_fn_ret_ty;
4778 }4789 }
...@@ -4871,6 +4882,7 @@ fn structDeclInner(...@@ -4871,6 +4882,7 @@ fn structDeclInner(
4871 .node = node,4882 .node = node,
4872 .inst = decl_inst,4883 .inst = decl_inst,
4873 .declaring_gz = gz,4884 .declaring_gz = gz,
4885 .maybe_generic = astgen.within_fn,
4874 };4886 };
4875 defer namespace.deinit(gpa);4887 defer namespace.deinit(gpa);
48764888
...@@ -5195,6 +5207,7 @@ fn unionDeclInner(...@@ -5195,6 +5207,7 @@ fn unionDeclInner(
5195 .node = node,5207 .node = node,
5196 .inst = decl_inst,5208 .inst = decl_inst,
5197 .declaring_gz = gz,5209 .declaring_gz = gz,
5210 .maybe_generic = astgen.within_fn,
5198 };5211 };
5199 defer namespace.deinit(gpa);5212 defer namespace.deinit(gpa);
52005213
...@@ -5543,6 +5556,7 @@ fn containerDecl(...@@ -5543,6 +5556,7 @@ fn containerDecl(
5543 .node = node,5556 .node = node,
5544 .inst = decl_inst,5557 .inst = decl_inst,
5545 .declaring_gz = gz,5558 .declaring_gz = gz,
5559 .maybe_generic = astgen.within_fn,
5546 };5560 };
5547 defer namespace.deinit(gpa);5561 defer namespace.deinit(gpa);
55485562
...@@ -5709,6 +5723,7 @@ fn containerDecl(...@@ -5709,6 +5723,7 @@ fn containerDecl(
5709 .node = node,5723 .node = node,
5710 .inst = decl_inst,5724 .inst = decl_inst,
5711 .declaring_gz = gz,5725 .declaring_gz = gz,
5726 .maybe_generic = astgen.within_fn,
5712 };5727 };
5713 defer namespace.deinit(gpa);5728 defer namespace.deinit(gpa);
57145729
...@@ -8247,9 +8262,14 @@ fn localVarRef(...@@ -8247,9 +8262,14 @@ fn localVarRef(
8247 const name_str_index = try astgen.identAsString(ident_token);8262 const name_str_index = try astgen.identAsString(ident_token);
8248 var s = scope;8263 var s = scope;
8249 var found_already: ?Ast.Node.Index = null; // we have found a decl with the same name already8264 var found_already: ?Ast.Node.Index = null; // we have found a decl with the same name already
8265 var found_needs_tunnel: bool = undefined; // defined when `found_already != null`
8266 var found_namespaces_out: u32 = undefined; // defined when `found_already != null`
8267
8268 // The number of namespaces above `gz` we currently are
8250 var num_namespaces_out: u32 = 0;8269 var num_namespaces_out: u32 = 0;
8251 // defined when `num_namespaces_out != 0`8270 // defined by `num_namespaces_out != 0`
8252 var capturing_namespace: *Scope.Namespace = undefined;8271 var capturing_namespace: *Scope.Namespace = undefined;
8272
8253 while (true) switch (s.tag) {8273 while (true) switch (s.tag) {
8254 .local_val => {8274 .local_val => {
8255 const local_val = s.cast(Scope.LocalVal).?;8275 const local_val = s.cast(Scope.LocalVal).?;
...@@ -8267,9 +8287,8 @@ fn localVarRef(...@@ -8267,9 +8287,8 @@ fn localVarRef(
8267 gz,8287 gz,
8268 ident,8288 ident,
8269 num_namespaces_out,8289 num_namespaces_out,
8270 capturing_namespace,8290 .{ .ref = local_val.inst },
8271 local_val.inst,8291 .{ .token = local_val.token_src },
8272 local_val.token_src,
8273 ) else local_val.inst;8292 ) else local_val.inst;
82748293
8275 return rvalueNoCoercePreRef(gz, ri, value_inst, ident);8294 return rvalueNoCoercePreRef(gz, ri, value_inst, ident);
...@@ -8298,9 +8317,8 @@ fn localVarRef(...@@ -8298,9 +8317,8 @@ fn localVarRef(
8298 gz,8317 gz,
8299 ident,8318 ident,
8300 num_namespaces_out,8319 num_namespaces_out,
8301 capturing_namespace,8320 .{ .ref = local_ptr.ptr },
8302 local_ptr.ptr,8321 .{ .token = local_ptr.token_src },
8303 local_ptr.token_src,
8304 ) else local_ptr.ptr;8322 ) else local_ptr.ptr;
83058323
8306 switch (ri.rl) {8324 switch (ri.rl) {
...@@ -8329,6 +8347,8 @@ fn localVarRef(...@@ -8329,6 +8347,8 @@ fn localVarRef(
8329 }8347 }
8330 // We found a match but must continue looking for ambiguous references to decls.8348 // We found a match but must continue looking for ambiguous references to decls.
8331 found_already = i;8349 found_already = i;
8350 found_needs_tunnel = ns.maybe_generic;
8351 found_namespaces_out = num_namespaces_out;
8332 }8352 }
8333 num_namespaces_out += 1;8353 num_namespaces_out += 1;
8334 capturing_namespace = ns;8354 capturing_namespace = ns;
...@@ -8343,6 +8363,29 @@ fn localVarRef(...@@ -8343,6 +8363,29 @@ fn localVarRef(
83438363
8344 // Decl references happen by name rather than ZIR index so that when unrelated8364 // Decl references happen by name rather than ZIR index so that when unrelated
8345 // decls are modified, ZIR code containing references to them can be unmodified.8365 // decls are modified, ZIR code containing references to them can be unmodified.
8366
8367 if (found_namespaces_out > 0 and found_needs_tunnel) {
8368 switch (ri.rl) {
8369 .ref, .ref_coerced_ty => return tunnelThroughClosure(
8370 gz,
8371 ident,
8372 found_namespaces_out,
8373 .{ .decl_ref = name_str_index },
8374 .{ .node = found_already.? },
8375 ),
8376 else => {
8377 const result = try tunnelThroughClosure(
8378 gz,
8379 ident,
8380 found_namespaces_out,
8381 .{ .decl_val = name_str_index },
8382 .{ .node = found_already.? },
8383 );
8384 return rvalueNoCoercePreRef(gz, ri, result, ident);
8385 },
8386 }
8387 }
8388
8346 switch (ri.rl) {8389 switch (ri.rl) {
8347 .ref, .ref_coerced_ty => return gz.addStrTok(.decl_ref, name_str_index, ident_token),8390 .ref, .ref_coerced_ty => return gz.addStrTok(.decl_ref, name_str_index, ident_token),
8348 else => {8391 else => {
...@@ -8361,17 +8404,22 @@ fn tunnelThroughClosure(...@@ -8361,17 +8404,22 @@ fn tunnelThroughClosure(
8361 inner_ref_node: Ast.Node.Index,8404 inner_ref_node: Ast.Node.Index,
8362 /// The number of namespaces being tunnelled through. At least 1.8405 /// The number of namespaces being tunnelled through. At least 1.
8363 num_tunnels: u32,8406 num_tunnels: u32,
8364 /// The namespace being captured from.
8365 ns: *Scope.Namespace,
8366 /// The value being captured.8407 /// The value being captured.
8367 value: Zir.Inst.Ref,8408 value: union(enum) {
8368 /// The token of the value's declaration.8409 ref: Zir.Inst.Ref,
8369 token: Ast.TokenIndex,8410 decl_val: Zir.NullTerminatedString,
8411 decl_ref: Zir.NullTerminatedString,
8412 },
8413 /// The location of the value's declaration.
8414 decl_src: union(enum) {
8415 token: Ast.TokenIndex,
8416 node: Ast.Node.Index,
8417 },
8370) !Zir.Inst.Ref {8418) !Zir.Inst.Ref {
8371 const value_inst = value.toIndex() orelse {8419 switch (value) {
8372 // For trivial values, we don't need a tunnel; just return the ref.8420 .ref => |v| if (v.toIndex() == null) return v, // trivia value; do not need tunnel
8373 return value;8421 .decl_val, .decl_ref => {},
8374 };8422 }
83758423
8376 const astgen = gz.astgen;8424 const astgen = gz.astgen;
8377 const gpa = astgen.gpa;8425 const gpa = astgen.gpa;
...@@ -8382,7 +8430,7 @@ fn tunnelThroughClosure(...@@ -8382,7 +8430,7 @@ fn tunnelThroughClosure(
8382 var sfba = std.heap.stackFallback(@sizeOf(usize) * 2, astgen.arena);8430 var sfba = std.heap.stackFallback(@sizeOf(usize) * 2, astgen.arena);
8383 var intermediate_tunnels = try sfba.get().alloc(*Scope.Namespace, num_tunnels - 1);8431 var intermediate_tunnels = try sfba.get().alloc(*Scope.Namespace, num_tunnels - 1);
83848432
8385 {8433 const root_ns = ns: {
8386 var i: usize = num_tunnels - 1;8434 var i: usize = num_tunnels - 1;
8387 var scope: *Scope = gz.parent;8435 var scope: *Scope = gz.parent;
8388 while (i > 0) {8436 while (i > 0) {
...@@ -8392,15 +8440,27 @@ fn tunnelThroughClosure(...@@ -8392,15 +8440,27 @@ fn tunnelThroughClosure(
8392 }8440 }
8393 scope = scope.parent().?;8441 scope = scope.parent().?;
8394 }8442 }
8395 }8443 while (true) {
8444 if (scope.cast(Scope.Namespace)) |ns| break :ns ns;
8445 scope = scope.parent().?;
8446 }
8447 };
83968448
8397 // Now that we know the scopes we're tunneling through, begin adding8449 // Now that we know the scopes we're tunneling through, begin adding
8398 // captures as required, starting with the outermost namespace.8450 // captures as required, starting with the outermost namespace.
8451 const root_capture = Zir.Inst.Capture.wrap(switch (value) {
8452 .ref => |v| .{ .instruction = v.toIndex().? },
8453 .decl_val => |str| .{ .decl_val = str },
8454 .decl_ref => |str| .{ .decl_ref = str },
8455 });
8399 var cur_capture_index = std.math.cast(8456 var cur_capture_index = std.math.cast(
8400 u16,8457 u16,
8401 (try ns.captures.getOrPut(gpa, Zir.Inst.Capture.wrap(.{ .inst = value_inst }))).index,8458 (try root_ns.captures.getOrPut(gpa, root_capture)).index,
8402 ) orelse return astgen.failNodeNotes(ns.node, "this compiler implementation only supports up to 65536 captures per namespace", .{}, &.{8459 ) orelse return astgen.failNodeNotes(root_ns.node, "this compiler implementation only supports up to 65536 captures per namespace", .{}, &.{
8403 try astgen.errNoteTok(token, "captured value here", .{}),8460 switch (decl_src) {
8461 .token => |t| try astgen.errNoteTok(t, "captured value here", .{}),
8462 .node => |n| try astgen.errNoteNode(n, "captured value here", .{}),
8463 },
8404 try astgen.errNoteNode(inner_ref_node, "value used here", .{}),8464 try astgen.errNoteNode(inner_ref_node, "value used here", .{}),
8405 });8465 });
84068466
...@@ -8409,7 +8469,10 @@ fn tunnelThroughClosure(...@@ -8409,7 +8469,10 @@ fn tunnelThroughClosure(
8409 u16,8469 u16,
8410 (try tunnel_ns.captures.getOrPut(gpa, Zir.Inst.Capture.wrap(.{ .nested = cur_capture_index }))).index,8470 (try tunnel_ns.captures.getOrPut(gpa, Zir.Inst.Capture.wrap(.{ .nested = cur_capture_index }))).index,
8411 ) orelse return astgen.failNodeNotes(tunnel_ns.node, "this compiler implementation only supports up to 65536 captures per namespace", .{}, &.{8471 ) orelse return astgen.failNodeNotes(tunnel_ns.node, "this compiler implementation only supports up to 65536 captures per namespace", .{}, &.{
8412 try astgen.errNoteTok(token, "captured value here", .{}),8472 switch (decl_src) {
8473 .token => |t| try astgen.errNoteTok(t, "captured value here", .{}),
8474 .node => |n| try astgen.errNoteNode(n, "captured value here", .{}),
8475 },
8413 try astgen.errNoteNode(inner_ref_node, "value used here", .{}),8476 try astgen.errNoteNode(inner_ref_node, "value used here", .{}),
8414 });8477 });
8415 }8478 }
...@@ -11752,6 +11815,7 @@ const Scope = struct {...@@ -11752,6 +11815,7 @@ const Scope = struct {
11752 decls: std.AutoHashMapUnmanaged(Zir.NullTerminatedString, Ast.Node.Index) = .{},11815 decls: std.AutoHashMapUnmanaged(Zir.NullTerminatedString, Ast.Node.Index) = .{},
11753 node: Ast.Node.Index,11816 node: Ast.Node.Index,
11754 inst: Zir.Inst.Index,11817 inst: Zir.Inst.Index,
11818 maybe_generic: bool,
1175511819
11756 /// The astgen scope containing this namespace.11820 /// The astgen scope containing this namespace.
11757 /// Only valid during astgen.11821 /// Only valid during astgen.
lib/std/zig/Zir.zig+36-12
...@@ -3057,26 +3057,50 @@ pub const Inst = struct {...@@ -3057,26 +3057,50 @@ pub const Inst = struct {
3057 };3057 };
30583058
3059 /// Represents a single value being captured in a type declaration's closure.3059 /// Represents a single value being captured in a type declaration's closure.
3060 /// If high bit is 0, this represents a `Zir.Inst,Index`.3060 pub const Capture = packed struct(u32) {
3061 /// If high bit is 1, this represents an index into the last closure.3061 tag: enum(u2) {
3062 pub const Capture = enum(u32) {3062 /// `data` is a `u16` index into the parent closure.
3063 _,3063 nested,
3064 /// `data` is a `Zir.Inst.Index` to an instruction whose value is being captured.
3065 instruction,
3066 /// `data` is a `NullTerminatedString` to a decl name.
3067 decl_val,
3068 /// `data` is a `NullTerminatedString` to a decl name.
3069 decl_ref,
3070 },
3071 data: u30,
3064 pub const Unwrapped = union(enum) {3072 pub const Unwrapped = union(enum) {
3065 inst: Zir.Inst.Index,
3066 nested: u16,3073 nested: u16,
3074 instruction: Zir.Inst.Index,
3075 decl_val: NullTerminatedString,
3076 decl_ref: NullTerminatedString,
3067 };3077 };
3068 pub fn wrap(cap: Unwrapped) Capture {3078 pub fn wrap(cap: Unwrapped) Capture {
3069 return switch (cap) {3079 return switch (cap) {
3070 .inst => |inst| @enumFromInt(@intFromEnum(inst)),3080 .nested => |idx| .{
3071 .nested => |idx| @enumFromInt((1 << 31) | @as(u32, idx)),3081 .tag = .nested,
3082 .data = idx,
3083 },
3084 .instruction => |inst| .{
3085 .tag = .instruction,
3086 .data = @intCast(@intFromEnum(inst)),
3087 },
3088 .decl_val => |str| .{
3089 .tag = .decl_val,
3090 .data = @intCast(@intFromEnum(str)),
3091 },
3092 .decl_ref => |str| .{
3093 .tag = .decl_ref,
3094 .data = @intCast(@intFromEnum(str)),
3095 },
3072 };3096 };
3073 }3097 }
3074 pub fn unwrap(cap: Capture) Unwrapped {3098 pub fn unwrap(cap: Capture) Unwrapped {
3075 const raw = @intFromEnum(cap);3099 return switch (cap.tag) {
3076 const tag: u1 = @intCast(raw >> 31);3100 .nested => .{ .nested = @intCast(cap.data) },
3077 return switch (tag) {3101 .instruction => .{ .instruction = @enumFromInt(cap.data) },
3078 0 => .{ .inst = @enumFromInt(raw) },3102 .decl_val => .{ .decl_val = @enumFromInt(cap.data) },
3079 1 => .{ .nested = @truncate(raw) },3103 .decl_ref => .{ .decl_ref = @enumFromInt(cap.data) },
3080 };3104 };
3081 }3105 }
3082 };3106 };
src/Autodoc.zig+19-3
...@@ -459,11 +459,21 @@ const Scope = struct {...@@ -459,11 +459,21 @@ const Scope = struct {
459 NotRequested: u32, // instr_index459 NotRequested: u32, // instr_index
460 };460 };
461461
462 fn getCapture(scope: Scope, idx: u16) struct { Zir.Inst.Index, *Scope } {462 fn getCapture(scope: Scope, idx: u16) struct {
463 union(enum) { inst: Zir.Inst.Index, decl: Zir.NullTerminatedString },
464 *Scope,
465 } {
463 const parent = scope.parent.?;466 const parent = scope.parent.?;
464 return switch (scope.captures[idx].unwrap()) {467 return switch (scope.captures[idx].unwrap()) {
465 .inst => |inst| .{ inst, parent },
466 .nested => |parent_idx| parent.getCapture(parent_idx),468 .nested => |parent_idx| parent.getCapture(parent_idx),
469 .instruction => |inst| .{
470 .{ .inst = inst },
471 parent,
472 },
473 .decl_val, .decl_ref => |str| .{
474 .{ .decl = str },
475 parent,
476 },
467 };477 };
468 }478 }
469479
...@@ -4048,7 +4058,13 @@ fn walkInstruction(...@@ -4048,7 +4058,13 @@ fn walkInstruction(
4048 },4058 },
4049 .closure_get => {4059 .closure_get => {
4050 const captured, const scope = parent_scope.getCapture(extended.small);4060 const captured, const scope = parent_scope.getCapture(extended.small);
4051 return self.walkInstruction(file, scope, parent_src, captured, need_type, call_ctx);4061 switch (captured) {
4062 .inst => |cap_inst| return self.walkInstruction(file, scope, parent_src, cap_inst, need_type, call_ctx),
4063 .decl => |str| {
4064 const decl_status = parent_scope.resolveDeclName(str, file, inst.toOptional());
4065 return .{ .expr = .{ .declRef = decl_status } };
4066 },
4067 }
4052 },4068 },
4053 }4069 }
4054 },4070 },
src/InternPool.zig+13-4
...@@ -503,22 +503,29 @@ pub const OptionalNullTerminatedString = enum(u32) {...@@ -503,22 +503,29 @@ pub const OptionalNullTerminatedString = enum(u32) {
503};503};
504504
505/// A single value captured in the closure of a namespace type. This is not a plain505/// A single value captured in the closure of a namespace type. This is not a plain
506/// `Index` because we must differentiate between runtime-known values (where we506/// `Index` because we must differentiate between the following cases:
507/// store the type) and comptime-known values (where we store the value).507/// * runtime-known value (where we store the type)
508/// * comptime-known value (where we store the value)
509/// * decl val (so that we can analyze the value lazily)
510/// * decl ref (so that we can analyze the reference lazily)
508pub const CaptureValue = packed struct(u32) {511pub const CaptureValue = packed struct(u32) {
509 tag: enum { @"comptime", runtime },512 tag: enum { @"comptime", runtime, decl_val, decl_ref },
510 idx: u31,513 idx: u30,
511514
512 pub fn wrap(val: Unwrapped) CaptureValue {515 pub fn wrap(val: Unwrapped) CaptureValue {
513 return switch (val) {516 return switch (val) {
514 .@"comptime" => |i| .{ .tag = .@"comptime", .idx = @intCast(@intFromEnum(i)) },517 .@"comptime" => |i| .{ .tag = .@"comptime", .idx = @intCast(@intFromEnum(i)) },
515 .runtime => |i| .{ .tag = .runtime, .idx = @intCast(@intFromEnum(i)) },518 .runtime => |i| .{ .tag = .runtime, .idx = @intCast(@intFromEnum(i)) },
519 .decl_val => |i| .{ .tag = .decl_val, .idx = @intCast(@intFromEnum(i)) },
520 .decl_ref => |i| .{ .tag = .decl_ref, .idx = @intCast(@intFromEnum(i)) },
516 };521 };
517 }522 }
518 pub fn unwrap(val: CaptureValue) Unwrapped {523 pub fn unwrap(val: CaptureValue) Unwrapped {
519 return switch (val.tag) {524 return switch (val.tag) {
520 .@"comptime" => .{ .@"comptime" = @enumFromInt(val.idx) },525 .@"comptime" => .{ .@"comptime" = @enumFromInt(val.idx) },
521 .runtime => .{ .runtime = @enumFromInt(val.idx) },526 .runtime => .{ .runtime = @enumFromInt(val.idx) },
527 .decl_val => .{ .decl_val = @enumFromInt(val.idx) },
528 .decl_ref => .{ .decl_ref = @enumFromInt(val.idx) },
522 };529 };
523 }530 }
524531
...@@ -527,6 +534,8 @@ pub const CaptureValue = packed struct(u32) {...@@ -527,6 +534,8 @@ pub const CaptureValue = packed struct(u32) {
527 @"comptime": Index,534 @"comptime": Index,
528 /// Index refers to the type.535 /// Index refers to the type.
529 runtime: Index,536 runtime: Index,
537 decl_val: DeclIndex,
538 decl_ref: DeclIndex,
530 };539 };
531540
532 pub const Slice = struct {541 pub const Slice = struct {
src/Sema.zig+21-11
...@@ -2671,26 +2671,34 @@ fn analyzeAsInt(...@@ -2671,26 +2671,34 @@ fn analyzeAsInt(
26712671
2672/// Given a ZIR extra index which points to a list of `Zir.Inst.Capture`,2672/// Given a ZIR extra index which points to a list of `Zir.Inst.Capture`,
2673/// resolves this into a list of `InternPool.CaptureValue` allocated by `arena`.2673/// resolves this into a list of `InternPool.CaptureValue` allocated by `arena`.
2674fn getCaptures(sema: *Sema, parent_namespace: ?InternPool.NamespaceIndex, extra_index: usize, captures_len: u32) ![]InternPool.CaptureValue {2674fn getCaptures(sema: *Sema, block: *Block, extra_index: usize, captures_len: u32) ![]InternPool.CaptureValue {
2675 const zcu = sema.mod;2675 const zcu = sema.mod;
2676 const ip = &zcu.intern_pool;2676 const ip = &zcu.intern_pool;
2677 const parent_captures: InternPool.CaptureValue.Slice = if (parent_namespace) |p| parent: {2677 const parent_captures: InternPool.CaptureValue.Slice = zcu.namespacePtr(block.namespace).ty.getCaptures(zcu);
2678 break :parent zcu.namespacePtr(p).ty.getCaptures(zcu);
2679 } else undefined; // never used so `undefined` is safe
26802678
2681 const captures = try sema.arena.alloc(InternPool.CaptureValue, captures_len);2679 const captures = try sema.arena.alloc(InternPool.CaptureValue, captures_len);
26822680
2683 for (sema.code.extra[extra_index..][0..captures_len], captures) |raw, *capture| {2681 for (sema.code.extra[extra_index..][0..captures_len], captures) |raw, *capture| {
2684 const zir_capture: Zir.Inst.Capture = @enumFromInt(raw);2682 const zir_capture: Zir.Inst.Capture = @bitCast(raw);
2685 capture.* = switch (zir_capture.unwrap()) {2683 capture.* = switch (zir_capture.unwrap()) {
2686 .inst => |inst| InternPool.CaptureValue.wrap(capture: {2684 .nested => |parent_idx| parent_captures.get(ip)[parent_idx],
2685 .instruction => |inst| InternPool.CaptureValue.wrap(capture: {
2687 const air_ref = try sema.resolveInst(inst.toRef());2686 const air_ref = try sema.resolveInst(inst.toRef());
2688 if (try sema.resolveValueResolveLazy(air_ref)) |val| {2687 if (try sema.resolveValueResolveLazy(air_ref)) |val| {
2689 break :capture .{ .@"comptime" = val.toIntern() };2688 break :capture .{ .@"comptime" = val.toIntern() };
2690 }2689 }
2691 break :capture .{ .runtime = sema.typeOf(air_ref).toIntern() };2690 break :capture .{ .runtime = sema.typeOf(air_ref).toIntern() };
2692 }),2691 }),
2693 .nested => |parent_idx| parent_captures.get(ip)[parent_idx],2692 .decl_val => |str| capture: {
2693 const decl_name = try ip.getOrPutString(sema.gpa, sema.code.nullTerminatedString(str));
2694 const decl = try sema.lookupIdentifier(block, .unneeded, decl_name); // TODO: could we need this src loc?
2695 break :capture InternPool.CaptureValue.wrap(.{ .decl_val = decl });
2696 },
2697 .decl_ref => |str| capture: {
2698 const decl_name = try ip.getOrPutString(sema.gpa, sema.code.nullTerminatedString(str));
2699 const decl = try sema.lookupIdentifier(block, .unneeded, decl_name); // TODO: could we need this src loc?
2700 break :capture InternPool.CaptureValue.wrap(.{ .decl_ref = decl });
2701 },
2694 };2702 };
2695 }2703 }
26962704
...@@ -2727,7 +2735,7 @@ fn zirStructDecl(...@@ -2727,7 +2735,7 @@ fn zirStructDecl(
2727 break :blk decls_len;2735 break :blk decls_len;
2728 } else 0;2736 } else 0;
27292737
2730 const captures = try sema.getCaptures(block.namespace, extra_index, captures_len);2738 const captures = try sema.getCaptures(block, extra_index, captures_len);
2731 extra_index += captures_len;2739 extra_index += captures_len;
27322740
2733 if (small.has_backing_int) {2741 if (small.has_backing_int) {
...@@ -2944,7 +2952,7 @@ fn zirEnumDecl(...@@ -2944,7 +2952,7 @@ fn zirEnumDecl(
2944 break :blk decls_len;2952 break :blk decls_len;
2945 } else 0;2953 } else 0;
29462954
2947 const captures = try sema.getCaptures(block.namespace, extra_index, captures_len);2955 const captures = try sema.getCaptures(block, extra_index, captures_len);
2948 extra_index += captures_len;2956 extra_index += captures_len;
29492957
2950 const decls = sema.code.bodySlice(extra_index, decls_len);2958 const decls = sema.code.bodySlice(extra_index, decls_len);
...@@ -3209,7 +3217,7 @@ fn zirUnionDecl(...@@ -3209,7 +3217,7 @@ fn zirUnionDecl(
3209 break :blk decls_len;3217 break :blk decls_len;
3210 } else 0;3218 } else 0;
32113219
3212 const captures = try sema.getCaptures(block.namespace, extra_index, captures_len);3220 const captures = try sema.getCaptures(block, extra_index, captures_len);
3213 extra_index += captures_len;3221 extra_index += captures_len;
32143222
3215 const wip_ty = switch (try ip.getUnionType(gpa, .{3223 const wip_ty = switch (try ip.getUnionType(gpa, .{
...@@ -3315,7 +3323,7 @@ fn zirOpaqueDecl(...@@ -3315,7 +3323,7 @@ fn zirOpaqueDecl(
3315 break :blk decls_len;3323 break :blk decls_len;
3316 } else 0;3324 } else 0;
33173325
3318 const captures = try sema.getCaptures(block.namespace, extra_index, captures_len);3326 const captures = try sema.getCaptures(block, extra_index, captures_len);
3319 extra_index += captures_len;3327 extra_index += captures_len;
33203328
3321 const wip_ty = switch (try ip.getOpaqueType(gpa, .{3329 const wip_ty = switch (try ip.getOpaqueType(gpa, .{
...@@ -17268,6 +17276,8 @@ fn zirClosureGet(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstDat...@@ -17268,6 +17276,8 @@ fn zirClosureGet(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstDat
17268 const capture_ty = switch (captures.get(ip)[extended.small].unwrap()) {17276 const capture_ty = switch (captures.get(ip)[extended.small].unwrap()) {
17269 .@"comptime" => |index| return Air.internedToRef(index),17277 .@"comptime" => |index| return Air.internedToRef(index),
17270 .runtime => |index| index,17278 .runtime => |index| index,
17279 .decl_val => |decl_index| return sema.analyzeDeclVal(block, src, decl_index),
17280 .decl_ref => |decl_index| return sema.analyzeDeclRef(decl_index),
17271 };17281 };
1727217282
17273 // The comptime case is handled already above. Runtime case below.17283 // The comptime case is handled already above. Runtime case below.
src/print_zir.zig+15-9
...@@ -1427,11 +1427,11 @@ const Writer = struct {...@@ -1427,11 +1427,11 @@ const Writer = struct {
1427 try stream.writeAll("{}, ");1427 try stream.writeAll("{}, ");
1428 } else {1428 } else {
1429 try stream.writeAll("{ ");1429 try stream.writeAll("{ ");
1430 try self.writeCapture(stream, @enumFromInt(self.code.extra[extra_index]));1430 try self.writeCapture(stream, @bitCast(self.code.extra[extra_index]));
1431 extra_index += 1;1431 extra_index += 1;
1432 for (1..captures_len) |_| {1432 for (1..captures_len) |_| {
1433 try stream.writeAll(", ");1433 try stream.writeAll(", ");
1434 try self.writeCapture(stream, @enumFromInt(self.code.extra[extra_index]));1434 try self.writeCapture(stream, @bitCast(self.code.extra[extra_index]));
1435 extra_index += 1;1435 extra_index += 1;
1436 }1436 }
1437 try stream.writeAll(" }, ");1437 try stream.writeAll(" }, ");
...@@ -1652,11 +1652,11 @@ const Writer = struct {...@@ -1652,11 +1652,11 @@ const Writer = struct {
1652 try stream.writeAll("{}, ");1652 try stream.writeAll("{}, ");
1653 } else {1653 } else {
1654 try stream.writeAll("{ ");1654 try stream.writeAll("{ ");
1655 try self.writeCapture(stream, @enumFromInt(self.code.extra[extra_index]));1655 try self.writeCapture(stream, @bitCast(self.code.extra[extra_index]));
1656 extra_index += 1;1656 extra_index += 1;
1657 for (1..captures_len) |_| {1657 for (1..captures_len) |_| {
1658 try stream.writeAll(", ");1658 try stream.writeAll(", ");
1659 try self.writeCapture(stream, @enumFromInt(self.code.extra[extra_index]));1659 try self.writeCapture(stream, @bitCast(self.code.extra[extra_index]));
1660 extra_index += 1;1660 extra_index += 1;
1661 }1661 }
1662 try stream.writeAll(" }, ");1662 try stream.writeAll(" }, ");
...@@ -1817,11 +1817,11 @@ const Writer = struct {...@@ -1817,11 +1817,11 @@ const Writer = struct {
1817 try stream.writeAll("{}, ");1817 try stream.writeAll("{}, ");
1818 } else {1818 } else {
1819 try stream.writeAll("{ ");1819 try stream.writeAll("{ ");
1820 try self.writeCapture(stream, @enumFromInt(self.code.extra[extra_index]));1820 try self.writeCapture(stream, @bitCast(self.code.extra[extra_index]));
1821 extra_index += 1;1821 extra_index += 1;
1822 for (1..captures_len) |_| {1822 for (1..captures_len) |_| {
1823 try stream.writeAll(", ");1823 try stream.writeAll(", ");
1824 try self.writeCapture(stream, @enumFromInt(self.code.extra[extra_index]));1824 try self.writeCapture(stream, @bitCast(self.code.extra[extra_index]));
1825 extra_index += 1;1825 extra_index += 1;
1826 }1826 }
1827 try stream.writeAll(" }, ");1827 try stream.writeAll(" }, ");
...@@ -1930,11 +1930,11 @@ const Writer = struct {...@@ -1930,11 +1930,11 @@ const Writer = struct {
1930 try stream.writeAll("{}, ");1930 try stream.writeAll("{}, ");
1931 } else {1931 } else {
1932 try stream.writeAll("{ ");1932 try stream.writeAll("{ ");
1933 try self.writeCapture(stream, @enumFromInt(self.code.extra[extra_index]));1933 try self.writeCapture(stream, @bitCast(self.code.extra[extra_index]));
1934 extra_index += 1;1934 extra_index += 1;
1935 for (1..captures_len) |_| {1935 for (1..captures_len) |_| {
1936 try stream.writeAll(", ");1936 try stream.writeAll(", ");
1937 try self.writeCapture(stream, @enumFromInt(self.code.extra[extra_index]));1937 try self.writeCapture(stream, @bitCast(self.code.extra[extra_index]));
1938 extra_index += 1;1938 extra_index += 1;
1939 }1939 }
1940 try stream.writeAll(" }, ");1940 try stream.writeAll(" }, ");
...@@ -2808,8 +2808,14 @@ const Writer = struct {...@@ -2808,8 +2808,14 @@ const Writer = struct {
28082808
2809 fn writeCapture(self: *Writer, stream: anytype, capture: Zir.Inst.Capture) !void {2809 fn writeCapture(self: *Writer, stream: anytype, capture: Zir.Inst.Capture) !void {
2810 switch (capture.unwrap()) {2810 switch (capture.unwrap()) {
2811 .inst => |inst| return self.writeInstIndex(stream, inst),
2812 .nested => |i| return stream.print("[{d}]", .{i}),2811 .nested => |i| return stream.print("[{d}]", .{i}),
2812 .instruction => |inst| return self.writeInstIndex(stream, inst),
2813 .decl_val => |str| try stream.print("decl_val \"{}\"", .{
2814 std.zig.fmtEscapes(self.code.nullTerminatedString(str)),
2815 }),
2816 .decl_ref => |str| try stream.print("decl_ref \"{}\"", .{
2817 std.zig.fmtEscapes(self.code.nullTerminatedString(str)),
2818 }),
2813 }2819 }
2814 }2820 }
28152821