authorgravatar for evan@lagerdata.comEvan Haas <evan@lagerdata.com> 2021-02-05 15:37:18-08:00
committergravatar for git@vexu.euVeikka Tuominen <git@vexu.eu> 2021-02-08 10:15:00+02:00
log221f1d898c39e9ea25f1d7fc9642bfbb3c97e894
treee7fd7ba2007c9af7cf55c9114b677b62b0186a71
parent1adac0a55bafbba864228ac38c4684612e84f522

translate-c: Improve function pointer handling

Omit address-of operator if operand is a function. Improve handling of function-call translation when using function pointers Fixes #4124

6 files changed, 107 insertions(+), 21 deletions(-)

src/clang.zig+4-1
......@@ -848,7 +848,10 @@ pub const UnaryOperator = opaque {
848848 extern fn ZigClangUnaryOperator_getBeginLoc(*const UnaryOperator) SourceLocation;
849849};
850850
851pub const ValueDecl = opaque {};
851pub const ValueDecl = opaque {
852 pub const getType = ZigClangValueDecl_getType;
853 extern fn ZigClangValueDecl_getType(*const ValueDecl) QualType;
854};
852855
853856pub const VarDecl = opaque {
854857 pub const getLocation = ZigClangVarDecl_getLocation;
src/translate_c.zig+38-18
......@@ -3208,6 +3208,38 @@ fn transArrayAccess(rp: RestorePoint, scope: *Scope, stmt: *const clang.ArraySub
32083208 return maybeSuppressResult(rp, scope, result_used, &node.base);
32093209}
32103210
3211/// Check if an expression is ultimately a reference to a function declaration
3212/// (which means it should not be unwrapped with `.?` in translated code)
3213fn cIsFunctionDeclRef(expr: *const clang.Expr) bool {
3214 switch (expr.getStmtClass()) {
3215 .ParenExprClass => {
3216 const op_expr = @ptrCast(*const clang.ParenExpr, expr).getSubExpr();
3217 return cIsFunctionDeclRef(op_expr);
3218 },
3219 .DeclRefExprClass => {
3220 const decl_ref = @ptrCast(*const clang.DeclRefExpr, expr);
3221 const value_decl = decl_ref.getDecl();
3222 const qt = value_decl.getType();
3223 return qualTypeChildIsFnProto(qt);
3224 },
3225 .ImplicitCastExprClass => {
3226 const implicit_cast = @ptrCast(*const clang.ImplicitCastExpr, expr);
3227 const cast_kind = implicit_cast.getCastKind();
3228 if (cast_kind == .BuiltinFnToFnPtr) return true;
3229 if (cast_kind == .FunctionToPointerDecay) {
3230 return cIsFunctionDeclRef(implicit_cast.getSubExpr());
3231 }
3232 return false;
3233 },
3234 .UnaryOperatorClass => {
3235 const un_op = @ptrCast(*const clang.UnaryOperator, expr);
3236 const opcode = un_op.getOpcode();
3237 return (opcode == .AddrOf or opcode == .Deref) and cIsFunctionDeclRef(un_op.getSubExpr());
3238 },
3239 else => return false,
3240 }
3241}
3242
32113243fn transCallExpr(rp: RestorePoint, scope: *Scope, stmt: *const clang.CallExpr, result_used: ResultUsed) TransError!*ast.Node {
32123244 const callee = stmt.getCallee();
32133245 var raw_fn_expr = try transExpr(rp, scope, callee, .used, .r_value);
......@@ -3215,24 +3247,9 @@ fn transCallExpr(rp: RestorePoint, scope: *Scope, stmt: *const clang.CallExpr, r
32153247 var is_ptr = false;
32163248 const fn_ty = qualTypeGetFnProto(callee.getType(), &is_ptr);
32173249
3218 const fn_expr = if (is_ptr and fn_ty != null) blk: {
3219 if (callee.getStmtClass() == .ImplicitCastExprClass) {
3220 const implicit_cast = @ptrCast(*const clang.ImplicitCastExpr, callee);
3221 const cast_kind = implicit_cast.getCastKind();
3222 if (cast_kind == .BuiltinFnToFnPtr) break :blk raw_fn_expr;
3223 if (cast_kind == .FunctionToPointerDecay) {
3224 const subexpr = implicit_cast.getSubExpr();
3225 if (subexpr.getStmtClass() == .DeclRefExprClass) {
3226 const decl_ref = @ptrCast(*const clang.DeclRefExpr, subexpr);
3227 const named_decl = decl_ref.getFoundDecl();
3228 if (@ptrCast(*const clang.Decl, named_decl).getKind() == .Function) {
3229 break :blk raw_fn_expr;
3230 }
3231 }
3232 }
3233 }
3234 break :blk try transCreateNodeUnwrapNull(rp.c, raw_fn_expr);
3235 } else
3250 const fn_expr = if (is_ptr and fn_ty != null and !cIsFunctionDeclRef(callee))
3251 try transCreateNodeUnwrapNull(rp.c, raw_fn_expr)
3252 else
32363253 raw_fn_expr;
32373254
32383255 const num_args = stmt.getNumArgs();
......@@ -3379,6 +3396,9 @@ fn transUnaryOperator(rp: RestorePoint, scope: *Scope, stmt: *const clang.UnaryO
33793396 else
33803397 return transCreatePreCrement(rp, scope, stmt, .AssignSub, .MinusEqual, "-=", used),
33813398 .AddrOf => {
3399 if (cIsFunctionDeclRef(op_expr)) {
3400 return transExpr(rp, scope, op_expr, used, .r_value);
3401 }
33823402 const op_node = try transCreateNodeSimplePrefixOp(rp.c, .AddressOf, .Ampersand, "&");
33833403 op_node.rhs = try transExpr(rp, scope, op_expr, used, .r_value);
33843404 return &op_node.base;
src/zig_clang.cpp+5
......@@ -2773,6 +2773,11 @@ struct ZigClangSourceLocation ZigClangUnaryOperator_getBeginLoc(const struct Zig
27732773 return bitcast(casted->getBeginLoc());
27742774}
27752775
2776struct ZigClangQualType ZigClangValueDecl_getType(const struct ZigClangValueDecl *self) {
2777 auto casted = reinterpret_cast<const clang::ValueDecl *>(self);
2778 return bitcast(casted->getType());
2779}
2780
27762781const struct ZigClangExpr *ZigClangWhileStmt_getCond(const struct ZigClangWhileStmt *self) {
27772782 auto casted = reinterpret_cast<const clang::WhileStmt *>(self);
27782783 return reinterpret_cast<const struct ZigClangExpr *>(casted->getCond());
src/zig_clang.h+2
......@@ -1200,6 +1200,8 @@ ZIG_EXTERN_C struct ZigClangQualType ZigClangUnaryOperator_getType(const struct
12001200ZIG_EXTERN_C const struct ZigClangExpr *ZigClangUnaryOperator_getSubExpr(const struct ZigClangUnaryOperator *);
12011201ZIG_EXTERN_C struct ZigClangSourceLocation ZigClangUnaryOperator_getBeginLoc(const struct ZigClangUnaryOperator *);
12021202
1203ZIG_EXTERN_C struct ZigClangQualType ZigClangValueDecl_getType(const struct ZigClangValueDecl *);
1204
12031205ZIG_EXTERN_C const struct ZigClangExpr *ZigClangWhileStmt_getCond(const struct ZigClangWhileStmt *);
12041206ZIG_EXTERN_C const struct ZigClangStmt *ZigClangWhileStmt_getBody(const struct ZigClangWhileStmt *);
12051207
test/run_translated_c.zig+56
......@@ -818,4 +818,60 @@ pub fn addCases(cases: *tests.RunTranslatedCContext) void {
818818 \\ return 0;
819819 \\}
820820 , "");
821
822 cases.add("Address of function is no-op",
823 \\#include <stdlib.h>
824 \\#include <stdbool.h>
825 \\typedef int (*myfunc)(int);
826 \\int a(int arg) { return arg + 1;}
827 \\int b(int arg) { return arg + 2;}
828 \\int caller(myfunc fn, int arg) {
829 \\ return fn(arg);
830 \\}
831 \\int main() {
832 \\ myfunc arr[3] = {&a, &b, a};
833 \\ myfunc foo = a;
834 \\ myfunc bar = &(a);
835 \\ if (foo != bar) abort();
836 \\ if (arr[0] == arr[1]) abort();
837 \\ if (arr[0] != arr[2]) abort();
838 \\ if (caller(b, 40) != 42) abort();
839 \\ if (caller(&b, 40) != 42) abort();
840 \\ return 0;
841 \\}
842 , "");
843
844 cases.add("Obscure ways of calling functions; issue #4124",
845 \\#include <stdlib.h>
846 \\static int add(int a, int b) {
847 \\ return a + b;
848 \\}
849 \\typedef int (*adder)(int, int);
850 \\typedef void (*funcptr)(void);
851 \\int main() {
852 \\ if ((add)(1, 2) != 3) abort();
853 \\ if ((&add)(1, 2) != 3) abort();
854 \\ if (add(3, 1) != 4) abort();
855 \\ if ((*add)(2, 3) != 5) abort();
856 \\ if ((**add)(7, -1) != 6) abort();
857 \\ if ((***add)(-2, 9) != 7) abort();
858 \\
859 \\ int (*ptr)(int a, int b);
860 \\ ptr = add;
861 \\
862 \\ if (ptr(1, 2) != 3) abort();
863 \\ if ((*ptr)(3, 1) != 4) abort();
864 \\ if ((**ptr)(2, 3) != 5) abort();
865 \\ if ((***ptr)(7, -1) != 6) abort();
866 \\ if ((****ptr)(-2, 9) != 7) abort();
867 \\
868 \\ funcptr addr1 = (funcptr)(add);
869 \\ funcptr addr2 = (funcptr)(&add);
870 \\
871 \\ if (addr1 != addr2) abort();
872 \\ if (((int(*)(int, int))addr1)(1, 2) != 3) abort();
873 \\ if (((adder)addr2)(1, 2) != 3) abort();
874 \\ return 0;
875 \\}
876 , "");
821877}
test/translate_c.zig+2-2
......@@ -2802,8 +2802,8 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
28022802 \\ fn_f64(3);
28032803 \\ fn_bool(@as(c_int, 123) != 0);
28042804 \\ fn_bool(@as(c_int, 0) != 0);
2805 \\ fn_bool(@ptrToInt(&fn_int) != 0);
2806 \\ fn_int(@intCast(c_int, @ptrToInt(&fn_int)));
2805 \\ fn_bool(@ptrToInt(fn_int) != 0);
2806 \\ fn_int(@intCast(c_int, @ptrToInt(fn_int)));
28072807 \\ fn_ptr(@intToPtr(?*c_void, @as(c_int, 42)));
28082808 \\}
28092809 });