| author | |
| committer | |
| log | 48e2ba3b3c1e224b59e3c97ed462ac88df4d8a4b |
| tree | 9c673832dc4223d5d5886c54d99d532efc33c33d |
| parent | bc626e8b896bee26f565a271da7e60b2564ee341 |
| parent | 0fa8cf44f69dcb00dacf180d88ebfa09bc462120 |
| signature |
translate-c fixes5 files changed, 58 insertions(+), 7 deletions(-)
src/AstGen.zig+6-1| ... | ... | @@ -7468,7 +7468,7 @@ fn localVarRef( |
| 7468 | 7468 | } |
| 7469 | 7469 | |
| 7470 | 7470 | // 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 | 7472 | const ident_name = try astgen.identifierTokenString(ident_token); |
| 7473 | 7473 | return astgen.failNodeNotes(ident, "mutable '{s}' not accessible from here", .{ident_name}, &.{ |
| 7474 | 7474 | try astgen.errNoteTok(local_ptr.token_src, "declared mutable here", .{}), |
| ... | ... | @@ -8041,6 +8041,7 @@ fn typeOf( |
| 8041 | 8041 | |
| 8042 | 8042 | var typeof_scope = gz.makeSubBlock(scope); |
| 8043 | 8043 | typeof_scope.is_comptime = false; |
| 8044 | typeof_scope.is_typeof = true; | |
| 8044 | 8045 | typeof_scope.c_import = false; |
| 8045 | 8046 | defer typeof_scope.unstack(); |
| 8046 | 8047 | |
| ... | ... | @@ -10882,6 +10883,9 @@ const GenZir = struct { |
| 10882 | 10883 | /// whenever we know Sema will analyze the current block with `is_comptime`, |
| 10883 | 10884 | /// for instance when we're within a `struct_decl` or a `block_comptime`. |
| 10884 | 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 | 10889 | /// This is set to true for inline loops; false otherwise. |
| 10886 | 10890 | is_inline: bool = false, |
| 10887 | 10891 | c_import: bool = false, |
| ... | ... | @@ -10953,6 +10957,7 @@ const GenZir = struct { |
| 10953 | 10957 | fn makeSubBlock(gz: *GenZir, scope: *Scope) GenZir { |
| 10954 | 10958 | return .{ |
| 10955 | 10959 | .is_comptime = gz.is_comptime, |
| 10960 | .is_typeof = gz.is_typeof, | |
| 10956 | 10961 | .c_import = gz.c_import, |
| 10957 | 10962 | .decl_node_index = gz.decl_node_index, |
| 10958 | 10963 | .decl_line = gz.decl_line, |
src/translate_c.zig+26-6| ... | ... | @@ -5524,22 +5524,38 @@ const MacroCtx = struct { |
| 5524 | 5524 | return MacroSlicer{ .source = self.source, .tokens = self.list }; |
| 5525 | 5525 | } |
| 5526 | 5526 | |
| 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 | 5533 | const slicer = self.makeSlicer(); |
| 5534 | var last_is_type_kw = false; | |
| 5529 | 5535 | var i: usize = 1; // index 0 is the macro name |
| 5530 | 5536 | while (i < self.list.len) : (i += 1) { |
| 5531 | 5537 | const token = self.list[i]; |
| 5532 | 5538 | switch (token.id) { |
| 5533 | 5539 | .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 | 5544 | .Identifier => { |
| 5535 | 5545 | const identifier = slicer.slice(token); |
| 5536 | 5546 | const is_param = for (params) |param| { |
| 5537 | 5547 | if (param.name != null and mem.eql(u8, identifier, param.name.?)) break true; |
| 5538 | 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 | 5556 | else => {}, |
| 5542 | 5557 | } |
| 5558 | last_is_type_kw = false; | |
| 5543 | 5559 | } |
| 5544 | 5560 | return null; |
| 5545 | 5561 | } |
| ... | ... | @@ -5649,8 +5665,10 @@ fn transPreprocessorEntities(c: *Context, unit: *clang.ASTUnit) Error!void { |
| 5649 | 5665 | fn transMacroDefine(c: *Context, m: *MacroCtx) ParseError!void { |
| 5650 | 5666 | const scope = &c.global_scope.base; |
| 5651 | 5667 | |
| 5652 | if (m.containsUndefinedIdentifier(scope, &.{})) |ident| | |
| 5653 | return m.fail(c, "unable to translate macro: undefined identifier `{s}`", .{ident}); | |
| 5668 | if (m.checkTranslatableMacro(scope, &.{})) |err| switch (err) { | |
| 5669 | .undefined_identifier => |ident| return m.fail(c, "unable to translate macro: undefined identifier `{s}`", .{ident}), | |
| 5670 | .invalid_arg_usage => unreachable, // no args | |
| 5671 | }; | |
| 5654 | 5672 | |
| 5655 | 5673 | const init_node = try parseCExpr(c, m, scope); |
| 5656 | 5674 | const last = m.next().?; |
| ... | ... | @@ -5698,8 +5716,10 @@ fn transMacroFnDefine(c: *Context, m: *MacroCtx) ParseError!void { |
| 5698 | 5716 | |
| 5699 | 5717 | try m.skip(c, .RParen); |
| 5700 | 5718 | |
| 5701 | if (m.containsUndefinedIdentifier(scope, fn_params.items)) |ident| | |
| 5702 | return m.fail(c, "unable to translate macro: undefined identifier `{s}`", .{ident}); | |
| 5719 | if (m.checkTranslatableMacro(scope, fn_params.items)) |err| switch (err) { | |
| 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 | }; | |
| 5703 | 5723 | |
| 5704 | 5724 | const expr = try parseCExpr(c, m, scope); |
| 5705 | 5725 | const last = m.next().?; |
test/behavior/eval.zig+10| ... | ... | @@ -991,6 +991,16 @@ test "closure capture type of runtime-known parameter" { |
| 991 | 991 | try S.b(c); |
| 992 | 992 | } |
| 993 | 993 | |
| 994 | test "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 | ||
| 994 | 1004 | test "comptime break passing through runtime condition converted to runtime break" { |
| 995 | 1005 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 996 | 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 | 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 | 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 | } |