Commit aa83cbb697

xackus <14938807+xackus@users.noreply.github.com>
2021-06-12 15:54:39
translate-c: better typename parsing
1 parent 46b9e06
Changed files (2)
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);
         },
     }
 }
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 <stdint.h>
+        \\#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 <stdint.h>
         \\#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)));
+    });
 }