Commit bcf2eb1a00

Andrew Kelley <andrew@ziglang.org>
2022-03-25 05:45:22
Sema: fix closure capture typeof runtime-known parameter
Closures are not necessarily constant values. For example, Zig code might do something like this: fn foo(x: anytype) void { const S = struct {field: @TypeOf(x)}; } ...in which case the closure_capture instruction has access to a runtime value only. In such case we preserve the type and use a dummy runtime value. closes #11292
1 parent b802a67
Changed files (2)
src
test
behavior
src/Sema.zig
@@ -10483,10 +10483,19 @@ fn zirClosureCapture(
 ) CompileError!void {
     // TODO: Compile error when closed over values are modified
     const inst_data = sema.code.instructions.items(.data)[inst].un_tok;
-    const tv = try sema.resolveInstConst(block, inst_data.src(), inst_data.operand);
+    const src = inst_data.src();
+    // Closures are not necessarily constant values. For example, the
+    // code might do something like this:
+    // fn foo(x: anytype) void { const S = struct {field: @TypeOf(x)}; }
+    // ...in which case the closure_capture instruction has access to a runtime
+    // value only. In such case we preserve the type and use a dummy runtime value.
+    const operand = sema.resolveInst(inst_data.operand);
+    const val = (try sema.resolveMaybeUndefValAllowVariables(block, src, operand)) orelse
+        Value.initTag(.generic_poison);
+
     try block.wip_capture_scope.captures.putNoClobber(sema.gpa, inst, .{
-        .ty = try tv.ty.copy(sema.perm_arena),
-        .val = try tv.val.copy(sema.perm_arena),
+        .ty = try sema.typeOf(operand).copy(sema.perm_arena),
+        .val = try val.copy(sema.perm_arena),
     });
 }
 
test/behavior/eval.zig
@@ -878,3 +878,18 @@ test "const local with comptime init through array init" {
 
     try comptime expect(decls[0][0].name[0] == 'a');
 }
+
+test "closure capture type of runtime-known parameter" {
+    if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
+    if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
+
+    const S = struct {
+        fn b(c: anytype) !void {
+            const D = struct { c: @TypeOf(c) };
+            var d = D{ .c = c };
+            try expect(d.c == 1234);
+        }
+    };
+    var c: i32 = 1234;
+    try S.b(c);
+}