diff --git a/src/Sema.zig b/src/Sema.zig index 5dbf26f37555..d86a332de95a 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -23492,15 +23492,25 @@ fn zirBitCount( const operand_src = block.builtinCallArgSrc(inst_data.src_node, 0); const operand = try sema.resolveInst(inst_data.operand); const operand_ty = sema.typeOf(operand); - _ = try sema.checkIntOrVector(block, operand, operand_src); - const bits = operand_ty.intInfo(zcu).bits; - - if (try sema.typeHasOnePossibleValue(operand_ty)) |val| { - return Air.internedToRef(val.toIntern()); + const operand_ty_tag = operand_ty.zigTypeTag(zcu); + if (air_tag == .clz) { + _ = try sema.checkIntOrVector(block, operand, operand_src); + } else { + _ = try sema.checkIntOrVectorAllowComptime(block, operand_ty, operand_src); } - const result_scalar_ty = try pt.smallestUnsignedInt(bits); - switch (operand_ty.zigTypeTag(zcu)) { + const result_scalar_ty = if (operand_ty_tag == .comptime_int) + operand_ty + else blk: { + const bits = operand_ty.intInfo(zcu).bits; + + if (try sema.typeHasOnePossibleValue(operand_ty)) |val| { + return Air.internedToRef(val.toIntern()); + } + + break :blk try pt.smallestUnsignedInt(bits); + }; + switch (operand_ty_tag) { .vector => { const vec_len = operand_ty.vectorLen(zcu); const result_ty = try pt.vectorType(.{ @@ -23526,11 +23536,39 @@ fn zirBitCount( return block.addTyOp(air_tag, result_ty, operand); } }, - .int => { + .int, .comptime_int => { if (try sema.resolveValueResolveLazy(operand)) |val| { if (val.isUndef(zcu)) return pt.undefRef(result_scalar_ty); + if (operand_ty_tag == .comptime_int) { + var space: InternPool.Key.Int.Storage.BigIntSpace = undefined; + const bigint = val.toBigInt(&space, zcu); + switch (air_tag) { + .ctz => { + if (bigint.eqlZero()) { + return sema.fail( + block, + operand_src, + "cannot count number of least-significant zeroes in integer '0' of type 'comptime_int'", + .{}, + ); + } + }, + .popcount => { + if (!bigint.positive) { + return sema.fail( + block, + operand_src, + "cannot count number of bits set in negative integer '{}' of type 'comptime_int'", + .{val.fmtValueSema(pt, sema)}, + ); + } + }, + else => unreachable, + } + } return pt.intRef(result_scalar_ty, comptimeOp(val, operand_ty, zcu)); } else { + std.debug.assert(operand_ty_tag != .comptime_int); try sema.requireRuntimeBlock(block, src, operand_src); return block.addTyOp(air_tag, result_scalar_ty, operand); } diff --git a/src/Value.zig b/src/Value.zig index cac4134e5898..4f41354371d3 100644 --- a/src/Value.zig +++ b/src/Value.zig @@ -924,13 +924,13 @@ pub fn clz(val: Value, ty: Type, zcu: *Zcu) u64 { pub fn ctz(val: Value, ty: Type, zcu: *Zcu) u64 { var bigint_buf: BigIntSpace = undefined; const bigint = val.toBigInt(&bigint_buf, zcu); - return bigint.ctz(ty.intInfo(zcu).bits); + return bigint.ctz(if (ty.toIntern() == .comptime_int_type) std.math.maxInt(std.math.big.Limb) else ty.intInfo(zcu).bits); } pub fn popCount(val: Value, ty: Type, zcu: *Zcu) u64 { var bigint_buf: BigIntSpace = undefined; const bigint = val.toBigInt(&bigint_buf, zcu); - return @intCast(bigint.popCount(ty.intInfo(zcu).bits)); + return @intCast(bigint.popCount(if (ty.toIntern() == .comptime_int_type) undefined else ty.intInfo(zcu).bits)); } pub fn bitReverse(val: Value, ty: Type, pt: Zcu.PerThread, arena: Allocator) !Value { diff --git a/test/behavior/math.zig b/test/behavior/math.zig index 5cb41aa22876..566535e855f6 100644 --- a/test/behavior/math.zig +++ b/test/behavior/math.zig @@ -168,6 +168,13 @@ fn testCtz() !void { try expect(testOneCtz(u16, 0b00000000) == 16); } +test "@ctz comptime_int" { + try expectEqual(5, @ctz(0b10100000)); + try expectEqual(1, @ctz(0b10001010)); + try expectEqual(0, @ctz(-1)); + try expectEqual(1, @ctz(-2)); +} + fn testOneCtz(comptime T: type, x: T) u32 { return @ctz(x); } diff --git a/test/behavior/popcount.zig b/test/behavior/popcount.zig index 1bf5f9651557..b255f17ba815 100644 --- a/test/behavior/popcount.zig +++ b/test/behavior/popcount.zig @@ -75,6 +75,15 @@ fn testPopCountIntegers() !void { } } +test "@popCount comptime_int" { + try expectEqual(0, @popCount(0)); + try expectEqual(32, @popCount(0xffffffff)); + try expectEqual(5, @popCount(0x1f)); + try expectEqual(4, @popCount(0xaa)); + try expectEqual(16, @popCount(0xaaaaaaaa)); + try expectEqual(24, @popCount(0b11111111000110001100010000100001000011000011100101010001)); +} + test "@popCount vectors" { if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO diff --git a/test/cases/compile_errors/ctz-comptime_int-zero.zig b/test/cases/compile_errors/ctz-comptime_int-zero.zig new file mode 100644 index 000000000000..c28d69e2735a --- /dev/null +++ b/test/cases/compile_errors/ctz-comptime_int-zero.zig @@ -0,0 +1,9 @@ +export fn entry() void { + _ = @ctz(0); +} + +// error +// backend=stage2 +// target=native +// +// :2:14: error: cannot count number of least-significant zeroes in integer '0' of type 'comptime_int' diff --git a/test/cases/compile_errors/popCount-negative-comptime_int.zig b/test/cases/compile_errors/popCount-negative-comptime_int.zig new file mode 100644 index 000000000000..44e1e6f9d527 --- /dev/null +++ b/test/cases/compile_errors/popCount-negative-comptime_int.zig @@ -0,0 +1,9 @@ +export fn entry() void { + _ = @popCount(-1); +} + +// error +// backend=stage2 +// target=native +// +// :2:19: error: cannot count number of bits set in negative integer '-1' of type 'comptime_int'