diff --git a/src/translate_c.cpp b/src/translate_c.cpp index c6a25271b8..21a085ebba 100644 --- a/src/translate_c.cpp +++ b/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(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; + return res; - 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; - } + 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(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(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(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(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(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(); diff --git a/test/translate_c.zig b/test/translate_c.zig index 0cfefe79ab..9a69c2b03e 100644 --- a/test/translate_c.zig +++ b/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) {