Commit 68b95a39b1
Changed files (4)
src/InternPool.zig
@@ -55,6 +55,7 @@ pub const Key = union(enum) {
lib_name: u32,
},
int: Key.Int,
+ ptr: Key.Ptr,
enum_tag: struct {
ty: Index,
tag: BigIntConst,
@@ -140,6 +141,16 @@ pub const Key = union(enum) {
};
};
+ pub const Ptr = struct {
+ ty: Index,
+ addr: Addr,
+
+ pub const Addr = union(enum) {
+ decl: DeclIndex,
+ int: Index,
+ };
+ };
+
pub fn hash32(key: Key) u32 {
return @truncate(u32, key.hash64());
}
@@ -176,6 +187,16 @@ pub const Key = union(enum) {
for (big_int.limbs) |limb| std.hash.autoHash(hasher, limb);
},
+ .ptr => |ptr| {
+ std.hash.autoHash(hasher, ptr.ty);
+ // Int-to-ptr pointers are hashed separately than decl-referencing pointers.
+ // This is sound due to pointer province rules.
+ switch (ptr.addr) {
+ .int => |int| std.hash.autoHash(hasher, int),
+ .decl => @panic("TODO"),
+ }
+ },
+
.enum_tag => |enum_tag| {
std.hash.autoHash(hasher, enum_tag.ty);
std.hash.autoHash(hasher, enum_tag.tag.positive);
@@ -237,8 +258,30 @@ pub const Key = union(enum) {
return std.meta.eql(a_info, b_info);
},
+ .ptr => |a_info| {
+ const b_info = b.ptr;
+
+ if (a_info.ty != b_info.ty)
+ return false;
+
+ return switch (a_info.addr) {
+ .int => |a_int| switch (b_info.addr) {
+ .int => |b_int| a_int == b_int,
+ .decl => false,
+ },
+ .decl => |a_decl| switch (b_info.addr) {
+ .int => false,
+ .decl => |b_decl| a_decl == b_decl,
+ },
+ };
+ },
+
.int => |a_info| {
const b_info = b.int;
+
+ if (a_info.ty != b_info.ty)
+ return false;
+
return switch (a_info.storage) {
.u64 => |aa| switch (b_info.storage) {
.u64 => |bb| aa == bb,
@@ -298,9 +341,11 @@ pub const Key = union(enum) {
.union_type,
=> return .type_type,
- .int => |x| return x.ty,
- .extern_func => |x| return x.ty,
- .enum_tag => |x| return x.ty,
+ inline .ptr,
+ .int,
+ .extern_func,
+ .enum_tag,
+ => |x| return x.ty,
.simple_value => |s| switch (s) {
.undefined => return .undefined_type,
@@ -724,6 +769,9 @@ pub const Tag = enum(u8) {
/// only an enum tag, but will be presented via the API with a different Key.
/// data is SimpleInternal enum value.
simple_internal,
+ /// A pointer to an integer value.
+ /// data is extra index of PtrInt, which contains the type and address.
+ ptr_int,
/// Type: u8
/// data is integer value
int_u8,
@@ -897,16 +945,13 @@ pub const Array = struct {
child: Index,
sentinel: Index,
- pub const Length = packed struct(u64) {
- len0: u32,
- len1: u32,
- };
+ pub const Length = PackedU64;
pub fn getLength(a: Array) u64 {
- return @bitCast(u64, Length{
- .len0 = a.len0,
- .len1 = a.len1,
- });
+ return (PackedU64{
+ .a = a.len0,
+ .b = a.len1,
+ }).get();
}
};
@@ -929,6 +974,24 @@ pub const EnumSimple = struct {
fields_len: u32,
};
+pub const PackedU64 = packed struct(u64) {
+ a: u32,
+ b: u32,
+
+ pub fn get(x: PackedU64) u64 {
+ return @bitCast(u64, x);
+ }
+
+ pub fn init(x: u64) PackedU64 {
+ return @bitCast(PackedU64, x);
+ }
+};
+
+pub const PtrInt = struct {
+ ty: Index,
+ addr: Index,
+};
+
/// Trailing: Limb for every limbs_len
pub const Int = struct {
ty: Index,
@@ -1066,6 +1129,13 @@ pub fn indexToKey(ip: InternPool, index: Index) Key {
.fields_len = 0,
} },
},
+ .ptr_int => {
+ const info = ip.extraData(PtrInt, data);
+ return .{ .ptr = .{
+ .ty = info.ty,
+ .addr = .{ .int = info.addr },
+ } };
+ },
.int_u8 => .{ .int = .{
.ty = .u8_type,
.storage = .{ .u64 = data },
@@ -1188,12 +1258,12 @@ pub fn get(ip: *InternPool, gpa: Allocator, key: Key) Allocator.Error!Index {
}
}
- const length = @bitCast(Array.Length, array_type.len);
+ const length = Array.Length.init(array_type.len);
ip.items.appendAssumeCapacity(.{
.tag = .type_array_big,
.data = try ip.addExtra(gpa, Array{
- .len0 = length.len0,
- .len1 = length.len1,
+ .len0 = length.a,
+ .len1 = length.b,
.child = array_type.child,
.sentinel = array_type.sentinel,
}),
@@ -1237,6 +1307,20 @@ pub fn get(ip: *InternPool, gpa: Allocator, key: Key) Allocator.Error!Index {
},
.extern_func => @panic("TODO"),
+ .ptr => |ptr| switch (ptr.addr) {
+ .decl => @panic("TODO"),
+ .int => |int| {
+ assert(ptr.ty != .none);
+ ip.items.appendAssumeCapacity(.{
+ .tag = .ptr_int,
+ .data = try ip.addExtra(gpa, PtrInt{
+ .ty = ptr.ty,
+ .addr = int,
+ }),
+ });
+ },
+ },
+
.int => |int| b: {
switch (int.ty) {
.none => unreachable,
@@ -1620,38 +1704,43 @@ pub fn slicePtrType(ip: InternPool, i: Index) Index {
}
}
-/// Given an existing integer value, returns the same numerical value but with
-/// the supplied type.
-pub fn getCoercedInt(ip: *InternPool, gpa: Allocator, val: Index, new_ty: Index) Allocator.Error!Index {
- const key = ip.indexToKey(val);
- // The key cannot be passed directly to `get`, otherwise in the case of
- // big_int storage, the limbs would be invalidated before they are read.
- // Here we pre-reserve the limbs to ensure that the logic in `addInt` will
- // not use an invalidated limbs pointer.
- switch (key.int.storage) {
- .u64 => |x| return ip.get(gpa, .{ .int = .{
- .ty = new_ty,
- .storage = .{ .u64 = x },
- } }),
- .i64 => |x| return ip.get(gpa, .{ .int = .{
- .ty = new_ty,
- .storage = .{ .i64 = x },
- } }),
-
- .big_int => |big_int| {
- const positive = big_int.positive;
- const limbs = ip.limbsSliceToIndex(big_int.limbs);
- // This line invalidates the limbs slice, but the indexes computed in the
- // previous line are still correct.
- try reserveLimbs(ip, gpa, @typeInfo(Int).Struct.fields.len + big_int.limbs.len);
- return ip.get(gpa, .{ .int = .{
- .ty = new_ty,
- .storage = .{ .big_int = .{
- .limbs = ip.limbsIndexToSlice(limbs),
- .positive = positive,
- } },
- } });
+/// Given an existing value, returns the same value but with the supplied type.
+/// Only some combinations are allowed:
+/// * int to int
+pub fn getCoerced(ip: *InternPool, gpa: Allocator, val: Index, new_ty: Index) Allocator.Error!Index {
+ switch (ip.indexToKey(val)) {
+ .int => |int| {
+ // The key cannot be passed directly to `get`, otherwise in the case of
+ // big_int storage, the limbs would be invalidated before they are read.
+ // Here we pre-reserve the limbs to ensure that the logic in `addInt` will
+ // not use an invalidated limbs pointer.
+ switch (int.storage) {
+ .u64 => |x| return ip.get(gpa, .{ .int = .{
+ .ty = new_ty,
+ .storage = .{ .u64 = x },
+ } }),
+ .i64 => |x| return ip.get(gpa, .{ .int = .{
+ .ty = new_ty,
+ .storage = .{ .i64 = x },
+ } }),
+
+ .big_int => |big_int| {
+ const positive = big_int.positive;
+ const limbs = ip.limbsSliceToIndex(big_int.limbs);
+ // This line invalidates the limbs slice, but the indexes computed in the
+ // previous line are still correct.
+ try reserveLimbs(ip, gpa, @typeInfo(Int).Struct.fields.len + big_int.limbs.len);
+ return ip.get(gpa, .{ .int = .{
+ .ty = new_ty,
+ .storage = .{ .big_int = .{
+ .limbs = ip.limbsIndexToSlice(limbs),
+ .positive = positive,
+ } },
+ } });
+ },
+ }
},
+ else => unreachable,
}
}
@@ -1708,6 +1797,7 @@ fn dumpFallible(ip: InternPool, arena: Allocator) anyerror!void {
.simple_type => 0,
.simple_value => 0,
.simple_internal => 0,
+ .ptr_int => @sizeOf(PtrInt),
.int_u8 => 0,
.int_u16 => 0,
.int_u32 => 0,
src/Module.zig
@@ -6887,17 +6887,23 @@ pub fn singleConstPtrType(mod: *Module, child_type: Type) Allocator.Error!Type {
return ptrType(mod, .{ .elem_type = child_type.ip_index, .is_const = true });
}
+pub fn ptrIntValue(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,
+ .storage = .{ .u64 = x },
+ } }) },
+ } });
+ return i.toValue();
+}
+
pub fn intValue(mod: *Module, ty: Type, x: anytype) Allocator.Error!Value {
if (std.debug.runtime_safety) {
- // TODO: decide if this also works for ABI int types like enums
const tag = ty.zigTypeTag(mod);
assert(tag == .Int or tag == .ComptimeInt);
}
- if (@TypeOf(x) == comptime_int) {
- if (comptime std.math.cast(u64, x)) |casted| return intValue_u64(mod, ty, casted);
- if (comptime std.math.cast(i64, x)) |casted| return intValue_i64(mod, ty, casted);
- @compileError("Out-of-range comptime_int passed to Module.intValue");
- }
if (std.math.cast(u64, x)) |casted| return intValue_u64(mod, ty, casted);
if (std.math.cast(i64, x)) |casted| return intValue_i64(mod, ty, casted);
var limbs_buffer: [4]usize = undefined;
src/Sema.zig
@@ -15096,7 +15096,7 @@ fn analyzePtrArithmetic(
.ptr_sub => addr - elem_size * offset_int,
else => unreachable,
};
- const new_ptr_val = try mod.intValue(new_ptr_ty, new_addr);
+ const new_ptr_val = try mod.ptrIntValue(new_ptr_ty, new_addr);
return sema.addConstant(new_ptr_ty, new_ptr_val);
}
if (air_tag == .ptr_sub) {
@@ -19931,7 +19931,7 @@ fn zirIntToPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
if (addr != 0 and ptr_align != 0 and addr % ptr_align != 0)
return sema.fail(block, operand_src, "pointer type '{}' requires aligned address", .{ptr_ty.fmt(sema.mod)});
- return sema.addConstant(ptr_ty, try mod.intValue(ptr_ty, addr));
+ return sema.addConstant(ptr_ty, try mod.ptrIntValue(ptr_ty, addr));
}
try sema.requireRuntimeBlock(block, src, operand_src);
@@ -25640,8 +25640,13 @@ fn coerceExtra(
var in_memory_result = try sema.coerceInMemoryAllowed(block, dest_ty, inst_ty, false, target, dest_ty_src, inst_src);
if (in_memory_result == .ok) {
if (maybe_inst_val) |val| {
- // Keep the comptime Value representation; take the new type.
- return sema.addConstant(dest_ty, val);
+ if (val.ip_index == .none or val.ip_index == .null_value) {
+ // Keep the comptime Value representation; take the new type.
+ return sema.addConstant(dest_ty, val);
+ } else {
+ const new_val = try mod.intern_pool.getCoerced(mod.gpa, val.ip_index, dest_ty.ip_index);
+ return sema.addConstant(dest_ty, new_val.toValue());
+ }
}
try sema.requireRuntimeBlock(block, inst_src, null);
return block.addBitCast(dest_ty, inst);
@@ -26014,7 +26019,7 @@ fn coerceExtra(
if (!opts.report_err) return error.NotCoercible;
return sema.fail(block, inst_src, "type '{}' cannot represent integer value '{}'", .{ dest_ty.fmt(sema.mod), val.fmtValue(inst_ty, sema.mod) });
}
- const new_val = try mod.intern_pool.getCoercedInt(sema.gpa, val.ip_index, dest_ty.ip_index);
+ const new_val = try mod.intern_pool.getCoerced(sema.gpa, val.ip_index, dest_ty.ip_index);
return try sema.addConstant(dest_ty, new_val.toValue());
}
if (dest_ty.zigTypeTag(mod) == .ComptimeInt) {
@@ -31673,10 +31678,13 @@ pub fn resolveTypeRequiresComptime(sema: *Sema, ty: Type) CompileError!bool {
},
.struct_type => @panic("TODO"),
.union_type => @panic("TODO"),
+
+ // values, not types
.simple_value => unreachable,
.extern_func => unreachable,
.int => unreachable,
- .enum_tag => unreachable, // it's a value, not a type
+ .ptr => unreachable,
+ .enum_tag => unreachable,
},
};
}
@@ -33193,10 +33201,13 @@ pub fn typeHasOnePossibleValue(sema: *Sema, ty: Type) CompileError!?Value {
},
.struct_type => @panic("TODO"),
.union_type => @panic("TODO"),
+
+ // values, not types
.simple_value => unreachable,
.extern_func => unreachable,
.int => unreachable,
- .enum_tag => unreachable, // it's a value, not a type
+ .ptr => unreachable,
+ .enum_tag => unreachable,
},
}
}
@@ -33253,7 +33264,14 @@ pub fn addConstant(sema: *Sema, ty: Type, val: Value) SemaError!Air.Inst.Ref {
const result = Air.indexToRef(@intCast(u32, sema.air_instructions.len - 1));
// This assertion can be removed when the `ty` parameter is removed from
// this function thanks to the InternPool transition being complete.
- assert(Type.eql(sema.typeOf(result), ty, sema.mod));
+ if (std.debug.runtime_safety) {
+ const val_ty = sema.typeOf(result);
+ if (!Type.eql(val_ty, ty, sema.mod)) {
+ std.debug.panic("addConstant type mismatch: '{}' vs '{}'\n", .{
+ ty.fmt(sema.mod), val_ty.fmt(sema.mod),
+ });
+ }
+ }
return result;
}
const ty_inst = try sema.addType(ty);
@@ -33752,10 +33770,13 @@ pub fn typeRequiresComptime(sema: *Sema, ty: Type) CompileError!bool {
},
.struct_type => @panic("TODO"),
.union_type => @panic("TODO"),
+
+ // values, not types
.simple_value => unreachable,
.extern_func => unreachable,
.int => unreachable,
- .enum_tag => unreachable, // it's a value, not a type
+ .ptr => unreachable,
+ .enum_tag => unreachable,
},
};
}
src/type.zig
@@ -142,11 +142,12 @@ pub const Type = struct {
.var_args_param => unreachable,
},
- .extern_func,
- .int,
- .enum_tag,
- .simple_value,
- => unreachable, // it's a value, not a type
+ // values, not types
+ .extern_func => unreachable,
+ .int => unreachable,
+ .ptr => unreachable,
+ .enum_tag => unreachable,
+ .simple_value => unreachable,
},
}
}
@@ -1576,6 +1577,7 @@ pub const Type = struct {
.simple_value => unreachable,
.extern_func => unreachable,
.int => unreachable,
+ .ptr => unreachable,
.enum_tag => unreachable,
},
}
@@ -1842,10 +1844,13 @@ pub const Type = struct {
},
.struct_type => @panic("TODO"),
.union_type => @panic("TODO"),
+
+ // values, not types
.simple_value => unreachable,
.extern_func => unreachable,
.int => unreachable,
- .enum_tag => unreachable, // it's a value, not a type
+ .ptr => unreachable,
+ .enum_tag => unreachable,
},
}
}
@@ -1950,10 +1955,13 @@ pub const Type = struct {
},
.struct_type => @panic("TODO"),
.union_type => @panic("TODO"),
+
+ // values, not types
.simple_value => unreachable,
.extern_func => unreachable,
.int => unreachable,
- .enum_tag => unreachable, // it's a value, not a type
+ .ptr => unreachable,
+ .enum_tag => unreachable,
},
};
}
@@ -2348,10 +2356,13 @@ pub const Type = struct {
},
.struct_type => @panic("TODO"),
.union_type => @panic("TODO"),
+
+ // values, not types
.simple_value => unreachable,
.extern_func => unreachable,
.int => unreachable,
- .enum_tag => unreachable, // it's a value, not a type
+ .ptr => unreachable,
+ .enum_tag => unreachable,
},
}
}
@@ -2759,10 +2770,13 @@ pub const Type = struct {
},
.struct_type => @panic("TODO"),
.union_type => @panic("TODO"),
+
+ // values, not types
.simple_value => unreachable,
.extern_func => unreachable,
.int => unreachable,
- .enum_tag => unreachable, // it's a value, not a type
+ .ptr => unreachable,
+ .enum_tag => unreachable,
},
}
}
@@ -2926,10 +2940,13 @@ pub const Type = struct {
},
.struct_type => @panic("TODO"),
.union_type => @panic("TODO"),
+
+ // values, not types
.simple_value => unreachable,
.extern_func => unreachable,
.int => unreachable,
- .enum_tag => unreachable, // it's a value, not a type
+ .ptr => unreachable,
+ .enum_tag => unreachable,
};
const strat: AbiAlignmentAdvancedStrat = if (opt_sema) |sema| .{ .sema = sema } else .eager;
@@ -3780,9 +3797,12 @@ pub const Type = struct {
.simple_type => unreachable, // handled via Index enum tag above
.struct_type => @panic("TODO"),
.union_type => unreachable,
+
+ // values, not types
.simple_value => unreachable,
.extern_func => unreachable,
.int => unreachable,
+ .ptr => unreachable,
.enum_tag => unreachable,
},
};
@@ -4152,10 +4172,13 @@ pub const Type = struct {
},
.struct_type => @panic("TODO"),
.union_type => @panic("TODO"),
+
+ // values, not types
.simple_value => unreachable,
.extern_func => unreachable,
.int => unreachable,
- .enum_tag => unreachable, // it's a value, not a type
+ .ptr => unreachable,
+ .enum_tag => unreachable,
},
};
}
@@ -4319,6 +4342,7 @@ pub const Type = struct {
.simple_value => unreachable,
.extern_func => unreachable,
.int => unreachable,
+ .ptr => unreachable,
.enum_tag => unreachable, // it's a value, not a type
},
};