authorgravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2023-09-16 22:35:42+01:00
committergravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2023-09-17 12:41:11+01:00
log28caaea0938e938de96cc66a49f670005a2df9d4
tree7e2975d48dd85296bc79d6918d6520c689ebc268
parentf40f81cbfb0a867b32d4406e198550730de268cd
signaturelock-open Commit is signed but in an unrecognized format.

AstGen: allow closure over known-runtime values within @TypeOf

AstGen emits an error when a closure over a known-runtime value crosses a namespace boundary. This usually makes sense: however, this usage is actually valid if the capture is within a `@TypeOf` operand. Sema already has a special case to allow such closure within `@TypeOf` when AstGen could not determine a value to be runtime-known. This commit simply introduces analagous logic to AstGen to allow `var`s to cross namespace boundaries within `@TypeOf`.

3 files changed, 26 insertions(+), 1 deletions(-)

src/AstGen.zig+6-1
......@@ -7468,7 +7468,7 @@ fn localVarRef(
74687468 }
74697469
74707470 // Can't close over a runtime variable
7471 if (num_namespaces_out != 0 and !local_ptr.maybe_comptime) {
7471 if (num_namespaces_out != 0 and !local_ptr.maybe_comptime and !gz.is_typeof) {
74727472 const ident_name = try astgen.identifierTokenString(ident_token);
74737473 return astgen.failNodeNotes(ident, "mutable '{s}' not accessible from here", .{ident_name}, &.{
74747474 try astgen.errNoteTok(local_ptr.token_src, "declared mutable here", .{}),
......@@ -8041,6 +8041,7 @@ fn typeOf(
80418041
80428042 var typeof_scope = gz.makeSubBlock(scope);
80438043 typeof_scope.is_comptime = false;
8044 typeof_scope.is_typeof = true;
80448045 typeof_scope.c_import = false;
80458046 defer typeof_scope.unstack();
80468047
......@@ -10882,6 +10883,9 @@ const GenZir = struct {
1088210883 /// whenever we know Sema will analyze the current block with `is_comptime`,
1088310884 /// for instance when we're within a `struct_decl` or a `block_comptime`.
1088410885 is_comptime: bool,
10886 /// Whether we're in an expression within a `@TypeOf` operand. In this case, closure of runtime
10887 /// variables is permitted where it is usually not.
10888 is_typeof: bool = false,
1088510889 /// This is set to true for inline loops; false otherwise.
1088610890 is_inline: bool = false,
1088710891 c_import: bool = false,
......@@ -10953,6 +10957,7 @@ const GenZir = struct {
1095310957 fn makeSubBlock(gz: *GenZir, scope: *Scope) GenZir {
1095410958 return .{
1095510959 .is_comptime = gz.is_comptime,
10960 .is_typeof = gz.is_typeof,
1095610961 .c_import = gz.c_import,
1095710962 .decl_node_index = gz.decl_node_index,
1095810963 .decl_line = gz.decl_line,
test/behavior/eval.zig+10
......@@ -991,6 +991,16 @@ test "closure capture type of runtime-known parameter" {
991991 try S.b(c);
992992}
993993
994test "closure capture type of runtime-known var" {
995 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
996 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
997
998 var x: u32 = 1234;
999 const S = struct { val: @TypeOf(x + 100) };
1000 const s: S = .{ .val = x };
1001 try expect(s.val == 1234);
1002}
1003
9941004test "comptime break passing through runtime condition converted to runtime break" {
9951005 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
9961006 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
test/run_translated_c.zig+10
......@@ -1895,4 +1895,14 @@ pub fn addCases(cases: *tests.RunTranslatedCContext) void {
18951895 \\ return 0;
18961896 \\}
18971897 , "");
1898
1899 cases.add("Closure over local in typeof",
1900 \\#include <stdlib.h>
1901 \\int main(void) {
1902 \\ int x = 123;
1903 \\ union { typeof(x) val; } u = { x };
1904 \\ if (u.val != 123) abort();
1905 \\ return 0;
1906 \\}
1907 , "");
18981908}