Commit c558a1ae26

Evan Haas <evan@lagerdata.com>
2021-03-10 05:49:24
translate-c: Implement generic selection expressions
Enables translation of C code that uses the `_Generic` keyword
1 parent 9a94415
src/clang.zig
@@ -537,6 +537,11 @@ pub const FunctionType = opaque {
     extern fn ZigClangFunctionType_getReturnType(*const FunctionType) QualType;
 };
 
+pub const GenericSelectionExpr = opaque {
+    pub const getResultExpr = ZigClangGenericSelectionExpr_getResultExpr;
+    extern fn ZigClangGenericSelectionExpr_getResultExpr(*const GenericSelectionExpr) *const Expr;
+};
+
 pub const IfStmt = opaque {
     pub const getThen = ZigClangIfStmt_getThen;
     extern fn ZigClangIfStmt_getThen(*const IfStmt) *const Stmt;
src/translate_c.zig
@@ -1058,6 +1058,10 @@ fn transStmt(
             const compound_literal = @ptrCast(*const clang.CompoundLiteralExpr, stmt);
             return transExpr(c, scope, compound_literal.getInitializer(), result_used);
         },
+        .GenericSelectionExprClass => {
+            const gen_sel = @ptrCast(*const clang.GenericSelectionExpr, stmt);
+            return transExpr(c, scope, gen_sel.getResultExpr(), result_used);
+        },
         else => {
             return fail(c, error.UnsupportedTranslation, stmt.getBeginLoc(), "TODO implement translation of stmt class {s}", .{@tagName(sc)});
         },
@@ -1582,6 +1586,10 @@ fn exprIsNarrowStringLiteral(expr: *const clang.Expr) bool {
             const op_expr = @ptrCast(*const clang.ParenExpr, expr).getSubExpr();
             return exprIsNarrowStringLiteral(op_expr);
         },
+        .GenericSelectionExprClass => {
+            const gen_sel = @ptrCast(*const clang.GenericSelectionExpr, expr);
+            return exprIsNarrowStringLiteral(gen_sel.getResultExpr());
+        },
         else => return false,
     }
 }
@@ -2726,6 +2734,10 @@ fn cIsFunctionDeclRef(expr: *const clang.Expr) bool {
             const opcode = un_op.getOpcode();
             return (opcode == .AddrOf or opcode == .Deref) and cIsFunctionDeclRef(un_op.getSubExpr());
         },
+        .GenericSelectionExprClass => {
+            const gen_sel = @ptrCast(*const clang.GenericSelectionExpr, expr);
+            return cIsFunctionDeclRef(gen_sel.getResultExpr());
+        },
         else => return false,
     }
 }
src/zig_clang.cpp
@@ -2445,6 +2445,11 @@ struct ZigClangQualType ZigClangFunctionType_getReturnType(const struct ZigClang
     return bitcast(casted->getReturnType());
 }
 
+const struct ZigClangExpr *ZigClangGenericSelectionExpr_getResultExpr(const struct ZigClangGenericSelectionExpr *self) {
+    auto casted = reinterpret_cast<const clang::GenericSelectionExpr *>(self);
+    return reinterpret_cast<const struct ZigClangExpr *>(casted->getResultExpr());
+}
+
 bool ZigClangFunctionProtoType_isVariadic(const struct ZigClangFunctionProtoType *self) {
     auto casted = reinterpret_cast<const clang::FunctionProtoType *>(self);
     return casted->isVariadic();
src/zig_clang.h
@@ -1116,6 +1116,8 @@ ZIG_EXTERN_C bool ZigClangFunctionType_getNoReturnAttr(const struct ZigClangFunc
 ZIG_EXTERN_C enum ZigClangCallingConv ZigClangFunctionType_getCallConv(const struct ZigClangFunctionType *self);
 ZIG_EXTERN_C struct ZigClangQualType ZigClangFunctionType_getReturnType(const struct ZigClangFunctionType *self);
 
+ZIG_EXTERN_C const struct ZigClangExpr *ZigClangGenericSelectionExpr_getResultExpr(const struct ZigClangGenericSelectionExpr *self);
+
 ZIG_EXTERN_C bool ZigClangFunctionProtoType_isVariadic(const struct ZigClangFunctionProtoType *self);
 ZIG_EXTERN_C unsigned ZigClangFunctionProtoType_getNumParams(const struct ZigClangFunctionProtoType *self);
 ZIG_EXTERN_C struct ZigClangQualType ZigClangFunctionProtoType_getParamType(const struct ZigClangFunctionProtoType *self, unsigned i);
test/run_translated_c.zig
@@ -1187,4 +1187,38 @@ pub fn addCases(cases: *tests.RunTranslatedCContext) void {
         \\    return 0;
         \\}
     , "");
+
+    cases.add("Generic selections",
+        \\#include <stdlib.h>
+        \\#include <string.h>
+        \\#include <stdint.h>
+        \\#define my_generic_fn(X) _Generic((X),    \
+        \\              int: abs,                   \
+        \\              char *: strlen,             \
+        \\              size_t: malloc,             \
+        \\              default: free               \
+        \\)(X)
+        \\#define my_generic_val(X) _Generic((X),   \
+        \\              int: 1,                     \
+        \\              const char *: "bar"         \
+        \\)
+        \\int main(void) {
+        \\    if (my_generic_val(100) != 1) abort();
+        \\
+        \\    const char *foo = "foo";
+        \\    const char *bar = my_generic_val(foo);
+        \\    if (strcmp(bar, "bar") != 0) abort();
+        \\
+        \\    if (my_generic_fn(-42) != 42) abort();
+        \\    if (my_generic_fn("hello") != 5) abort();
+        \\
+        \\    size_t size = 8192;
+        \\    uint8_t *mem = my_generic_fn(size);
+        \\    memset(mem, 42, size);
+        \\    if (mem[size - 1] != 42) abort();
+        \\    my_generic_fn(mem);
+        \\
+        \\    return 0;
+        \\}
+    , "");
 }