compiler: remove i0 from the language

Resolves: https://github.com/ziglang/zig/issues/1593
This commit is contained in:
Matthew Lugg
2026-04-29 12:35:38 +01:00
parent 213c4fc25f
commit 57634b7809
12 changed files with 84 additions and 82 deletions
+58 -50
View File
@@ -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 },
.{ "u16", .u16_type },
.{ "u29", .u29_type },
.{ "u32", .u32_type },
.{ "u64", .u64_type },
.{ "u128", .u128_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 },
.{ "undefined", .undef },
.{ "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 },
.{ "i128", .i128_type },
.{ "u256", .u256_type },
.{ "usize", .usize_type },
.{ "void", .void_type },
.{ "isize", .isize_type },
});
comptime {
-1
View File
@@ -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,
-1
View File
@@ -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),
+1 -8
View File
@@ -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,
@@ -4350,11 +4349,6 @@ pub const static_keys: [static_len]Key = .{
.bits = 0,
} },
.{ .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,
+5 -2
View File
@@ -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;
+5 -9
View File
@@ -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,
+9 -5
View File
@@ -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);
+1 -1
View File
@@ -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);
+1 -1
View File
@@ -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) {
+1 -1
View File
@@ -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,
+1 -1
View File
@@ -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| {
+2 -2
View File
@@ -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),