Commit 2e010c60ae

Jimmi Holst Christensen <jhc@liab.dk>
2018-03-08 15:34:00
Translate C now correctly converts ints, floats, ptrs and enums to bools * Boolean "and" and "or" should also work with these types. * This new method also simplifies to output code.
1 parent b288762
Changed files (2)
src/translate_c.cpp
@@ -119,7 +119,7 @@ static int trans_stmt_extra(Context *c, TransScope *scope, const Stmt *stmt,
 static TransScope *trans_stmt(Context *c, TransScope *scope, const Stmt *stmt, AstNode **out_node);
 static AstNode *trans_expr(Context *c, ResultUsed result_used, TransScope *scope, const Expr *expr, TransLRValue lrval);
 static AstNode *trans_qual_type(Context *c, QualType qt, const SourceLocation &source_loc);
-static AstNode *trans_to_bool_expr(Context *c, TransScope *scope, AstNode *expr);
+static AstNode *trans_bool_expr(Context *c, ResultUsed result_used, TransScope *scope, const Expr *expr, TransLRValue lrval);
 
 ATTRIBUTE_PRINTF(3, 4)
 static void emit_warning(Context *c, const SourceLocation &sl, const char *format, ...) {
@@ -467,6 +467,14 @@ static QualType get_expr_qual_type(Context *c, const Expr *expr) {
     return expr->getType();
 }
 
+static QualType get_expr_qual_type_before_implicit_cast(Context *c, const Expr *expr) {
+    if (expr->getStmtClass() == Stmt::ImplicitCastExprClass) {
+        const ImplicitCastExpr *cast_expr = static_cast<const ImplicitCastExpr *>(expr);
+        return get_expr_qual_type(c, cast_expr->getSubExpr());
+    }
+    return expr->getType();
+}
+
 static AstNode *get_expr_type(Context *c, const Expr *expr) {
     return trans_qual_type(c, get_expr_qual_type(c, expr), expr->getLocStart());
 }
@@ -1152,6 +1160,22 @@ static AstNode *trans_create_bin_op(Context *c, TransScope *scope, Expr *lhs, Bi
     return node;
 }
 
+static AstNode *trans_create_bool_bin_op(Context *c, TransScope *scope, Expr *lhs, BinOpType bin_op, Expr *rhs) {
+    assert(bin_op == BinOpTypeBoolAnd || bin_op == BinOpTypeBoolOr);
+    AstNode *node = trans_create_node(c, NodeTypeBinOpExpr);
+    node->data.bin_op_expr.bin_op = bin_op;
+
+    node->data.bin_op_expr.op1 = trans_bool_expr(c, ResultUsedYes, scope, lhs, TransRValue);
+    if (node->data.bin_op_expr.op1 == nullptr)
+        return nullptr;
+
+    node->data.bin_op_expr.op2 = trans_bool_expr(c, ResultUsedYes, scope, rhs, TransRValue);
+    if (node->data.bin_op_expr.op2 == nullptr)
+        return nullptr;
+
+    return node;
+}
+
 static AstNode *trans_create_assign(Context *c, ResultUsed result_used, TransScope *scope, Expr *lhs, Expr *rhs) {
     if (result_used == ResultUsedNo) {
         // common case
@@ -1293,10 +1317,9 @@ static AstNode *trans_binary_operator(Context *c, ResultUsed result_used, TransS
         case BO_Or:
             return trans_create_bin_op(c, scope, stmt->getLHS(), BinOpTypeBinOr, stmt->getRHS());
         case BO_LAnd:
-            return trans_create_bin_op(c, scope, stmt->getLHS(), BinOpTypeBoolAnd, stmt->getRHS());
+            return trans_create_bool_bin_op(c, scope, stmt->getLHS(), BinOpTypeBoolAnd, stmt->getRHS());
         case BO_LOr:
-            // TODO: int vs bool
-            return trans_create_bin_op(c, scope, stmt->getLHS(), BinOpTypeBoolOr, stmt->getRHS());
+            return trans_create_bool_bin_op(c, scope, stmt->getLHS(), BinOpTypeBoolOr, stmt->getRHS());
         case BO_Assign:
             return trans_create_assign(c, result_used, scope, stmt->getLHS(), stmt->getRHS());
         case BO_Comma:
@@ -1924,7 +1947,6 @@ static AstNode *trans_unary_operator(Context *c, ResultUsed result_used, TransSc
                     return nullptr;
                 }
             }
-        case UO_LNot:
         case UO_Not:
             {
                 Expr *op_expr = stmt->getSubExpr();
@@ -1932,14 +1954,16 @@ static AstNode *trans_unary_operator(Context *c, ResultUsed result_used, TransSc
                 if (sub_node == nullptr)
                     return nullptr;
 
-                switch (stmt->getOpcode()) {
-                    case UO_LNot:
-                        return trans_create_node_prefix_op(c, PrefixOpBoolNot, trans_to_bool_expr(c, scope, sub_node));
-                    case UO_Not:
-                        return trans_create_node_prefix_op(c, PrefixOpBinNot, sub_node);
-                    default:
-                        zig_unreachable();
-                }
+                return trans_create_node_prefix_op(c, PrefixOpBinNot, sub_node);
+            }
+        case UO_LNot:
+            {
+                Expr *op_expr = stmt->getSubExpr();
+                AstNode *sub_node = trans_bool_expr(c, ResultUsedYes, scope, op_expr, TransRValue);
+                if (sub_node == nullptr)
+                    return nullptr;
+
+                return trans_create_node_prefix_op(c, PrefixOpBoolNot, sub_node);
             }
         case UO_Real:
             emit_warning(c, stmt->getLocStart(), "TODO handle C translation UO_Real");
@@ -2220,10 +2244,30 @@ static int trans_local_declaration(Context *c, TransScope *scope, const DeclStmt
     return ErrorNone;
 }
 
-static AstNode *trans_to_bool_expr(Context *c, TransScope *scope, AstNode *expr) {
-    switch (expr->type) {
+static AstNode *to_enum_zero_cmp(Context *c, AstNode *expr, AstNode *enum_type) {
+    AstNode *tag_type = trans_create_node_builtin_fn_call_str(c, "TagType");
+    tag_type->data.fn_call_expr.params.append(enum_type);
+
+    // @TagType(Enum)(0)
+    AstNode *zero = trans_create_node_unsigned_negative(c, 0, false);
+    AstNode *casted_zero = trans_create_node_fn_call_1(c, tag_type, zero);
+
+    // @bitCast(Enum, @TagType(Enum)(0))
+    AstNode *bitcast = trans_create_node_builtin_fn_call_str(c, "bitCast");
+    bitcast->data.fn_call_expr.params.append(enum_type);
+    bitcast->data.fn_call_expr.params.append(casted_zero);
+
+    return trans_create_node_bin_op(c, expr, BinOpTypeCmpNotEq, bitcast);
+}
+
+static AstNode *trans_bool_expr(Context *c, ResultUsed result_used, TransScope *scope, const Expr *expr, TransLRValue lrval) {
+    AstNode *res = trans_expr(c, result_used, scope, expr, lrval);
+    if (res == nullptr)
+        return nullptr;
+
+    switch (res->type) {
         case NodeTypeBinOpExpr:
-            switch (expr->data.bin_op_expr.bin_op) {
+            switch (res->data.bin_op_expr.bin_op) {
                 case BinOpTypeBoolOr:
                 case BinOpTypeBoolAnd:
                 case BinOpTypeCmpEq:
@@ -2232,76 +2276,208 @@ static AstNode *trans_to_bool_expr(Context *c, TransScope *scope, AstNode *expr)
                 case BinOpTypeCmpGreaterThan:
                 case BinOpTypeCmpLessOrEq:
                 case BinOpTypeCmpGreaterOrEq:
-                    return expr;
+                    return res;
                 default:
-                    goto convert_to_bitcast;
+                    break;
             }
 
         case NodeTypePrefixOpExpr:
-            switch (expr->data.prefix_op_expr.prefix_op) {
+            switch (res->data.prefix_op_expr.prefix_op) {
                 case PrefixOpBoolNot:
-                    return expr;
+                    return res;
                 default:
-                    goto convert_to_bitcast;
+                    break;
             }
 
         case NodeTypeBoolLiteral:
-            return expr;
-
-        default: {
-            // In Zig, float, int and pointer does not implicitly cast to bool.
-            // To make it work, we bitcast any value we get to an int of the right size
-            // and comp it to 0
-            // TODO: This doesn't work for pointers, as they become nullable on
-            //       translate
-            // c: expr
-            // zig: __to_bool_expr: {
-            // zig:     const _tmp = cond;
-            // zig:     break :__to_bool_expr @bitCast(@IntType(false, @sizeOf(@typeOf(_tmp)) * 8), _tmp) != 0;
-            // zig: }
-            convert_to_bitcast:
-            TransScopeBlock *child_scope = trans_scope_block_create(c, scope);
-            Buf *label_name = buf_create_from_str("__to_bool_expr");
-            child_scope->node->data.block.name = label_name;
-
-            // const _tmp = cond;
-            // TODO: avoid name collisions with generated variable names
-            Buf *tmp_var_name = buf_create_from_str("_tmp");
-            AstNode *tmp_var_decl = trans_create_node_var_decl_local(c, true, tmp_var_name, nullptr, expr);
-            child_scope->node->data.block.statements.append(tmp_var_decl);
-
-            // @sizeOf(@typeOf(_tmp)) * 8
-            AstNode *typeof_tmp = trans_create_node_builtin_fn_call_str(c, "typeOf");
-            typeof_tmp->data.fn_call_expr.params.append(trans_create_node_symbol(c, tmp_var_name));
-            AstNode *sizeof_tmp = trans_create_node_builtin_fn_call_str(c, "sizeOf");
-            sizeof_tmp->data.fn_call_expr.params.append(typeof_tmp);
-            AstNode *sizeof_tmp_in_bits = trans_create_node_bin_op(
-                    c, sizeof_tmp, BinOpTypeMult,
-                    trans_create_node_unsigned_negative(c, 8, false));
-
-            // @IntType(false, @sizeOf(@typeOf(_tmp)) * 8)
-            AstNode *int_type = trans_create_node_builtin_fn_call_str(c, "IntType");
-            int_type->data.fn_call_expr.params.append(trans_create_node_bool(c, false));
-            int_type->data.fn_call_expr.params.append(sizeof_tmp_in_bits);
-
-            // @bitCast(@IntType(false, @sizeOf(@typeOf(_tmp)) * 8), _tmp)
-            AstNode *bit_cast = trans_create_node_builtin_fn_call_str(c, "bitCast");
-            bit_cast->data.fn_call_expr.params.append(int_type);
-            bit_cast->data.fn_call_expr.params.append(trans_create_node_symbol(c, tmp_var_name));
-
-            // break :__to_bool_expr @bitCast(@IntType(false, @sizeOf(@typeOf(_tmp)) * 8), _tmp) != 0
-            AstNode *not_eql_zero = trans_create_node_bin_op(c, bit_cast, BinOpTypeCmpNotEq, trans_create_node_unsigned_negative(c, 0, false));
-            child_scope->node->data.block.statements.append(trans_create_node_break(c, label_name, not_eql_zero));
-
-            return child_scope->node;
+            return res;
+
+        default:
+            break;
+    }
+
+
+    const Type *ty = get_expr_qual_type_before_implicit_cast(c, expr).getTypePtr();
+    auto classs = ty->getTypeClass();
+    switch (classs) {
+        case Type::Builtin:
+        {
+            const BuiltinType *builtin_ty = static_cast<const BuiltinType*>(ty);
+            switch (builtin_ty->getKind()) {
+                case BuiltinType::Bool:
+                case BuiltinType::Char_U:
+                case BuiltinType::UChar:
+                case BuiltinType::Char_S:
+                case BuiltinType::SChar:
+                case BuiltinType::UShort:
+                case BuiltinType::UInt:
+                case BuiltinType::ULong:
+                case BuiltinType::ULongLong:
+                case BuiltinType::Short:
+                case BuiltinType::Int:
+                case BuiltinType::Long:
+                case BuiltinType::LongLong:
+                case BuiltinType::UInt128:
+                case BuiltinType::Int128:
+                case BuiltinType::Float:
+                case BuiltinType::Double:
+                case BuiltinType::Float128:
+                case BuiltinType::LongDouble:
+                case BuiltinType::WChar_U:
+                case BuiltinType::Char16:
+                case BuiltinType::Char32:
+                case BuiltinType::WChar_S:
+                    return trans_create_node_bin_op(c, res, BinOpTypeCmpNotEq, trans_create_node_unsigned_negative(c, 0, false));
+                case BuiltinType::NullPtr:
+                    return trans_create_node_bin_op(c, res, BinOpTypeCmpNotEq, trans_create_node(c, NodeTypeNullLiteral));
+
+                case BuiltinType::Void:
+                case BuiltinType::Half:
+                case BuiltinType::ObjCId:
+                case BuiltinType::ObjCClass:
+                case BuiltinType::ObjCSel:
+                case BuiltinType::OMPArraySection:
+                case BuiltinType::Dependent:
+                case BuiltinType::Overload:
+                case BuiltinType::BoundMember:
+                case BuiltinType::PseudoObject:
+                case BuiltinType::UnknownAny:
+                case BuiltinType::BuiltinFn:
+                case BuiltinType::ARCUnbridgedCast:
+                case BuiltinType::OCLImage1dRO:
+                case BuiltinType::OCLImage1dArrayRO:
+                case BuiltinType::OCLImage1dBufferRO:
+                case BuiltinType::OCLImage2dRO:
+                case BuiltinType::OCLImage2dArrayRO:
+                case BuiltinType::OCLImage2dDepthRO:
+                case BuiltinType::OCLImage2dArrayDepthRO:
+                case BuiltinType::OCLImage2dMSAARO:
+                case BuiltinType::OCLImage2dArrayMSAARO:
+                case BuiltinType::OCLImage2dMSAADepthRO:
+                case BuiltinType::OCLImage2dArrayMSAADepthRO:
+                case BuiltinType::OCLImage3dRO:
+                case BuiltinType::OCLImage1dWO:
+                case BuiltinType::OCLImage1dArrayWO:
+                case BuiltinType::OCLImage1dBufferWO:
+                case BuiltinType::OCLImage2dWO:
+                case BuiltinType::OCLImage2dArrayWO:
+                case BuiltinType::OCLImage2dDepthWO:
+                case BuiltinType::OCLImage2dArrayDepthWO:
+                case BuiltinType::OCLImage2dMSAAWO:
+                case BuiltinType::OCLImage2dArrayMSAAWO:
+                case BuiltinType::OCLImage2dMSAADepthWO:
+                case BuiltinType::OCLImage2dArrayMSAADepthWO:
+                case BuiltinType::OCLImage3dWO:
+                case BuiltinType::OCLImage1dRW:
+                case BuiltinType::OCLImage1dArrayRW:
+                case BuiltinType::OCLImage1dBufferRW:
+                case BuiltinType::OCLImage2dRW:
+                case BuiltinType::OCLImage2dArrayRW:
+                case BuiltinType::OCLImage2dDepthRW:
+                case BuiltinType::OCLImage2dArrayDepthRW:
+                case BuiltinType::OCLImage2dMSAARW:
+                case BuiltinType::OCLImage2dArrayMSAARW:
+                case BuiltinType::OCLImage2dMSAADepthRW:
+                case BuiltinType::OCLImage2dArrayMSAADepthRW:
+                case BuiltinType::OCLImage3dRW:
+                case BuiltinType::OCLSampler:
+                case BuiltinType::OCLEvent:
+                case BuiltinType::OCLClkEvent:
+                case BuiltinType::OCLQueue:
+                case BuiltinType::OCLReserveID:
+                    return res;
+            }
+            break;
+        }
+        case Type::Pointer:
+            return trans_create_node_bin_op(c, res, BinOpTypeCmpNotEq, trans_create_node(c, NodeTypeNullLiteral));
+
+        case Type::Typedef:
+        {
+            const TypedefType *typedef_ty = static_cast<const TypedefType*>(ty);
+            const TypedefNameDecl *typedef_decl = typedef_ty->getDecl();
+            auto existing_entry = c->decl_table.maybe_get((void*)typedef_decl->getCanonicalDecl());
+            if (existing_entry) {
+                return existing_entry->value;
+            }
+
+            return res;
         }
+
+        case Type::Enum:
+        {
+            const EnumType *enum_ty = static_cast<const EnumType*>(ty);
+            AstNode *enum_type = resolve_enum_decl(c, enum_ty->getDecl());
+            return to_enum_zero_cmp(c, res, enum_type);
+        }
+
+        case Type::Elaborated:
+        {
+            const ElaboratedType *elaborated_ty = static_cast<const ElaboratedType*>(ty);
+            switch (elaborated_ty->getKeyword()) {
+                case ETK_Enum: {
+                    AstNode *enum_type = trans_qual_type(c, elaborated_ty->getNamedType(), expr->getLocStart());
+                    return to_enum_zero_cmp(c, res, enum_type);
+                }
+                case ETK_Struct:
+                case ETK_Union:
+                case ETK_Interface:
+                case ETK_Class:
+                case ETK_Typename:
+                case ETK_None:
+                    return res;
+            }
+        }
+
+        case Type::FunctionProto:
+        case Type::Record:
+        case Type::ConstantArray:
+        case Type::Paren:
+        case Type::Decayed:
+        case Type::Attributed:
+        case Type::IncompleteArray:
+        case Type::BlockPointer:
+        case Type::LValueReference:
+        case Type::RValueReference:
+        case Type::MemberPointer:
+        case Type::VariableArray:
+        case Type::DependentSizedArray:
+        case Type::DependentSizedExtVector:
+        case Type::Vector:
+        case Type::ExtVector:
+        case Type::FunctionNoProto:
+        case Type::UnresolvedUsing:
+        case Type::Adjusted:
+        case Type::TypeOfExpr:
+        case Type::TypeOf:
+        case Type::Decltype:
+        case Type::UnaryTransform:
+        case Type::TemplateTypeParm:
+        case Type::SubstTemplateTypeParm:
+        case Type::SubstTemplateTypeParmPack:
+        case Type::TemplateSpecialization:
+        case Type::Auto:
+        case Type::InjectedClassName:
+        case Type::DependentName:
+        case Type::DependentTemplateSpecialization:
+        case Type::PackExpansion:
+        case Type::ObjCObject:
+        case Type::ObjCInterface:
+        case Type::Complex:
+        case Type::ObjCObjectPointer:
+        case Type::Atomic:
+        case Type::Pipe:
+        case Type::ObjCTypeParam:
+        case Type::DeducedTemplateSpecialization:
+            return res;
     }
+    zig_unreachable();
 }
 
 static AstNode *trans_while_loop(Context *c, TransScope *scope, const WhileStmt *stmt) {
     TransScopeWhile *while_scope = trans_scope_while_create(c, scope);
 
-    while_scope->node->data.while_expr.condition = trans_to_bool_expr(c, scope, trans_expr(c, ResultUsedYes, scope, stmt->getCond(), TransRValue));
+    while_scope->node->data.while_expr.condition = trans_bool_expr(c, ResultUsedYes, scope, stmt->getCond(), TransRValue);
     if (while_scope->node->data.while_expr.condition == nullptr)
         return nullptr;
 
@@ -2328,11 +2504,10 @@ static AstNode *trans_if_statement(Context *c, TransScope *scope, const IfStmt *
             return nullptr;
     }
 
-    AstNode *condition_node = trans_expr(c, ResultUsedYes, scope, stmt->getCond(), TransRValue);
-    if (condition_node == nullptr)
+    if_node->data.if_bool_expr.condition = trans_bool_expr(c, ResultUsedYes, scope, stmt->getCond(), TransRValue);
+    if (if_node->data.if_bool_expr.condition == nullptr)
         return nullptr;
 
-    if_node->data.if_bool_expr.condition = trans_to_bool_expr(c, scope, condition_node);
     return if_node;
 }
 
@@ -2524,12 +2699,18 @@ static AstNode *trans_for_loop(Context *c, TransScope *parent_scope, const ForSt
     if (cond_stmt == nullptr) {
         while_scope->node->data.while_expr.condition = trans_create_node_bool(c, true);
     } else {
-        TransScope *end_cond_scope = trans_stmt(c, cond_scope, cond_stmt,
-                &while_scope->node->data.while_expr.condition);
-        if (end_cond_scope == nullptr)
-            return nullptr;
+        if (Expr::classof(cond_stmt)) {
+            const Expr *cond_expr = static_cast<const Expr*>(cond_stmt);
+            while_scope->node->data.while_expr.condition = trans_bool_expr(c, ResultUsedYes, cond_scope, cond_expr, TransRValue);
 
-        while_scope->node->data.while_expr.condition = trans_to_bool_expr(c, cond_scope, while_scope->node->data.while_expr.condition);
+            if (while_scope->node->data.while_expr.condition == nullptr)
+                return nullptr;
+        } else {
+            TransScope *end_cond_scope = trans_stmt(c, cond_scope, cond_stmt,
+                                                    &while_scope->node->data.while_expr.condition);
+            if (end_cond_scope == nullptr)
+                return nullptr;
+        }
     }
 
     const Stmt *inc_stmt = stmt->getInc();
test/translate_c.zig
@@ -451,6 +451,28 @@ 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;
+        \\}
+    ,
+        \\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("assign",
         \\int max(int a) {
         \\    int tmp;
@@ -1102,17 +1124,18 @@ pub fn addCases(cases: &tests.TranslateCContext) void {
     );
 
     cases.add("bool not",
-        \\int foo(int x) {
-        \\    return !(x == 0);
-        \\    return !x;
+        \\int foo(int a, float b, void *c) {
+        \\    return !(a == 0);
+        \\    return !a;
+        \\    return !b;
+        \\    return !c;
         \\}
     ,
-        \\pub fn foo(x: c_int) c_int {
-        \\    return !(x == 0);
-        \\    return !__to_bool_expr: {
-        \\        const _tmp = x;
-        \\        break :__to_bool_expr @bitCast(@IntType(false, @sizeOf(@typeOf(_tmp)) * 8), _tmp) != 0;
-        \\    };
+        \\pub fn foo(a: c_int, b: f32, c: ?&c_void) c_int {
+        \\    return !(a == 0);
+        \\    return !(a != 0);
+        \\    return !(b != 0);
+        \\    return !(c != null);
         \\}
     );
 
@@ -1148,75 +1171,66 @@ pub fn addCases(cases: &tests.TranslateCContext) void {
         \\pub const NRF_GPIO = if (@typeId(@typeOf(NRF_GPIO_BASE)) == @import("builtin").TypeId.Pointer) @ptrCast(&NRF_GPIO_Type, NRF_GPIO_BASE) else if (@typeId(@typeOf(NRF_GPIO_BASE)) == @import("builtin").TypeId.Int) @intToPtr(&NRF_GPIO_Type, NRF_GPIO_BASE) else (&NRF_GPIO_Type)(NRF_GPIO_BASE);
     );
 
-    cases.add("if on int",
-        \\int if_int(int i) {
-        \\    if (i) {
-        \\        return 0;
-        \\    } else {
-        \\        return 1;
-        \\    }
+    cases.add("if on none bool",
+        \\enum SomeEnum { A, B, C };
+        \\int if_none_bool(int a, float b, void *c, enum SomeEnum d) {
+        \\    if (a) return 0;
+        \\    if (b) return 1;
+        \\    if (c) return 2;
+        \\    if (d) return 3;
+        \\    return 4;
         \\}
     ,
-       \\pub fn if_int(i: c_int) c_int {
-       \\    if (__to_bool_expr: {
-       \\        const _tmp = i;
-       \\        break :__to_bool_expr @bitCast(@IntType(false, @sizeOf(@typeOf(_tmp)) * 8), _tmp) != 0;
-       \\    }) {
-       \\        return 0;
-       \\    } else {
-       \\        return 1;
-       \\    }
-       \\}
+        \\pub const A = enum_SomeEnum.A;
+        \\pub const B = enum_SomeEnum.B;
+        \\pub const C = enum_SomeEnum.C;
+        \\pub const enum_SomeEnum = extern enum {
+        \\    A,
+        \\    B,
+        \\    C,
+        \\};
+        \\pub fn if_none_bool(a: c_int, b: f32, c: ?&c_void, d: enum_SomeEnum) c_int {
+        \\    if (a != 0) return 0;
+        \\    if (b != 0) return 1;
+        \\    if (c != null) return 2;
+        \\    if (d != @bitCast(enum_SomeEnum, @TagType(enum_SomeEnum)(0))) return 3;
+        \\    return 4;
+        \\}
     );
 
-    cases.add("while on int",
-        \\int while_int(int i) {
-        \\    while (i) {
-        \\        return 0;
-        \\    }
+    cases.add("while on none bool",
+        \\int while_none_bool(int a, float b, void *c) {
+        \\    while (a) return 0;
+        \\    while (b) return 1;
+        \\    while (c) return 2;
+        \\    return 3;
         \\}
     ,
-       \\pub fn while_int(i: c_int) c_int {
-       \\    while (__to_bool_expr: {
-       \\        const _tmp = i;
-       \\        break :__to_bool_expr @bitCast(@IntType(false, @sizeOf(@typeOf(_tmp)) * 8), _tmp) != 0;
-       \\    }) {
-       \\        return 0;
-       \\    }
-       \\}
+        \\pub fn while_none_bool(a: c_int, b: f32, c: ?&c_void) c_int {
+        \\    while (a != 0) return 0;
+        \\    while (b != 0) return 1;
+        \\    while (c != null) return 2;
+        \\    return 3;
+        \\}
     );
 
-    cases.add("for on int",
-        \\int for_int(int i) {
-        \\    for (;i;) {
-        \\        return 0;
-        \\    }
-        \\
-        \\    for (int j = 4;j;j--) {
-        \\        return 0;
-        \\    }
+    cases.add("for on none bool",
+        \\int for_none_bool(int a, float b, void *c) {
+        \\    for (;a;) return 0;
+        \\    for (;b;) return 1;
+        \\    for (;c;) return 2;
+        \\    return 3;
         \\}
     ,
-       \\pub fn for_int(i: c_int) c_int {
-       \\    while (__to_bool_expr: {
-       \\        const _tmp = i;
-       \\        break :__to_bool_expr @bitCast(@IntType(false, @sizeOf(@typeOf(_tmp)) * 8), _tmp) != 0;
-       \\    }) {
-       \\        return 0;
-       \\    }
-       \\    {
-       \\        var j: c_int = 4;
-       \\        while (__to_bool_expr: {
-       \\            const _tmp = j;
-       \\            break :__to_bool_expr @bitCast(@IntType(false, @sizeOf(@typeOf(_tmp)) * 8), _tmp) != 0;
-       \\        }) : (j -= 1) {
-       \\            return 0;
-       \\        }
-       \\    }
-       \\}
+        \\pub fn for_none_bool(a: c_int, b: f32, c: ?&c_void) c_int {
+        \\    while (a != 0) return 0;
+        \\    while (b != 0) return 1;
+        \\    while (c != null) return 2;
+        \\    return 3;
+        \\}
     );
 
-    cases.add("for on int",
+    cases.add("switch on int",
         \\int switch_fn(int i) {
         \\    int res = 0;
         \\    switch (i) {