From 60ae92deaf57874c7c50f45e0bc94567dfbbdcf5 Mon Sep 17 00:00:00 2001 From: xackus <14938807+xackus@users.noreply.github.com> Date: Sat, 12 Jun 2021 15:54:39 +0200 Subject: [PATCH] translate-c: better typename parsing --- src/translate_c.zig | 243 +++++++++++++++++++++++++++---------------- test/translate_c.zig | 14 ++- 2 files changed, 164 insertions(+), 93 deletions(-) diff --git a/src/translate_c.zig b/src/translate_c.zig index daf775e93fba..e048f6ad7729 100644 --- a/src/translate_c.zig +++ b/src/translate_c.zig @@ -280,6 +280,8 @@ pub const Context = struct { opaque_demotes: std.AutoHashMapUnmanaged(usize, void) = .{}, /// Table of unnamed enums and records that are child types of typedefs. unnamed_typedefs: std.AutoHashMapUnmanaged(usize, []const u8) = .{}, + /// Needed to decide if we are parsing a typename + typedefs: std.StringArrayHashMapUnmanaged(void) = .{}, /// This one is different than the root scope's name table. This contains /// a list of names that we found by visiting all the top level decls without @@ -348,6 +350,7 @@ pub fn translate( context.global_names.deinit(gpa); context.opaque_demotes.deinit(gpa); context.unnamed_typedefs.deinit(gpa); + context.typedefs.deinit(gpa); context.global_scope.deinit(); } @@ -461,6 +464,7 @@ fn declVisitorNamesOnly(c: *Context, decl: *const clang.Decl) Error!void { result.value_ptr.* = name; // Put this typedef in the decl_table to avoid redefinitions. try c.decl_table.putNoClobber(c.gpa, @ptrToInt(typedef_decl.getCanonicalDecl()), name); + try c.typedefs.put(c.gpa, name, {}); } } } @@ -792,6 +796,8 @@ fn transTypeDef(c: *Context, scope: *Scope, typedef_decl: *const clang.TypedefNa // TODO https://github.com/ziglang/zig/issues/3756 // TODO https://github.com/ziglang/zig/issues/1802 var name: []const u8 = if (isZigPrimitiveType(bare_name)) try std.fmt.allocPrint(c.arena, "{s}_{d}", .{ bare_name, c.getMangle() }) else bare_name; + try c.typedefs.put(c.gpa, name, {}); + if (builtin_typedef_map.get(name)) |builtin| { return c.decl_table.putNoClobber(c.gpa, @ptrToInt(typedef_decl.getCanonicalDecl()), builtin); } @@ -5303,56 +5309,6 @@ fn parseCPrimaryExprInner(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!N .IntegerLiteral, .FloatLiteral => { return parseCNumLit(c, m); }, - // eventually this will be replaced by std.c.parse which will handle these correctly - .Keyword_void => return Tag.type.create(c.arena, "c_void"), - .Keyword_bool => return Tag.type.create(c.arena, "bool"), - .Keyword_double => return Tag.type.create(c.arena, "f64"), - .Keyword_long => return Tag.type.create(c.arena, "c_long"), - .Keyword_int => return Tag.type.create(c.arena, "c_int"), - .Keyword_float => return Tag.type.create(c.arena, "f32"), - .Keyword_short => return Tag.type.create(c.arena, "c_short"), - .Keyword_char => return Tag.type.create(c.arena, "u8"), - .Keyword_unsigned => if (m.next()) |t| switch (t) { - .Keyword_char => return Tag.type.create(c.arena, "u8"), - .Keyword_short => return Tag.type.create(c.arena, "c_ushort"), - .Keyword_int => return Tag.type.create(c.arena, "c_uint"), - .Keyword_long => if (m.peek() != null and m.peek().? == .Keyword_long) { - _ = m.next(); - return Tag.type.create(c.arena, "c_ulonglong"); - } else return Tag.type.create(c.arena, "c_ulong"), - else => { - m.i -= 1; - return Tag.type.create(c.arena, "c_uint"); - }, - } else { - return Tag.type.create(c.arena, "c_uint"); - }, - .Keyword_signed => if (m.next()) |t| switch (t) { - .Keyword_char => return Tag.type.create(c.arena, "i8"), - .Keyword_short => return Tag.type.create(c.arena, "c_short"), - .Keyword_int => return Tag.type.create(c.arena, "c_int"), - .Keyword_long => if (m.peek() != null and m.peek().? == .Keyword_long) { - _ = m.next(); - return Tag.type.create(c.arena, "c_longlong"); - } else return Tag.type.create(c.arena, "c_long"), - else => { - m.i -= 1; - return Tag.type.create(c.arena, "c_int"); - }, - } else { - return Tag.type.create(c.arena, "c_int"); - }, - .Keyword_enum, .Keyword_struct, .Keyword_union => { - // struct Foo will be declared as struct_Foo by transRecordDecl - const next_id = m.next().?; - if (next_id != .Identifier) { - try m.fail(c, "unable to translate C expr: expected Identifier instead got: {s}", .{@tagName(next_id)}); - return error.ParseError; - } - - const name = try std.fmt.allocPrint(c.arena, "{s}_{s}", .{ slice, m.slice() }); - return Tag.identifier.create(c.arena, name); - }, .Identifier => { const mangled_name = scope.getAlias(slice); if (mem.startsWith(u8, mangled_name, "__builtin_") and !isBuiltinDefined(mangled_name)) { @@ -5369,37 +5325,15 @@ fn parseCPrimaryExprInner(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!N try m.fail(c, "unable to translate C expr: expected ')' instead got: {s}", .{@tagName(next_id)}); return error.ParseError; } - var saw_l_paren = false; - var saw_integer_literal = false; - switch (m.peek().?) { - // (type)(to_cast) - .LParen => { - saw_l_paren = true; - _ = m.next(); - }, - // (type)sizeof(x) - .Keyword_sizeof, - // (type)alignof(x) - .Keyword_alignof, - // (type)identifier - .Identifier, - => {}, - // (type)integer - .IntegerLiteral => { - saw_integer_literal = true; - }, - else => return inner_node, - } - const node_to_cast = try parseCExpr(c, m, scope); - - if (saw_l_paren and m.next().? != .RParen) { - try m.fail(c, "unable to translate C expr: expected ')'", .{}); - return error.ParseError; - } - - return Tag.std_meta_cast.create(c.arena, .{ .lhs = inner_node, .rhs = node_to_cast }); + return inner_node; }, else => { + // for handling type macros (EVIL) + // TODO maybe detect and treat type macros as typedefs in parseCSpecifierQualifierList? + m.i -= 1; + if (try parseCTypeName(c, m, scope)) |type_name| { + return type_name; + } try m.fail(c, "unable to translate C expr: unexpected token .{s}", .{@tagName(tok)}); return error.ParseError; }, @@ -5605,7 +5539,7 @@ fn parseCAddSubExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!Node { } fn parseCMulExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!Node { - var node = try parseCUnaryExpr(c, m, scope); + var node = try parseCCastExpr(c, m, scope); while (true) { switch (m.next().?) { .Asterisk => { @@ -5633,18 +5567,18 @@ fn parseCMulExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!Node { } else { // expr * expr const lhs = try macroBoolToInt(c, node); - const rhs = try macroBoolToInt(c, try parseCUnaryExpr(c, m, scope)); + const rhs = try macroBoolToInt(c, try parseCCastExpr(c, m, scope)); node = try Tag.mul.create(c.arena, .{ .lhs = lhs, .rhs = rhs }); } }, .Slash => { const lhs = try macroBoolToInt(c, node); - const rhs = try macroBoolToInt(c, try parseCUnaryExpr(c, m, scope)); + const rhs = try macroBoolToInt(c, try parseCCastExpr(c, m, scope)); node = try Tag.div.create(c.arena, .{ .lhs = lhs, .rhs = rhs }); }, .Percent => { const lhs = try macroBoolToInt(c, node); - const rhs = try macroBoolToInt(c, try parseCUnaryExpr(c, m, scope)); + const rhs = try macroBoolToInt(c, try parseCCastExpr(c, m, scope)); node = try Tag.mod.create(c.arena, .{ .lhs = lhs, .rhs = rhs }); }, else => { @@ -5655,8 +5589,133 @@ fn parseCMulExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!Node { } } -fn parseCPostfixExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!Node { - var node = try parseCPrimaryExpr(c, m, scope); +fn parseCCastExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!Node { + switch (m.next().?) { + .LParen => { + if (try parseCTypeName(c, m, scope)) |type_name| { + if (m.next().? != .RParen) { + try m.fail(c, "unable to translate C expr: expected ')'", .{}); + return error.ParseError; + } + if (m.peek().? == .LBrace) { + // initializer list + return parseCPostfixExpr(c, m, scope, type_name); + } + const node_to_cast = try parseCCastExpr(c, m, scope); + return Tag.std_meta_cast.create(c.arena, .{ .lhs = type_name, .rhs = node_to_cast }); + } + }, + else => {}, + } + m.i -= 1; + return parseCUnaryExpr(c, m, scope); +} + +fn parseCTypeName(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!?Node { + if (try parseCSpecifierQualifierList(c, m, scope)) |node| { + return try parseCAbstractDeclarator(c, m, scope, node); + } else { + return null; + } +} + +fn parseCSpecifierQualifierList(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!?Node { + switch (m.next().?) { + .Identifier => { + const mangled_name = scope.getAlias(m.slice()); + if (c.typedefs.contains(mangled_name)) { + return try Tag.identifier.create(c.arena, builtin_typedef_map.get(mangled_name) orelse mangled_name); + } + }, + // eventually this will be replaced by std.c.parse which will handle these correctly + .Keyword_void => return try Tag.type.create(c.arena, "c_void"), + .Keyword_bool => return try Tag.type.create(c.arena, "bool"), + .Keyword_double => return try Tag.type.create(c.arena, "f64"), + .Keyword_long => return try Tag.type.create(c.arena, "c_long"), + .Keyword_int => return try Tag.type.create(c.arena, "c_int"), + .Keyword_float => return try Tag.type.create(c.arena, "f32"), + .Keyword_short => return try Tag.type.create(c.arena, "c_short"), + .Keyword_char => return try Tag.type.create(c.arena, "u8"), + .Keyword_unsigned => if (m.next()) |t| switch (t) { + .Keyword_char => return try Tag.type.create(c.arena, "u8"), + .Keyword_short => return try Tag.type.create(c.arena, "c_ushort"), + .Keyword_int => return try Tag.type.create(c.arena, "c_uint"), + .Keyword_long => if (m.peek() != null and m.peek().? == .Keyword_long) { + _ = m.next(); + return try Tag.type.create(c.arena, "c_ulonglong"); + } else return try Tag.type.create(c.arena, "c_ulong"), + else => { + m.i -= 1; + return try Tag.type.create(c.arena, "c_uint"); + }, + } else { + return try Tag.type.create(c.arena, "c_uint"); + }, + .Keyword_signed => if (m.next()) |t| switch (t) { + .Keyword_char => return try Tag.type.create(c.arena, "i8"), + .Keyword_short => return try Tag.type.create(c.arena, "c_short"), + .Keyword_int => return try Tag.type.create(c.arena, "c_int"), + .Keyword_long => if (m.peek() != null and m.peek().? == .Keyword_long) { + _ = m.next(); + return try Tag.type.create(c.arena, "c_longlong"); + } else return try Tag.type.create(c.arena, "c_long"), + else => { + m.i -= 1; + return try Tag.type.create(c.arena, "c_int"); + }, + } else { + return try Tag.type.create(c.arena, "c_int"); + }, + .Keyword_enum, .Keyword_struct, .Keyword_union => { + // struct Foo will be declared as struct_Foo by transRecordDecl + const slice = m.slice(); + const next_id = m.next().?; + if (next_id != .Identifier) { + try m.fail(c, "unable to translate C expr: expected Identifier instead got: {s}", .{@tagName(next_id)}); + return error.ParseError; + } + + const name = try std.fmt.allocPrint(c.arena, "{s}_{s}", .{ slice, m.slice() }); + return try Tag.identifier.create(c.arena, name); + }, + .Keyword_complex => {}, // TODO + else => {}, + } + + m.i -= 1; + return null; +} + +fn parseCAbstractDeclarator(c: *Context, m: *MacroCtx, scope: *Scope, node: Node) ParseError!Node { + switch (m.next().?) { + .Asterisk => { + // last token of `node` + const prev_id = m.list[m.i - 1].id; + + if (prev_id == .Keyword_void) { + const ptr = try Tag.single_pointer.create(c.arena, .{ + .is_const = false, + .is_volatile = false, + .elem_type = node, + }); + return Tag.optional_type.create(c.arena, ptr); + } else { + return Tag.c_pointer.create(c.arena, .{ + .is_const = false, + .is_volatile = false, + .elem_type = node, + }); + } + }, + else => { + m.i -= 1; + return node; + }, + } +} + +fn parseCPostfixExpr(c: *Context, m: *MacroCtx, scope: *Scope, type_name: ?Node) ParseError!Node { + var node = type_name orelse try parseCPrimaryExpr(c, m, scope); while (true) { switch (m.next().?) { .Period => { @@ -5776,24 +5835,24 @@ fn parseCPostfixExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!Node { fn parseCUnaryExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!Node { switch (m.next().?) { .Bang => { - const operand = try macroIntToBool(c, try parseCUnaryExpr(c, m, scope)); + const operand = try macroIntToBool(c, try parseCCastExpr(c, m, scope)); return Tag.not.create(c.arena, operand); }, .Minus => { - const operand = try macroBoolToInt(c, try parseCUnaryExpr(c, m, scope)); + const operand = try macroBoolToInt(c, try parseCCastExpr(c, m, scope)); return Tag.negate.create(c.arena, operand); }, - .Plus => return try parseCUnaryExpr(c, m, scope), + .Plus => return try parseCCastExpr(c, m, scope), .Tilde => { - const operand = try macroBoolToInt(c, try parseCUnaryExpr(c, m, scope)); + const operand = try macroBoolToInt(c, try parseCCastExpr(c, m, scope)); return Tag.bit_not.create(c.arena, operand); }, .Asterisk => { - const operand = try parseCUnaryExpr(c, m, scope); + const operand = try parseCCastExpr(c, m, scope); return Tag.deref.create(c.arena, operand); }, .Ampersand => { - const operand = try parseCUnaryExpr(c, m, scope); + const operand = try parseCCastExpr(c, m, scope); return Tag.address_of.create(c.arena, operand); }, .Keyword_sizeof => { @@ -5834,7 +5893,7 @@ fn parseCUnaryExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!Node { }, else => { m.i -= 1; - return try parseCPostfixExpr(c, m, scope); + return try parseCPostfixExpr(c, m, scope, null); }, } } diff --git a/test/translate_c.zig b/test/translate_c.zig index d200265e1c24..d49d4329d2ca 100644 --- a/test/translate_c.zig +++ b/test/translate_c.zig @@ -238,7 +238,8 @@ pub fn addCases(cases: *tests.TranslateCContext) void { }); cases.add("use cast param as macro fn return type", - \\#define MEM_PHYSICAL_TO_K0(x) (void*)((u32)(x) + SYS_BASE_CACHED) + \\#include + \\#define MEM_PHYSICAL_TO_K0(x) (void*)((uint32_t)(x) + SYS_BASE_CACHED) , &[_][]const u8{ \\pub inline fn MEM_PHYSICAL_TO_K0(x: anytype) ?*c_void { \\ return @import("std").meta.cast(?*c_void, @import("std").meta.cast(u32, x) + SYS_BASE_CACHED); @@ -1878,6 +1879,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { }); cases.add("macro pointer cast", + \\typedef struct { int dummy; } NRF_GPIO_Type; \\#define NRF_GPIO ((NRF_GPIO_Type *) NRF_GPIO_BASE) , &[_][]const u8{ \\pub const NRF_GPIO = @import("std").meta.cast([*c]NRF_GPIO_Type, NRF_GPIO_BASE); @@ -3175,6 +3177,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { }); cases.add("macro cast", + \\#include \\#define FOO(bar) baz((void *)(baz)) \\#define BAR (void*) a \\#define BAZ (uint32_t)(2) @@ -3633,4 +3636,13 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\ return foo.static.x; \\} }); + + cases.add("macro with nontrivial cast", + \\#define MAP_FAILED ((void *) -1) + \\typedef long long LONG_PTR; + \\#define INVALID_HANDLE_VALUE ((void *)(LONG_PTR)-1) + , &[_][]const u8{ + \\pub const MAP_FAILED = @import("std").meta.cast(?*c_void, -@as(c_int, 1)); + \\pub const INVALID_HANDLE_VALUE = @import("std").meta.cast(?*c_void, @import("std").meta.cast(LONG_PTR, -@as(c_int, 1))); + }); }