authorgravatar for git@vexu.euVeikka Tuominen <git@vexu.eu> 2019-12-14 17:00:10+02:00
committergravatar for git@vexu.euVeikka Tuominen <git@vexu.eu> 2019-12-14 17:00:10+02:00
log1f8458683658ed22a30145e5c48ea3e23785cbae
tree18b356ee8a465afccfc9dd49ec3a484dd5082666
parent4dae70e702f10c385be1dea552f3d6e815dc93c9
signature Commit is signed but in an unrecognized format.

translate-c-2 avoid collisions with zig keywords


3 files changed, 118 insertions(+), 100 deletions(-)

lib/std/zig/tokenizer.zig+64-53
......@@ -9,66 +9,77 @@ pub const Token = struct {
99 pub const Keyword = struct {
1010 bytes: []const u8,
1111 id: Id,
12 hash: u32,
13
14 fn init(bytes: []const u8, id: Id) Keyword {
15 @setEvalBranchQuota(2000);
16 return .{
17 .bytes = bytes,
18 .id = id,
19 .hash = std.hash_map.hashString(bytes),
20 };
21 }
1222 };
1323
1424 pub const keywords = [_]Keyword{
15 Keyword{ .bytes = "align", .id = Id.Keyword_align },
16 Keyword{ .bytes = "allowzero", .id = Id.Keyword_allowzero },
17 Keyword{ .bytes = "and", .id = Id.Keyword_and },
18 Keyword{ .bytes = "anyframe", .id = Id.Keyword_anyframe },
19 Keyword{ .bytes = "asm", .id = Id.Keyword_asm },
20 Keyword{ .bytes = "async", .id = Id.Keyword_async },
21 Keyword{ .bytes = "await", .id = Id.Keyword_await },
22 Keyword{ .bytes = "break", .id = Id.Keyword_break },
23 Keyword{ .bytes = "catch", .id = Id.Keyword_catch },
24 Keyword{ .bytes = "comptime", .id = Id.Keyword_comptime },
25 Keyword{ .bytes = "const", .id = Id.Keyword_const },
26 Keyword{ .bytes = "continue", .id = Id.Keyword_continue },
27 Keyword{ .bytes = "defer", .id = Id.Keyword_defer },
28 Keyword{ .bytes = "else", .id = Id.Keyword_else },
29 Keyword{ .bytes = "enum", .id = Id.Keyword_enum },
30 Keyword{ .bytes = "errdefer", .id = Id.Keyword_errdefer },
31 Keyword{ .bytes = "error", .id = Id.Keyword_error },
32 Keyword{ .bytes = "export", .id = Id.Keyword_export },
33 Keyword{ .bytes = "extern", .id = Id.Keyword_extern },
34 Keyword{ .bytes = "false", .id = Id.Keyword_false },
35 Keyword{ .bytes = "fn", .id = Id.Keyword_fn },
36 Keyword{ .bytes = "for", .id = Id.Keyword_for },
37 Keyword{ .bytes = "if", .id = Id.Keyword_if },
38 Keyword{ .bytes = "inline", .id = Id.Keyword_inline },
39 Keyword{ .bytes = "nakedcc", .id = Id.Keyword_nakedcc },
40 Keyword{ .bytes = "noalias", .id = Id.Keyword_noalias },
41 Keyword{ .bytes = "noasync", .id = Id.Keyword_noasync },
42 Keyword{ .bytes = "noinline", .id = Id.Keyword_noinline },
43 Keyword{ .bytes = "null", .id = Id.Keyword_null },
44 Keyword{ .bytes = "or", .id = Id.Keyword_or },
45 Keyword{ .bytes = "orelse", .id = Id.Keyword_orelse },
46 Keyword{ .bytes = "packed", .id = Id.Keyword_packed },
47 Keyword{ .bytes = "pub", .id = Id.Keyword_pub },
48 Keyword{ .bytes = "resume", .id = Id.Keyword_resume },
49 Keyword{ .bytes = "return", .id = Id.Keyword_return },
50 Keyword{ .bytes = "linksection", .id = Id.Keyword_linksection },
51 Keyword{ .bytes = "stdcallcc", .id = Id.Keyword_stdcallcc },
52 Keyword{ .bytes = "struct", .id = Id.Keyword_struct },
53 Keyword{ .bytes = "suspend", .id = Id.Keyword_suspend },
54 Keyword{ .bytes = "switch", .id = Id.Keyword_switch },
55 Keyword{ .bytes = "test", .id = Id.Keyword_test },
56 Keyword{ .bytes = "threadlocal", .id = Id.Keyword_threadlocal },
57 Keyword{ .bytes = "true", .id = Id.Keyword_true },
58 Keyword{ .bytes = "try", .id = Id.Keyword_try },
59 Keyword{ .bytes = "undefined", .id = Id.Keyword_undefined },
60 Keyword{ .bytes = "union", .id = Id.Keyword_union },
61 Keyword{ .bytes = "unreachable", .id = Id.Keyword_unreachable },
62 Keyword{ .bytes = "usingnamespace", .id = Id.Keyword_usingnamespace },
63 Keyword{ .bytes = "var", .id = Id.Keyword_var },
64 Keyword{ .bytes = "volatile", .id = Id.Keyword_volatile },
65 Keyword{ .bytes = "while", .id = Id.Keyword_while },
25 Keyword.init("align", .Keyword_align),
26 Keyword.init("allowzero", .Keyword_allowzero),
27 Keyword.init("and", .Keyword_and),
28 Keyword.init("anyframe", .Keyword_anyframe),
29 Keyword.init("asm", .Keyword_asm),
30 Keyword.init("async", .Keyword_async),
31 Keyword.init("await", .Keyword_await),
32 Keyword.init("break", .Keyword_break),
33 Keyword.init("catch", .Keyword_catch),
34 Keyword.init("comptime", .Keyword_comptime),
35 Keyword.init("const", .Keyword_const),
36 Keyword.init("continue", .Keyword_continue),
37 Keyword.init("defer", .Keyword_defer),
38 Keyword.init("else", .Keyword_else),
39 Keyword.init("enum", .Keyword_enum),
40 Keyword.init("errdefer", .Keyword_errdefer),
41 Keyword.init("error", .Keyword_error),
42 Keyword.init("export", .Keyword_export),
43 Keyword.init("extern", .Keyword_extern),
44 Keyword.init("false", .Keyword_false),
45 Keyword.init("fn", .Keyword_fn),
46 Keyword.init("for", .Keyword_for),
47 Keyword.init("if", .Keyword_if),
48 Keyword.init("inline", .Keyword_inline),
49 Keyword.init("nakedcc", .Keyword_nakedcc),
50 Keyword.init("noalias", .Keyword_noalias),
51 Keyword.init("noasync", .Keyword_noasync),
52 Keyword.init("noinline", .Keyword_noinline),
53 Keyword.init("null", .Keyword_null),
54 Keyword.init("or", .Keyword_or),
55 Keyword.init("orelse", .Keyword_orelse),
56 Keyword.init("packed", .Keyword_packed),
57 Keyword.init("pub", .Keyword_pub),
58 Keyword.init("resume", .Keyword_resume),
59 Keyword.init("return", .Keyword_return),
60 Keyword.init("linksection", .Keyword_linksection),
61 Keyword.init("stdcallcc", .Keyword_stdcallcc),
62 Keyword.init("struct", .Keyword_struct),
63 Keyword.init("suspend", .Keyword_suspend),
64 Keyword.init("switch", .Keyword_switch),
65 Keyword.init("test", .Keyword_test),
66 Keyword.init("threadlocal", .Keyword_threadlocal),
67 Keyword.init("true", .Keyword_true),
68 Keyword.init("try", .Keyword_try),
69 Keyword.init("undefined", .Keyword_undefined),
70 Keyword.init("union", .Keyword_union),
71 Keyword.init("unreachable", .Keyword_unreachable),
72 Keyword.init("usingnamespace", .Keyword_usingnamespace),
73 Keyword.init("var", .Keyword_var),
74 Keyword.init("volatile", .Keyword_volatile),
75 Keyword.init("while", .Keyword_while),
6676 };
6777
6878 // TODO perfect hash at comptime
69 fn getKeyword(bytes: []const u8) ?Id {
79 pub fn getKeyword(bytes: []const u8) ?Id {
80 var hash = std.hash_map.hashString(bytes);
7081 for (keywords) |kw| {
71 if (mem.eql(u8, kw.bytes, bytes)) {
82 if (kw.hash == hash and mem.eql(u8, kw.bytes, bytes)) {
7283 return kw.id;
7384 }
7485 }
src-self-hosted/translate_c.zig+53-46
......@@ -348,7 +348,7 @@ fn visitVarDecl(c: *Context, var_decl: *const ZigClangVarDecl) Error!void {
348348 else
349349 try appendToken(c, .Keyword_var, "var");
350350
351 const name_tok = try appendToken(c, .Identifier, var_name);
351 const name_tok = try appendIdentifier(c, var_name);
352352
353353 _ = try appendToken(c, .Colon, ":");
354354 const type_node = transQualType(rp, qual_type, var_decl_loc) catch |err| switch (err) {
......@@ -407,7 +407,7 @@ fn resolveTypeDef(c: *Context, typedef_decl: *const ZigClangTypedefNameDecl) Err
407407 const const_tok = try appendToken(c, .Keyword_const, "const");
408408
409409 const typedef_name = try c.str(ZigClangDecl_getName_bytes_begin(@ptrCast(*const ZigClangDecl, typedef_decl)));
410 const name_tok = try appendToken(c, .Identifier, typedef_name);
410 const name_tok = try appendIdentifier(c, typedef_name);
411411 const eq_tok = try appendToken(c, .Equal, "=");
412412
413413 const child_qt = ZigClangTypedefNameDecl_getUnderlyingType(typedef_decl);
......@@ -460,7 +460,7 @@ fn resolveRecordDecl(c: *Context, record_decl: *const ZigClangRecordDecl) Error!
460460 const const_tok = try appendToken(c, .Keyword_const, "const");
461461
462462 const name = try std.fmt.allocPrint(c.a(), "{}_{}", .{ container_kind_name, bare_name });
463 const name_tok = try appendToken(c, .Identifier, name);
463 const name_tok = try appendIdentifier(c, name);
464464
465465 const eq_tok = try appendToken(c, .Equal, "=");
466466 const init_node = transRecordDecl(c, record_decl) catch |err| switch (err) {
......@@ -497,10 +497,10 @@ fn resolveRecordDecl(c: *Context, record_decl: *const ZigClangRecordDecl) Error!
497497fn createAlias(c: *Context, alias: var) !void {
498498 const visib_tok = try appendToken(c, .Keyword_pub, "pub");
499499 const mut_tok = try appendToken(c, .Keyword_const, "const");
500 const name_tok = try appendToken(c, .Identifier, alias.alias);
500 const name_tok = try appendIdentifier(c, alias.alias);
501501
502502 const eq_tok = try appendToken(c, .Equal, "=");
503 const init_node = try appendIdentifier(c, alias.name);
503 const init_node = try transCreateNodeIdentifier(c, alias.name);
504504
505505 const node = try c.a().create(ast.Node.VarDecl);
506506 node.* = ast.Node.VarDecl{
......@@ -787,7 +787,7 @@ fn transDeclStmt(rp: RestorePoint, parent_scope: *Scope, stmt: *const ZigClangDe
787787 const c_name = try c.str(ZigClangDecl_getName_bytes_begin(
788788 @ptrCast(*const ZigClangDecl, var_decl),
789789 ));
790 const name_token = try appendToken(c, .Identifier, c_name);
790 const name_token = try appendIdentifier(c, c_name);
791791
792792 const var_scope = try c.a().create(Scope.Var);
793793 var_scope.* = Scope.Var{
......@@ -856,7 +856,7 @@ fn transDeclRefExpr(
856856 const c_name = try rp.c.str(ZigClangDecl_getName_bytes_begin(@ptrCast(*const ZigClangDecl, value_decl)));
857857 const zig_name = transLookupZigIdentifier(scope, c_name);
858858 if (lrvalue == .l_value) try rp.c.ptr_params.put(zig_name);
859 const node = try appendIdentifier(rp.c, zig_name);
859 const node = try transCreateNodeIdentifier(rp.c, zig_name);
860860 return TransResult{
861861 .node = node,
862862 .node_scope = scope,
......@@ -1292,7 +1292,7 @@ fn maybeSuppressResult(
12921292 if (used == .used) return result;
12931293 // NOTE: This is backwards, but the semicolon must immediately follow the node.
12941294 _ = try appendToken(rp.c, .Semicolon, ";");
1295 const lhs = try appendIdentifier(rp.c, "_");
1295 const lhs = try transCreateNodeIdentifier(rp.c, "_");
12961296 const op_token = try appendToken(rp.c, .Equal, "=");
12971297 const op_node = try rp.c.a().create(ast.Node.InfixOp);
12981298 op_node.* = ast.Node.InfixOp{
......@@ -1374,7 +1374,7 @@ fn transRecordDecl(c: *Context, record_decl: *const ZigClangRecordDecl) TypeErro
13741374 return node;
13751375 }
13761376
1377 const field_name = try appendToken(c, .Identifier, try c.str(ZigClangDecl_getName_bytes_begin(@ptrCast(*const ZigClangDecl, field_decl))));
1377 const field_name = try appendIdentifier(c, try c.str(ZigClangDecl_getName_bytes_begin(@ptrCast(*const ZigClangDecl, field_decl))));
13781378 _ = try appendToken(c, .Colon, ":");
13791379 const field_type = try transQualType(rp, ZigClangFieldDecl_getType(field_decl), field_loc);
13801380
......@@ -1644,7 +1644,7 @@ fn transCreateNodePtrType(
16441644 .Identifier => blk: {
16451645 const lbracket = try appendToken(c, .LBracket, "["); // Rendering checks if this token + 2 == .Identifier, so needs to return this token
16461646 _ = try appendToken(c, .Asterisk, "*");
1647 _ = try appendToken(c, .Identifier, "c");
1647 _ = try appendIdentifier(c, "c");
16481648 _ = try appendToken(c, .RBracket, "]");
16491649 break :blk lbracket;
16501650 },
......@@ -1793,25 +1793,25 @@ fn transType(rp: RestorePoint, ty: *const ZigClangType, source_loc: ZigClangSour
17931793 .Builtin => {
17941794 const builtin_ty = @ptrCast(*const ZigClangBuiltinType, ty);
17951795 switch (ZigClangBuiltinType_getKind(builtin_ty)) {
1796 .Void => return appendIdentifier(rp.c, "c_void"),
1797 .Bool => return appendIdentifier(rp.c, "bool"),
1798 .Char_U, .UChar, .Char_S, .Char8 => return appendIdentifier(rp.c, "u8"),
1799 .SChar => return appendIdentifier(rp.c, "i8"),
1800 .UShort => return appendIdentifier(rp.c, "c_ushort"),
1801 .UInt => return appendIdentifier(rp.c, "c_uint"),
1802 .ULong => return appendIdentifier(rp.c, "c_ulong"),
1803 .ULongLong => return appendIdentifier(rp.c, "c_ulonglong"),
1804 .Short => return appendIdentifier(rp.c, "c_short"),
1805 .Int => return appendIdentifier(rp.c, "c_int"),
1806 .Long => return appendIdentifier(rp.c, "c_long"),
1807 .LongLong => return appendIdentifier(rp.c, "c_longlong"),
1808 .UInt128 => return appendIdentifier(rp.c, "u128"),
1809 .Int128 => return appendIdentifier(rp.c, "i128"),
1810 .Float => return appendIdentifier(rp.c, "f32"),
1811 .Double => return appendIdentifier(rp.c, "f64"),
1812 .Float128 => return appendIdentifier(rp.c, "f128"),
1813 .Float16 => return appendIdentifier(rp.c, "f16"),
1814 .LongDouble => return appendIdentifier(rp.c, "c_longdouble"),
1796 .Void => return transCreateNodeIdentifier(rp.c, "c_void"),
1797 .Bool => return transCreateNodeIdentifier(rp.c, "bool"),
1798 .Char_U, .UChar, .Char_S, .Char8 => return transCreateNodeIdentifier(rp.c, "u8"),
1799 .SChar => return transCreateNodeIdentifier(rp.c, "i8"),
1800 .UShort => return transCreateNodeIdentifier(rp.c, "c_ushort"),
1801 .UInt => return transCreateNodeIdentifier(rp.c, "c_uint"),
1802 .ULong => return transCreateNodeIdentifier(rp.c, "c_ulong"),
1803 .ULongLong => return transCreateNodeIdentifier(rp.c, "c_ulonglong"),
1804 .Short => return transCreateNodeIdentifier(rp.c, "c_short"),
1805 .Int => return transCreateNodeIdentifier(rp.c, "c_int"),
1806 .Long => return transCreateNodeIdentifier(rp.c, "c_long"),
1807 .LongLong => return transCreateNodeIdentifier(rp.c, "c_longlong"),
1808 .UInt128 => return transCreateNodeIdentifier(rp.c, "u128"),
1809 .Int128 => return transCreateNodeIdentifier(rp.c, "i128"),
1810 .Float => return transCreateNodeIdentifier(rp.c, "f32"),
1811 .Double => return transCreateNodeIdentifier(rp.c, "f64"),
1812 .Float128 => return transCreateNodeIdentifier(rp.c, "f128"),
1813 .Float16 => return transCreateNodeIdentifier(rp.c, "f16"),
1814 .LongDouble => return transCreateNodeIdentifier(rp.c, "c_longdouble"),
18151815 else => return revertAndWarn(rp, error.UnsupportedType, source_loc, "unsupported builtin type", .{}),
18161816 }
18171817 },
......@@ -1891,14 +1891,14 @@ fn transType(rp: RestorePoint, ty: *const ZigClangType, source_loc: ZigClangSour
18911891
18921892 const typedef_decl = ZigClangTypedefType_getDecl(typedef_ty);
18931893 const typedef_name = try rp.c.str(ZigClangDecl_getName_bytes_begin(@ptrCast(*const ZigClangDecl, typedef_decl)));
1894 return appendIdentifier(rp.c, typedef_name);
1894 return transCreateNodeIdentifier(rp.c, typedef_name);
18951895 },
18961896 .Record => {
18971897 const record_ty = @ptrCast(*const ZigClangRecordType, ty);
18981898
18991899 const record_decl = ZigClangRecordType_getDecl(record_ty);
1900 if (try getContainerName(rp.c, record_decl)) |name|
1901 return appendIdentifier(rp.c, name)
1900 if (try getContainerName(rp, record_decl)) |name|
1901 return transCreateNodeIdentifier(rp.c, name)
19021902 else
19031903 return transRecordDecl(rp.c, record_decl);
19041904 },
......@@ -1913,22 +1913,20 @@ fn transType(rp: RestorePoint, ty: *const ZigClangType, source_loc: ZigClangSour
19131913 }
19141914}
19151915
1916fn getContainerName(c: *Context, record_decl: *const ZigClangRecordDecl) !?[]const u8 {
1917 const bare_name = try c.str(ZigClangDecl_getName_bytes_begin(@ptrCast(*const ZigClangDecl, record_decl)));
1916fn getContainerName(rp: RestorePoint, record_decl: *const ZigClangRecordDecl) !?[]const u8 {
1917 const bare_name = try rp.c.str(ZigClangDecl_getName_bytes_begin(@ptrCast(*const ZigClangDecl, record_decl)));
19181918
19191919 const container_kind_name = if (ZigClangRecordDecl_isUnion(record_decl))
19201920 "union"
19211921 else if (ZigClangRecordDecl_isStruct(record_decl))
19221922 "struct"
1923 else {
1924 try emitWarning(c, ZigClangRecordDecl_getLocation(record_decl), "record {} is not a struct or union", .{bare_name});
1925 return null;
1926 };
1923 else
1924 return revertAndWarn(rp, error.UnsupportedType, ZigClangRecordDecl_getLocation(record_decl), "record {} is not a struct or union", .{bare_name});
19271925
19281926 if (ZigClangRecordDecl_isAnonymousStructOrUnion(record_decl) or bare_name.len == 0)
19291927 return null;
19301928
1931 return try std.fmt.allocPrint(c.a(), "{}_{}", .{ container_kind_name, bare_name });
1929 return try std.fmt.allocPrint(rp.c.a(), "{}_{}", .{ container_kind_name, bare_name });
19321930}
19331931
19341932fn isCVoid(qt: ZigClangQualType) bool {
......@@ -2020,7 +2018,7 @@ fn finishTransFnProto(
20202018 else
20212019 null;
20222020 const fn_tok = try appendToken(rp.c, .Keyword_fn, "fn");
2023 const name_tok = if (fn_decl_context) |ctx| try appendToken(rp.c, .Identifier, ctx.fn_name) else null;
2021 const name_tok = if (fn_decl_context) |ctx| try appendIdentifier(rp.c, ctx.fn_name) else null;
20242022 const lparen_tok = try appendToken(rp.c, .LParen, "(");
20252023
20262024 var fn_params = ast.Node.FnProto.ParamList.init(rp.c.a());
......@@ -2038,7 +2036,7 @@ fn finishTransFnProto(
20382036 const param_name = try rp.c.str(ZigClangDecl_getName_bytes_begin(@ptrCast(*const ZigClangDecl, param)));
20392037 if (param_name.len > 0) {
20402038 // TODO: If len == 0, auto-generate arg1, arg2, etc? Or leave the name blank?
2041 const result = try appendToken(rp.c, .Identifier, param_name);
2039 const result = try appendIdentifier(rp.c, param_name);
20422040 _ = try appendToken(rp.c, .Colon, ":");
20432041 break :blk result;
20442042 }
......@@ -2087,12 +2085,12 @@ fn finishTransFnProto(
20872085
20882086 const return_type_node = blk: {
20892087 if (ZigClangFunctionType_getNoReturnAttr(fn_ty)) {
2090 break :blk try appendIdentifier(rp.c, "noreturn");
2088 break :blk try transCreateNodeIdentifier(rp.c, "noreturn");
20912089 } else {
20922090 const return_qt = ZigClangFunctionType_getReturnType(fn_ty);
20932091 if (isCVoid(return_qt)) {
20942092 // convert primitive c_void to actual void (only for return type)
2095 break :blk try appendIdentifier(rp.c, "void");
2093 break :blk try transCreateNodeIdentifier(rp.c, "void");
20962094 } else {
20972095 break :blk transQualType(rp, return_qt, source_loc) catch |err| switch (err) {
20982096 error.UnsupportedType => {
......@@ -2145,7 +2143,7 @@ fn emitWarning(c: *Context, loc: ZigClangSourceLocation, comptime format: []cons
21452143fn failDecl(c: *Context, loc: ZigClangSourceLocation, name: []const u8, comptime format: []const u8, args: var) !void {
21462144 // const name = @compileError(msg);
21472145 const const_tok = try appendToken(c, .Keyword_const, "const");
2148 const name_tok = try appendToken(c, .Identifier, name);
2146 const name_tok = try appendIdentifier(c, name);
21492147 const eq_tok = try appendToken(c, .Equal, "=");
21502148 const builtin_tok = try appendToken(c, .Builtin, "@compileError");
21512149 const lparen_tok = try appendToken(c, .LParen, "(");
......@@ -2190,6 +2188,7 @@ fn failDecl(c: *Context, loc: ZigClangSourceLocation, name: []const u8, comptime
21902188}
21912189
21922190fn appendToken(c: *Context, token_id: Token.Id, bytes: []const u8) !ast.TokenIndex {
2191 std.debug.assert(token_id != .Identifier); // use appendIdentifier
21932192 return appendTokenFmt(c, token_id, "{}", .{bytes});
21942193}
21952194
......@@ -2218,8 +2217,16 @@ fn appendTokenFmt(c: *Context, token_id: Token.Id, comptime format: []const u8,
22182217 return token_index;
22192218}
22202219
2221fn appendIdentifier(c: *Context, name: []const u8) !*ast.Node {
2222 const token_index = try appendToken(c, .Identifier, name);
2220fn appendIdentifier(c: *Context, name: []const u8) !ast.TokenIndex {
2221 if (std.zig.Token.getKeyword(name)) |_| {
2222 return appendTokenFmt(c, .Identifier, "@\"{}\"", .{name});
2223 } else {
2224 return appendTokenFmt(c, .Identifier, "{}", .{name});
2225 }
2226}
2227
2228fn transCreateNodeIdentifier(c: *Context, name: []const u8) !*ast.Node {
2229 const token_index = try appendIdentifier(c, name);
22232230 const identifier = try c.a().create(ast.Node.Identifier);
22242231 identifier.* = ast.Node.Identifier{
22252232 .base = ast.Node{ .id = ast.Node.Id.Identifier },
test/translate_c.zig+1-1
......@@ -686,7 +686,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
686686 \\pub const SDL_INIT_VIDEO = @as(c_ulonglong, 32);
687687 });
688688
689 cases.add("zig keywords in C code",
689 cases.add_both("zig keywords in C code",
690690 \\struct comptime {
691691 \\ int defer;
692692 \\};