diff --git a/src/ast_render.cpp b/src/ast_render.cpp index 599d4b2659..5ffec92030 100644 --- a/src/ast_render.cpp +++ b/src/ast_render.cpp @@ -625,7 +625,7 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) { fprintf(ar->f, "@"); } AstNode *fn_ref_node = node->data.fn_call_expr.fn_ref_expr; - bool grouped = (fn_ref_node->type != NodeTypeBinOpExpr); + bool grouped = (fn_ref_node->type != NodeTypePrefixOpExpr); render_node_extra(ar, fn_ref_node, grouped); fprintf(ar->f, "("); for (size_t i = 0; i < node->data.fn_call_expr.params.length; i += 1) { diff --git a/src/parsec.cpp b/src/parsec.cpp index 273222b2d0..adcae0946b 100644 --- a/src/parsec.cpp +++ b/src/parsec.cpp @@ -350,6 +350,21 @@ static AstNode* trans_c_cast(Context *c, const SourceLocation &source_location, return trans_create_node_fn_call_1(c, trans_qual_type(c, qt, source_location), expr); } +static bool qual_type_is_fn_ptr(Context *c, const QualType &qt) { + const Type *ty = qt.getTypePtr(); + if (ty->getTypeClass() != Type::Pointer) { + return false; + } + const PointerType *pointer_ty = static_cast(ty); + QualType child_qt = pointer_ty->getPointeeType(); + const Type *child_ty = child_qt.getTypePtr(); + if (child_ty->getTypeClass() != Type::Paren) { + return false; + } + const ParenType *paren_ty = static_cast(child_ty); + return paren_ty->getInnerType().getTypePtr()->getTypeClass() == Type::FunctionProto; +} + static uint32_t qual_type_int_bit_width(Context *c, const QualType &qt, const SourceLocation &source_loc) { const Type *ty = qt.getTypePtr(); switch (ty->getTypeClass()) { @@ -1575,8 +1590,14 @@ static AstNode *trans_unary_operator(Context *c, bool result_used, AstNode *bloc emit_warning(c, stmt->getLocStart(), "TODO handle C translation UO_AddrOf"); return nullptr; case UO_Deref: - emit_warning(c, stmt->getLocStart(), "TODO handle C translation UO_Deref"); - return nullptr; + { + bool is_fn_ptr = qual_type_is_fn_ptr(c, stmt->getSubExpr()->getType()); + AstNode *value_node = trans_expr(c, result_used, block, stmt->getSubExpr(), TransRValue); + if (is_fn_ptr) + return value_node; + AstNode *unwrapped = trans_create_node_prefix_op(c, PrefixOpUnwrapMaybe, value_node); + return trans_create_node_prefix_op(c, PrefixOpDereference, unwrapped); + } case UO_Plus: emit_warning(c, stmt->getLocStart(), "TODO handle C translation UO_Plus"); return nullptr; @@ -1919,10 +1940,20 @@ static AstNode *trans_if_statement(Context *c, AstNode *block, IfStmt *stmt) { static AstNode *trans_call_expr(Context *c, bool result_used, AstNode *block, CallExpr *stmt) { AstNode *node = trans_create_node(c, NodeTypeFnCallExpr); - node->data.fn_call_expr.fn_ref_expr = trans_expr(c, true, block, stmt->getCallee(), TransRValue); - if (node->data.fn_call_expr.fn_ref_expr == nullptr) + + AstNode *callee_raw_node = trans_expr(c, true, block, stmt->getCallee(), TransRValue); + if (callee_raw_node == nullptr) return nullptr; + AstNode *callee_node; + if (qual_type_is_fn_ptr(c, stmt->getCallee()->getType())) { + callee_node = trans_create_node_prefix_op(c, PrefixOpUnwrapMaybe, callee_raw_node); + } else { + callee_node = callee_raw_node; + } + + node->data.fn_call_expr.fn_ref_expr = callee_node; + unsigned num_args = stmt->getNumArgs(); Expr **args = stmt->getArgs(); for (unsigned i = 0; i < num_args; i += 1) { diff --git a/test/parsec.zig b/test/parsec.zig index 077dcf832c..f9e90cb705 100644 --- a/test/parsec.zig +++ b/test/parsec.zig @@ -203,13 +203,13 @@ pub fn addCases(cases: &tests.ParseCContext) { \\pub extern var fn_ptr: ?extern fn(); , \\pub inline fn foo() { - \\ ??fn_ptr() + \\ (??fn_ptr)() \\} , \\pub extern var fn_ptr2: ?extern fn(c_int, f32) -> u8; , \\pub inline fn bar(arg0: c_int, arg1: f32) -> u8 { - \\ ??fn_ptr2(arg0, arg1) + \\ (??fn_ptr2)(arg0, arg1) \\} ); @@ -831,6 +831,32 @@ pub fn addCases(cases: &tests.ParseCContext) { \\ }; \\} ); + + cases.addC("deref function pointer", + \\void foo(void) {} + \\void bar(void) { + \\ void(*f)(void) = foo; + \\ f(); + \\ (*(f))(); + \\} + , + \\export fn foo() {} + \\export fn bar() { + \\ var f: ?extern fn() = foo; + \\ (??f)(); + \\ (??f)(); + \\} + ); + + cases.addC("normal deref", + \\void foo(int *x) { + \\ *x = 1; + \\} + , + \\export fn foo(x: ?&c_int) { + \\ (*(??x)) = 1; + \\} + ); }