authorgravatar for evan@lagerdata.comEvan Haas <evan@lagerdata.com> 2021-08-31 12:23:23-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-09-01 17:54:07-07:00
logdf589eecd61a4a2c93922302f8f486f18d0ff06b
treedffedae386db77c3a7beea33efbd91976679c324
parent8d2acff197a46a9de33ce78713291428aa45ee7c

translate-c: improve handling of undefined identifiers


3 files changed, 46 insertions(+), 32 deletions(-)

src/translate_c.zig+19-9
...@@ -395,7 +395,15 @@ pub fn translate(...@@ -395,7 +395,15 @@ pub fn translate(
395 context.pattern_list.deinit(gpa);395 context.pattern_list.deinit(gpa);
396 }396 }
397397
398 try context.global_scope.nodes.append(Tag.usingnamespace_builtins.init());398 inline for (meta.declarations(std.zig.c_builtins)) |decl| {
399 if (decl.is_pub) {
400 const builtin = try Tag.pub_var_simple.create(context.arena, .{
401 .name = decl.name,
402 .init = try Tag.import_builtin.create(context.arena, decl.name),
403 });
404 try context.global_scope.nodes.append(builtin);
405 }
406 }
399407
400 try prepopulateGlobalNameTable(ast_unit, &context);408 try prepopulateGlobalNameTable(ast_unit, &context);
401409
...@@ -5249,16 +5257,19 @@ const MacroCtx = struct {...@@ -5249,16 +5257,19 @@ const MacroCtx = struct {
5249 return MacroSlicer{ .source = self.source, .tokens = self.list };5257 return MacroSlicer{ .source = self.source, .tokens = self.list };
5250 }5258 }
52515259
5252 fn containsUndefinedIdentifier(self: *MacroCtx, scope: *Scope) ?[]const u8 {5260 fn containsUndefinedIdentifier(self: *MacroCtx, scope: *Scope, params: []const ast.Payload.Param) ?[]const u8 {
5253 const slicer = self.makeSlicer();5261 const slicer = self.makeSlicer();
5254 var i: usize = 1; // index 0 is the macro name5262 var i: usize = 1; // index 0 is the macro name
5255 while (i < self.list.len) : (i += 1) {5263 while (i < self.list.len) : (i += 1) {
5256 const token = self.list[i];5264 const token = self.list[i];
5257 switch (token.id) {5265 switch (token.id) {
5258 .Period => i += 1, // skip next token since field identifiers can be unknown5266 .Period, .Arrow => i += 1, // skip next token since field identifiers can be unknown
5259 .Identifier => {5267 .Identifier => {
5260 const identifier = slicer.slice(token);5268 const identifier = slicer.slice(token);
5261 if (!scope.contains(identifier)) return identifier;5269 const is_param = for (params) |param| {
5270 if (param.name != null and mem.eql(u8, identifier, param.name.?)) break true;
5271 } else false;
5272 if (!scope.contains(identifier) and !isBuiltinDefined(identifier) and !is_param) return identifier;
5262 },5273 },
5263 else => {},5274 else => {},
5264 }5275 }
...@@ -5361,7 +5372,7 @@ fn transPreprocessorEntities(c: *Context, unit: *clang.ASTUnit) Error!void {...@@ -5361,7 +5372,7 @@ fn transPreprocessorEntities(c: *Context, unit: *clang.ASTUnit) Error!void {
5361fn transMacroDefine(c: *Context, m: *MacroCtx) ParseError!void {5372fn transMacroDefine(c: *Context, m: *MacroCtx) ParseError!void {
5362 const scope = &c.global_scope.base;5373 const scope = &c.global_scope.base;
53635374
5364 if (m.containsUndefinedIdentifier(scope)) |ident|5375 if (m.containsUndefinedIdentifier(scope, &.{})) |ident|
5365 return m.fail(c, "unable to translate macro: undefined identifier `{s}`", .{ident});5376 return m.fail(c, "unable to translate macro: undefined identifier `{s}`", .{ident});
53665377
5367 const init_node = try parseCExpr(c, m, scope);5378 const init_node = try parseCExpr(c, m, scope);
...@@ -5414,6 +5425,9 @@ fn transMacroFnDefine(c: *Context, m: *MacroCtx) ParseError!void {...@@ -5414,6 +5425,9 @@ fn transMacroFnDefine(c: *Context, m: *MacroCtx) ParseError!void {
5414 return m.fail(c, "unable to translate C expr: expected ')'", .{});5425 return m.fail(c, "unable to translate C expr: expected ')'", .{});
5415 }5426 }
54165427
5428 if (m.containsUndefinedIdentifier(scope, fn_params.items)) |ident|
5429 return m.fail(c, "unable to translate macro: undefined identifier `{s}`", .{ident});
5430
5417 const expr = try parseCExpr(c, m, scope);5431 const expr = try parseCExpr(c, m, scope);
5418 const last = m.next().?;5432 const last = m.next().?;
5419 if (last != .Eof and last != .Nl)5433 if (last != .Eof and last != .Nl)
...@@ -5755,10 +5769,6 @@ fn parseCPrimaryExprInner(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!N...@@ -5755,10 +5769,6 @@ fn parseCPrimaryExprInner(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!N
5755 },5769 },
5756 .Identifier => {5770 .Identifier => {
5757 const mangled_name = scope.getAlias(slice);5771 const mangled_name = scope.getAlias(slice);
5758 if (mem.startsWith(u8, mangled_name, "__builtin_") and !isBuiltinDefined(mangled_name)) {
5759 try m.fail(c, "TODO implement function '{s}' in std.zig.c_builtins", .{mangled_name});
5760 return error.ParseError;
5761 }
5762 if (builtin_typedef_map.get(mangled_name)) |ty| return Tag.type.create(c.arena, ty);5772 if (builtin_typedef_map.get(mangled_name)) |ty| return Tag.type.create(c.arena, ty);
5763 const identifier = try Tag.identifier.create(c.arena, mangled_name);5773 const identifier = try Tag.identifier.create(c.arena, mangled_name);
5764 scope.skipVariableDiscard(identifier.castTag(.identifier).?.data);5774 scope.skipVariableDiscard(identifier.castTag(.identifier).?.data);
src/translate_c/ast.zig+14-21
...@@ -31,8 +31,6 @@ pub const Node = extern union {...@@ -31,8 +31,6 @@ pub const Node = extern union {
31 @"anytype",31 @"anytype",
32 @"continue",32 @"continue",
33 @"break",33 @"break",
34 /// pub usingnamespace @import("std").zig.c_builtins
35 usingnamespace_builtins,
36 // After this, the tag requires a payload.34 // After this, the tag requires a payload.
3735
38 integer_literal,36 integer_literal,
...@@ -119,6 +117,8 @@ pub const Node = extern union {...@@ -119,6 +117,8 @@ pub const Node = extern union {
119 ellipsis3,117 ellipsis3,
120 assign,118 assign,
121119
120 /// @import("std").zig.c_builtins.<name>
121 import_builtin,
122 log2_int_type,122 log2_int_type,
123 /// @import("std").math.Log2Int(operand)123 /// @import("std").math.Log2Int(operand)
124 std_math_Log2Int,124 std_math_Log2Int,
...@@ -224,7 +224,7 @@ pub const Node = extern union {...@@ -224,7 +224,7 @@ pub const Node = extern union {
224 /// [1]type{val} ** count224 /// [1]type{val} ** count
225 array_filler,225 array_filler,
226226
227 pub const last_no_payload_tag = Tag.usingnamespace_builtins;227 pub const last_no_payload_tag = Tag.@"break";
228 pub const no_payload_count = @enumToInt(last_no_payload_tag) + 1;228 pub const no_payload_count = @enumToInt(last_no_payload_tag) + 1;
229229
230 pub fn Type(comptime t: Tag) type {230 pub fn Type(comptime t: Tag) type {
...@@ -236,7 +236,6 @@ pub const Node = extern union {...@@ -236,7 +236,6 @@ pub const Node = extern union {
236 .true_literal,236 .true_literal,
237 .false_literal,237 .false_literal,
238 .empty_block,238 .empty_block,
239 .usingnamespace_builtins,
240 .return_void,239 .return_void,
241 .zero_literal,240 .zero_literal,
242 .one_literal,241 .one_literal,
...@@ -344,6 +343,7 @@ pub const Node = extern union {...@@ -344,6 +343,7 @@ pub const Node = extern union {
344 .warning,343 .warning,
345 .type,344 .type,
346 .helpers_macro,345 .helpers_macro,
346 .import_builtin,
347 => Payload.Value,347 => Payload.Value,
348 .discard => Payload.Discard,348 .discard => Payload.Discard,
349 .@"if" => Payload.If,349 .@"if" => Payload.If,
...@@ -871,22 +871,6 @@ fn renderNode(c: *Context, node: Node) Allocator.Error!NodeIndex {...@@ -871,22 +871,6 @@ fn renderNode(c: *Context, node: Node) Allocator.Error!NodeIndex {
871 try c.buf.append('\n');871 try c.buf.append('\n');
872 return @as(NodeIndex, 0); // error: integer value 0 cannot be coerced to type 'std.mem.Allocator.Error!u32'872 return @as(NodeIndex, 0); // error: integer value 0 cannot be coerced to type 'std.mem.Allocator.Error!u32'
873 },873 },
874 .usingnamespace_builtins => {
875 // pub usingnamespace @import("std").c.builtins;
876 _ = try c.addToken(.keyword_pub, "pub");
877 const usingnamespace_token = try c.addToken(.keyword_usingnamespace, "usingnamespace");
878 const import_node = try renderStdImport(c, &.{ "zig", "c_builtins" });
879 _ = try c.addToken(.semicolon, ";");
880
881 return c.addNode(.{
882 .tag = .@"usingnamespace",
883 .main_token = usingnamespace_token,
884 .data = .{
885 .lhs = import_node,
886 .rhs = undefined,
887 },
888 });
889 },
890 .std_math_Log2Int => {874 .std_math_Log2Int => {
891 const payload = node.castTag(.std_math_Log2Int).?.data;875 const payload = node.castTag(.std_math_Log2Int).?.data;
892 const import_node = try renderStdImport(c, &.{ "math", "Log2Int" });876 const import_node = try renderStdImport(c, &.{ "math", "Log2Int" });
...@@ -1143,6 +1127,15 @@ fn renderNode(c: *Context, node: Node) Allocator.Error!NodeIndex {...@@ -1143,6 +1127,15 @@ fn renderNode(c: *Context, node: Node) Allocator.Error!NodeIndex {
1143 };1127 };
1144 return renderStdImport(c, &chain);1128 return renderStdImport(c, &chain);
1145 },1129 },
1130 .import_builtin => {
1131 const payload = node.castTag(.import_builtin).?.data;
1132 const chain = [_][]const u8{
1133 "zig",
1134 "c_builtins",
1135 payload,
1136 };
1137 return renderStdImport(c, &chain);
1138 },
1146 .string_slice => {1139 .string_slice => {
1147 const payload = node.castTag(.string_slice).?.data;1140 const payload = node.castTag(.string_slice).?.data;
11481141
...@@ -2352,7 +2345,6 @@ fn renderNodeGrouped(c: *Context, node: Node) !NodeIndex {...@@ -2352,7 +2345,6 @@ fn renderNodeGrouped(c: *Context, node: Node) !NodeIndex {
2352 .@"comptime",2345 .@"comptime",
2353 .@"defer",2346 .@"defer",
2354 .asm_simple,2347 .asm_simple,
2355 .usingnamespace_builtins,
2356 .while_true,2348 .while_true,
2357 .if_not_break,2349 .if_not_break,
2358 .switch_else,2350 .switch_else,
...@@ -2371,6 +2363,7 @@ fn renderNodeGrouped(c: *Context, node: Node) !NodeIndex {...@@ -2371,6 +2363,7 @@ fn renderNodeGrouped(c: *Context, node: Node) !NodeIndex {
2371 .bit_xor_assign,2363 .bit_xor_assign,
2372 .assign,2364 .assign,
2373 .helpers_macro,2365 .helpers_macro,
2366 .import_builtin,
2374 => {2367 => {
2375 // these should never appear in places where grouping might be needed.2368 // these should never appear in places where grouping might be needed.
2376 unreachable;2369 unreachable;
test/translate_c.zig+13-2
...@@ -195,6 +195,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {...@@ -195,6 +195,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
195195
196 cases.add("use cast param as macro fn return type",196 cases.add("use cast param as macro fn return type",
197 \\#include <stdint.h>197 \\#include <stdint.h>
198 \\#define SYS_BASE_CACHED 0
198 \\#define MEM_PHYSICAL_TO_K0(x) (void*)((uint32_t)(x) + SYS_BASE_CACHED)199 \\#define MEM_PHYSICAL_TO_K0(x) (void*)((uint32_t)(x) + SYS_BASE_CACHED)
199 , &[_][]const u8{200 , &[_][]const u8{
200 \\pub inline fn MEM_PHYSICAL_TO_K0(x: anytype) ?*c_void {201 \\pub inline fn MEM_PHYSICAL_TO_K0(x: anytype) ?*c_void {
...@@ -364,6 +365,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {...@@ -364,6 +365,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
364 });365 });
365366
366 cases.add("correct semicolon after infixop",367 cases.add("correct semicolon after infixop",
368 \\#define _IO_ERR_SEEN 0
367 \\#define __ferror_unlocked_body(_fp) (((_fp)->_flags & _IO_ERR_SEEN) != 0)369 \\#define __ferror_unlocked_body(_fp) (((_fp)->_flags & _IO_ERR_SEEN) != 0)
368 , &[_][]const u8{370 , &[_][]const u8{
369 \\pub inline fn __ferror_unlocked_body(_fp: anytype) @TypeOf((_fp.*._flags & _IO_ERR_SEEN) != @as(c_int, 0)) {371 \\pub inline fn __ferror_unlocked_body(_fp: anytype) @TypeOf((_fp.*._flags & _IO_ERR_SEEN) != @as(c_int, 0)) {
...@@ -430,6 +432,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {...@@ -430,6 +432,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
430432
431 cases.add("macro comma operator",433 cases.add("macro comma operator",
432 \\#define foo (foo, bar)434 \\#define foo (foo, bar)
435 \\int baz(int x, int y) { return 0; }
433 \\#define bar(x) (&x, +3, 4 == 4, 5 * 6, baz(1, 2), 2 % 2, baz(1,2))436 \\#define bar(x) (&x, +3, 4 == 4, 5 * 6, baz(1, 2), 2 % 2, baz(1,2))
434 , &[_][]const u8{437 , &[_][]const u8{
435 \\pub const foo = blk: {438 \\pub const foo = blk: {
...@@ -2573,6 +2576,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {...@@ -2573,6 +2576,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
25732576
2574 cases.add("macro call",2577 cases.add("macro call",
2575 \\#define CALL(arg) bar(arg)2578 \\#define CALL(arg) bar(arg)
2579 \\int bar(int x) { return x; }
2576 , &[_][]const u8{2580 , &[_][]const u8{
2577 \\pub inline fn CALL(arg: anytype) @TypeOf(bar(arg)) {2581 \\pub inline fn CALL(arg: anytype) @TypeOf(bar(arg)) {
2578 \\ return bar(arg);2582 \\ return bar(arg);
...@@ -2581,6 +2585,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {...@@ -2581,6 +2585,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
25812585
2582 cases.add("macro call with no args",2586 cases.add("macro call with no args",
2583 \\#define CALL(arg) bar()2587 \\#define CALL(arg) bar()
2588 \\int bar(void) { return 0; }
2584 , &[_][]const u8{2589 , &[_][]const u8{
2585 \\pub inline fn CALL(arg: anytype) @TypeOf(bar()) {2590 \\pub inline fn CALL(arg: anytype) @TypeOf(bar()) {
2586 \\ _ = arg;2591 \\ _ = arg;
...@@ -3139,6 +3144,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {...@@ -3139,6 +3144,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
31393144
3140 cases.add("macro cast",3145 cases.add("macro cast",
3141 \\#include <stdint.h>3146 \\#include <stdint.h>
3147 \\int baz(void *arg) { return 0; }
3142 \\#define FOO(bar) baz((void *)(baz))3148 \\#define FOO(bar) baz((void *)(baz))
3143 \\#define BAR (void*) a3149 \\#define BAR (void*) a
3144 \\#define BAZ (uint32_t)(2)3150 \\#define BAZ (uint32_t)(2)
...@@ -3475,11 +3481,10 @@ pub fn addCases(cases: *tests.TranslateCContext) void {...@@ -3475,11 +3481,10 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
3475 \\pub const MAY_NEED_PROMOTION_OCT = @import("std").zig.c_translation.promoteIntLiteral(c_int, 0o20000000000, .octal);3481 \\pub const MAY_NEED_PROMOTION_OCT = @import("std").zig.c_translation.promoteIntLiteral(c_int, 0o20000000000, .octal);
3476 });3482 });
34773483
3478 // See __builtin_alloca_with_align comment in std.zig.c_builtins
3479 cases.add("demote un-implemented builtins",3484 cases.add("demote un-implemented builtins",
3480 \\#define FOO(X) __builtin_alloca_with_align((X), 8)3485 \\#define FOO(X) __builtin_alloca_with_align((X), 8)
3481 , &[_][]const u8{3486 , &[_][]const u8{
3482 \\pub const FOO = @compileError("TODO implement function '__builtin_alloca_with_align' in std.zig.c_builtins");3487 \\pub const FOO = @compileError("unable to translate macro: undefined identifier `__builtin_alloca_with_align`");
3483 });3488 });
34843489
3485 cases.add("null sentinel arrays when initialized from string literal. Issue #8256",3490 cases.add("null sentinel arrays when initialized from string literal. Issue #8256",
...@@ -3661,4 +3666,10 @@ pub fn addCases(cases: *tests.TranslateCContext) void {...@@ -3661,4 +3666,10 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
3661 , &[_][]const u8{3666 , &[_][]const u8{
3662 \\pub const FOO = @compileError("unable to translate macro: undefined identifier `BAR`");3667 \\pub const FOO = @compileError("unable to translate macro: undefined identifier `BAR`");
3663 });3668 });
3669
3670 cases.add("Macro redefines builtin",
3671 \\#define FOO __builtin_popcount
3672 , &[_][]const u8{
3673 \\pub const FOO = __builtin_popcount;
3674 });
3664}3675}