diff --git a/src/InternPool.zig b/src/InternPool.zig index 27d0fb9445..69037c3899 100644 --- a/src/InternPool.zig +++ b/src/InternPool.zig @@ -55,7 +55,8 @@ pub const Key = union(enum) { lib_name: u32, }, int: Key.Int, - ptr: Key.Ptr, + ptr: Ptr, + opt: Opt, enum_tag: struct { ty: Index, tag: BigIntConst, @@ -151,6 +152,13 @@ pub const Key = union(enum) { }; }; + /// `null` is represented by the `val` field being `none`. + pub const Opt = struct { + ty: Index, + /// This could be `none`, indicating the optional is `null`. + val: Index, + }; + pub fn hash32(key: Key) u32 { return @truncate(u32, key.hash64()); } @@ -175,6 +183,7 @@ pub const Key = union(enum) { .simple_type, .simple_value, .extern_func, + .opt, => |info| std.hash.autoHash(hasher, info), .int => |int| { @@ -257,6 +266,10 @@ pub const Key = union(enum) { const b_info = b.extern_func; return std.meta.eql(a_info, b_info); }, + .opt => |a_info| { + const b_info = b.opt; + return std.meta.eql(a_info, b_info); + }, .ptr => |a_info| { const b_info = b.ptr; @@ -343,6 +356,7 @@ pub const Key = union(enum) { inline .ptr, .int, + .opt, .extern_func, .enum_tag, => |x| return x.ty, @@ -771,7 +785,15 @@ pub const Tag = enum(u8) { simple_internal, /// A pointer to an integer value. /// data is extra index of PtrInt, which contains the type and address. + /// Only pointer types are allowed to have this encoding. Optional types must use + /// `opt_payload` or `opt_null`. ptr_int, + /// An optional value that is non-null. + /// data is Index of the payload value. + opt_payload, + /// An optional value that is null. + /// data is Index of the payload type. + opt_null, /// Type: u8 /// data is integer value int_u8, @@ -1129,6 +1151,14 @@ pub fn indexToKey(ip: InternPool, index: Index) Key { .fields_len = 0, } }, }, + .opt_null => .{ .opt = .{ + .ty = @intToEnum(Index, data), + .val = .none, + } }, + .opt_payload => .{ .opt = .{ + .ty = indexToKey(ip, @intToEnum(Index, data)).typeOf(), + .val = @intToEnum(Index, data), + } }, .ptr_int => { const info = ip.extraData(PtrInt, data); return .{ .ptr = .{ @@ -1321,6 +1351,17 @@ pub fn get(ip: *InternPool, gpa: Allocator, key: Key) Allocator.Error!Index { }, }, + .opt => |opt| { + assert(opt.ty != .none); + ip.items.appendAssumeCapacity(if (opt.val == .none) .{ + .tag = .opt_null, + .data = @enumToInt(opt.ty), + } else .{ + .tag = .opt_payload, + .data = @enumToInt(opt.val), + }); + }, + .int => |int| b: { switch (int.ty) { .none => unreachable, @@ -1342,62 +1383,51 @@ pub fn get(ip: *InternPool, gpa: Allocator, key: Key) Allocator.Error!Index { }, .u16_type => switch (int.storage) { .big_int => |big_int| { - if (big_int.to(u32)) |casted| { - ip.items.appendAssumeCapacity(.{ - .tag = .int_u16, - .data = casted, - }); - break :b; - } else |_| {} + ip.items.appendAssumeCapacity(.{ + .tag = .int_u16, + .data = big_int.to(u16) catch unreachable, + }); + break :b; }, inline .u64, .i64 => |x| { - if (std.math.cast(u32, x)) |casted| { - ip.items.appendAssumeCapacity(.{ - .tag = .int_u16, - .data = casted, - }); - break :b; - } + ip.items.appendAssumeCapacity(.{ + .tag = .int_u16, + .data = @intCast(u16, x), + }); + break :b; }, }, .u32_type => switch (int.storage) { .big_int => |big_int| { - if (big_int.to(u32)) |casted| { - ip.items.appendAssumeCapacity(.{ - .tag = .int_u32, - .data = casted, - }); - break :b; - } else |_| {} + ip.items.appendAssumeCapacity(.{ + .tag = .int_u32, + .data = big_int.to(u32) catch unreachable, + }); + break :b; }, inline .u64, .i64 => |x| { - if (std.math.cast(u32, x)) |casted| { - ip.items.appendAssumeCapacity(.{ - .tag = .int_u32, - .data = casted, - }); - break :b; - } + ip.items.appendAssumeCapacity(.{ + .tag = .int_u32, + .data = @intCast(u32, x), + }); + break :b; }, }, .i32_type => switch (int.storage) { .big_int => |big_int| { - if (big_int.to(i32)) |casted| { - ip.items.appendAssumeCapacity(.{ - .tag = .int_i32, - .data = @bitCast(u32, casted), - }); - break :b; - } else |_| {} + const casted = big_int.to(i32) catch unreachable; + ip.items.appendAssumeCapacity(.{ + .tag = .int_i32, + .data = @bitCast(u32, casted), + }); + break :b; }, inline .u64, .i64 => |x| { - if (std.math.cast(i32, x)) |casted| { - ip.items.appendAssumeCapacity(.{ - .tag = .int_i32, - .data = @bitCast(u32, casted), - }); - break :b; - } + ip.items.appendAssumeCapacity(.{ + .tag = .int_i32, + .data = @bitCast(u32, @intCast(i32, x)), + }); + break :b; }, }, .usize_type => switch (int.storage) { @@ -1798,6 +1828,8 @@ fn dumpFallible(ip: InternPool, arena: Allocator) anyerror!void { .simple_value => 0, .simple_internal => 0, .ptr_int => @sizeOf(PtrInt), + .opt_null => 0, + .opt_payload => 0, .int_u8 => 0, .int_u16 => 0, .int_u32 => 0, diff --git a/src/Module.zig b/src/Module.zig index a5e61fa4f9..dc7e34adc3 100644 --- a/src/Module.zig +++ b/src/Module.zig @@ -6887,12 +6887,32 @@ pub fn singleConstPtrType(mod: *Module, child_type: Type) Allocator.Error!Type { return ptrType(mod, .{ .elem_type = child_type.ip_index, .is_const = true }); } +/// Supports optionals in addition to pointers. pub fn ptrIntValue(mod: *Module, ty: Type, x: u64) Allocator.Error!Value { + if (ty.isPtrLikeOptional(mod)) { + const i = try intern(mod, .{ .opt = .{ + .ty = ty.ip_index, + .val = try intern(mod, .{ .ptr = .{ + .ty = ty.childType(mod).ip_index, + .addr = .{ .int = try intern(mod, .{ .int = .{ + .ty = .usize_type, + .storage = .{ .u64 = x }, + } }) }, + } }), + } }); + return i.toValue(); + } else { + return ptrIntValue_ptronly(mod, ty, x); + } +} + +/// Supports only pointers. See `ptrIntValue` for pointer-like optional support. +pub fn ptrIntValue_ptronly(mod: *Module, ty: Type, x: u64) Allocator.Error!Value { assert(ty.zigTypeTag(mod) == .Pointer); const i = try intern(mod, .{ .ptr = .{ .ty = ty.ip_index, .addr = .{ .int = try intern(mod, .{ .int = .{ - .ty = ty.ip_index, + .ty = .usize_type, .storage = .{ .u64 = x }, } }) }, } }); diff --git a/src/Sema.zig b/src/Sema.zig index 58c87db371..3d67324673 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -31684,6 +31684,7 @@ pub fn resolveTypeRequiresComptime(sema: *Sema, ty: Type) CompileError!bool { .extern_func => unreachable, .int => unreachable, .ptr => unreachable, + .opt => unreachable, .enum_tag => unreachable, }, }; @@ -33207,6 +33208,7 @@ pub fn typeHasOnePossibleValue(sema: *Sema, ty: Type) CompileError!?Value { .extern_func => unreachable, .int => unreachable, .ptr => unreachable, + .opt => unreachable, .enum_tag => unreachable, }, } @@ -33776,6 +33778,7 @@ pub fn typeRequiresComptime(sema: *Sema, ty: Type) CompileError!bool { .extern_func => unreachable, .int => unreachable, .ptr => unreachable, + .opt => unreachable, .enum_tag => unreachable, }, }; diff --git a/src/type.zig b/src/type.zig index 8fc4c20c45..e7dad91422 100644 --- a/src/type.zig +++ b/src/type.zig @@ -146,6 +146,7 @@ pub const Type = struct { .extern_func => unreachable, .int => unreachable, .ptr => unreachable, + .opt => unreachable, .enum_tag => unreachable, .simple_value => unreachable, }, @@ -1574,10 +1575,13 @@ pub const Type = struct { .simple_type => |s| return writer.writeAll(@tagName(s)), .struct_type => @panic("TODO"), .union_type => @panic("TODO"), + + // values, not types .simple_value => unreachable, .extern_func => unreachable, .int => unreachable, .ptr => unreachable, + .opt => unreachable, .enum_tag => unreachable, }, } @@ -1850,6 +1854,7 @@ pub const Type = struct { .extern_func => unreachable, .int => unreachable, .ptr => unreachable, + .opt => unreachable, .enum_tag => unreachable, }, } @@ -1961,6 +1966,7 @@ pub const Type = struct { .extern_func => unreachable, .int => unreachable, .ptr => unreachable, + .opt => unreachable, .enum_tag => unreachable, }, }; @@ -2362,6 +2368,7 @@ pub const Type = struct { .extern_func => unreachable, .int => unreachable, .ptr => unreachable, + .opt => unreachable, .enum_tag => unreachable, }, } @@ -2776,6 +2783,7 @@ pub const Type = struct { .extern_func => unreachable, .int => unreachable, .ptr => unreachable, + .opt => unreachable, .enum_tag => unreachable, }, } @@ -2946,6 +2954,7 @@ pub const Type = struct { .extern_func => unreachable, .int => unreachable, .ptr => unreachable, + .opt => unreachable, .enum_tag => unreachable, }; @@ -3803,6 +3812,7 @@ pub const Type = struct { .extern_func => unreachable, .int => unreachable, .ptr => unreachable, + .opt => unreachable, .enum_tag => unreachable, }, }; @@ -4178,6 +4188,7 @@ pub const Type = struct { .extern_func => unreachable, .int => unreachable, .ptr => unreachable, + .opt => unreachable, .enum_tag => unreachable, }, }; @@ -4339,11 +4350,14 @@ pub const Type = struct { }, .struct_type => @panic("TODO"), .union_type => @panic("TODO"), + + // values, not types .simple_value => unreachable, .extern_func => unreachable, .int => unreachable, .ptr => unreachable, - .enum_tag => unreachable, // it's a value, not a type + .opt => unreachable, + .enum_tag => unreachable, }, }; } diff --git a/src/value.zig b/src/value.zig index e381f4cd17..c95f218dbe 100644 --- a/src/value.zig +++ b/src/value.zig @@ -2336,6 +2336,14 @@ pub const Value = struct { // The value is runtime-known and shouldn't affect the hash. if (val.isRuntimeValue()) return; + if (val.ip_index != .none) { + // The InternPool data structure hashes based on Key to make interned objects + // unique. An Index can be treated simply as u32 value for the + // purpose of Type/Value hashing and equality. + std.hash.autoHash(hasher, val.ip_index); + return; + } + switch (ty.zigTypeTag(mod)) { .Opaque => unreachable, // Cannot hash opaque types .Void,