From 4210f1f6a0f55fb7c7b287ac691582752340f79d Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 18 Jun 2018 03:07:16 -0400 Subject: [PATCH] remove bool to int syntax. add @boolToInt add missing docs See #1061 --- doc/langref.html.in | 90 ++++++++++++++++++++------ src/all_types.hpp | 8 +++ src/codegen.cpp | 2 + src/ir.cpp | 60 +++++++++++++++-- src/ir_print.cpp | 9 +++ std/fmt/errol/index.zig | 4 +- std/math/big/int.zig | 18 +++--- std/special/compiler_rt/comparetf2.zig | 2 +- test/cases/bool.zig | 8 +-- 9 files changed, 158 insertions(+), 43 deletions(-) diff --git a/doc/langref.html.in b/doc/langref.html.in index 35ca9a13b4..7c1f9b53d9 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -4560,6 +4560,19 @@ comptime {

{#see_also|Alignment#} {#header_close#} + + {#header_open|@boolToInt#} +
@boolToInt(value: bool) u1
+

+ Converts true to u1(1) and false to + u1(0). +

+

+ If the value is known at compile-time, the return type is comptime_int + instead of u1. +

+ {#header_close#} + {#header_open|@cDefine#}
@cDefine(comptime name: []u8, value)

@@ -4834,21 +4847,6 @@ test "main" { Creates a symbol in the output object file.

{#header_close#} - {#header_open|@tagName#} -
@tagName(value: var) []const u8
-

- Converts an enum value or union value to a slice of bytes representing the name. -

- {#header_close#} - {#header_open|@TagType#} -
@TagType(T: type) type
-

- For an enum, returns the integer type that is used to store the enumeration value. -

-

- For a union, returns the enum type that is used to store the tag value. -

- {#header_close#} {#header_open|@errorName#}
@errorName(err: error) []u8

@@ -4883,6 +4881,12 @@ test "main" {

{#see_also|Compile Variables#} {#header_close#} + + {#header_open|@field#} +
@field(lhs: var, comptime field_name: []const u8) (field)
+

Preforms field access equivalent to lhs.->field_name-<.

+ {#header_close#} + {#header_open|@fieldParentPtr#}
@fieldParentPtr(comptime ParentType: type, comptime field_name: []const u8,
     field_ptr: *T) *ParentType
@@ -4890,6 +4894,23 @@ test "main" { Given a pointer to a field, returns the base pointer of a struct.

{#header_close#} + + {#header_open|@floatCast#} +
@floatCast(comptime DestType: type, value: var) DestType
+

+ Convert from one float type to another. This cast is safe, but may cause the + numeric value to lose precision. +

+ {#header_close#} + + {#header_open|@floatToInt#} +
@floatToInt(comptime DestType: type, float: var) DestType
+

+ Converts the integer part of a floating point number to the destination type. + To convert the other way, use {#link|@intToFloat#}. This cast is always safe. +

+ {#header_close#} + {#header_open|@frameAddress#}
@frameAddress()

@@ -4944,12 +4965,30 @@ fn add(a: i32, b: i32) i32 { return a + b; }

{#see_also|@noInlineCall#} {#header_close#} + + {#header_open|@intCast#} +
@intCast(comptime DestType: type, int: var) DestType
+

+ Converts an integer to another integer while keeping the same numerical value. + Attempting to convert a number which is out of range of the destination type results in + {#link|Undefined Behavior#}. +

+ {#header_close#} + + {#header_open|@intToFloat#} +
@intToFloat(comptime DestType: type, int: var) DestType
+

+ Converts an integer to the closest floating point representation. To convert the other way, use {#link|@floatToInt#}. This cast is always safe. +

+ {#header_close#} + {#header_open|@intToPtr#}
@intToPtr(comptime DestType: type, int: usize) DestType

Converts an integer to a pointer. To convert the other way, use {#link|@ptrToInt#}.

{#header_close#} + {#header_open|@IntType#}
@IntType(comptime is_signed: bool, comptime bit_count: u8) type

@@ -4987,10 +5026,6 @@ fn add(a: i32, b: i32) i32 { return a + b; } It does not include functions, variables, or constants.

{#header_close#} - {#header_open|@field#} -
@field(lhs: var, comptime field_name: []const u8) (field)
-

Preforms field access equivalent to lhs.->field_name-<.

- {#header_close#} {#header_open|@memberType#}
@memberType(comptime T: type, comptime index: usize) type

Returns the field type of a struct or union.

@@ -5370,6 +5405,21 @@ pub const FloatMode = enum { If no overflow or underflow occurs, returns false.

{#header_close#} + {#header_open|@tagName#} +
@tagName(value: var) []const u8
+

+ Converts an enum value or union value to a slice of bytes representing the name. +

+ {#header_close#} + {#header_open|@TagType#} +
@TagType(T: type) type
+

+ For an enum, returns the integer type that is used to store the enumeration value. +

+

+ For a union, returns the enum type that is used to store the tag value. +

+ {#header_close#} {#header_open|@truncate#}
@truncate(comptime T: type, integer) T

@@ -6665,7 +6715,7 @@ hljs.registerLanguage("zig", function(t) { a = t.IR + "\\s*\\(", c = { keyword: "const align var extern stdcallcc nakedcc volatile export pub noalias inline struct packed enum union break return try catch test continue unreachable comptime and or asm defer errdefer if else switch while for fn use bool f32 f64 void type noreturn error i8 u8 i16 u16 i32 u32 i64 u64 isize usize i8w u8w i16w i32w u32w i64w u64w isizew usizew c_short c_ushort c_int c_uint c_long c_ulong c_longlong c_ulonglong resume cancel await async orelse", - built_in: "atomicLoad breakpoint returnAddress frameAddress fieldParentPtr setFloatMode IntType OpaqueType compileError compileLog setCold setRuntimeSafety setEvalBranchQuota offsetOf memcpy inlineCall setGlobalLinkage setGlobalSection divTrunc divFloor enumTagName intToPtr ptrToInt panic ptrCast bitCast rem mod memset sizeOf alignOf alignCast maxValue minValue memberCount memberName memberType typeOf addWithOverflow subWithOverflow mulWithOverflow shlWithOverflow shlExact shrExact cInclude cDefine cUndef ctz clz import cImport errorName embedFile cmpxchgStrong cmpxchgWeak fence divExact truncate atomicRmw sqrt field typeInfo typeName newStackCall", + built_in: "atomicLoad breakpoint returnAddress frameAddress fieldParentPtr setFloatMode IntType OpaqueType compileError compileLog setCold setRuntimeSafety setEvalBranchQuota offsetOf memcpy inlineCall setGlobalLinkage setGlobalSection divTrunc divFloor enumTagName intToPtr ptrToInt panic ptrCast intCast floatCast intToFloat floatToInt boolToInt bitCast rem mod memset sizeOf alignOf alignCast maxValue minValue memberCount memberName memberType typeOf addWithOverflow subWithOverflow mulWithOverflow shlWithOverflow shlExact shrExact cInclude cDefine cUndef ctz clz import cImport errorName embedFile cmpxchgStrong cmpxchgWeak fence divExact truncate atomicRmw sqrt field typeInfo typeName newStackCall", literal: "true false null undefined" }, n = [e, t.CLCM, t.CBCM, s, r]; diff --git a/src/all_types.hpp b/src/all_types.hpp index bc2fe07c18..d94dfa0fcb 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -1361,6 +1361,7 @@ enum BuiltinFnId { BuiltinFnIdFloatCast, BuiltinFnIdIntToFloat, BuiltinFnIdFloatToInt, + BuiltinFnIdBoolToInt, BuiltinFnIdIntType, BuiltinFnIdSetCold, BuiltinFnIdSetRuntimeSafety, @@ -2048,6 +2049,7 @@ enum IrInstructionId { IrInstructionIdFloatCast, IrInstructionIdIntToFloat, IrInstructionIdFloatToInt, + IrInstructionIdBoolToInt, IrInstructionIdIntType, IrInstructionIdBoolNot, IrInstructionIdMemset, @@ -2668,6 +2670,12 @@ struct IrInstructionFloatToInt { IrInstruction *target; }; +struct IrInstructionBoolToInt { + IrInstruction base; + + IrInstruction *target; +}; + struct IrInstructionIntType { IrInstruction base; diff --git a/src/codegen.cpp b/src/codegen.cpp index 4108cbbd68..84335d4e06 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -4726,6 +4726,7 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable, case IrInstructionIdFloatCast: case IrInstructionIdIntToFloat: case IrInstructionIdFloatToInt: + case IrInstructionIdBoolToInt: zig_unreachable(); case IrInstructionIdReturn: @@ -6318,6 +6319,7 @@ static void define_builtin_fns(CodeGen *g) { create_builtin_fn(g, BuiltinFnIdFloatCast, "floatCast", 2); create_builtin_fn(g, BuiltinFnIdIntToFloat, "intToFloat", 2); create_builtin_fn(g, BuiltinFnIdFloatToInt, "floatToInt", 2); + create_builtin_fn(g, BuiltinFnIdBoolToInt, "boolToInt", 1); create_builtin_fn(g, BuiltinFnIdCompileErr, "compileError", 1); create_builtin_fn(g, BuiltinFnIdCompileLog, "compileLog", SIZE_MAX); create_builtin_fn(g, BuiltinFnIdIntType, "IntType", 2); // TODO rename to Int diff --git a/src/ir.cpp b/src/ir.cpp index 0b847fc4e4..e6339a72f6 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -476,6 +476,10 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionFloatToInt *) { return IrInstructionIdFloatToInt; } +static constexpr IrInstructionId ir_instruction_id(IrInstructionBoolToInt *) { + return IrInstructionIdBoolToInt; +} + static constexpr IrInstructionId ir_instruction_id(IrInstructionIntType *) { return IrInstructionIdIntType; } @@ -1959,6 +1963,15 @@ static IrInstruction *ir_build_float_to_int(IrBuilder *irb, Scope *scope, AstNod return &instruction->base; } +static IrInstruction *ir_build_bool_to_int(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *target) { + IrInstructionBoolToInt *instruction = ir_build_instruction(irb, scope, source_node); + instruction->target = target; + + ir_ref_instruction(target, irb->current_basic_block); + + return &instruction->base; +} + static IrInstruction *ir_build_int_type(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *is_signed, IrInstruction *bit_count) { IrInstructionIntType *instruction = ir_build_instruction(irb, scope, source_node); instruction->is_signed = is_signed; @@ -4071,6 +4084,16 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo IrInstruction *result = ir_build_float_to_int(irb, scope, node, arg0_value, arg1_value); return ir_lval_wrap(irb, scope, result, lval); } + case BuiltinFnIdBoolToInt: + { + AstNode *arg0_node = node->data.fn_call_expr.params.at(0); + IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); + if (arg0_value == irb->codegen->invalid_instruction) + return arg0_value; + + IrInstruction *result = ir_build_bool_to_int(irb, scope, node, arg0_value); + return ir_lval_wrap(irb, scope, result, lval); + } case BuiltinFnIdIntType: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); @@ -10055,13 +10078,6 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst return ir_resolve_cast(ira, source_instr, value, wanted_type, CastOpNoop, false); } - // explicit cast from bool to int - if (wanted_type->id == TypeTableEntryIdInt && - actual_type->id == TypeTableEntryIdBool) - { - return ir_resolve_cast(ira, source_instr, value, wanted_type, CastOpBoolToInt, false); - } - // explicit widening conversion if (wanted_type->id == TypeTableEntryIdInt && actual_type->id == TypeTableEntryIdInt && @@ -17605,6 +17621,33 @@ static TypeTableEntry *ir_analyze_instruction_float_to_int(IrAnalyze *ira, IrIns return dest_type; } +static TypeTableEntry *ir_analyze_instruction_bool_to_int(IrAnalyze *ira, IrInstructionBoolToInt *instruction) { + IrInstruction *target = instruction->target->other; + if (type_is_invalid(target->value.type)) + return ira->codegen->builtin_types.entry_invalid; + + if (target->value.type->id != TypeTableEntryIdBool) { + ir_add_error(ira, instruction->target, buf_sprintf("expected bool, found '%s'", + buf_ptr(&target->value.type->name))); + return ira->codegen->builtin_types.entry_invalid; + } + + if (instr_is_comptime(target)) { + bool is_true; + if (!ir_resolve_bool(ira, target, &is_true)) + return ira->codegen->builtin_types.entry_invalid; + + ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base); + bigint_init_unsigned(&out_val->data.x_bigint, is_true ? 1 : 0); + return ira->codegen->builtin_types.entry_num_lit_int; + } + + TypeTableEntry *u1_type = get_int_type(ira->codegen, false, 1); + IrInstruction *result = ir_resolve_cast(ira, &instruction->base, target, u1_type, CastOpBoolToInt, false); + ir_link_new_instruction(result, &instruction->base); + return u1_type; +} + static TypeTableEntry *ir_analyze_instruction_int_type(IrAnalyze *ira, IrInstructionIntType *instruction) { IrInstruction *is_signed_value = instruction->is_signed->other; bool is_signed; @@ -20143,6 +20186,8 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi return ir_analyze_instruction_int_to_float(ira, (IrInstructionIntToFloat *)instruction); case IrInstructionIdFloatToInt: return ir_analyze_instruction_float_to_int(ira, (IrInstructionFloatToInt *)instruction); + case IrInstructionIdBoolToInt: + return ir_analyze_instruction_bool_to_int(ira, (IrInstructionBoolToInt *)instruction); case IrInstructionIdIntType: return ir_analyze_instruction_int_type(ira, (IrInstructionIntType *)instruction); case IrInstructionIdBoolNot: @@ -20490,6 +20535,7 @@ bool ir_has_side_effects(IrInstruction *instruction) { case IrInstructionIdFloatCast: case IrInstructionIdIntToFloat: case IrInstructionIdFloatToInt: + case IrInstructionIdBoolToInt: return false; case IrInstructionIdAsm: diff --git a/src/ir_print.cpp b/src/ir_print.cpp index b5722c52fa..cb91720180 100644 --- a/src/ir_print.cpp +++ b/src/ir_print.cpp @@ -680,6 +680,12 @@ static void ir_print_float_to_int(IrPrint *irp, IrInstructionFloatToInt *instruc fprintf(irp->f, ")"); } +static void ir_print_bool_to_int(IrPrint *irp, IrInstructionBoolToInt *instruction) { + fprintf(irp->f, "@boolToInt("); + ir_print_other_instruction(irp, instruction->target); + fprintf(irp->f, ")"); +} + static void ir_print_int_type(IrPrint *irp, IrInstructionIntType *instruction) { fprintf(irp->f, "@IntType("); ir_print_other_instruction(irp, instruction->is_signed); @@ -1461,6 +1467,9 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) { case IrInstructionIdFloatToInt: ir_print_float_to_int(irp, (IrInstructionFloatToInt *)instruction); break; + case IrInstructionIdBoolToInt: + ir_print_bool_to_int(irp, (IrInstructionBoolToInt *)instruction); + break; case IrInstructionIdIntType: ir_print_int_type(irp, (IrInstructionIntType *)instruction); break; diff --git a/std/fmt/errol/index.zig b/std/fmt/errol/index.zig index a5fb692857..3222913107 100644 --- a/std/fmt/errol/index.zig +++ b/std/fmt/errol/index.zig @@ -329,7 +329,7 @@ fn errolInt(val: f64, buffer: []u8) FloatDecimal { var mi: i32 = mismatch10(l64, h64); var x: u64 = 1; { - var i = i32(lf == hf); + var i: i32 = @boolToInt(lf == hf); while (i < mi) : (i += 1) { x *= 10; } @@ -341,7 +341,7 @@ fn errolInt(val: f64, buffer: []u8) FloatDecimal { var buf_index = u64toa(m64, buffer) - 1; if (mi != 0) { - buffer[buf_index - 1] += u8(buffer[buf_index] >= '5'); + buffer[buf_index - 1] += @boolToInt(buffer[buf_index] >= '5'); } else { buf_index += 1; } diff --git a/std/math/big/int.zig b/std/math/big/int.zig index 21d9347c01..29673538eb 100644 --- a/std/math/big/int.zig +++ b/std/math/big/int.zig @@ -117,7 +117,7 @@ pub const Int = struct { fn bitcount(self: Int) usize { const u_bit_count = (self.len - 1) * Limb.bit_count + (Limb.bit_count - @clz(self.limbs[self.len - 1])); - return usize(!self.positive) + u_bit_count; + return usize(@boolToInt(!self.positive)) + u_bit_count; } pub fn sizeInBase(self: Int, base: usize) usize { @@ -499,13 +499,13 @@ pub const Int = struct { while (i < b.len) : (i += 1) { var c: Limb = 0; - c += Limb(@addWithOverflow(Limb, a[i], b[i], &r[i])); - c += Limb(@addWithOverflow(Limb, r[i], carry, &r[i])); + c += @boolToInt(@addWithOverflow(Limb, a[i], b[i], &r[i])); + c += @boolToInt(@addWithOverflow(Limb, r[i], carry, &r[i])); carry = c; } while (i < a.len) : (i += 1) { - carry = Limb(@addWithOverflow(Limb, a[i], carry, &r[i])); + carry = @boolToInt(@addWithOverflow(Limb, a[i], carry, &r[i])); } r[i] = carry; @@ -577,13 +577,13 @@ pub const Int = struct { while (i < b.len) : (i += 1) { var c: Limb = 0; - c += Limb(@subWithOverflow(Limb, a[i], b[i], &r[i])); - c += Limb(@subWithOverflow(Limb, r[i], borrow, &r[i])); + c += @boolToInt(@subWithOverflow(Limb, a[i], b[i], &r[i])); + c += @boolToInt(@subWithOverflow(Limb, r[i], borrow, &r[i])); borrow = c; } while (i < a.len) : (i += 1) { - borrow = Limb(@subWithOverflow(Limb, a[i], borrow, &r[i])); + borrow = @boolToInt(@subWithOverflow(Limb, a[i], borrow, &r[i])); } debug.assert(borrow == 0); @@ -624,7 +624,7 @@ pub const Int = struct { var r1: Limb = undefined; // r1 = a + *carry - const c1 = Limb(@addWithOverflow(Limb, a, carry.*, &r1)); + const c1: Limb = @boolToInt(@addWithOverflow(Limb, a, carry.*, &r1)); // r2 = b * c // @@ -639,7 +639,7 @@ pub const Int = struct { const c2 = @truncate(Limb, bc >> Limb.bit_count); // r1 = r1 + r2 - const c3 = Limb(@addWithOverflow(Limb, r1, r2, &r1)); + const c3: Limb = @boolToInt(@addWithOverflow(Limb, r1, r2, &r1)); // This never overflows, c1, c3 are either 0 or 1 and if both are 1 then // c2 is at least <= @maxValue(Limb) - 2. diff --git a/std/special/compiler_rt/comparetf2.zig b/std/special/compiler_rt/comparetf2.zig index d63b7a7c92..479ba51962 100644 --- a/std/special/compiler_rt/comparetf2.zig +++ b/std/special/compiler_rt/comparetf2.zig @@ -91,5 +91,5 @@ pub extern fn __unordtf2(a: f128, b: f128) c_int { const aAbs = @bitCast(rep_t, a) & absMask; const bAbs = @bitCast(rep_t, b) & absMask; - return c_int(aAbs > infRep or bAbs > infRep); + return @boolToInt(aAbs > infRep or bAbs > infRep); } diff --git a/test/cases/bool.zig b/test/cases/bool.zig index 07d30454ee..3e4ac9c1cf 100644 --- a/test/cases/bool.zig +++ b/test/cases/bool.zig @@ -8,14 +8,14 @@ test "bool literals" { test "cast bool to int" { const t = true; const f = false; - assert(i32(t) == i32(1)); - assert(i32(f) == i32(0)); + assert(@boolToInt(t) == u32(1)); + assert(@boolToInt(f) == u32(0)); nonConstCastBoolToInt(t, f); } fn nonConstCastBoolToInt(t: bool, f: bool) void { - assert(i32(t) == i32(1)); - assert(i32(f) == i32(0)); + assert(@boolToInt(t) == u32(1)); + assert(@boolToInt(f) == u32(0)); } test "bool cmp" {