Commit 89ef635b35

Merlyn Morgan-Graham <kavika@gmail.com>
2019-12-16 10:33:27
Add boolean and, boolean or binary ops in translate-c-2
1 parent acff2d4
Changed files (3)
src-self-hosted/clang.zig
@@ -967,6 +967,16 @@ pub extern fn ZigClangDeclRefExpr_getDecl(*const ZigClangDeclRefExpr) *const Zig
 pub extern fn ZigClangParenType_getInnerType(*const ZigClangParenType) ZigClangQualType;
 
 pub extern fn ZigClangElaboratedType_getNamedType(*const ZigClangElaboratedType) ZigClangQualType;
+pub extern fn ZigClangElaboratedType_getKeyword(*const ZigClangElaboratedType) ZigClangElaboratedTypeKeyword;
+pub const ZigClangElaboratedTypeKeyword = extern enum {
+    Struct,
+    Interface,
+    Union,
+    Class,
+    Enum,
+    Typename,
+    None,
+};
 
 pub extern fn ZigClangAttributedType_getEquivalentType(*const ZigClangAttributedType) ZigClangQualType;
 
src-self-hosted/translate_c.zig
@@ -592,14 +592,14 @@ fn transCreateNodeShiftOp(
     const rhs_location = ZigClangExpr_getBeginLoc(rhs_expr);
     // lhs >> u5(rh)
 
-    const lhs = try transExpr(rp, scope, lhs_expr, .used, .l_value);
+    const lhs = try transExpr(rp, scope, lhs_expr, .used, .r_value);
     const op_token = try appendToken(rp.c, op_tok_id, bytes);
 
     const as_node = try transCreateNodeBuiltinFnCall(rp.c, "@as");
     const rhs_type = try qualTypeToLog2IntRef(rp, ZigClangBinaryOperator_getType(stmt), rhs_location);
     try as_node.params.push(rhs_type);
     _ = try appendToken(rp.c, .Comma, ",");
-    const rhs = try transExpr(rp, scope, rhs_expr, .used, .l_value);
+    const rhs = try transExpr(rp, scope, rhs_expr, .used, .r_value);
     try as_node.params.push(rhs.node);
     as_node.rparen_token = try appendToken(rp.c, .RParen, ")");
 
@@ -672,7 +672,7 @@ fn transBinaryOperator(
             if (!cIsUnsignedInteger(qt)) {
                 // signed integer division uses @divTrunc
                 const div_trunc_node = try transCreateNodeBuiltinFnCall(rp.c, "@divTrunc");
-                const lhs = try transExpr(rp, scope, ZigClangBinaryOperator_getLHS(stmt), .used, .l_value);
+                const lhs = try transExpr(rp, scope, ZigClangBinaryOperator_getLHS(stmt), .used, .r_value);
                 try div_trunc_node.params.push(lhs.node);
                 _ = try appendToken(rp.c, .Comma, ",");
                 const rhs = try transExpr(rp, scope, ZigClangBinaryOperator_getRHS(stmt), .used, .r_value);
@@ -697,7 +697,7 @@ fn transBinaryOperator(
             if (!cIsUnsignedInteger(qt)) {
                 // signed integer division uses @rem
                 const rem_node = try transCreateNodeBuiltinFnCall(rp.c, "@rem");
-                const lhs = try transExpr(rp, scope, ZigClangBinaryOperator_getLHS(stmt), .used, .l_value);
+                const lhs = try transExpr(rp, scope, ZigClangBinaryOperator_getLHS(stmt), .used, .r_value);
                 try rem_node.params.push(lhs.node);
                 _ = try appendToken(rp.c, .Comma, ",");
                 const rhs = try transExpr(rp, scope, ZigClangBinaryOperator_getRHS(stmt), .used, .r_value);
@@ -806,10 +806,23 @@ fn transBinaryOperator(
                 .node_scope = scope,
             });
         },
-        .LAnd,
-        .LOr,
-        .Comma,
-        => return revertAndWarn(
+        .LAnd => {
+            const node = try transCreateNodeBoolInfixOp(rp, scope, stmt, .BoolAnd, .Keyword_and, "and");
+            return maybeSuppressResult(rp, scope, result_used, TransResult{
+                .node = node,
+                .child_scope = scope,
+                .node_scope = scope,
+            });
+        },
+        .LOr => {
+            const node = try transCreateNodeBoolInfixOp(rp, scope, stmt, .BoolOr, .Keyword_or, "or");
+            return maybeSuppressResult(rp, scope, result_used, TransResult{
+                .node = node,
+                .child_scope = scope,
+                .node_scope = scope,
+            });
+        },
+        .Comma => return revertAndWarn(
             rp,
             error.UnsupportedTranslation,
             ZigClangBinaryOperator_getBeginLoc(stmt),
@@ -1040,6 +1053,321 @@ fn transImplicitCastExpr(
     }
 }
 
+fn toEnumZeroCmp(
+    rp: RestorePoint,
+    scope: *Scope,
+    expr: *ast.Node,
+    generate_enum_node: fn (RestorePoint, *const struct_ZigClangType, source_loc: ZigClangSourceLocation) TransError!*ast.Node,
+    enum_ty: *const struct_ZigClangType,
+    enum_source_loc: ZigClangSourceLocation,
+) !*ast.Node {
+    // expr != @bitCast(EnumType, @as(@TagType(EnumType), 0))
+
+    // @bitCast(Enum,
+    const bitcast = try transCreateNodeBuiltinFnCall(rp.c, "@bitCast");
+    const bitcast_enum_identifier = try generate_enum_node(rp, enum_ty, enum_source_loc);
+    try bitcast.params.push(bitcast_enum_identifier);
+    _ = try appendToken(rp.c, .Comma, ",");
+
+    // @as(
+    const cast_node = try transCreateNodeBuiltinFnCall(rp.c, "@as");
+
+    // @TagType(Enum),
+    const tag_type = try transCreateNodeBuiltinFnCall(rp.c, "@TagType");
+    const tag_type_enum_identifier = try generate_enum_node(rp, enum_ty, enum_source_loc);
+    try tag_type.params.push(tag_type_enum_identifier);
+    tag_type.rparen_token = try appendToken(rp.c, .RParen, ")");
+    try cast_node.params.push(&tag_type.base);
+    _ = try appendToken(rp.c, .Comma, ",");
+
+    // 0)
+    const zero = try transCreateNodeInt(rp.c, 0);
+    try cast_node.params.push(zero);
+    cast_node.rparen_token = try appendToken(rp.c, .RParen, ")");
+
+    try bitcast.params.push(&cast_node.base);
+    bitcast.rparen_token = try appendToken(rp.c, .RParen, ")");
+
+    // expr != @bitCast(EnumType, @as(@TagType(EnumType), 0))
+    return transCreateNodeNotEqual(rp, scope, expr, &bitcast.base);
+}
+
+fn transBoolExpr(
+    rp: RestorePoint,
+    scope: *Scope,
+    expr: *const ZigClangExpr,
+    used: ResultUsed,
+    lrvalue: LRValue,
+) !*ast.Node {
+    var res = try transExpr(rp, scope, expr, used, lrvalue);
+
+    switch (res.node.id) {
+        .InfixOp => switch (@ptrCast(*const ast.Node.InfixOp, &res.node).op) {
+            .BoolOr,
+            .BoolAnd,
+            .EqualEqual,
+            .BangEqual,
+            .LessThan,
+            .GreaterThan,
+            .LessOrEqual,
+            .GreaterOrEqual,
+            => return res.node,
+
+            else => {},
+        },
+
+        .PrefixOp => switch (@ptrCast(*const ast.Node.PrefixOp, &res.node).op) {
+            .BoolNot => return res.node,
+
+            else => {},
+        },
+
+        .BoolLiteral => return res.node,
+
+        else => {},
+    }
+
+    const ty = ZigClangQualType_getTypePtr(getExprQualTypeBeforeImplicitCast(rp.c, expr));
+
+    switch (ZigClangType_getTypeClass(ty)) {
+        .Builtin => {
+            const builtin_ty = @ptrCast(*const ZigClangBuiltinType, ty);
+
+            switch (ZigClangBuiltinType_getKind(builtin_ty)) {
+                .Bool,
+                .Char_U,
+                .UChar,
+                .Char_S,
+                .SChar,
+                .UShort,
+                .UInt,
+                .ULong,
+                .ULongLong,
+                .Short,
+                .Int,
+                .Long,
+                .LongLong,
+                .UInt128,
+                .Int128,
+                .Float,
+                .Double,
+                .Float128,
+                .LongDouble,
+                .WChar_U,
+                .Char8,
+                .Char16,
+                .Char32,
+                .WChar_S,
+                .Float16,
+                => return transCreateNodeNotEqual(rp, scope, res.node, try transCreateNodeInt(rp.c, 0)),
+
+                .NullPtr => return transCreateNodeNotEqual(rp, scope, res.node, try transCreateNodeNullLiteral(rp.c)),
+
+                .Void,
+                .Half,
+                .ObjCId,
+                .ObjCClass,
+                .ObjCSel,
+                .OMPArraySection,
+                .Dependent,
+                .Overload,
+                .BoundMember,
+                .PseudoObject,
+                .UnknownAny,
+                .BuiltinFn,
+                .ARCUnbridgedCast,
+                .OCLImage1dRO,
+                .OCLImage1dArrayRO,
+                .OCLImage1dBufferRO,
+                .OCLImage2dRO,
+                .OCLImage2dArrayRO,
+                .OCLImage2dDepthRO,
+                .OCLImage2dArrayDepthRO,
+                .OCLImage2dMSAARO,
+                .OCLImage2dArrayMSAARO,
+                .OCLImage2dMSAADepthRO,
+                .OCLImage2dArrayMSAADepthRO,
+                .OCLImage3dRO,
+                .OCLImage1dWO,
+                .OCLImage1dArrayWO,
+                .OCLImage1dBufferWO,
+                .OCLImage2dWO,
+                .OCLImage2dArrayWO,
+                .OCLImage2dDepthWO,
+                .OCLImage2dArrayDepthWO,
+                .OCLImage2dMSAAWO,
+                .OCLImage2dArrayMSAAWO,
+                .OCLImage2dMSAADepthWO,
+                .OCLImage2dArrayMSAADepthWO,
+                .OCLImage3dWO,
+                .OCLImage1dRW,
+                .OCLImage1dArrayRW,
+                .OCLImage1dBufferRW,
+                .OCLImage2dRW,
+                .OCLImage2dArrayRW,
+                .OCLImage2dDepthRW,
+                .OCLImage2dArrayDepthRW,
+                .OCLImage2dMSAARW,
+                .OCLImage2dArrayMSAARW,
+                .OCLImage2dMSAADepthRW,
+                .OCLImage2dArrayMSAADepthRW,
+                .OCLImage3dRW,
+                .OCLSampler,
+                .OCLEvent,
+                .OCLClkEvent,
+                .OCLQueue,
+                .OCLReserveID,
+                .ShortAccum,
+                .Accum,
+                .LongAccum,
+                .UShortAccum,
+                .UAccum,
+                .ULongAccum,
+                .ShortFract,
+                .Fract,
+                .LongFract,
+                .UShortFract,
+                .UFract,
+                .ULongFract,
+                .SatShortAccum,
+                .SatAccum,
+                .SatLongAccum,
+                .SatUShortAccum,
+                .SatUAccum,
+                .SatULongAccum,
+                .SatShortFract,
+                .SatFract,
+                .SatLongFract,
+                .SatUShortFract,
+                .SatUFract,
+                .SatULongFract,
+                .OCLIntelSubgroupAVCMcePayload,
+                .OCLIntelSubgroupAVCImePayload,
+                .OCLIntelSubgroupAVCRefPayload,
+                .OCLIntelSubgroupAVCSicPayload,
+                .OCLIntelSubgroupAVCMceResult,
+                .OCLIntelSubgroupAVCImeResult,
+                .OCLIntelSubgroupAVCRefResult,
+                .OCLIntelSubgroupAVCSicResult,
+                .OCLIntelSubgroupAVCImeResultSingleRefStreamout,
+                .OCLIntelSubgroupAVCImeResultDualRefStreamout,
+                .OCLIntelSubgroupAVCImeSingleRefStreamin,
+                .OCLIntelSubgroupAVCImeDualRefStreamin,
+                => return res.node,
+            }
+        },
+        .Pointer => return transCreateNodeNotEqual(rp, scope, res.node, try transCreateNodeNullLiteral(rp.c)),
+
+        .Typedef => {
+            return transCreateNodeNotEqual(rp, scope, res.node, try transCreateNodeInt(rp.c, 0)); // TODO currently assuming it is like an int/char/bool builtin type. Coerce the type and recurse? Add a toTypedefZeroCmp function?
+
+            // TODO This is the code that was in translate-c, but it seems like it is giving wrong results! It just prints the typedef name instead of the value
+            // const typedef_ty = @ptrCast(*const ZigClangTypedefType, ty);
+            // const typedef_decl = ZigClangTypedefType_getDecl(typedef_ty);
+            // const typedef_name_decl = ZigClangTypedefNameDecl_getCanonicalDecl(typedef_decl);
+
+            // const typedef_name = if (rp.c.decl_table.get(@ptrToInt(typedef_name_decl))) |existing_entry|
+            //     existing_entry.value
+            // else
+            //     try rp.c.str(ZigClangDecl_getName_bytes_begin(@ptrCast(*const ZigClangDecl, typedef_name_decl)));
+
+            // return transCreateNodeIdentifier(rp.c, typedef_name);
+        },
+
+        .Enum => {
+            const gen_enum_decl_node = struct {
+                // Have to use a callback because node must be generated inline in order to avoid weird AST printing behavior,
+                // and the code to generate the nodes is a little different for each case
+                fn generate_node(inner_rp: RestorePoint, enum_ty: *const struct_ZigClangType, source_loc: ZigClangSourceLocation) TransError!*ast.Node {
+                    const actual_enum_ty = @ptrCast(*const ZigClangEnumType, enum_ty);
+                    const enum_decl = ZigClangEnumType_getDecl(actual_enum_ty);
+                    const enum_type = (try transEnumDecl(inner_rp.c, enum_decl)) orelse {
+                        return revertAndWarn(inner_rp, error.UnsupportedType, source_loc, "unable to translate enum declaration", .{});
+                    };
+                    return enum_type;
+                }
+            };
+
+            return toEnumZeroCmp(rp, scope, res.node, gen_enum_decl_node.generate_node, ty, ZigClangExpr_getBeginLoc(expr));
+        },
+
+        .Elaborated => {
+            const elaborated_ty = @ptrCast(*const ZigClangElaboratedType, ty);
+
+            switch (ZigClangElaboratedType_getKeyword(elaborated_ty)) {
+                .Enum => {
+                    // Have to use a callback because node must be generated inline in order to avoid weird AST printing behavior,
+                    // and the code to generate the nodes is a little different for each case
+                    const gen_enum_type_node = struct {
+                        fn generate_node(inner_rp: RestorePoint, enum_ty: *const struct_ZigClangType, source_loc: ZigClangSourceLocation) TransError!*ast.Node {
+                            const inner_elaborated_ty = @ptrCast(*const ZigClangElaboratedType, enum_ty);
+                            const enum_type = try transQualType(inner_rp, ZigClangElaboratedType_getNamedType(inner_elaborated_ty), source_loc);
+                            return enum_type;
+                        }
+                    };
+
+                    return toEnumZeroCmp(rp, scope, res.node, gen_enum_type_node.generate_node, ty, ZigClangExpr_getBeginLoc(expr));
+                },
+
+                .Struct,
+                .Union,
+                .Interface,
+                .Class,
+                .Typename,
+                .None,
+                => return res.node,
+            }
+        },
+
+        .FunctionProto,
+        .Record,
+        .ConstantArray,
+        .Paren,
+        .Decayed,
+        .Attributed,
+        .IncompleteArray,
+        .BlockPointer,
+        .LValueReference,
+        .RValueReference,
+        .MemberPointer,
+        .VariableArray,
+        .DependentSizedArray,
+        .DependentSizedExtVector,
+        .Vector,
+        .ExtVector,
+        .FunctionNoProto,
+        .UnresolvedUsing,
+        .Adjusted,
+        .TypeOfExpr,
+        .TypeOf,
+        .Decltype,
+        .UnaryTransform,
+        .TemplateTypeParm,
+        .SubstTemplateTypeParm,
+        .SubstTemplateTypeParmPack,
+        .TemplateSpecialization,
+        .Auto,
+        .InjectedClassName,
+        .DependentName,
+        .DependentTemplateSpecialization,
+        .PackExpansion,
+        .ObjCObject,
+        .ObjCInterface,
+        .Complex,
+        .ObjCObjectPointer,
+        .Atomic,
+        .Pipe,
+        .ObjCTypeParam,
+        .DeducedTemplateSpecialization,
+        .DependentAddressSpace,
+        .DependentVector,
+        .MacroQualified,
+        => return res.node,
+    }
+
+    unreachable;
+}
+
 fn transIntegerLiteral(
     rp: RestorePoint,
     scope: *Scope,
@@ -1848,6 +2176,14 @@ fn getExprQualType(c: *Context, expr: *const ZigClangExpr) ZigClangQualType {
     return ZigClangExpr_getType(expr);
 }
 
+fn getExprQualTypeBeforeImplicitCast(c: *Context, expr: *const ZigClangExpr) ZigClangQualType {
+    if (ZigClangExpr_getStmtClass(expr) == .ImplicitCastExprClass) {
+        const cast_expr = @ptrCast(*const ZigClangImplicitCastExpr, expr);
+        return getExprQualType(c, ZigClangImplicitCastExpr_getSubExpr(cast_expr));
+    }
+    return ZigClangExpr_getType(expr);
+}
+
 fn typeIsOpaque(c: *Context, ty: *const ZigClangType, loc: ZigClangSourceLocation) bool {
     switch (ZigClangType_getTypeClass(ty)) {
         .Builtin => {
@@ -2000,25 +2336,24 @@ fn transCreateNodePrefixOp(
     return node;
 }
 
-fn transCreateNodeInfixOp(
+fn transCreateNodeInfixOpImpl(
     rp: RestorePoint,
     scope: *Scope,
-    stmt: *const ZigClangBinaryOperator,
+    lhs_node: *ast.Node,
+    rhs_node: *ast.Node,
     op: ast.Node.InfixOp.Op,
     op_tok_id: std.zig.Token.Id,
     bytes: []const u8,
     grouped: bool,
 ) !*ast.Node {
     const lparen = if (grouped) try appendToken(rp.c, .LParen, "(") else undefined;
-    const lhs = try transExpr(rp, scope, ZigClangBinaryOperator_getLHS(stmt), .used, .l_value);
     const op_token = try appendToken(rp.c, op_tok_id, bytes);
-    const rhs = try transExpr(rp, scope, ZigClangBinaryOperator_getRHS(stmt), .used, .r_value);
     const node = try rp.c.a().create(ast.Node.InfixOp);
     node.* = ast.Node.InfixOp{
         .op_token = op_token,
-        .lhs = lhs.node,
+        .lhs = lhs_node,
         .op = op,
-        .rhs = rhs.node,
+        .rhs = rhs_node,
     };
     if (!grouped) return &node.base;
     const rparen = try appendToken(rp.c, .RParen, ")");
@@ -2031,6 +2366,60 @@ fn transCreateNodeInfixOp(
     return &grouped_expr.base;
 }
 
+fn transCreateNodeInfixOp(
+    rp: RestorePoint,
+    scope: *Scope,
+    stmt: *const ZigClangBinaryOperator,
+    op: ast.Node.InfixOp.Op,
+    op_tok_id: std.zig.Token.Id,
+    bytes: []const u8,
+    grouped: bool,
+) !*ast.Node {
+    return transCreateNodeInfixOpImpl(
+        rp,
+        scope,
+        (try transExpr(rp, scope, ZigClangBinaryOperator_getLHS(stmt), .used, .r_value)).node,
+        (try transExpr(rp, scope, ZigClangBinaryOperator_getRHS(stmt), .used, .r_value)).node,
+        op,
+        op_tok_id,
+        bytes,
+        grouped,
+    );
+}
+
+fn transCreateNodeNotEqual(
+    rp: RestorePoint,
+    scope: *Scope,
+    lhs_node: *ast.Node,
+    rhs_node: *ast.Node,
+) !*ast.Node {
+    return transCreateNodeInfixOpImpl(rp, scope, lhs_node, rhs_node, .BangEqual, .BangEqual, "!=", true);
+}
+
+fn transCreateNodeBoolInfixOp(
+    rp: RestorePoint,
+    scope: *Scope,
+    stmt: *const ZigClangBinaryOperator,
+    comptime op: ast.Node.InfixOp.Op,
+    comptime op_tok_id: std.zig.Token.Id,
+    comptime bytes: []const u8,
+) !*ast.Node {
+    if (!(op == .BoolAnd or op == .BoolOr)) {
+        @compileError("op must be either .BoolAnd or .BoolOr");
+    }
+
+    return transCreateNodeInfixOpImpl(
+        rp,
+        scope,
+        try transBoolExpr(rp, scope, ZigClangBinaryOperator_getLHS(stmt), .used, .r_value),
+        try transBoolExpr(rp, scope, ZigClangBinaryOperator_getRHS(stmt), .used, .r_value),
+        op,
+        op_tok_id,
+        bytes,
+        true,
+    );
+}
+
 fn transCreateNodePtrType(
     c: *Context,
     is_const: bool,
test/translate_c.zig
@@ -949,26 +949,99 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
         \\}
     });
 
-    cases.addC("logical and, logical or on none bool values",
-        \\int and_or_none_bool(int a, float b, void *c) {
-        \\    if (a && b) return 0;
-        \\    if (b && c) return 1;
-        \\    if (a && c) return 2;
-        \\    if (a || b) return 3;
-        \\    if (b || c) return 4;
-        \\    if (a || c) return 5;
-        \\    return 6;
-        \\}
-    , &[_][]const u8{
-        \\pub export fn and_or_none_bool(a: c_int, b: f32, c: ?*c_void) c_int {
-        \\    if ((a != 0) and (b != 0)) return 0;
-        \\    if ((b != 0) and (c != null)) return 1;
-        \\    if ((a != 0) and (c != null)) return 2;
-        \\    if ((a != 0) or (b != 0)) return 3;
-        \\    if ((b != 0) or (c != null)) return 4;
-        \\    if ((a != 0) or (c != null)) return 5;
-        \\    return 6;
+    cases.addC("logical and, logical or, on non-bool values", // Note this gets cut off by extra C symbols being injected in middle: `pub const Foo = enum_Foo;`
+        \\enum Foo {
+        \\    FooA,
+        \\    FooB,
+        \\    FooC,
+        \\};
+        \\int and_or_non_bool(int a, float b, void *c) {
+        \\    enum Foo d = FooA;
+        \\    int e = (a && b);
+        \\    int f = (b && c);
+        \\    int g = (a && c);
+        \\    int h = (a || b);
+        \\    int i = (b || c);
+        \\    int j = (a || c);
+        \\    int k = (a || d);
+        \\    int l = (d && b);
+        \\    int m = (c || d);
+        \\    return (((((((e + f) + g) + h) + i) + j) + k) + l) + m;
         \\}
+    , &[_][]const u8{
+        \\pub const FooA = enum_Foo.A;
+        \\pub const FooB = enum_Foo.B;
+        \\pub const FooC = enum_Foo.C;
+        \\pub const enum_Foo = extern enum {
+        \\    A,
+        \\    B,
+        \\    C,
+        \\};
+        \\pub export fn and_or_non_bool(a: c_int, b: f32, c: ?*c_void) c_int {
+        \\    var d: enum_Foo = @as(enum_Foo, FooA);
+        \\    var e: c_int = (a != 0) and (b != 0);
+        \\    var f: c_int = (b != 0) and (c != null);
+        \\    var g: c_int = (a != 0) and (c != null);
+        \\    var h: c_int = (a != 0) or (b != 0);
+        \\    var i: c_int = (b != 0) or (c != null);
+        \\    var j: c_int = (a != 0) or (c != null);
+        \\    var k: c_int = (a != 0) or (@as(c_int, d) != @bitCast(enum_Foo, @as(@TagType(enum_Foo), 0)));
+        \\    var l: c_int = (@as(c_int, d) != @bitCast(enum_Foo, @as(@TagType(enum_Foo), 0))) and (b != 0);
+        \\    var m: c_int = (c != null) or (@as(c_int, d) != @bitCast(enum_Foo, @as(@TagType(enum_Foo), 0)));
+        \\    return (((((((e + f) + g) + h) + i) + j) + k) + l) + m;
+        \\}
+    });
+
+    cases.add_2("logical and, logical or, on non-bool values, extra parens",
+        \\enum Foo {
+        \\    FooA,
+        \\    FooB,
+        \\    FooC,
+        \\};
+        \\typedef int SomeTypedef;
+        \\int and_or_non_bool(int a, float b, void *c) {
+        \\    enum Foo d = FooA;
+        \\    int e = (a && b);
+        \\    int f = (b && c);
+        \\    int g = (a && c);
+        \\    int h = (a || b);
+        \\    int i = (b || c);
+        \\    int j = (a || c);
+        \\    int k = (a || d);
+        \\    int l = (d && b);
+        \\    int m = (c || d);
+        \\    SomeTypedef td = 44;
+        \\    int o = (td || b);
+        \\    int p = (c && td);
+        \\    return ((((((((((e + f) + g) + h) + i) + j) + k) + l) + m) + o) + p);
+        \\}
+    , &[_][]const u8{
+        \\pub const FooA = enum_Foo.A;
+        \\pub const FooB = enum_Foo.B;
+        \\pub const FooC = enum_Foo.C;
+        \\pub const enum_Foo = extern enum {
+        \\    A,
+        \\    B,
+        \\    C,
+        \\};
+        \\pub const SomeTypedef = c_int;
+        \\pub export fn and_or_non_bool(a: c_int, b: f32, c: ?*c_void) c_int {
+        \\    var d: enum_Foo = @as(enum_Foo, FooA);
+        \\    var e: c_int = ((a != 0) and (b != 0));
+        \\    var f: c_int = ((b != 0) and (c != null));
+        \\    var g: c_int = ((a != 0) and (c != null));
+        \\    var h: c_int = ((a != 0) or (b != 0));
+        \\    var i: c_int = ((b != 0) or (c != null));
+        \\    var j: c_int = ((a != 0) or (c != null));
+        \\    var k: c_int = ((a != 0) or (@as(c_int, d) != @bitCast(enum_Foo, @as(@TagType(enum_Foo), 0))));
+        \\    var l: c_int = ((@as(c_int, d) != @bitCast(enum_Foo, @as(@TagType(enum_Foo), 0))) and (b != 0));
+        \\    var m: c_int = ((c != null) or (@as(c_int, d) != @bitCast(enum_Foo, @as(@TagType(enum_Foo), 0))));
+        \\    var td: SomeTypedef = 44;
+        \\    var o: c_int = ((td != 0) or (b != 0));
+        \\    var p: c_int = ((c != null) and (td != 0));
+        \\    return ((((((((((e + f) + g) + h) + i) + j) + k) + l) + m) + o) + p);
+        \\}
+        \\pub const Foo = enum_Foo;
     });
 
     cases.addC("assign",