commit 57634b7809d07c8a07a015bec55829937d5795e1 (tree)
parent 213c4fc25f214a0d8763bd7976db9ae3eec0f232
Author: Matthew Lugg <mlugg@mlugg.co.uk>
Date: Wed, 29 Apr 2026 12:35:38 +0100
compiler: remove `i0` from the language
Resolves: https://github.com/ziglang/zig/issues/1593
Diffstat:
12 files changed, 81 insertions(+), 79 deletions(-)
diff --git a/lib/std/zig/AstGen.zig b/lib/std/zig/AstGen.zig
@@ -8073,39 +8073,42 @@ fn identifier(
return rvalue(gz, ri, zir_const_ref, ident);
}
- if (ident_name_raw.len >= 2) integer: {
- // Keep in sync with logic in `comptimeExpr2`.
- const first_c = ident_name_raw[0];
- if (first_c == 'i' or first_c == 'u') {
- const signedness: std.builtin.Signedness = switch (first_c == 'i') {
- true => .signed,
- false => .unsigned,
- };
- if (ident_name_raw.len >= 3 and ident_name_raw[1] == '0') {
- return astgen.failNode(
- ident,
- "primitive integer type '{s}' has leading zero",
- .{ident_name_raw},
- );
- }
- const bit_count = parseBitCount(ident_name_raw[1..]) catch |err| switch (err) {
- error.Overflow => return astgen.failNode(
- ident,
- "primitive integer type '{s}' exceeds maximum bit width of 65535",
- .{ident_name_raw},
- ),
- error.InvalidCharacter => break :integer,
- };
- const result = try gz.add(.{
- .tag = .int_type,
- .data = .{ .int_type = .{
- .src_node = gz.nodeIndexToRelative(ident),
- .signedness = signedness,
- .bit_count = bit_count,
- } },
- });
- return rvalue(gz, ri, result, ident);
+ int_type: {
+ if (ident_name_raw.len < 2) break :int_type;
+ const signedness: std.builtin.Signedness = switch (ident_name_raw[0]) {
+ 'u' => .unsigned,
+ 'i' => .signed,
+ else => break :int_type,
+ };
+ // `u0` already handled by `primitive_instrs`
+ if (std.mem.eql(u8, ident_name_raw, "i0")) {
+ return astgen.failNode(ident, "signed integer cannot have bit width 0", .{});
+ }
+ if (ident_name_raw[1] == '0') {
+ assert(ident_name_raw.len >= 3); // `u0` and `i0` handled
+ return astgen.failNode(
+ ident,
+ "primitive integer type '{s}' has leading zero",
+ .{ident_name_raw},
+ );
}
+ const bit_count = parseBitCount(ident_name_raw[1..]) catch |err| switch (err) {
+ error.Overflow => return astgen.failNode(
+ ident,
+ "primitive integer type '{s}' exceeds maximum bit width of 65535",
+ .{ident_name_raw},
+ ),
+ error.InvalidCharacter => break :int_type,
+ };
+ const result = try gz.add(.{
+ .tag = .int_type,
+ .data = .{ .int_type = .{
+ .src_node = gz.nodeIndexToRelative(ident),
+ .signedness = signedness,
+ .bit_count = bit_count,
+ } },
+ });
+ return rvalue(gz, ri, result, ident);
}
}
@@ -10122,32 +10125,37 @@ const primitive_instrs = std.StaticStringMap(Zir.Inst.Ref).initComptime(.{
.{ "c_ushort", .c_ushort_type },
.{ "comptime_float", .comptime_float_type },
.{ "comptime_int", .comptime_int_type },
- .{ "f128", .f128_type },
- .{ "f16", .f16_type },
- .{ "f32", .f32_type },
- .{ "f64", .f64_type },
- .{ "f80", .f80_type },
.{ "false", .bool_false },
- .{ "i16", .i16_type },
- .{ "i32", .i32_type },
- .{ "i64", .i64_type },
- .{ "i128", .i128_type },
- .{ "i8", .i8_type },
- .{ "isize", .isize_type },
.{ "noreturn", .noreturn_type },
.{ "null", .null_value },
.{ "true", .bool_true },
.{ "type", .type_type },
+ .{ "undefined", .undef },
+ .{ "void", .void_type },
+
+ .{ "f16", .f16_type },
+ .{ "f32", .f32_type },
+ .{ "f64", .f64_type },
+ .{ "f80", .f80_type },
+ .{ "f128", .f128_type },
+
+ .{ "u0", .u0_type },
+ .{ "u1", .u1_type },
+ .{ "u8", .u8_type },
+ .{ "i8", .i8_type },
.{ "u16", .u16_type },
+ .{ "i16", .i16_type },
.{ "u29", .u29_type },
.{ "u32", .u32_type },
+ .{ "i32", .i32_type },
.{ "u64", .u64_type },
+ .{ "i64", .i64_type },
+ .{ "u80", .u80_type },
.{ "u128", .u128_type },
- .{ "u1", .u1_type },
- .{ "u8", .u8_type },
- .{ "undefined", .undef },
+ .{ "i128", .i128_type },
+ .{ "u256", .u256_type },
.{ "usize", .usize_type },
- .{ "void", .void_type },
+ .{ "isize", .isize_type },
});
comptime {
diff --git a/lib/std/zig/Zir.zig b/lib/std/zig/Zir.zig
@@ -2197,7 +2197,6 @@ pub const Inst = struct {
/// and `[]Ref`.
pub const Ref = enum(u32) {
u0_type,
- i0_type,
u1_type,
u8_type,
i8_type,
diff --git a/src/Air.zig b/src/Air.zig
@@ -1032,7 +1032,6 @@ pub const Inst = struct {
/// The ref `none` is an exception: it has the tag bit set but refers to the InternPool.
pub const Ref = enum(u32) {
u0_type = @intFromEnum(InternPool.Index.u0_type),
- i0_type = @intFromEnum(InternPool.Index.i0_type),
u1_type = @intFromEnum(InternPool.Index.u1_type),
u8_type = @intFromEnum(InternPool.Index.u8_type),
i8_type = @intFromEnum(InternPool.Index.i8_type),
diff --git a/src/InternPool.zig b/src/InternPool.zig
@@ -3902,7 +3902,6 @@ pub const Index = enum(u32) {
pub const last_value: Index = .empty_tuple;
u0_type,
- i0_type,
u1_type,
u8_type,
i8_type,
@@ -4351,11 +4350,6 @@ pub const static_keys: [static_len]Key = .{
} },
.{ .int_type = .{
- .signedness = .signed,
- .bits = 0,
- } },
-
- .{ .int_type = .{
.signedness = .unsigned,
.bits = 1,
} },
@@ -7207,6 +7201,7 @@ pub fn get(ip: *InternPool, gpa: Allocator, io: Io, tid: Zcu.PerThread.Id, key:
try items.ensureUnusedCapacity(1);
switch (key) {
.int_type => |int_type| {
+ if (int_type.signedness == .signed) assert(int_type.bits > 0);
const t: Tag = switch (int_type.signedness) {
.signed => .type_int_signed,
.unsigned => .type_int_unsigned,
@@ -11447,7 +11442,6 @@ pub fn typeOf(ip: *const InternPool, index: Index) Index {
// mean that the range of type indices would not be dense.
return switch (index) {
.u0_type,
- .i0_type,
.u1_type,
.u8_type,
.i8_type,
@@ -11772,7 +11766,6 @@ pub fn getBackingAddrTag(ip: *const InternPool, val: Index) ?Key.Ptr.BaseAddr.Ta
pub fn zigTypeTag(ip: *const InternPool, index: Index) std.builtin.TypeId {
return switch (index) {
.u0_type,
- .i0_type,
.u1_type,
.u8_type,
.i8_type,
diff --git a/src/Sema.zig b/src/Sema.zig
@@ -19617,6 +19617,9 @@ fn zirReifyInt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
const signedness = try sema.resolveBuiltinEnum(block, signedness_src, extra.lhs, .Signedness, .{ .simple = .int_signedness });
const bits: u16 = @intCast(try sema.resolveInt(block, bits_src, extra.rhs, .u16, .{ .simple = .int_bit_width }));
+ if (bits == 0 and signedness == .signed) {
+ return sema.fail(block, bits_src, "signed integer cannot have bit width 0", .{});
+ }
return .fromType(try sema.pt.intType(signedness, bits));
}
@@ -20708,7 +20711,7 @@ fn zirIntFromFloat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileErro
}
try sema.requireRuntimeBlock(block, src, operand_src);
- if (dest_scalar_ty.intInfo(zcu).bits == 0) {
+ if (dest_scalar_ty.toIntern() == .u0_type) {
if (block.wantSafety()) {
// Emit an explicit safety check. We can do this one like `abs(x) < 1`.
const abs_ref = try block.addTyOp(.abs, operand_ty, operand);
@@ -20824,7 +20827,7 @@ fn zirRoundCast(
try sema.requireRuntimeBlock(block, src, operand_src);
- if (dest_scalar_ty.intInfo(zcu).bits == 0) {
+ if (dest_scalar_ty.toIntern() == .u0_type) {
if (block.wantSafety()) {
const abs_ref = try block.addTyOp(.abs, operand_ty, operand);
const is_vector = dest_ty.zigTypeTag(zcu) == .vector;
diff --git a/src/Sema/LowerZon.zig b/src/Sema/LowerZon.zig
@@ -448,16 +448,12 @@ fn lowerInt(
// If lhs has less than the 32 bits rhs can hold, we need to check the max and
// min values
if (std.math.cast(u5, lhs_info.bits)) |bits| {
- const min_int: i32 = if (lhs_info.signedness == .unsigned or bits == 0) b: {
- break :b 0;
- } else b: {
- break :b -(@as(i32, 1) << (bits - 1));
- };
- const max_int: i32 = if (bits == 0) b: {
- break :b 0;
- } else b: {
- break :b (@as(i32, 1) << (bits - @intFromBool(lhs_info.signedness == .signed))) - 1;
+ const unsigned_bits = bits - @intFromBool(lhs_info.signedness == .signed);
+ const min_int: i32 = switch (lhs_info.signedness) {
+ .unsigned => 0,
+ .signed => -(@as(i32, 1) << unsigned_bits),
};
+ const max_int: i32 = (@as(i32, 1) << unsigned_bits) - 1;
if (rhs < min_int or rhs > max_int) {
return self.fail(
node,
diff --git a/src/Sema/arith.zig b/src/Sema/arith.zig
@@ -20,7 +20,7 @@ pub fn incrementDefinedInt(
const zcu = pt.zcu;
assert(prev_val.typeOf(zcu).toIntern() == ty.toIntern());
assert(!prev_val.isUndef(zcu));
- if (ty.intInfo(zcu).bits == 0) {
+ if (ty.toIntern() == .u0_type) {
return .{ .overflow = true, .val = try comptimeIntAdd(sema, prev_val, .one_comptime_int) };
}
const res = try intAdd(sema, prev_val, try pt.intValue(ty, 1), ty);
@@ -1313,7 +1313,7 @@ fn bitwiseBinScalar(
0b11 => return pt.undefValue(ty),
};
};
- if (ty.toIntern() == .u0_type or ty.toIntern() == .i0_type) return pt.intValue(ty, 0);
+ if (ty.toIntern() == .u0_type) return pt.intValue(ty, 0);
// zig fmt: off
switch (op) {
.@"and" => return intBitwiseAnd(sema, def_lhs, def_rhs, ty),
@@ -2209,9 +2209,13 @@ fn intBitwiseNot(sema: *Sema, val: Value, ty: Type) !Value {
const zcu = pt.zcu;
if (val.isUndef(zcu)) return pt.undefValue(ty);
- if (ty.toIntern() == .bool_type) return .makeBool(!val.toBool());
+ switch (ty.toIntern()) {
+ .bool_type => return .makeBool(!val.toBool()),
+ .u0_type => return val,
+ else => {},
+ }
+
const info = ty.intInfo(zcu);
- if (info.bits == 0) return val;
var val_space: Value.BigIntSpace = undefined;
const val_bigint = val.toBigInt(&val_space, zcu);
@@ -2231,7 +2235,7 @@ fn intValueAa(sema: *Sema, ty: Type) !Value {
const zcu = pt.zcu;
if (ty.toIntern() == .bool_type) return .true;
- if (ty.toIntern() == .u0_type or ty.toIntern() == .i0_type) return pt.intValue(ty, 0);
+ if (ty.toIntern() == .u0_type) return pt.intValue(ty, 0);
const info = ty.intInfo(zcu);
const buf = try sema.arena.alloc(u8, (info.bits + 7) / 8);
diff --git a/src/Type.zig b/src/Type.zig
@@ -2272,7 +2272,7 @@ pub fn minInt(ty: Type, pt: Zcu.PerThread, dest_ty: Type) !Value {
pub fn minIntScalar(ty: Type, pt: Zcu.PerThread, dest_ty: Type) !Value {
const zcu = pt.zcu;
const info = ty.intInfo(zcu);
- if (info.signedness == .unsigned or info.bits == 0) return pt.intValue(dest_ty, 0);
+ if (info.signedness == .unsigned) return pt.intValue(dest_ty, 0);
if (std.math.cast(u6, info.bits - 1)) |shift| {
const n = @as(i64, std.math.minInt(i64)) >> (63 - shift);
diff --git a/src/Value.zig b/src/Value.zig
@@ -518,9 +518,9 @@ pub fn readFromPackedMemory(
},
.int => {
if (buffer.len == 0) return pt.intValue(ty, 0);
+ if (ty.toIntern() == .u0_type) return pt.intValue(ty, 0);
const int_info = ty.intInfo(zcu);
const bits = int_info.bits;
- if (bits == 0) return pt.intValue(ty, 0);
// Fast path for integers <= u64
if (bits <= 64) switch (int_info.signedness) {
diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig
@@ -2948,7 +2948,7 @@ pub const Object = struct {
const target = zcu.getTarget();
const ip = &zcu.intern_pool;
return switch (t.toIntern()) {
- .u0_type, .i0_type => unreachable, // no runtime bits
+ .u0_type => unreachable, // no runtime bits
inline .u1_type,
.u8_type,
.i8_type,
diff --git a/src/codegen/llvm/FuncGen.zig b/src/codegen/llvm/FuncGen.zig
@@ -7410,7 +7410,7 @@ fn toLlvmAtomicRmwBinOp(
fn minIntConst(b: *Builder, min_ty: Type, as_ty: Builder.Type, zcu: *const Zcu) Allocator.Error!Builder.Constant {
const info = min_ty.intInfo(zcu);
- if (info.signedness == .unsigned or info.bits == 0) {
+ if (info.signedness == .unsigned) {
return b.intConst(as_ty, 0);
}
if (std.math.cast(u6, info.bits - 1)) |shift| {
diff --git a/src/codegen/spirv/CodeGen.zig b/src/codegen/spirv/CodeGen.zig
@@ -1327,12 +1327,12 @@ fn resolveType(cg: *CodeGen, ty: Type, repr: Repr) Error!Id {
.indirect => return try cg.resolveType(.u1, .indirect),
},
.int => {
- const int_info = ty.intInfo(zcu);
- if (int_info.bits == 0) {
+ if (ty.toIntern() == .u0_type) {
assert(repr == .indirect);
if (target.os.tag != .opencl) return cg.fail("cannot generate opaque type", .{});
return try cg.module.opaqueType("u0");
}
+ const int_info = ty.intInfo(zcu);
return try cg.module.intType(int_info.signedness, int_info.bits);
},
.@"enum" => return try cg.resolveType(ty.intTagType(zcu), repr),