authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2023-09-17 13:54:35-07:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2023-09-17 13:54:35-07:00
log48e2ba3b3c1e224b59e3c97ed462ac88df4d8a4b
tree9c673832dc4223d5d5886c54d99d532efc33c33d
parentbc626e8b896bee26f565a271da7e60b2564ee341
parent0fa8cf44f69dcb00dacf180d88ebfa09bc462120
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #17179 from mlugg/fix/translate-c

translate-c fixes

5 files changed, 58 insertions(+), 7 deletions(-)

src/AstGen.zig+6-1
...@@ -7468,7 +7468,7 @@ fn localVarRef(...@@ -7468,7 +7468,7 @@ fn localVarRef(
7468 }7468 }
74697469
7470 // Can't close over a runtime variable7470 // 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) {
7472 const ident_name = try astgen.identifierTokenString(ident_token);7472 const ident_name = try astgen.identifierTokenString(ident_token);
7473 return astgen.failNodeNotes(ident, "mutable '{s}' not accessible from here", .{ident_name}, &.{7473 return astgen.failNodeNotes(ident, "mutable '{s}' not accessible from here", .{ident_name}, &.{
7474 try astgen.errNoteTok(local_ptr.token_src, "declared mutable here", .{}),7474 try astgen.errNoteTok(local_ptr.token_src, "declared mutable here", .{}),
...@@ -8041,6 +8041,7 @@ fn typeOf(...@@ -8041,6 +8041,7 @@ fn typeOf(
80418041
8042 var typeof_scope = gz.makeSubBlock(scope);8042 var typeof_scope = gz.makeSubBlock(scope);
8043 typeof_scope.is_comptime = false;8043 typeof_scope.is_comptime = false;
8044 typeof_scope.is_typeof = true;
8044 typeof_scope.c_import = false;8045 typeof_scope.c_import = false;
8045 defer typeof_scope.unstack();8046 defer typeof_scope.unstack();
80468047
...@@ -10882,6 +10883,9 @@ const GenZir = struct {...@@ -10882,6 +10883,9 @@ const GenZir = struct {
10882 /// whenever we know Sema will analyze the current block with `is_comptime`,10883 /// whenever we know Sema will analyze the current block with `is_comptime`,
10883 /// for instance when we're within a `struct_decl` or a `block_comptime`.10884 /// for instance when we're within a `struct_decl` or a `block_comptime`.
10884 is_comptime: bool,10885 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,
10885 /// This is set to true for inline loops; false otherwise.10889 /// This is set to true for inline loops; false otherwise.
10886 is_inline: bool = false,10890 is_inline: bool = false,
10887 c_import: bool = false,10891 c_import: bool = false,
...@@ -10953,6 +10957,7 @@ const GenZir = struct {...@@ -10953,6 +10957,7 @@ const GenZir = struct {
10953 fn makeSubBlock(gz: *GenZir, scope: *Scope) GenZir {10957 fn makeSubBlock(gz: *GenZir, scope: *Scope) GenZir {
10954 return .{10958 return .{
10955 .is_comptime = gz.is_comptime,10959 .is_comptime = gz.is_comptime,
10960 .is_typeof = gz.is_typeof,
10956 .c_import = gz.c_import,10961 .c_import = gz.c_import,
10957 .decl_node_index = gz.decl_node_index,10962 .decl_node_index = gz.decl_node_index,
10958 .decl_line = gz.decl_line,10963 .decl_line = gz.decl_line,
src/translate_c.zig+26-6
...@@ -5524,22 +5524,38 @@ const MacroCtx = struct {...@@ -5524,22 +5524,38 @@ const MacroCtx = struct {
5524 return MacroSlicer{ .source = self.source, .tokens = self.list };5524 return MacroSlicer{ .source = self.source, .tokens = self.list };
5525 }5525 }
55265526
5527 fn containsUndefinedIdentifier(self: *MacroCtx, scope: *Scope, params: []const ast.Payload.Param) ?[]const u8 {5527 const MacroTranslateError = union(enum) {
5528 undefined_identifier: []const u8,
5529 invalid_arg_usage: []const u8,
5530 };
5531
5532 fn checkTranslatableMacro(self: *MacroCtx, scope: *Scope, params: []const ast.Payload.Param) ?MacroTranslateError {
5528 const slicer = self.makeSlicer();5533 const slicer = self.makeSlicer();
5534 var last_is_type_kw = false;
5529 var i: usize = 1; // index 0 is the macro name5535 var i: usize = 1; // index 0 is the macro name
5530 while (i < self.list.len) : (i += 1) {5536 while (i < self.list.len) : (i += 1) {
5531 const token = self.list[i];5537 const token = self.list[i];
5532 switch (token.id) {5538 switch (token.id) {
5533 .Period, .Arrow => i += 1, // skip next token since field identifiers can be unknown5539 .Period, .Arrow => i += 1, // skip next token since field identifiers can be unknown
5540 .Keyword_struct, .Keyword_union, .Keyword_enum => if (!last_is_type_kw) {
5541 last_is_type_kw = true;
5542 continue;
5543 },
5534 .Identifier => {5544 .Identifier => {
5535 const identifier = slicer.slice(token);5545 const identifier = slicer.slice(token);
5536 const is_param = for (params) |param| {5546 const is_param = for (params) |param| {
5537 if (param.name != null and mem.eql(u8, identifier, param.name.?)) break true;5547 if (param.name != null and mem.eql(u8, identifier, param.name.?)) break true;
5538 } else false;5548 } else false;
5539 if (!scope.contains(identifier) and !isBuiltinDefined(identifier) and !is_param) return identifier;5549 if (is_param and last_is_type_kw) {
5550 return .{ .invalid_arg_usage = identifier };
5551 }
5552 if (!scope.contains(identifier) and !isBuiltinDefined(identifier) and !is_param) {
5553 return .{ .undefined_identifier = identifier };
5554 }
5540 },5555 },
5541 else => {},5556 else => {},
5542 }5557 }
5558 last_is_type_kw = false;
5543 }5559 }
5544 return null;5560 return null;
5545 }5561 }
...@@ -5649,8 +5665,10 @@ fn transPreprocessorEntities(c: *Context, unit: *clang.ASTUnit) Error!void {...@@ -5649,8 +5665,10 @@ fn transPreprocessorEntities(c: *Context, unit: *clang.ASTUnit) Error!void {
5649fn transMacroDefine(c: *Context, m: *MacroCtx) ParseError!void {5665fn transMacroDefine(c: *Context, m: *MacroCtx) ParseError!void {
5650 const scope = &c.global_scope.base;5666 const scope = &c.global_scope.base;
56515667
5652 if (m.containsUndefinedIdentifier(scope, &.{})) |ident|5668 if (m.checkTranslatableMacro(scope, &.{})) |err| switch (err) {
5653 return m.fail(c, "unable to translate macro: undefined identifier `{s}`", .{ident});5669 .undefined_identifier => |ident| return m.fail(c, "unable to translate macro: undefined identifier `{s}`", .{ident}),
5670 .invalid_arg_usage => unreachable, // no args
5671 };
56545672
5655 const init_node = try parseCExpr(c, m, scope);5673 const init_node = try parseCExpr(c, m, scope);
5656 const last = m.next().?;5674 const last = m.next().?;
...@@ -5698,8 +5716,10 @@ fn transMacroFnDefine(c: *Context, m: *MacroCtx) ParseError!void {...@@ -5698,8 +5716,10 @@ fn transMacroFnDefine(c: *Context, m: *MacroCtx) ParseError!void {
56985716
5699 try m.skip(c, .RParen);5717 try m.skip(c, .RParen);
57005718
5701 if (m.containsUndefinedIdentifier(scope, fn_params.items)) |ident|5719 if (m.checkTranslatableMacro(scope, fn_params.items)) |err| switch (err) {
5702 return m.fail(c, "unable to translate macro: undefined identifier `{s}`", .{ident});5720 .undefined_identifier => |ident| return m.fail(c, "unable to translate macro: undefined identifier `{s}`", .{ident}),
5721 .invalid_arg_usage => |ident| return m.fail(c, "unable to translate macro: untranslatable usage of arg `{s}`", .{ident}),
5722 };
57035723
5704 const expr = try parseCExpr(c, m, scope);5724 const expr = try parseCExpr(c, m, scope);
5705 const last = m.next().?;5725 const last = m.next().?;
test/behavior/eval.zig+10
...@@ -991,6 +991,16 @@ test "closure capture type of runtime-known parameter" {...@@ -991,6 +991,16 @@ test "closure capture type of runtime-known parameter" {
991 try S.b(c);991 try S.b(c);
992}992}
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
994test "comptime break passing through runtime condition converted to runtime break" {1004test "comptime break passing through runtime condition converted to runtime break" {
995 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO1005 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
996 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;1006 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 {...@@ -1895,4 +1895,14 @@ pub fn addCases(cases: *tests.RunTranslatedCContext) void {
1895 \\ return 0;1895 \\ return 0;
1896 \\}1896 \\}
1897 , "");1897 , "");
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 , "");
1898}1908}
test/translate_c.zig+6
...@@ -4129,4 +4129,10 @@ pub fn addCases(cases: *tests.TranslateCContext) void {...@@ -4129,4 +4129,10 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
4129 \\ }) != 0) {}4129 \\ }) != 0) {}
4130 \\}4130 \\}
4131 });4131 });
4132
4133 cases.add("macro using argument as struct name is not translated",
4134 \\#define FOO(x) struct x
4135 , &[_][]const u8{
4136 \\pub const FOO = @compileError("unable to translate macro: untranslatable usage of arg `x`");
4137 });
4132}4138}