mirror of
https://codeberg.org/ziglang/zig.git
synced 2026-03-08 02:44:43 +01:00
Now that https://github.com/ziglang/zig/issues/24657 has been implemented, the compiler can simplify its internal representation of comptime-known `packed struct` and `packed union` values. Instead of storing them field-wise, we can simply store their backing integer value. This simplifies many operations and improves efficiency in some cases.
3410 lines
123 KiB
Zig
3410 lines
123 KiB
Zig
//! Both types and values are canonically represented by a single 32-bit integer
|
|
//! which is an index into an `InternPool` data structure.
|
|
//! This struct abstracts around this storage by providing methods only
|
|
//! applicable to types rather than values in general.
|
|
|
|
const std = @import("std");
|
|
const builtin = @import("builtin");
|
|
const Allocator = std.mem.Allocator;
|
|
const Value = @import("Value.zig");
|
|
const assert = std.debug.assert;
|
|
const Target = std.Target;
|
|
const Zcu = @import("Zcu.zig");
|
|
const log = std.log.scoped(.Type);
|
|
const target_util = @import("target.zig");
|
|
const InternPool = @import("InternPool.zig");
|
|
const Alignment = InternPool.Alignment;
|
|
const Zir = std.zig.Zir;
|
|
const Type = @This();
|
|
|
|
ip_index: InternPool.Index,
|
|
|
|
pub fn zigTypeTag(ty: Type, zcu: *const Zcu) std.builtin.TypeId {
|
|
return zcu.intern_pool.zigTypeTag(ty.toIntern());
|
|
}
|
|
|
|
/// Asserts the type is resolved.
|
|
pub fn isSelfComparable(ty: Type, zcu: *const Zcu, is_equality_cmp: bool) bool {
|
|
return switch (ty.zigTypeTag(zcu)) {
|
|
.int,
|
|
.float,
|
|
.comptime_float,
|
|
.comptime_int,
|
|
=> true,
|
|
|
|
.vector => ty.childType(zcu).isSelfComparable(zcu, is_equality_cmp),
|
|
|
|
.bool,
|
|
.type,
|
|
.void,
|
|
.error_set,
|
|
.@"fn",
|
|
.@"opaque",
|
|
.@"anyframe",
|
|
.@"enum",
|
|
.enum_literal,
|
|
=> is_equality_cmp,
|
|
|
|
.noreturn,
|
|
.array,
|
|
.undefined,
|
|
.null,
|
|
.error_union,
|
|
.@"union",
|
|
.frame,
|
|
=> false,
|
|
|
|
.@"struct" => is_equality_cmp and ty.containerLayout(zcu) == .@"packed",
|
|
.pointer => !ty.isSlice(zcu) and (is_equality_cmp or ty.isCPtr(zcu)),
|
|
.optional => {
|
|
if (!is_equality_cmp) return false;
|
|
return ty.optionalChild(zcu).isSelfComparable(zcu, is_equality_cmp);
|
|
},
|
|
};
|
|
}
|
|
|
|
/// If it is a function pointer, returns the function type. Otherwise returns null.
|
|
pub fn castPtrToFn(ty: Type, zcu: *const Zcu) ?Type {
|
|
if (ty.zigTypeTag(zcu) != .pointer) return null;
|
|
const elem_ty = ty.childType(zcu);
|
|
if (elem_ty.zigTypeTag(zcu) != .@"fn") return null;
|
|
return elem_ty;
|
|
}
|
|
|
|
/// Asserts the type is a pointer.
|
|
pub fn ptrIsMutable(ty: Type, zcu: *const Zcu) bool {
|
|
return !zcu.intern_pool.indexToKey(ty.toIntern()).ptr_type.flags.is_const;
|
|
}
|
|
|
|
pub const ArrayInfo = struct {
|
|
elem_type: Type,
|
|
sentinel: ?Value = null,
|
|
len: u64,
|
|
};
|
|
|
|
pub fn arrayInfo(self: Type, zcu: *const Zcu) ArrayInfo {
|
|
return .{
|
|
.len = self.arrayLen(zcu),
|
|
.sentinel = self.sentinel(zcu),
|
|
.elem_type = self.childType(zcu),
|
|
};
|
|
}
|
|
|
|
pub fn ptrInfo(ty: Type, zcu: *const Zcu) InternPool.Key.PtrType {
|
|
return switch (zcu.intern_pool.indexToKey(ty.toIntern())) {
|
|
.ptr_type => |p| p,
|
|
.opt_type => |child| switch (zcu.intern_pool.indexToKey(child)) {
|
|
.ptr_type => |p| p,
|
|
else => unreachable,
|
|
},
|
|
else => unreachable,
|
|
};
|
|
}
|
|
|
|
pub fn eql(a: Type, b: Type, zcu: *const Zcu) bool {
|
|
_ = zcu; // TODO: remove this parameter
|
|
// 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.
|
|
return a.toIntern() == b.toIntern();
|
|
}
|
|
|
|
pub const format = @compileError("do not format types directly; use either ty.fmtDebug() or ty.fmt()");
|
|
|
|
pub const Formatter = std.fmt.Alt(Format, Format.default);
|
|
|
|
pub fn fmt(ty: Type, pt: Zcu.PerThread) Formatter {
|
|
return .{ .data = .{
|
|
.ty = ty,
|
|
.pt = pt,
|
|
} };
|
|
}
|
|
|
|
const Format = struct {
|
|
ty: Type,
|
|
pt: Zcu.PerThread,
|
|
|
|
fn default(f: Format, writer: *std.Io.Writer) std.Io.Writer.Error!void {
|
|
return print(f.ty, writer, f.pt, null);
|
|
}
|
|
};
|
|
|
|
pub fn fmtDebug(ty: Type) std.fmt.Alt(Type, dump) {
|
|
return .{ .data = ty };
|
|
}
|
|
|
|
/// This is a debug function. In order to print types in a meaningful way
|
|
/// we also need access to the module.
|
|
pub fn dump(start_type: Type, writer: *std.Io.Writer) std.Io.Writer.Error!void {
|
|
return writer.print("{any}", .{start_type.ip_index});
|
|
}
|
|
|
|
/// Prints a name suitable for `@typeName`.
|
|
/// TODO: take an `opt_sema` to pass to `fmtValue` when printing sentinels.
|
|
pub fn print(ty: Type, writer: *std.Io.Writer, pt: Zcu.PerThread, ctx: ?*Comparison) std.Io.Writer.Error!void {
|
|
if (ctx) |c| {
|
|
const should_dedupe = shouldDedupeType(ty, c, pt) catch |err| switch (err) {
|
|
error.OutOfMemory => return error.WriteFailed,
|
|
};
|
|
switch (should_dedupe) {
|
|
.dont_dedupe => {},
|
|
.dedupe => |placeholder| return placeholder.format(writer),
|
|
}
|
|
}
|
|
|
|
const zcu = pt.zcu;
|
|
const ip = &zcu.intern_pool;
|
|
switch (ip.indexToKey(ty.toIntern())) {
|
|
.undef => return writer.writeAll("@as(type, undefined)"),
|
|
.int_type => |int_type| {
|
|
const sign_char: u8 = switch (int_type.signedness) {
|
|
.signed => 'i',
|
|
.unsigned => 'u',
|
|
};
|
|
return writer.print("{c}{d}", .{ sign_char, int_type.bits });
|
|
},
|
|
.ptr_type => {
|
|
const info = ty.ptrInfo(zcu);
|
|
|
|
if (info.sentinel != .none) switch (info.flags.size) {
|
|
.one, .c => unreachable,
|
|
.many => try writer.print("[*:{f}]", .{Value.fromInterned(info.sentinel).fmtValue(pt)}),
|
|
.slice => try writer.print("[:{f}]", .{Value.fromInterned(info.sentinel).fmtValue(pt)}),
|
|
} else switch (info.flags.size) {
|
|
.one => try writer.writeAll("*"),
|
|
.many => try writer.writeAll("[*]"),
|
|
.c => try writer.writeAll("[*c]"),
|
|
.slice => try writer.writeAll("[]"),
|
|
}
|
|
if (info.flags.is_allowzero and info.flags.size != .c) try writer.writeAll("allowzero ");
|
|
if (info.flags.alignment != .none or
|
|
info.packed_offset.host_size != 0 or
|
|
info.flags.vector_index != .none)
|
|
{
|
|
const alignment = if (info.flags.alignment != .none)
|
|
info.flags.alignment
|
|
else
|
|
Type.fromInterned(info.child).abiAlignment(pt.zcu);
|
|
try writer.print("align({d}", .{alignment.toByteUnits() orelse 0});
|
|
|
|
if (info.packed_offset.bit_offset != 0 or info.packed_offset.host_size != 0) {
|
|
try writer.print(":{d}:{d}", .{
|
|
info.packed_offset.bit_offset, info.packed_offset.host_size,
|
|
});
|
|
}
|
|
if (info.flags.vector_index != .none) {
|
|
try writer.print(":{d}", .{@intFromEnum(info.flags.vector_index)});
|
|
}
|
|
try writer.writeAll(") ");
|
|
}
|
|
if (info.flags.address_space != .generic) {
|
|
try writer.print("addrspace(.{s}) ", .{@tagName(info.flags.address_space)});
|
|
}
|
|
if (info.flags.is_const) try writer.writeAll("const ");
|
|
if (info.flags.is_volatile) try writer.writeAll("volatile ");
|
|
|
|
try print(Type.fromInterned(info.child), writer, pt, ctx);
|
|
return;
|
|
},
|
|
.array_type => |array_type| {
|
|
if (array_type.sentinel == .none) {
|
|
try writer.print("[{d}]", .{array_type.len});
|
|
try print(Type.fromInterned(array_type.child), writer, pt, ctx);
|
|
} else {
|
|
try writer.print("[{d}:{f}]", .{
|
|
array_type.len,
|
|
Value.fromInterned(array_type.sentinel).fmtValue(pt),
|
|
});
|
|
try print(Type.fromInterned(array_type.child), writer, pt, ctx);
|
|
}
|
|
return;
|
|
},
|
|
.vector_type => |vector_type| {
|
|
try writer.print("@Vector({d}, ", .{vector_type.len});
|
|
try print(Type.fromInterned(vector_type.child), writer, pt, ctx);
|
|
try writer.writeAll(")");
|
|
return;
|
|
},
|
|
.opt_type => |child| {
|
|
try writer.writeByte('?');
|
|
return print(Type.fromInterned(child), writer, pt, ctx);
|
|
},
|
|
.error_union_type => |error_union_type| {
|
|
try print(Type.fromInterned(error_union_type.error_set_type), writer, pt, ctx);
|
|
try writer.writeByte('!');
|
|
if (error_union_type.payload_type == .generic_poison_type) {
|
|
try writer.writeAll("anytype");
|
|
} else {
|
|
try print(Type.fromInterned(error_union_type.payload_type), writer, pt, ctx);
|
|
}
|
|
return;
|
|
},
|
|
.inferred_error_set_type => |func_index| {
|
|
const func_nav = ip.getNav(zcu.funcInfo(func_index).owner_nav);
|
|
try writer.print("@typeInfo(@typeInfo(@TypeOf({f})).@\"fn\".return_type.?).error_union.error_set", .{
|
|
func_nav.fqn.fmt(ip),
|
|
});
|
|
},
|
|
.error_set_type => |error_set_type| {
|
|
const NullTerminatedString = InternPool.NullTerminatedString;
|
|
const sorted_names = zcu.gpa.dupe(NullTerminatedString, error_set_type.names.get(ip)) catch {
|
|
zcu.comp.setAllocFailure();
|
|
return writer.writeAll("error{...}");
|
|
};
|
|
defer zcu.gpa.free(sorted_names);
|
|
|
|
std.mem.sortUnstable(NullTerminatedString, sorted_names, ip, struct {
|
|
fn lessThan(ip_: *InternPool, lhs: NullTerminatedString, rhs: NullTerminatedString) bool {
|
|
const lhs_slice = lhs.toSlice(ip_);
|
|
const rhs_slice = rhs.toSlice(ip_);
|
|
return std.mem.lessThan(u8, lhs_slice, rhs_slice);
|
|
}
|
|
}.lessThan);
|
|
|
|
try writer.writeAll("error{");
|
|
for (sorted_names, 0..) |name, i| {
|
|
if (i != 0) try writer.writeByte(',');
|
|
try writer.print("{f}", .{name.fmt(ip)});
|
|
}
|
|
try writer.writeAll("}");
|
|
},
|
|
.simple_type => |s| switch (s) {
|
|
.f16,
|
|
.f32,
|
|
.f64,
|
|
.f80,
|
|
.f128,
|
|
.usize,
|
|
.isize,
|
|
.c_char,
|
|
.c_short,
|
|
.c_ushort,
|
|
.c_int,
|
|
.c_uint,
|
|
.c_long,
|
|
.c_ulong,
|
|
.c_longlong,
|
|
.c_ulonglong,
|
|
.c_longdouble,
|
|
.anyopaque,
|
|
.bool,
|
|
.void,
|
|
.type,
|
|
.anyerror,
|
|
.comptime_int,
|
|
.comptime_float,
|
|
.noreturn,
|
|
.adhoc_inferred_error_set,
|
|
=> return writer.writeAll(@tagName(s)),
|
|
|
|
.null,
|
|
.undefined,
|
|
=> try writer.print("@TypeOf({s})", .{@tagName(s)}),
|
|
|
|
.enum_literal => try writer.writeAll("@EnumLiteral()"),
|
|
|
|
.generic_poison => unreachable,
|
|
},
|
|
.struct_type => {
|
|
const name = ip.loadStructType(ty.toIntern()).name;
|
|
try writer.print("{f}", .{name.fmt(ip)});
|
|
},
|
|
.tuple_type => |tuple| {
|
|
if (tuple.types.len == 0) {
|
|
return writer.writeAll("@TypeOf(.{})");
|
|
}
|
|
try writer.writeAll("struct {");
|
|
for (tuple.types.get(ip), tuple.values.get(ip), 0..) |field_ty, val, i| {
|
|
try writer.writeAll(if (i == 0) " " else ", ");
|
|
if (val != .none) try writer.writeAll("comptime ");
|
|
try print(Type.fromInterned(field_ty), writer, pt, ctx);
|
|
if (val != .none) try writer.print(" = {f}", .{Value.fromInterned(val).fmtValue(pt)});
|
|
}
|
|
try writer.writeAll(" }");
|
|
},
|
|
|
|
.union_type => {
|
|
const name = ip.loadUnionType(ty.toIntern()).name;
|
|
try writer.print("{f}", .{name.fmt(ip)});
|
|
},
|
|
.opaque_type => {
|
|
const name = ip.loadOpaqueType(ty.toIntern()).name;
|
|
try writer.print("{f}", .{name.fmt(ip)});
|
|
},
|
|
.enum_type => {
|
|
const name = ip.loadEnumType(ty.toIntern()).name;
|
|
try writer.print("{f}", .{name.fmt(ip)});
|
|
},
|
|
.func_type => |fn_info| {
|
|
if (fn_info.is_noinline) {
|
|
try writer.writeAll("noinline ");
|
|
}
|
|
try writer.writeAll("fn (");
|
|
const param_types = fn_info.param_types.get(&zcu.intern_pool);
|
|
for (param_types, 0..) |param_ty, i| {
|
|
if (i != 0) try writer.writeAll(", ");
|
|
if (std.math.cast(u5, i)) |index| {
|
|
if (fn_info.paramIsComptime(index)) {
|
|
try writer.writeAll("comptime ");
|
|
}
|
|
if (fn_info.paramIsNoalias(index)) {
|
|
try writer.writeAll("noalias ");
|
|
}
|
|
}
|
|
if (param_ty == .generic_poison_type) {
|
|
try writer.writeAll("anytype");
|
|
} else {
|
|
try print(Type.fromInterned(param_ty), writer, pt, ctx);
|
|
}
|
|
}
|
|
if (fn_info.is_var_args) {
|
|
if (param_types.len != 0) {
|
|
try writer.writeAll(", ");
|
|
}
|
|
try writer.writeAll("...");
|
|
}
|
|
try writer.writeAll(") ");
|
|
if (fn_info.cc != .auto) print_cc: {
|
|
if (zcu.getTarget().cCallingConvention()) |ccc| {
|
|
if (fn_info.cc.eql(ccc)) {
|
|
try writer.writeAll("callconv(.c) ");
|
|
break :print_cc;
|
|
}
|
|
}
|
|
switch (fn_info.cc) {
|
|
.auto, .async, .naked, .@"inline" => try writer.print("callconv(.{f}) ", .{
|
|
std.zig.fmtId(@tagName(fn_info.cc)),
|
|
}),
|
|
else => try writer.print("callconv({any}) ", .{fn_info.cc}),
|
|
}
|
|
}
|
|
if (fn_info.return_type == .generic_poison_type) {
|
|
try writer.writeAll("anytype");
|
|
} else {
|
|
try print(Type.fromInterned(fn_info.return_type), writer, pt, ctx);
|
|
}
|
|
},
|
|
.anyframe_type => |child| {
|
|
if (child == .none) return writer.writeAll("anyframe");
|
|
try writer.writeAll("anyframe->");
|
|
return print(Type.fromInterned(child), writer, pt, ctx);
|
|
},
|
|
|
|
// values, not types
|
|
.simple_value,
|
|
.variable,
|
|
.@"extern",
|
|
.func,
|
|
.int,
|
|
.err,
|
|
.error_union,
|
|
.enum_literal,
|
|
.enum_tag,
|
|
.empty_enum_value,
|
|
.float,
|
|
.ptr,
|
|
.slice,
|
|
.opt,
|
|
.aggregate,
|
|
.un,
|
|
.bitpack,
|
|
// memoization, not types
|
|
.memoized_call,
|
|
=> unreachable,
|
|
}
|
|
}
|
|
|
|
pub fn fromInterned(i: InternPool.Index) Type {
|
|
assert(i != .none);
|
|
return .{ .ip_index = i };
|
|
}
|
|
|
|
pub fn toIntern(ty: Type) InternPool.Index {
|
|
assert(ty.ip_index != .none);
|
|
return ty.ip_index;
|
|
}
|
|
|
|
pub fn toValue(self: Type) Value {
|
|
return .fromInterned(self.toIntern());
|
|
}
|
|
|
|
/// true if and only if the type takes up space in memory at runtime.
|
|
/// There are two reasons a type will return false:
|
|
/// * the type is a comptime-only type. For example, the type `type` itself.
|
|
/// - note, however, that a struct can have mixed fields and only the non-comptime-only
|
|
/// fields will count towards the ABI size. For example, `struct {T: type, x: i32}`
|
|
/// hasRuntimeBits()=true and abiSize()=4
|
|
/// * the type has only one possible value, making its ABI size 0.
|
|
/// - an enum with an explicit tag type has the ABI size of the integer tag type,
|
|
/// making it one-possible-value only if the integer tag type has 0 bits.
|
|
pub fn hasRuntimeBits(ty: Type, zcu: *const Zcu) bool {
|
|
ty.assertHasLayout(zcu);
|
|
const ip = &zcu.intern_pool;
|
|
return switch (ip.indexToKey(ty.toIntern())) {
|
|
.int_type => |int_type| int_type.bits != 0,
|
|
.ptr_type => true,
|
|
.anyframe_type => true,
|
|
.array_type => |array_type| array_type.lenIncludingSentinel() > 0 and
|
|
Type.fromInterned(array_type.child).hasRuntimeBits(zcu),
|
|
.vector_type => |vector_type| vector_type.len > 0 and
|
|
Type.fromInterned(vector_type.child).hasRuntimeBits(zcu),
|
|
.opt_type => |child| !Type.fromInterned(child).isNoReturn(zcu),
|
|
|
|
.error_union_type,
|
|
.error_set_type,
|
|
.inferred_error_set_type,
|
|
=> true,
|
|
|
|
// These are function *bodies*, not pointers.
|
|
// They return false here because they are comptime-only types.
|
|
// Special exceptions have to be made when emitting functions due to
|
|
// this returning false.
|
|
.func_type => false,
|
|
|
|
.simple_type => |t| switch (t) {
|
|
.f16,
|
|
.f32,
|
|
.f64,
|
|
.f80,
|
|
.f128,
|
|
.usize,
|
|
.isize,
|
|
.c_char,
|
|
.c_short,
|
|
.c_ushort,
|
|
.c_int,
|
|
.c_uint,
|
|
.c_long,
|
|
.c_ulong,
|
|
.c_longlong,
|
|
.c_ulonglong,
|
|
.c_longdouble,
|
|
.bool,
|
|
.anyerror,
|
|
.adhoc_inferred_error_set,
|
|
.anyopaque,
|
|
=> true,
|
|
|
|
.void,
|
|
.noreturn,
|
|
=> false,
|
|
|
|
// primitive comptime-only types
|
|
.type,
|
|
.comptime_int,
|
|
.comptime_float,
|
|
.null,
|
|
.undefined,
|
|
.enum_literal,
|
|
=> false,
|
|
|
|
.generic_poison => unreachable,
|
|
},
|
|
.struct_type => {
|
|
const struct_obj = ip.loadStructType(ty.toIntern());
|
|
switch (struct_obj.layout) {
|
|
.auto, .@"extern" => return struct_obj.has_runtime_bits,
|
|
.@"packed" => return Type.fromInterned(struct_obj.packed_backing_int_type).hasRuntimeBits(zcu),
|
|
}
|
|
},
|
|
.union_type => {
|
|
const union_obj = ip.loadUnionType(ty.toIntern());
|
|
switch (union_obj.layout) {
|
|
.auto, .@"extern" => return union_obj.has_runtime_bits,
|
|
.@"packed" => return Type.fromInterned(union_obj.packed_backing_int_type).hasRuntimeBits(zcu),
|
|
}
|
|
},
|
|
.tuple_type => |tuple| {
|
|
for (tuple.types.get(ip), tuple.values.get(ip)) |field_ty, val| {
|
|
if (val != .none) continue; // comptime field
|
|
if (Type.fromInterned(field_ty).hasRuntimeBits(zcu)) return true;
|
|
}
|
|
return false;
|
|
},
|
|
|
|
// MLUGG TODO: this answer was already here but... does it actually make sense?
|
|
.opaque_type => true,
|
|
.enum_type => Type.fromInterned(ip.loadEnumType(ty.toIntern()).int_tag_type).hasRuntimeBits(zcu),
|
|
|
|
// values, not types
|
|
.undef,
|
|
.simple_value,
|
|
.variable,
|
|
.@"extern",
|
|
.func,
|
|
.int,
|
|
.err,
|
|
.error_union,
|
|
.enum_literal,
|
|
.enum_tag,
|
|
.empty_enum_value,
|
|
.float,
|
|
.ptr,
|
|
.slice,
|
|
.opt,
|
|
.aggregate,
|
|
.un,
|
|
.bitpack,
|
|
// memoization, not types
|
|
.memoized_call,
|
|
=> unreachable,
|
|
};
|
|
}
|
|
|
|
/// true if and only if the type has a well-defined memory layout
|
|
/// readFrom/writeToMemory are supported only for types with a well-
|
|
/// defined memory layout
|
|
pub fn hasWellDefinedLayout(ty: Type, zcu: *const Zcu) bool {
|
|
const ip = &zcu.intern_pool;
|
|
return switch (ip.indexToKey(ty.toIntern())) {
|
|
.int_type,
|
|
.vector_type,
|
|
=> true,
|
|
|
|
.error_union_type,
|
|
.error_set_type,
|
|
.inferred_error_set_type,
|
|
.tuple_type,
|
|
.opaque_type,
|
|
.anyframe_type,
|
|
// These are function bodies, not function pointers.
|
|
.func_type,
|
|
=> false,
|
|
|
|
.array_type => |array_type| Type.fromInterned(array_type.child).hasWellDefinedLayout(zcu),
|
|
.opt_type => ty.isPtrLikeOptional(zcu),
|
|
.ptr_type => |ptr_type| ptr_type.flags.size != .slice,
|
|
|
|
.simple_type => |t| switch (t) {
|
|
.f16,
|
|
.f32,
|
|
.f64,
|
|
.f80,
|
|
.f128,
|
|
.usize,
|
|
.isize,
|
|
.c_char,
|
|
.c_short,
|
|
.c_ushort,
|
|
.c_int,
|
|
.c_uint,
|
|
.c_long,
|
|
.c_ulong,
|
|
.c_longlong,
|
|
.c_ulonglong,
|
|
.c_longdouble,
|
|
.bool,
|
|
.void,
|
|
=> true,
|
|
|
|
.anyerror,
|
|
.adhoc_inferred_error_set,
|
|
.anyopaque,
|
|
.type,
|
|
.comptime_int,
|
|
.comptime_float,
|
|
.noreturn,
|
|
.null,
|
|
.undefined,
|
|
.enum_literal,
|
|
.generic_poison,
|
|
=> false,
|
|
},
|
|
.struct_type => switch (ip.loadStructType(ty.toIntern()).layout) {
|
|
.auto => false,
|
|
.@"extern", .@"packed" => true,
|
|
},
|
|
.union_type => switch (ip.loadUnionType(ty.toIntern()).layout) {
|
|
.auto => false,
|
|
.@"extern", .@"packed" => true,
|
|
},
|
|
.enum_type => switch (ip.loadEnumType(ty.toIntern()).int_tag_mode) {
|
|
.explicit => true,
|
|
.auto => false,
|
|
},
|
|
|
|
// values, not types
|
|
.undef,
|
|
.simple_value,
|
|
.variable,
|
|
.@"extern",
|
|
.func,
|
|
.int,
|
|
.err,
|
|
.error_union,
|
|
.enum_literal,
|
|
.enum_tag,
|
|
.empty_enum_value,
|
|
.float,
|
|
.ptr,
|
|
.slice,
|
|
.opt,
|
|
.aggregate,
|
|
.un,
|
|
.bitpack,
|
|
// memoization, not types
|
|
.memoized_call,
|
|
=> unreachable,
|
|
};
|
|
}
|
|
|
|
/// Determines whether a function type has runtime bits, i.e. whether a
|
|
/// function with this type can exist at runtime.
|
|
/// Asserts that `ty` is a function type.
|
|
pub fn fnHasRuntimeBits(fn_ty: Type, zcu: *Zcu) bool {
|
|
const fn_info = zcu.typeToFunc(fn_ty).?;
|
|
if (fn_info.comptime_bits != 0) return false;
|
|
for (fn_info.param_types.get(&zcu.intern_pool)) |param_ty| {
|
|
if (param_ty == .generic_poison_type) return false;
|
|
if (Type.fromInterned(param_ty).comptimeOnly(zcu)) return false;
|
|
}
|
|
const ret_ty: Type = .fromInterned(fn_info.return_type);
|
|
if (ret_ty.toIntern() == .generic_poison_type) {
|
|
return false;
|
|
}
|
|
if (ret_ty.zigTypeTag(zcu) == .error_union and
|
|
ret_ty.errorUnionPayload(zcu).toIntern() == .generic_poison_type)
|
|
{
|
|
return false;
|
|
}
|
|
if (fn_info.return_type == .generic_poison_type) return false;
|
|
if (Type.fromInterned(fn_info.return_type).comptimeOnly(zcu)) return false;
|
|
if (fn_info.cc == .@"inline") return false;
|
|
return true;
|
|
}
|
|
|
|
/// Like `hasRuntimeBits`, but also returns `true` for runtime functions.
|
|
pub fn isRuntimeFnOrHasRuntimeBits(ty: Type, zcu: *Zcu) bool {
|
|
switch (ty.zigTypeTag(zcu)) {
|
|
.@"fn" => return ty.fnHasRuntimeBits(zcu),
|
|
else => return ty.hasRuntimeBits(zcu),
|
|
}
|
|
}
|
|
|
|
pub fn isNoReturn(ty: Type, zcu: *const Zcu) bool {
|
|
return zcu.intern_pool.isNoReturn(ty.toIntern());
|
|
}
|
|
|
|
/// Never returns `none`. Asserts that all necessary type resolution is already done.
|
|
pub fn ptrAlignment(ptr_ty: Type, zcu: *Zcu) Alignment {
|
|
const ip = &zcu.intern_pool;
|
|
const ptr_key: InternPool.Key.PtrType = switch (ip.indexToKey(ptr_ty.toIntern())) {
|
|
.ptr_type => |key| key,
|
|
.opt_type => |child| ip.indexToKey(child).ptr_type,
|
|
else => unreachable,
|
|
};
|
|
if (ptr_key.flags.alignment != .none) return ptr_key.flags.alignment;
|
|
return Type.fromInterned(ptr_key.child).abiAlignment(zcu);
|
|
}
|
|
|
|
pub fn ptrAddressSpace(ty: Type, zcu: *const Zcu) std.builtin.AddressSpace {
|
|
return switch (zcu.intern_pool.indexToKey(ty.toIntern())) {
|
|
.ptr_type => |ptr_type| ptr_type.flags.address_space,
|
|
.opt_type => |child| zcu.intern_pool.indexToKey(child).ptr_type.flags.address_space,
|
|
else => unreachable,
|
|
};
|
|
}
|
|
|
|
/// Never returns `none`. Asserts that all necessary type resolution is already done.
|
|
pub fn abiAlignment(ty: Type, zcu: *const Zcu) Alignment {
|
|
const ip = &zcu.intern_pool;
|
|
const target = zcu.getTarget();
|
|
assertHasLayout(ty, zcu);
|
|
return switch (ip.indexToKey(ty.toIntern())) {
|
|
.int_type => |int_type| {
|
|
if (int_type.bits == 0) return .@"1";
|
|
return .fromByteUnits(std.zig.target.intAlignment(target, int_type.bits));
|
|
},
|
|
.ptr_type, .anyframe_type => ptrAbiAlignment(target),
|
|
.array_type => |array_type| Type.fromInterned(array_type.child).abiAlignment(zcu),
|
|
.vector_type => |vector_type| {
|
|
if (vector_type.len == 0) return .@"1";
|
|
switch (zcu.comp.getZigBackend()) {
|
|
else => {
|
|
const elem_bits: u32 = @intCast(Type.fromInterned(vector_type.child).bitSize(zcu));
|
|
if (elem_bits == 0) return .@"1";
|
|
const bytes = ((elem_bits * vector_type.len) + 7) / 8;
|
|
return .fromByteUnits(std.math.ceilPowerOfTwoAssert(u32, bytes));
|
|
},
|
|
.stage2_c => return Type.fromInterned(vector_type.child).abiAlignment(zcu),
|
|
.stage2_x86_64 => {
|
|
if (vector_type.child == .bool_type) {
|
|
if (vector_type.len > 256 and target.cpu.has(.x86, .avx512f)) return .@"64";
|
|
if (vector_type.len > 128 and target.cpu.has(.x86, .avx)) return .@"32";
|
|
if (vector_type.len > 64) return .@"16";
|
|
const bytes = std.math.divCeil(u32, vector_type.len, 8) catch unreachable;
|
|
return .fromByteUnits(std.math.ceilPowerOfTwoAssert(u32, bytes));
|
|
}
|
|
const elem_bytes: u32 = @intCast(Type.fromInterned(vector_type.child).abiSize(zcu));
|
|
if (elem_bytes == 0) return .@"1";
|
|
const bytes = elem_bytes * vector_type.len;
|
|
if (bytes > 32 and target.cpu.has(.x86, .avx512f)) return .@"64";
|
|
if (bytes > 16 and target.cpu.has(.x86, .avx)) return .@"32";
|
|
return .@"16";
|
|
},
|
|
}
|
|
},
|
|
|
|
.opt_type => |child| Type.fromInterned(child).abiAlignment(zcu),
|
|
.error_union_type => |eu| Alignment.maxStrict(
|
|
Type.fromInterned(eu.payload_type).abiAlignment(zcu),
|
|
errorAbiAlignment(zcu),
|
|
),
|
|
|
|
.error_set_type, .inferred_error_set_type => errorAbiAlignment(zcu),
|
|
|
|
.func_type => target_util.minFunctionAlignment(target),
|
|
|
|
.simple_type => |t| switch (t) {
|
|
.bool,
|
|
.void,
|
|
.noreturn,
|
|
.anyopaque,
|
|
.type,
|
|
.comptime_int,
|
|
.comptime_float,
|
|
.null,
|
|
.undefined,
|
|
.enum_literal,
|
|
=> .@"1",
|
|
|
|
.anyerror, .adhoc_inferred_error_set => errorAbiAlignment(zcu),
|
|
.usize, .isize => .fromByteUnits(std.zig.target.intAlignment(target, target.ptrBitWidth())),
|
|
|
|
.c_char => cTypeAlign(target, .char),
|
|
.c_short => cTypeAlign(target, .short),
|
|
.c_ushort => cTypeAlign(target, .ushort),
|
|
.c_int => cTypeAlign(target, .int),
|
|
.c_uint => cTypeAlign(target, .uint),
|
|
.c_long => cTypeAlign(target, .long),
|
|
.c_ulong => cTypeAlign(target, .ulong),
|
|
.c_longlong => cTypeAlign(target, .longlong),
|
|
.c_ulonglong => cTypeAlign(target, .ulonglong),
|
|
.c_longdouble => cTypeAlign(target, .longdouble),
|
|
|
|
.f16 => .@"2",
|
|
.f32 => cTypeAlign(target, .float),
|
|
.f64 => switch (target.cTypeBitSize(.double)) {
|
|
64 => cTypeAlign(target, .double),
|
|
else => .@"8",
|
|
},
|
|
.f80 => switch (target.cTypeBitSize(.longdouble)) {
|
|
80 => cTypeAlign(target, .longdouble),
|
|
else => Type.u80.abiAlignment(zcu),
|
|
},
|
|
.f128 => switch (target.cTypeBitSize(.longdouble)) {
|
|
128 => cTypeAlign(target, .longdouble),
|
|
else => .@"16",
|
|
},
|
|
|
|
.generic_poison => unreachable,
|
|
},
|
|
.tuple_type => |tuple| {
|
|
var big_align: Alignment = .@"1";
|
|
for (tuple.types.get(ip), tuple.values.get(ip)) |field_ty, val| {
|
|
if (val != .none) continue; // comptime field
|
|
const field_align = Type.fromInterned(field_ty).abiAlignment(zcu);
|
|
big_align = big_align.maxStrict(field_align);
|
|
}
|
|
return big_align;
|
|
},
|
|
.struct_type => {
|
|
const struct_obj = ip.loadStructType(ty.toIntern());
|
|
switch (struct_obj.layout) {
|
|
.@"packed" => return Type.fromInterned(struct_obj.packed_backing_int_type).abiAlignment(zcu),
|
|
.auto, .@"extern" => {
|
|
assert(struct_obj.alignment != .none);
|
|
return struct_obj.alignment;
|
|
},
|
|
}
|
|
},
|
|
.union_type => {
|
|
const union_obj = ip.loadUnionType(ty.toIntern());
|
|
switch (union_obj.layout) {
|
|
.@"packed" => return Type.fromInterned(union_obj.packed_backing_int_type).abiAlignment(zcu),
|
|
.auto, .@"extern" => {
|
|
assert(union_obj.alignment != .none);
|
|
return union_obj.alignment;
|
|
},
|
|
}
|
|
},
|
|
.enum_type => Type.fromInterned(ip.loadEnumType(ty.toIntern()).int_tag_type).abiAlignment(zcu),
|
|
.opaque_type => .@"1",
|
|
|
|
// values, not types
|
|
.undef,
|
|
.simple_value,
|
|
.variable,
|
|
.@"extern",
|
|
.func,
|
|
.int,
|
|
.err,
|
|
.error_union,
|
|
.enum_literal,
|
|
.enum_tag,
|
|
.empty_enum_value,
|
|
.float,
|
|
.ptr,
|
|
.slice,
|
|
.opt,
|
|
.aggregate,
|
|
.un,
|
|
.bitpack,
|
|
// memoization, not types
|
|
.memoized_call,
|
|
=> unreachable,
|
|
};
|
|
}
|
|
|
|
/// Asserts that `ty` is not an opaque type.
|
|
pub fn abiSize(ty: Type, zcu: *const Zcu) u64 {
|
|
const ip = &zcu.intern_pool;
|
|
const target = zcu.getTarget();
|
|
assertHasLayout(ty, zcu);
|
|
return switch (ip.indexToKey(ty.toIntern())) {
|
|
.int_type => |int_type| std.zig.target.intByteSize(target, int_type.bits),
|
|
.ptr_type => |ptr_type| switch (ptr_type.flags.size) {
|
|
.slice => ptrAbiSize(target) * 2,
|
|
.one, .many, .c => ptrAbiSize(target),
|
|
},
|
|
.anyframe_type => ptrAbiSize(target),
|
|
.array_type => |arr| arr.lenIncludingSentinel() * Type.fromInterned(arr.child).abiSize(zcu),
|
|
.vector_type => |vec| {
|
|
const elem_ty: Type = .fromInterned(vec.child);
|
|
const bytes = switch (zcu.comp.getZigBackend()) {
|
|
else => std.math.divCeil(u64, vec.len * elem_ty.bitSize(zcu), 8) catch unreachable,
|
|
.stage2_c => vec.len * elem_ty.abiSize(zcu),
|
|
.stage2_x86_64 => switch (elem_ty.toIntern()) {
|
|
.bool_type => std.math.divCeil(u64, vec.len, 8) catch unreachable,
|
|
else => vec.len * elem_ty.abiSize(zcu),
|
|
},
|
|
};
|
|
return ty.abiAlignment(zcu).forward(bytes);
|
|
},
|
|
.opt_type => |child_ty_ip| {
|
|
const child_ty: Type = .fromInterned(child_ty_ip);
|
|
if (child_ty.isNoReturn(zcu)) return 0;
|
|
const child_size = child_ty.abiSize(zcu);
|
|
if (ty.optionalReprIsPayload(zcu)) return child_size;
|
|
// Optional types are represented as a struct with the child type as the first
|
|
// field and a boolean as the second. Since the child type's abi alignment is
|
|
// guaranteed to be >= that of bool's (1 byte) the added size is exactly equal
|
|
// to the child type's ABI alignment.
|
|
return child_size + child_ty.abiAlignment(zcu).toByteUnits().?;
|
|
},
|
|
.error_set_type, .inferred_error_set_type => errorAbiSize(zcu),
|
|
.error_union_type => |error_union| {
|
|
const payload_ty: Type = .fromInterned(error_union.payload_type);
|
|
// This code needs to be kept in sync with the equivalent switch prong
|
|
// in abiAlignmentInner.
|
|
const code_size = errorAbiSize(zcu);
|
|
const code_align = errorAbiAlignment(zcu);
|
|
const payload_size = payload_ty.abiSize(zcu);
|
|
const payload_align = payload_ty.abiAlignment(zcu);
|
|
// The layout will either be (code, payload, padding) or (payload, code, padding)
|
|
// depending on which has larger alignment. So the overall size is just the code
|
|
// and payload sizes added and padded to the larger alignment.
|
|
const big_align = code_align.maxStrict(payload_align);
|
|
return big_align.forward(payload_size + code_size);
|
|
},
|
|
.func_type => 0,
|
|
.simple_type => |t| switch (t) {
|
|
.void,
|
|
.noreturn,
|
|
.type,
|
|
.comptime_int,
|
|
.comptime_float,
|
|
.null,
|
|
.undefined,
|
|
.enum_literal,
|
|
=> 0,
|
|
|
|
.bool => 1,
|
|
.anyerror, .adhoc_inferred_error_set => errorAbiSize(zcu),
|
|
.usize, .isize => ptrAbiSize(target),
|
|
|
|
.c_char => target.cTypeByteSize(.char),
|
|
.c_short => target.cTypeByteSize(.short),
|
|
.c_ushort => target.cTypeByteSize(.ushort),
|
|
.c_int => target.cTypeByteSize(.int),
|
|
.c_uint => target.cTypeByteSize(.uint),
|
|
.c_long => target.cTypeByteSize(.long),
|
|
.c_ulong => target.cTypeByteSize(.ulong),
|
|
.c_longlong => target.cTypeByteSize(.longlong),
|
|
.c_ulonglong => target.cTypeByteSize(.ulonglong),
|
|
.c_longdouble => target.cTypeByteSize(.longdouble),
|
|
|
|
.f16 => 2,
|
|
.f32 => 4,
|
|
.f64 => 8,
|
|
.f80 => switch (target.cTypeBitSize(.longdouble)) {
|
|
80 => target.cTypeByteSize(.longdouble),
|
|
else => Type.u80.abiSize(zcu),
|
|
},
|
|
.f128 => 16,
|
|
|
|
.anyopaque => unreachable,
|
|
.generic_poison => unreachable,
|
|
},
|
|
.tuple_type => |tuple| ty.structFieldOffset(tuple.types.len, zcu),
|
|
.struct_type => {
|
|
const struct_obj = ip.loadStructType(ty.toIntern());
|
|
switch (struct_obj.layout) {
|
|
.@"packed" => return Type.fromInterned(struct_obj.packed_backing_int_type).abiSize(zcu),
|
|
.auto, .@"extern" => return struct_obj.size,
|
|
}
|
|
},
|
|
.union_type => {
|
|
const union_obj = ip.loadUnionType(ty.toIntern());
|
|
switch (union_obj.layout) {
|
|
.@"packed" => return Type.fromInterned(union_obj.packed_backing_int_type).abiSize(zcu),
|
|
.auto, .@"extern" => return union_obj.size,
|
|
}
|
|
},
|
|
.enum_type => Type.fromInterned(ip.loadEnumType(ty.toIntern()).int_tag_type).abiSize(zcu),
|
|
.opaque_type => unreachable,
|
|
|
|
// values, not types
|
|
.undef,
|
|
.simple_value,
|
|
.variable,
|
|
.@"extern",
|
|
.func,
|
|
.int,
|
|
.err,
|
|
.error_union,
|
|
.enum_literal,
|
|
.enum_tag,
|
|
.empty_enum_value,
|
|
.float,
|
|
.ptr,
|
|
.slice,
|
|
.opt,
|
|
.aggregate,
|
|
.un,
|
|
.bitpack,
|
|
// memoization, not types
|
|
.memoized_call,
|
|
=> unreachable,
|
|
};
|
|
}
|
|
|
|
pub fn ptrAbiAlignment(target: *const Target) Alignment {
|
|
return .fromNonzeroByteUnits(@divExact(target.ptrBitWidth(), 8));
|
|
}
|
|
pub fn ptrAbiSize(target: *const Target) u64 {
|
|
return @divExact(target.ptrBitWidth(), 8);
|
|
}
|
|
pub fn errorAbiAlignment(zcu: *const Zcu) Alignment {
|
|
return .fromNonzeroByteUnits(std.zig.target.intAlignment(zcu.getTarget(), zcu.errorSetBits()));
|
|
}
|
|
pub fn errorAbiSize(zcu: *const Zcu) u64 {
|
|
return std.zig.target.intByteSize(zcu.getTarget(), zcu.errorSetBits());
|
|
}
|
|
|
|
/// Asserts that `ty` is not an opaque or comptime-only type.
|
|
/// Once #19755 is implemented, this query will only work on types with a defined bit-level representation.
|
|
pub fn bitSize(ty: Type, zcu: *const Zcu) u64 {
|
|
const target = zcu.getTarget();
|
|
const ip = &zcu.intern_pool;
|
|
assertHasLayout(ty, zcu);
|
|
return switch (ip.indexToKey(ty.toIntern())) {
|
|
.int_type => |int_type| int_type.bits,
|
|
.ptr_type => |ptr_type| switch (ptr_type.flags.size) {
|
|
.slice => target.ptrBitWidth() * 2,
|
|
else => target.ptrBitWidth(),
|
|
},
|
|
.anyframe_type => target.ptrBitWidth(),
|
|
.array_type => |array_type| {
|
|
const elem_ty: Type = .fromInterned(array_type.child);
|
|
const len = array_type.lenIncludingSentinel();
|
|
return switch (zcu.comp.getZigBackend()) {
|
|
.stage2_x86_64 => len * elem_ty.bitSize(zcu),
|
|
// this case will be removed under #19755
|
|
else => switch (len) {
|
|
0 => 0,
|
|
else => (len - 1) * 8 * elem_ty.abiSize(zcu) + elem_ty.bitSize(zcu),
|
|
},
|
|
};
|
|
},
|
|
.vector_type => |vec| vec.len * Type.fromInterned(vec.child).bitSize(zcu),
|
|
.error_set_type, .inferred_error_set_type => zcu.errorSetBits(),
|
|
.func_type => unreachable,
|
|
|
|
.simple_type => |t| switch (t) {
|
|
.void => 0,
|
|
.bool => 1,
|
|
.anyerror, .adhoc_inferred_error_set => zcu.errorSetBits(),
|
|
.usize, .isize => target.ptrBitWidth(),
|
|
|
|
.c_char => target.cTypeBitSize(.char),
|
|
.c_short => target.cTypeBitSize(.short),
|
|
.c_ushort => target.cTypeBitSize(.ushort),
|
|
.c_int => target.cTypeBitSize(.int),
|
|
.c_uint => target.cTypeBitSize(.uint),
|
|
.c_long => target.cTypeBitSize(.long),
|
|
.c_ulong => target.cTypeBitSize(.ulong),
|
|
.c_longlong => target.cTypeBitSize(.longlong),
|
|
.c_ulonglong => target.cTypeBitSize(.ulonglong),
|
|
.c_longdouble => target.cTypeBitSize(.longdouble),
|
|
|
|
.f16 => 16,
|
|
.f32 => 32,
|
|
.f64 => 64,
|
|
.f80 => 80,
|
|
.f128 => 128,
|
|
|
|
.anyopaque => unreachable,
|
|
.type => unreachable,
|
|
.comptime_int => unreachable,
|
|
.comptime_float => unreachable,
|
|
.noreturn => unreachable,
|
|
.null => unreachable,
|
|
.undefined => unreachable,
|
|
.enum_literal => unreachable,
|
|
.generic_poison => unreachable,
|
|
},
|
|
|
|
.struct_type => {
|
|
const struct_obj = ip.loadStructType(ty.toIntern());
|
|
switch (struct_obj.layout) {
|
|
.@"packed" => return Type.fromInterned(struct_obj.packed_backing_int_type).bitSize(zcu),
|
|
.auto, .@"extern" => return struct_obj.size * 8, // will be `unreachable` under #19755
|
|
}
|
|
},
|
|
.union_type => {
|
|
const union_obj = ip.loadUnionType(ty.toIntern());
|
|
switch (union_obj.layout) {
|
|
.@"packed" => return Type.fromInterned(union_obj.packed_backing_int_type).bitSize(zcu),
|
|
.auto, .@"extern" => return union_obj.size * 8, // will be `unreachable` under #19755
|
|
}
|
|
},
|
|
.enum_type => Type.fromInterned(ip.loadEnumType(ty.toIntern()).int_tag_type).bitSize(zcu),
|
|
|
|
// will be `unreachable` under #19755
|
|
.opt_type,
|
|
.error_union_type,
|
|
.tuple_type,
|
|
=> ty.abiSize(zcu) * 8,
|
|
|
|
.opaque_type => unreachable,
|
|
|
|
// values, not types
|
|
.undef,
|
|
.simple_value,
|
|
.variable,
|
|
.@"extern",
|
|
.func,
|
|
.int,
|
|
.err,
|
|
.error_union,
|
|
.enum_literal,
|
|
.enum_tag,
|
|
.empty_enum_value,
|
|
.float,
|
|
.ptr,
|
|
.slice,
|
|
.opt,
|
|
.aggregate,
|
|
.un,
|
|
.bitpack,
|
|
// memoization, not types
|
|
.memoized_call,
|
|
=> unreachable,
|
|
};
|
|
}
|
|
|
|
pub fn isSinglePointer(ty: Type, zcu: *const Zcu) bool {
|
|
return switch (zcu.intern_pool.indexToKey(ty.toIntern())) {
|
|
.ptr_type => |ptr_info| ptr_info.flags.size == .one,
|
|
else => false,
|
|
};
|
|
}
|
|
|
|
/// Asserts `ty` is a pointer.
|
|
pub fn ptrSize(ty: Type, zcu: *const Zcu) std.builtin.Type.Pointer.Size {
|
|
return ty.ptrSizeOrNull(zcu).?;
|
|
}
|
|
|
|
/// Returns `null` if `ty` is not a pointer.
|
|
pub fn ptrSizeOrNull(ty: Type, zcu: *const Zcu) ?std.builtin.Type.Pointer.Size {
|
|
return switch (zcu.intern_pool.indexToKey(ty.toIntern())) {
|
|
.ptr_type => |ptr_info| ptr_info.flags.size,
|
|
else => null,
|
|
};
|
|
}
|
|
|
|
pub fn isSlice(ty: Type, zcu: *const Zcu) bool {
|
|
return switch (zcu.intern_pool.indexToKey(ty.toIntern())) {
|
|
.ptr_type => |ptr_type| ptr_type.flags.size == .slice,
|
|
else => false,
|
|
};
|
|
}
|
|
|
|
pub fn isSliceAtRuntime(ty: Type, zcu: *const Zcu) bool {
|
|
return switch (zcu.intern_pool.indexToKey(ty.toIntern())) {
|
|
.ptr_type => |ptr_type| ptr_type.flags.size == .slice,
|
|
.opt_type => |child| switch (zcu.intern_pool.indexToKey(child)) {
|
|
.ptr_type => |ptr_type| !ptr_type.flags.is_allowzero and ptr_type.flags.size == .slice,
|
|
else => false,
|
|
},
|
|
else => false,
|
|
};
|
|
}
|
|
|
|
pub fn slicePtrFieldType(ty: Type, zcu: *const Zcu) Type {
|
|
return .fromInterned(zcu.intern_pool.slicePtrType(ty.toIntern()));
|
|
}
|
|
|
|
pub fn isConstPtr(ty: Type, zcu: *const Zcu) bool {
|
|
return switch (zcu.intern_pool.indexToKey(ty.toIntern())) {
|
|
.ptr_type => |ptr_type| ptr_type.flags.is_const,
|
|
else => false,
|
|
};
|
|
}
|
|
|
|
pub fn isVolatilePtr(ty: Type, zcu: *const Zcu) bool {
|
|
return isVolatilePtrIp(ty, &zcu.intern_pool);
|
|
}
|
|
|
|
pub fn isVolatilePtrIp(ty: Type, ip: *const InternPool) bool {
|
|
return switch (ip.indexToKey(ty.toIntern())) {
|
|
.ptr_type => |ptr_type| ptr_type.flags.is_volatile,
|
|
else => false,
|
|
};
|
|
}
|
|
|
|
pub fn isAllowzeroPtr(ty: Type, zcu: *const Zcu) bool {
|
|
return switch (zcu.intern_pool.indexToKey(ty.toIntern())) {
|
|
.ptr_type => |ptr_type| ptr_type.flags.is_allowzero,
|
|
.opt_type => true,
|
|
else => false,
|
|
};
|
|
}
|
|
|
|
pub fn isCPtr(ty: Type, zcu: *const Zcu) bool {
|
|
return switch (zcu.intern_pool.indexToKey(ty.toIntern())) {
|
|
.ptr_type => |ptr_type| ptr_type.flags.size == .c,
|
|
else => false,
|
|
};
|
|
}
|
|
|
|
pub fn isPtrAtRuntime(ty: Type, zcu: *const Zcu) bool {
|
|
return switch (zcu.intern_pool.indexToKey(ty.toIntern())) {
|
|
.ptr_type => |ptr_type| switch (ptr_type.flags.size) {
|
|
.slice => false,
|
|
.one, .many, .c => true,
|
|
},
|
|
.opt_type => |child| switch (zcu.intern_pool.indexToKey(child)) {
|
|
.ptr_type => |p| switch (p.flags.size) {
|
|
.slice, .c => false,
|
|
.many, .one => !p.flags.is_allowzero,
|
|
},
|
|
else => false,
|
|
},
|
|
else => false,
|
|
};
|
|
}
|
|
|
|
/// For pointer-like optionals, returns true, otherwise returns the allowzero property
|
|
/// of pointers.
|
|
pub fn ptrAllowsZero(ty: Type, zcu: *const Zcu) bool {
|
|
return ty.isPtrLikeOptional(zcu) or ty.ptrInfo(zcu).flags.is_allowzero;
|
|
}
|
|
|
|
/// See also `isPtrLikeOptional`.
|
|
pub fn optionalReprIsPayload(ty: Type, zcu: *const Zcu) bool {
|
|
return switch (zcu.intern_pool.indexToKey(ty.toIntern())) {
|
|
.opt_type => |child_type| child_type == .anyerror_type or switch (zcu.intern_pool.indexToKey(child_type)) {
|
|
.ptr_type => |ptr_type| ptr_type.flags.size != .c and !ptr_type.flags.is_allowzero,
|
|
.error_set_type, .inferred_error_set_type => true,
|
|
else => false,
|
|
},
|
|
.ptr_type => |ptr_type| ptr_type.flags.size == .c,
|
|
else => false,
|
|
};
|
|
}
|
|
|
|
/// Returns true if the type is optional and would be lowered to a single pointer
|
|
/// address value, using 0 for null. Note that this returns true for C pointers.
|
|
pub fn isPtrLikeOptional(ty: Type, zcu: *const Zcu) bool {
|
|
return switch (zcu.intern_pool.indexToKey(ty.toIntern())) {
|
|
.ptr_type => |ptr_type| ptr_type.flags.size == .c,
|
|
.opt_type => |child| switch (zcu.intern_pool.indexToKey(child)) {
|
|
.ptr_type => |ptr_type| switch (ptr_type.flags.size) {
|
|
.slice, .c => false,
|
|
.many, .one => !ptr_type.flags.is_allowzero,
|
|
},
|
|
else => false,
|
|
},
|
|
else => false,
|
|
};
|
|
}
|
|
|
|
/// For `*[N]T`, returns `[N]T`.
|
|
/// For `*T`, returns `T`.
|
|
/// For `[*]T`, returns `T`.
|
|
/// For `@Vector(N, T)`, returns `T`.
|
|
/// For `[N]T`, returns `T`.
|
|
/// For `?T`, returns `T`.
|
|
pub fn childType(ty: Type, zcu: *const Zcu) Type {
|
|
return childTypeIp(ty, &zcu.intern_pool);
|
|
}
|
|
|
|
pub fn childTypeIp(ty: Type, ip: *const InternPool) Type {
|
|
return Type.fromInterned(ip.childType(ty.toIntern()));
|
|
}
|
|
|
|
/// Similar to `childType`, but for pointer-like (or slice-like) optionals, gets the child type
|
|
/// of the *pointer* type. Asserts that `ty` is either a pointer or a pointer-like optional.
|
|
///
|
|
/// Essentially, unwraps any one of the following into `T`:
|
|
/// ```
|
|
/// *T ?*T *allowzero T
|
|
/// [*]T ?[*]T [*]allowzero T
|
|
/// []T ?[]T []allowzero T
|
|
/// [*c]T
|
|
/// ```
|
|
/// This is primarily useful in Sema to implement operations which can act on optional pointers.
|
|
pub fn nullablePtrElem(ty: Type, zcu: *const Zcu) Type {
|
|
switch (ty.zigTypeTag(zcu)) {
|
|
.pointer => return ty.childType(zcu),
|
|
.optional => {
|
|
const ptr_ty = ty.childType(zcu);
|
|
const ptr_info = zcu.intern_pool.indexToKey(ptr_ty.toIntern()).ptr_type;
|
|
assert(ptr_info.flags.size != .c);
|
|
assert(!ptr_info.flags.is_allowzero);
|
|
return .fromInterned(ptr_info.child);
|
|
},
|
|
else => unreachable,
|
|
}
|
|
}
|
|
|
|
/// Asserts that `ty` is an indexable type, and returns its element type. Tuples (and pointers to
|
|
/// tuples) are not supported because they do not have a single element type.
|
|
///
|
|
/// Returns `T` for each of the following types:
|
|
/// * `[n]T`
|
|
/// * `@Vector(n, T)`
|
|
/// * `*[n]T`
|
|
/// * `*@Vector(n, T)`
|
|
/// * `[]T`
|
|
/// * `[*]T`
|
|
/// * `[*c]T`
|
|
pub fn indexableElem(ty: Type, zcu: *const Zcu) Type {
|
|
const ip = &zcu.intern_pool;
|
|
return switch (ip.indexToKey(ty.toIntern())) {
|
|
inline .array_type, .vector_type => |arr| .fromInterned(arr.child),
|
|
.ptr_type => |ptr_type| switch (ptr_type.flags.size) {
|
|
.many, .slice, .c => .fromInterned(ptr_type.child),
|
|
.one => switch (ip.indexToKey(ptr_type.child)) {
|
|
inline .array_type, .vector_type => |arr| .fromInterned(arr.child),
|
|
else => unreachable,
|
|
},
|
|
},
|
|
else => unreachable,
|
|
};
|
|
}
|
|
|
|
/// For vectors, returns the element type. Otherwise returns self.
|
|
pub fn scalarType(ty: Type, zcu: *const Zcu) Type {
|
|
return switch (ty.zigTypeTag(zcu)) {
|
|
.vector => ty.childType(zcu),
|
|
else => ty,
|
|
};
|
|
}
|
|
|
|
/// Asserts that the type is an optional, or a C pointer.
|
|
/// For C pointers this returns the type unmodified.
|
|
pub fn optionalChild(ty: Type, zcu: *const Zcu) Type {
|
|
switch (zcu.intern_pool.indexToKey(ty.toIntern())) {
|
|
.opt_type => |child| return .fromInterned(child),
|
|
.ptr_type => |ptr_type| {
|
|
assert(ptr_type.flags.size == .c);
|
|
return ty;
|
|
},
|
|
else => unreachable,
|
|
}
|
|
}
|
|
|
|
/// Returns the tag type of a union, if the type is a union and it has a tag type.
|
|
/// Otherwise, returns `null`.
|
|
pub fn unionTagType(ty: Type, zcu: *const Zcu) ?Type {
|
|
assertHasLayout(ty, zcu);
|
|
const ip = &zcu.intern_pool;
|
|
switch (ip.indexToKey(ty.toIntern())) {
|
|
.union_type => {},
|
|
else => return null,
|
|
}
|
|
const union_obj = ip.loadUnionType(ty.toIntern());
|
|
return switch (union_obj.runtime_tag) {
|
|
.tagged => .fromInterned(union_obj.enum_tag_type),
|
|
.none, .safety => null,
|
|
};
|
|
}
|
|
|
|
/// Same as `unionTagType` but includes safety tag.
|
|
/// Codegen should use this version.
|
|
pub fn unionTagTypeSafety(ty: Type, zcu: *const Zcu) ?Type {
|
|
assertHasLayout(ty, zcu);
|
|
const ip = &zcu.intern_pool;
|
|
return switch (ip.indexToKey(ty.toIntern())) {
|
|
.union_type => {
|
|
const union_type = ip.loadUnionType(ty.toIntern());
|
|
if (union_type.runtime_tag == .none) return null;
|
|
return Type.fromInterned(union_type.enum_tag_type);
|
|
},
|
|
else => null,
|
|
};
|
|
}
|
|
|
|
/// Asserts the type is a union; returns the tag type, even if the tag will
|
|
/// not be stored at runtime.
|
|
pub fn unionTagTypeHypothetical(ty: Type, zcu: *const Zcu) Type {
|
|
assertHasLayout(ty, zcu);
|
|
const union_obj = zcu.typeToUnion(ty).?;
|
|
return Type.fromInterned(union_obj.enum_tag_type);
|
|
}
|
|
|
|
pub fn unionFieldType(ty: Type, enum_tag: Value, zcu: *const Zcu) ?Type {
|
|
assertHasLayout(ty, zcu);
|
|
const ip = &zcu.intern_pool;
|
|
const union_obj = zcu.typeToUnion(ty).?;
|
|
const union_fields = union_obj.field_types.get(ip);
|
|
const index = zcu.unionTagFieldIndex(union_obj, enum_tag) orelse return null;
|
|
return Type.fromInterned(union_fields[index]);
|
|
}
|
|
|
|
pub fn unionFieldTypeByIndex(ty: Type, index: usize, zcu: *const Zcu) Type {
|
|
assertHasLayout(ty, zcu);
|
|
const ip = &zcu.intern_pool;
|
|
const union_obj = zcu.typeToUnion(ty).?;
|
|
return Type.fromInterned(union_obj.field_types.get(ip)[index]);
|
|
}
|
|
|
|
pub fn unionTagFieldIndex(ty: Type, enum_tag: Value, zcu: *const Zcu) ?u32 {
|
|
assertHasLayout(ty, zcu);
|
|
const union_obj = zcu.typeToUnion(ty).?;
|
|
return zcu.unionTagFieldIndex(union_obj, enum_tag);
|
|
}
|
|
|
|
pub fn unionHasAllZeroBitFieldTypes(ty: Type, zcu: *Zcu) bool {
|
|
assertHasLayout(ty, zcu);
|
|
const ip = &zcu.intern_pool;
|
|
const union_obj = zcu.typeToUnion(ty).?;
|
|
for (union_obj.field_types.get(ip)) |field_ty| {
|
|
if (Type.fromInterned(field_ty).hasRuntimeBits(zcu)) return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/// Returns the type used for backing storage of this union during comptime operations.
|
|
/// Asserts the type is an extern union.
|
|
pub fn externUnionBackingType(ty: Type, pt: Zcu.PerThread) !Type {
|
|
const zcu = pt.zcu;
|
|
assertHasLayout(ty, zcu);
|
|
const loaded_union = zcu.intern_pool.loadUnionType(ty.toIntern());
|
|
switch (loaded_union.layout) {
|
|
.@"extern" => return pt.arrayType(.{ .len = ty.abiSize(zcu), .child = .u8_type }),
|
|
.@"packed" => unreachable,
|
|
.auto => unreachable,
|
|
}
|
|
}
|
|
|
|
pub fn unionGetLayout(ty: Type, zcu: *const Zcu) Zcu.UnionLayout {
|
|
assertHasLayout(ty, zcu);
|
|
const union_obj = zcu.intern_pool.loadUnionType(ty.toIntern());
|
|
return Type.getUnionLayout(union_obj, zcu);
|
|
}
|
|
|
|
pub fn containerLayout(ty: Type, zcu: *const Zcu) std.builtin.Type.ContainerLayout {
|
|
const ip = &zcu.intern_pool;
|
|
return switch (ip.indexToKey(ty.toIntern())) {
|
|
.tuple_type => .auto,
|
|
.struct_type => ip.loadStructType(ty.toIntern()).layout,
|
|
.union_type => ip.loadUnionType(ty.toIntern()).layout,
|
|
else => unreachable,
|
|
};
|
|
}
|
|
|
|
pub fn bitpackBackingInt(ty: Type, zcu: *const Zcu) Type {
|
|
const ip = &zcu.intern_pool;
|
|
return switch (ip.indexToKey(ty.toIntern())) {
|
|
.struct_type => .fromInterned(ip.loadStructType(ty.toIntern()).packed_backing_int_type),
|
|
.union_type => .fromInterned(ip.loadUnionType(ty.toIntern()).packed_backing_int_type),
|
|
else => unreachable,
|
|
};
|
|
}
|
|
|
|
/// Asserts that the type is an error union.
|
|
pub fn errorUnionPayload(ty: Type, zcu: *const Zcu) Type {
|
|
return Type.fromInterned(zcu.intern_pool.indexToKey(ty.toIntern()).error_union_type.payload_type);
|
|
}
|
|
|
|
/// Asserts that the type is an error union.
|
|
pub fn errorUnionSet(ty: Type, zcu: *const Zcu) Type {
|
|
return Type.fromInterned(zcu.intern_pool.errorUnionSet(ty.toIntern()));
|
|
}
|
|
|
|
/// Returns false for unresolved inferred error sets.
|
|
pub fn errorSetIsEmpty(ty: Type, zcu: *const Zcu) bool {
|
|
const ip = &zcu.intern_pool;
|
|
return switch (ty.toIntern()) {
|
|
.anyerror_type, .adhoc_inferred_error_set_type => false,
|
|
else => switch (ip.indexToKey(ty.toIntern())) {
|
|
.error_set_type => |error_set_type| error_set_type.names.len == 0,
|
|
.inferred_error_set_type => |i| switch (ip.funcIesResolvedUnordered(i)) {
|
|
.none, .anyerror_type => false,
|
|
else => |t| ip.indexToKey(t).error_set_type.names.len == 0,
|
|
},
|
|
else => unreachable,
|
|
},
|
|
};
|
|
}
|
|
|
|
/// Returns true if it is an error set that includes anyerror, false otherwise.
|
|
/// Note that the result may be a false negative if the type did not get error set
|
|
/// resolution prior to this call.
|
|
pub fn isAnyError(ty: Type, zcu: *const Zcu) bool {
|
|
const ip = &zcu.intern_pool;
|
|
return switch (ty.toIntern()) {
|
|
.anyerror_type => true,
|
|
.adhoc_inferred_error_set_type => false,
|
|
else => switch (zcu.intern_pool.indexToKey(ty.toIntern())) {
|
|
.inferred_error_set_type => |i| ip.funcIesResolvedUnordered(i) == .anyerror_type,
|
|
else => false,
|
|
},
|
|
};
|
|
}
|
|
|
|
pub fn isError(ty: Type, zcu: *const Zcu) bool {
|
|
return switch (ty.zigTypeTag(zcu)) {
|
|
.error_union, .error_set => true,
|
|
else => false,
|
|
};
|
|
}
|
|
|
|
/// Returns whether ty, which must be an error set, includes an error `name`.
|
|
/// Might return a false negative if `ty` is an inferred error set and not fully
|
|
/// resolved yet.
|
|
pub fn errorSetHasField(
|
|
ty: Type,
|
|
name: InternPool.NullTerminatedString,
|
|
zcu: *const Zcu,
|
|
) bool {
|
|
const ip = &zcu.intern_pool;
|
|
return switch (ty.toIntern()) {
|
|
.anyerror_type => true,
|
|
else => switch (ip.indexToKey(ty.toIntern())) {
|
|
.error_set_type => |error_set_type| error_set_type.nameIndex(ip, name) != null,
|
|
.inferred_error_set_type => |i| switch (ip.funcIesResolvedUnordered(i)) {
|
|
.anyerror_type => true,
|
|
.none => false,
|
|
else => |t| ip.indexToKey(t).error_set_type.nameIndex(ip, name) != null,
|
|
},
|
|
else => unreachable,
|
|
},
|
|
};
|
|
}
|
|
|
|
/// Asserts the type is an array or vector or struct.
|
|
pub fn arrayLen(ty: Type, zcu: *const Zcu) u64 {
|
|
return ty.arrayLenIp(&zcu.intern_pool);
|
|
}
|
|
|
|
pub fn arrayLenIp(ty: Type, ip: *const InternPool) u64 {
|
|
return ip.aggregateTypeLen(ty.toIntern());
|
|
}
|
|
|
|
pub fn arrayLenIncludingSentinel(ty: Type, zcu: *const Zcu) u64 {
|
|
return zcu.intern_pool.aggregateTypeLenIncludingSentinel(ty.toIntern());
|
|
}
|
|
|
|
pub fn vectorLen(ty: Type, zcu: *const Zcu) u32 {
|
|
return switch (zcu.intern_pool.indexToKey(ty.toIntern())) {
|
|
.vector_type => |vector_type| vector_type.len,
|
|
.tuple_type => |tuple| @intCast(tuple.types.len),
|
|
else => unreachable,
|
|
};
|
|
}
|
|
|
|
/// Asserts the type is an array, pointer or vector.
|
|
pub fn sentinel(ty: Type, zcu: *const Zcu) ?Value {
|
|
return switch (zcu.intern_pool.indexToKey(ty.toIntern())) {
|
|
.vector_type,
|
|
.struct_type,
|
|
.tuple_type,
|
|
=> null,
|
|
|
|
.array_type => |t| if (t.sentinel != .none) Value.fromInterned(t.sentinel) else null,
|
|
.ptr_type => |t| if (t.sentinel != .none) Value.fromInterned(t.sentinel) else null,
|
|
|
|
else => unreachable,
|
|
};
|
|
}
|
|
|
|
/// Returns true if and only if the type is a fixed-width integer.
|
|
pub fn isInt(self: Type, zcu: *const Zcu) bool {
|
|
return self.toIntern() != .comptime_int_type and
|
|
zcu.intern_pool.isIntegerType(self.toIntern());
|
|
}
|
|
|
|
/// Returns true if and only if the type is a fixed-width, signed integer.
|
|
pub fn isSignedInt(ty: Type, zcu: *const Zcu) bool {
|
|
return switch (ty.toIntern()) {
|
|
.c_char_type => zcu.getTarget().cCharSignedness() == .signed,
|
|
.isize_type, .c_short_type, .c_int_type, .c_long_type, .c_longlong_type => true,
|
|
else => switch (zcu.intern_pool.indexToKey(ty.toIntern())) {
|
|
.int_type => |int_type| int_type.signedness == .signed,
|
|
else => false,
|
|
},
|
|
};
|
|
}
|
|
|
|
/// Returns true if and only if the type is a fixed-width, unsigned integer.
|
|
pub fn isUnsignedInt(ty: Type, zcu: *const Zcu) bool {
|
|
return switch (ty.toIntern()) {
|
|
.c_char_type => zcu.getTarget().cCharSignedness() == .unsigned,
|
|
.usize_type, .c_ushort_type, .c_uint_type, .c_ulong_type, .c_ulonglong_type => true,
|
|
else => switch (zcu.intern_pool.indexToKey(ty.toIntern())) {
|
|
.int_type => |int_type| int_type.signedness == .unsigned,
|
|
else => false,
|
|
},
|
|
};
|
|
}
|
|
|
|
/// Returns true for integers, enums, error sets, and packed structs.
|
|
/// If this function returns true, then intInfo() can be called on the type.
|
|
pub fn isAbiInt(ty: Type, zcu: *const Zcu) bool {
|
|
return switch (ty.zigTypeTag(zcu)) {
|
|
.int, .@"enum", .error_set => true,
|
|
.@"struct" => ty.containerLayout(zcu) == .@"packed",
|
|
else => false,
|
|
};
|
|
}
|
|
|
|
/// Asserts the type is an integer, enum, error set, or vector of one of them.
|
|
pub fn intInfo(starting_ty: Type, zcu: *const Zcu) InternPool.Key.IntType {
|
|
const ip = &zcu.intern_pool;
|
|
const target = zcu.getTarget();
|
|
var ty = starting_ty;
|
|
|
|
while (true) switch (ty.toIntern()) {
|
|
.anyerror_type, .adhoc_inferred_error_set_type => {
|
|
return .{ .signedness = .unsigned, .bits = zcu.errorSetBits() };
|
|
},
|
|
.usize_type => return .{ .signedness = .unsigned, .bits = target.ptrBitWidth() },
|
|
.isize_type => return .{ .signedness = .signed, .bits = target.ptrBitWidth() },
|
|
.c_char_type => return .{ .signedness = zcu.getTarget().cCharSignedness(), .bits = target.cTypeBitSize(.char) },
|
|
.c_short_type => return .{ .signedness = .signed, .bits = target.cTypeBitSize(.short) },
|
|
.c_ushort_type => return .{ .signedness = .unsigned, .bits = target.cTypeBitSize(.ushort) },
|
|
.c_int_type => return .{ .signedness = .signed, .bits = target.cTypeBitSize(.int) },
|
|
.c_uint_type => return .{ .signedness = .unsigned, .bits = target.cTypeBitSize(.uint) },
|
|
.c_long_type => return .{ .signedness = .signed, .bits = target.cTypeBitSize(.long) },
|
|
.c_ulong_type => return .{ .signedness = .unsigned, .bits = target.cTypeBitSize(.ulong) },
|
|
.c_longlong_type => return .{ .signedness = .signed, .bits = target.cTypeBitSize(.longlong) },
|
|
.c_ulonglong_type => return .{ .signedness = .unsigned, .bits = target.cTypeBitSize(.ulonglong) },
|
|
else => switch (ip.indexToKey(ty.toIntern())) {
|
|
.int_type => |int_type| return int_type,
|
|
.struct_type => {
|
|
const struct_obj = ip.loadStructType(ty.toIntern());
|
|
assert(struct_obj.layout == .@"packed");
|
|
ty = .fromInterned(struct_obj.packed_backing_int_type);
|
|
},
|
|
.enum_type => ty = .fromInterned(ip.loadEnumType(ty.toIntern()).int_tag_type),
|
|
.vector_type => |vector_type| ty = Type.fromInterned(vector_type.child),
|
|
|
|
.error_set_type, .inferred_error_set_type => {
|
|
return .{ .signedness = .unsigned, .bits = zcu.errorSetBits() };
|
|
},
|
|
|
|
.tuple_type => unreachable,
|
|
|
|
.ptr_type => unreachable,
|
|
.anyframe_type => unreachable,
|
|
.array_type => unreachable,
|
|
|
|
.opt_type => unreachable,
|
|
.error_union_type => unreachable,
|
|
.func_type => unreachable,
|
|
.simple_type => unreachable, // handled via Index enum tag above
|
|
|
|
.union_type => unreachable,
|
|
.opaque_type => unreachable,
|
|
|
|
// values, not types
|
|
.undef,
|
|
.simple_value,
|
|
.variable,
|
|
.@"extern",
|
|
.func,
|
|
.int,
|
|
.err,
|
|
.error_union,
|
|
.enum_literal,
|
|
.enum_tag,
|
|
.empty_enum_value,
|
|
.float,
|
|
.ptr,
|
|
.slice,
|
|
.opt,
|
|
.aggregate,
|
|
.un,
|
|
.bitpack,
|
|
// memoization, not types
|
|
.memoized_call,
|
|
=> unreachable,
|
|
},
|
|
};
|
|
}
|
|
|
|
/// Returns `false` for `comptime_float`.
|
|
pub fn isRuntimeFloat(ty: Type) bool {
|
|
return switch (ty.toIntern()) {
|
|
.f16_type,
|
|
.f32_type,
|
|
.f64_type,
|
|
.f80_type,
|
|
.f128_type,
|
|
.c_longdouble_type,
|
|
=> true,
|
|
|
|
else => false,
|
|
};
|
|
}
|
|
|
|
/// Returns `true` for `comptime_float`.
|
|
pub fn isAnyFloat(ty: Type) bool {
|
|
return switch (ty.toIntern()) {
|
|
.f16_type,
|
|
.f32_type,
|
|
.f64_type,
|
|
.f80_type,
|
|
.f128_type,
|
|
.c_longdouble_type,
|
|
.comptime_float_type,
|
|
=> true,
|
|
|
|
else => false,
|
|
};
|
|
}
|
|
|
|
/// Asserts the type is a fixed-size float or comptime_float.
|
|
/// Returns 128 for comptime_float types.
|
|
pub fn floatBits(ty: Type, target: *const Target) u16 {
|
|
return switch (ty.toIntern()) {
|
|
.f16_type => 16,
|
|
.f32_type => 32,
|
|
.f64_type => 64,
|
|
.f80_type => 80,
|
|
.f128_type, .comptime_float_type => 128,
|
|
.c_longdouble_type => target.cTypeBitSize(.longdouble),
|
|
|
|
else => unreachable,
|
|
};
|
|
}
|
|
|
|
/// Asserts the type is a function or a function pointer.
|
|
pub fn fnReturnType(ty: Type, zcu: *const Zcu) Type {
|
|
return Type.fromInterned(zcu.intern_pool.funcTypeReturnType(ty.toIntern()));
|
|
}
|
|
|
|
/// Asserts the type is a function.
|
|
pub fn fnCallingConvention(ty: Type, zcu: *const Zcu) std.builtin.CallingConvention {
|
|
return zcu.intern_pool.indexToKey(ty.toIntern()).func_type.cc;
|
|
}
|
|
|
|
pub fn isValidParamType(self: Type, zcu: *const Zcu) bool {
|
|
if (self.toIntern() == .generic_poison_type) return true;
|
|
return switch (self.zigTypeTag(zcu)) {
|
|
.@"opaque", .noreturn => false,
|
|
else => true,
|
|
};
|
|
}
|
|
|
|
pub fn isValidReturnType(self: Type, zcu: *const Zcu) bool {
|
|
if (self.toIntern() == .generic_poison_type) return true;
|
|
return switch (self.zigTypeTag(zcu)) {
|
|
.@"opaque" => false,
|
|
else => true,
|
|
};
|
|
}
|
|
|
|
/// Asserts the type is a function.
|
|
pub fn fnIsVarArgs(ty: Type, zcu: *const Zcu) bool {
|
|
return zcu.intern_pool.indexToKey(ty.toIntern()).func_type.is_var_args;
|
|
}
|
|
|
|
pub fn fnPtrMaskOrNull(ty: Type, zcu: *const Zcu) ?u64 {
|
|
return switch (ty.zigTypeTag(zcu)) {
|
|
.@"fn" => target_util.functionPointerMask(zcu.getTarget()),
|
|
else => null,
|
|
};
|
|
}
|
|
|
|
pub fn isNumeric(ty: Type, zcu: *const Zcu) bool {
|
|
return switch (ty.toIntern()) {
|
|
.f16_type,
|
|
.f32_type,
|
|
.f64_type,
|
|
.f80_type,
|
|
.f128_type,
|
|
.c_longdouble_type,
|
|
.comptime_int_type,
|
|
.comptime_float_type,
|
|
.usize_type,
|
|
.isize_type,
|
|
.c_char_type,
|
|
.c_short_type,
|
|
.c_ushort_type,
|
|
.c_int_type,
|
|
.c_uint_type,
|
|
.c_long_type,
|
|
.c_ulong_type,
|
|
.c_longlong_type,
|
|
.c_ulonglong_type,
|
|
=> true,
|
|
|
|
else => switch (zcu.intern_pool.indexToKey(ty.toIntern())) {
|
|
.int_type => true,
|
|
else => false,
|
|
},
|
|
};
|
|
}
|
|
|
|
/// MLUGG TODO: deal with our friends structs and unions
|
|
pub fn onePossibleValue(starting_type: Type, pt: Zcu.PerThread) !?Value {
|
|
const zcu = pt.zcu;
|
|
const comp = zcu.comp;
|
|
const gpa = comp.gpa;
|
|
const ip = &zcu.intern_pool;
|
|
assertHasLayout(starting_type, zcu);
|
|
var ty = starting_type;
|
|
while (true) switch (ty.toIntern()) {
|
|
.empty_tuple_type => return .empty_tuple,
|
|
|
|
else => switch (ip.indexToKey(ty.toIntern())) {
|
|
.int_type => |int_type| {
|
|
if (int_type.bits == 0) {
|
|
return try pt.intValue(ty, 0);
|
|
} else {
|
|
return null;
|
|
}
|
|
},
|
|
|
|
.ptr_type,
|
|
.error_union_type,
|
|
.func_type,
|
|
.anyframe_type,
|
|
.error_set_type,
|
|
.inferred_error_set_type,
|
|
=> return null,
|
|
|
|
inline .array_type, .vector_type => |seq_type, seq_tag| {
|
|
const has_sentinel = seq_tag == .array_type and seq_type.sentinel != .none;
|
|
if (seq_type.len + @intFromBool(has_sentinel) == 0) {
|
|
return try pt.aggregateValue(ty, &.{});
|
|
}
|
|
if (try Type.fromInterned(seq_type.child).onePossibleValue(pt)) |opv| {
|
|
return try pt.aggregateSplatValue(ty, opv);
|
|
}
|
|
return null;
|
|
},
|
|
.opt_type => |child| {
|
|
if (child == .noreturn_type) {
|
|
return try pt.nullValue(ty);
|
|
} else {
|
|
return null;
|
|
}
|
|
},
|
|
|
|
.simple_type => |t| switch (t) {
|
|
.f16,
|
|
.f32,
|
|
.f64,
|
|
.f80,
|
|
.f128,
|
|
.usize,
|
|
.isize,
|
|
.c_char,
|
|
.c_short,
|
|
.c_ushort,
|
|
.c_int,
|
|
.c_uint,
|
|
.c_long,
|
|
.c_ulong,
|
|
.c_longlong,
|
|
.c_ulonglong,
|
|
.c_longdouble,
|
|
.anyopaque,
|
|
.bool,
|
|
.type,
|
|
.anyerror,
|
|
.comptime_int,
|
|
.comptime_float,
|
|
.enum_literal,
|
|
.adhoc_inferred_error_set,
|
|
=> return null,
|
|
|
|
.void => return .void,
|
|
.noreturn => return .@"unreachable",
|
|
.null => return .null,
|
|
.undefined => return .undef,
|
|
|
|
.generic_poison => unreachable,
|
|
},
|
|
.struct_type => {
|
|
const struct_obj = ip.loadStructType(ty.toIntern());
|
|
if (struct_obj.layout == .@"packed") {
|
|
const backing_ty: Type = .fromInterned(struct_obj.packed_backing_int_type);
|
|
const backing_val = try backing_ty.onePossibleValue(pt) orelse return null;
|
|
return try pt.bitpackValue(ty, backing_val);
|
|
} else {
|
|
if (!struct_obj.has_one_possible_value) return null;
|
|
}
|
|
// There is an OPV.
|
|
const field_vals = try gpa.alloc(InternPool.Index, struct_obj.field_types.len);
|
|
defer gpa.free(field_vals);
|
|
for (field_vals, 0..) |*field_val, i_usize| {
|
|
const i: u32 = @intCast(i_usize);
|
|
if (struct_obj.field_is_comptime_bits.get(ip, i)) {
|
|
field_val.* = struct_obj.field_defaults.get(ip)[i];
|
|
assert(field_val.* != .none);
|
|
continue;
|
|
}
|
|
const field_ty = Type.fromInterned(struct_obj.field_types.get(ip)[i]);
|
|
field_val.* = (try field_ty.onePossibleValue(pt)).?.toIntern();
|
|
}
|
|
|
|
// In this case the struct has no runtime-known fields and
|
|
// therefore has one possible value.
|
|
return try pt.aggregateValue(ty, field_vals);
|
|
},
|
|
|
|
.tuple_type => |tuple| {
|
|
if (tuple.types.len == 0) {
|
|
return try pt.aggregateValue(ty, &.{});
|
|
}
|
|
|
|
const field_vals = try zcu.gpa.alloc(
|
|
InternPool.Index,
|
|
tuple.types.len,
|
|
);
|
|
defer zcu.gpa.free(field_vals);
|
|
for (
|
|
field_vals,
|
|
tuple.types.get(ip),
|
|
tuple.values.get(ip),
|
|
) |*field_val, field_ty, field_comptime_val| {
|
|
if (field_comptime_val != .none) {
|
|
field_val.* = field_comptime_val;
|
|
continue;
|
|
}
|
|
if (try Type.fromInterned(field_ty).onePossibleValue(pt)) |opv| {
|
|
field_val.* = opv.toIntern();
|
|
} else return null;
|
|
}
|
|
|
|
return try pt.aggregateValue(ty, field_vals);
|
|
},
|
|
|
|
.union_type => {
|
|
const union_obj = ip.loadUnionType(ty.toIntern());
|
|
if (union_obj.layout == .@"packed") {
|
|
const backing_ty: Type = .fromInterned(union_obj.packed_backing_int_type);
|
|
const backing_val = try backing_ty.onePossibleValue(pt) orelse return null;
|
|
return try pt.bitpackValue(ty, backing_val);
|
|
}
|
|
// MLUGG TODO: is this nonsensical or what!!!!!!
|
|
const tag_val = (try Type.fromInterned(union_obj.enum_tag_type).onePossibleValue(pt)) orelse
|
|
return null;
|
|
if (union_obj.field_types.len == 0) {
|
|
const only = try pt.intern(.{ .empty_enum_value = ty.toIntern() });
|
|
return .fromInterned(only);
|
|
}
|
|
const only_field_ty = union_obj.field_types.get(ip)[0];
|
|
const val_val = (try Type.fromInterned(only_field_ty).onePossibleValue(pt)) orelse
|
|
return null;
|
|
const only = try pt.internUnion(.{
|
|
.ty = ty.toIntern(),
|
|
.tag = tag_val.toIntern(),
|
|
.val = val_val.toIntern(),
|
|
});
|
|
return .fromInterned(only);
|
|
},
|
|
.opaque_type => return null,
|
|
.enum_type => {
|
|
const enum_obj = ip.loadEnumType(ty.toIntern());
|
|
if (enum_obj.nonexhaustive) {
|
|
const int_opv = try Type.fromInterned(enum_obj.int_tag_type).onePossibleValue(pt) orelse return null;
|
|
return .fromInterned(try pt.intern(.{ .enum_tag = .{
|
|
.ty = ty.toIntern(),
|
|
.int = int_opv.toIntern(),
|
|
} }));
|
|
}
|
|
// MLUGG TODO: this is to preserve existing semantics, i REALLY don't fuck with it...
|
|
if (enum_obj.int_tag_type == .comptime_int_type) {
|
|
return switch (enum_obj.field_names.len) {
|
|
0 => .fromInterned(try pt.intern(.{ .empty_enum_value = ty.toIntern() })),
|
|
1 => try pt.enumValueFieldIndex(ty, 0),
|
|
else => null,
|
|
};
|
|
}
|
|
const int_tag_opv = try Type.fromInterned(enum_obj.int_tag_type).onePossibleValue(pt) orelse return null;
|
|
if (enum_obj.field_names.len == 0) {
|
|
return .fromInterned(try pt.intern(.{ .empty_enum_value = ty.toIntern() }));
|
|
}
|
|
return .fromInterned(try pt.intern(.{ .enum_tag = .{
|
|
.ty = ty.toIntern(),
|
|
.int = int_tag_opv.toIntern(),
|
|
} }));
|
|
},
|
|
|
|
// values, not types
|
|
.undef,
|
|
.simple_value,
|
|
.variable,
|
|
.@"extern",
|
|
.func,
|
|
.int,
|
|
.err,
|
|
.error_union,
|
|
.enum_literal,
|
|
.enum_tag,
|
|
.empty_enum_value,
|
|
.float,
|
|
.ptr,
|
|
.slice,
|
|
.opt,
|
|
.aggregate,
|
|
.un,
|
|
.bitpack,
|
|
// memoization, not types
|
|
.memoized_call,
|
|
=> unreachable,
|
|
},
|
|
};
|
|
}
|
|
|
|
/// Asserts that `ty` has its layout resolved. `generic_poison` will return `false`.
|
|
pub fn comptimeOnly(ty: Type, zcu: *const Zcu) bool {
|
|
const ip = &zcu.intern_pool;
|
|
return switch (ip.indexToKey(ty.toIntern())) {
|
|
.array_type => |array_type| return Type.fromInterned(array_type.child).comptimeOnly(zcu),
|
|
.vector_type => |vector_type| return Type.fromInterned(vector_type.child).comptimeOnly(zcu),
|
|
.opt_type => |child| return Type.fromInterned(child).comptimeOnly(zcu),
|
|
.error_union_type => |error_union_type| return Type.fromInterned(error_union_type.payload_type).comptimeOnly(zcu),
|
|
.enum_type => return Type.fromInterned(ip.loadEnumType(ty.toIntern()).int_tag_type).comptimeOnly(zcu),
|
|
|
|
.int_type,
|
|
.ptr_type,
|
|
.anyframe_type,
|
|
.error_set_type,
|
|
.inferred_error_set_type,
|
|
.opaque_type,
|
|
=> false,
|
|
|
|
// These are function bodies, not function pointers.
|
|
.func_type => true,
|
|
|
|
.simple_type => |t| switch (t) {
|
|
.f16,
|
|
.f32,
|
|
.f64,
|
|
.f80,
|
|
.f128,
|
|
.usize,
|
|
.isize,
|
|
.c_char,
|
|
.c_short,
|
|
.c_ushort,
|
|
.c_int,
|
|
.c_uint,
|
|
.c_long,
|
|
.c_ulong,
|
|
.c_longlong,
|
|
.c_ulonglong,
|
|
.c_longdouble,
|
|
.anyopaque,
|
|
.bool,
|
|
.void,
|
|
.anyerror,
|
|
.adhoc_inferred_error_set,
|
|
.noreturn,
|
|
.generic_poison,
|
|
=> false,
|
|
|
|
.type,
|
|
.comptime_int,
|
|
.comptime_float,
|
|
.null,
|
|
.undefined,
|
|
.enum_literal,
|
|
=> true,
|
|
},
|
|
.struct_type => {
|
|
const struct_obj = ip.loadStructType(ty.toIntern());
|
|
return switch (struct_obj.layout) {
|
|
.@"packed" => false,
|
|
.auto, .@"extern" => struct_obj.comptime_only,
|
|
};
|
|
},
|
|
.union_type => {
|
|
const union_obj = ip.loadUnionType(ty.toIntern());
|
|
return switch (union_obj.layout) {
|
|
.@"packed" => false,
|
|
.auto, .@"extern" => union_obj.comptime_only,
|
|
};
|
|
},
|
|
.tuple_type => |tuple| {
|
|
for (tuple.types.get(ip), tuple.values.get(ip)) |field_ty, val| {
|
|
if (val != .none) continue;
|
|
if (!Type.fromInterned(field_ty).comptimeOnly(zcu)) continue;
|
|
return true;
|
|
}
|
|
return false;
|
|
},
|
|
|
|
// values, not types
|
|
.undef,
|
|
.simple_value,
|
|
.variable,
|
|
.@"extern",
|
|
.func,
|
|
.int,
|
|
.err,
|
|
.error_union,
|
|
.enum_literal,
|
|
.enum_tag,
|
|
.empty_enum_value,
|
|
.float,
|
|
.ptr,
|
|
.slice,
|
|
.opt,
|
|
.aggregate,
|
|
.un,
|
|
.bitpack,
|
|
// memoization, not types
|
|
.memoized_call,
|
|
=> unreachable,
|
|
};
|
|
}
|
|
|
|
pub fn isVector(ty: Type, zcu: *const Zcu) bool {
|
|
return ty.zigTypeTag(zcu) == .vector;
|
|
}
|
|
|
|
/// Returns 0 if not a vector, otherwise returns @bitSizeOf(Element) * vector_len.
|
|
pub fn totalVectorBits(ty: Type, zcu: *Zcu) u64 {
|
|
if (!ty.isVector(zcu)) return 0;
|
|
const v = zcu.intern_pool.indexToKey(ty.toIntern()).vector_type;
|
|
return v.len * Type.fromInterned(v.child).bitSize(zcu);
|
|
}
|
|
|
|
pub fn isArrayOrVector(ty: Type, zcu: *const Zcu) bool {
|
|
return switch (ty.zigTypeTag(zcu)) {
|
|
.array, .vector => true,
|
|
else => false,
|
|
};
|
|
}
|
|
|
|
pub fn isIndexable(ty: Type, zcu: *const Zcu) bool {
|
|
return switch (ty.zigTypeTag(zcu)) {
|
|
.array, .vector => true,
|
|
.pointer => switch (ty.ptrSize(zcu)) {
|
|
.slice, .many, .c => true,
|
|
.one => switch (ty.childType(zcu).zigTypeTag(zcu)) {
|
|
.array, .vector => true,
|
|
.@"struct" => ty.childType(zcu).isTuple(zcu),
|
|
else => false,
|
|
},
|
|
},
|
|
.@"struct" => ty.isTuple(zcu),
|
|
else => false,
|
|
};
|
|
}
|
|
|
|
pub fn indexableHasLen(ty: Type, zcu: *const Zcu) bool {
|
|
return switch (ty.zigTypeTag(zcu)) {
|
|
.array, .vector => true,
|
|
.pointer => switch (ty.ptrSize(zcu)) {
|
|
.many, .c => false,
|
|
.slice => true,
|
|
.one => switch (ty.childType(zcu).zigTypeTag(zcu)) {
|
|
.array, .vector => true,
|
|
.@"struct" => ty.childType(zcu).isTuple(zcu),
|
|
else => false,
|
|
},
|
|
},
|
|
.@"struct" => ty.isTuple(zcu),
|
|
else => false,
|
|
};
|
|
}
|
|
|
|
/// Asserts that the type can have a namespace.
|
|
pub fn getNamespaceIndex(ty: Type, zcu: *Zcu) InternPool.NamespaceIndex {
|
|
return ty.getNamespace(zcu).unwrap().?;
|
|
}
|
|
|
|
/// Returns null if the type has no namespace.
|
|
pub fn getNamespace(ty: Type, zcu: *Zcu) InternPool.OptionalNamespaceIndex {
|
|
const ip = &zcu.intern_pool;
|
|
return switch (ip.indexToKey(ty.toIntern())) {
|
|
.opaque_type => ip.loadOpaqueType(ty.toIntern()).namespace.toOptional(),
|
|
.struct_type => ip.loadStructType(ty.toIntern()).namespace.toOptional(),
|
|
.union_type => ip.loadUnionType(ty.toIntern()).namespace.toOptional(),
|
|
.enum_type => ip.loadEnumType(ty.toIntern()).namespace.toOptional(),
|
|
else => .none,
|
|
};
|
|
}
|
|
|
|
// TODO: new dwarf structure will also need the enclosing code block for types created in imperative scopes
|
|
pub fn getParentNamespace(ty: Type, zcu: *Zcu) InternPool.OptionalNamespaceIndex {
|
|
return zcu.namespacePtr(ty.getNamespace(zcu).unwrap() orelse return .none).parent;
|
|
}
|
|
|
|
// Works for vectors and vectors of integers.
|
|
pub fn minInt(ty: Type, pt: Zcu.PerThread, dest_ty: Type) !Value {
|
|
const zcu = pt.zcu;
|
|
const scalar = try minIntScalar(ty.scalarType(zcu), pt, dest_ty.scalarType(zcu));
|
|
return if (ty.zigTypeTag(zcu) == .vector) pt.aggregateSplatValue(dest_ty, scalar) else scalar;
|
|
}
|
|
|
|
/// Asserts that the type is an integer.
|
|
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 (std.math.cast(u6, info.bits - 1)) |shift| {
|
|
const n = @as(i64, std.math.minInt(i64)) >> (63 - shift);
|
|
return pt.intValue(dest_ty, n);
|
|
}
|
|
|
|
var res = try std.math.big.int.Managed.init(zcu.gpa);
|
|
defer res.deinit();
|
|
|
|
try res.setTwosCompIntLimit(.min, info.signedness, info.bits);
|
|
|
|
return pt.intValue_big(dest_ty, res.toConst());
|
|
}
|
|
|
|
// Works for vectors and vectors of integers.
|
|
/// The returned Value will have type dest_ty.
|
|
pub fn maxInt(ty: Type, pt: Zcu.PerThread, dest_ty: Type) !Value {
|
|
const zcu = pt.zcu;
|
|
const scalar = try maxIntScalar(ty.scalarType(zcu), pt, dest_ty.scalarType(zcu));
|
|
return if (ty.zigTypeTag(zcu) == .vector) pt.aggregateSplatValue(dest_ty, scalar) else scalar;
|
|
}
|
|
|
|
/// The returned Value will have type dest_ty.
|
|
pub fn maxIntScalar(ty: Type, pt: Zcu.PerThread, dest_ty: Type) !Value {
|
|
const info = ty.intInfo(pt.zcu);
|
|
|
|
switch (info.bits) {
|
|
0 => return pt.intValue(dest_ty, 0),
|
|
1 => return switch (info.signedness) {
|
|
.signed => try pt.intValue(dest_ty, 0),
|
|
.unsigned => try pt.intValue(dest_ty, 1),
|
|
},
|
|
else => {},
|
|
}
|
|
|
|
if (std.math.cast(u6, info.bits - 1)) |shift| switch (info.signedness) {
|
|
.signed => {
|
|
const n = @as(i64, std.math.maxInt(i64)) >> (63 - shift);
|
|
return pt.intValue(dest_ty, n);
|
|
},
|
|
.unsigned => {
|
|
const n = @as(u64, std.math.maxInt(u64)) >> (63 - shift);
|
|
return pt.intValue(dest_ty, n);
|
|
},
|
|
};
|
|
|
|
var res = try std.math.big.int.Managed.init(pt.zcu.gpa);
|
|
defer res.deinit();
|
|
|
|
try res.setTwosCompIntLimit(.max, info.signedness, info.bits);
|
|
|
|
return pt.intValue_big(dest_ty, res.toConst());
|
|
}
|
|
|
|
/// Asserts the type is an enum or a union.
|
|
pub fn intTagType(ty: Type, zcu: *const Zcu) Type {
|
|
const ip = &zcu.intern_pool;
|
|
const enum_ty: Type = switch (ip.indexToKey(ty.toIntern())) {
|
|
.union_type => .fromInterned(ip.loadUnionType(ty.toIntern()).enum_tag_type),
|
|
.enum_type => ty,
|
|
else => unreachable,
|
|
};
|
|
return .fromInterned(ip.loadEnumType(enum_ty.toIntern()).int_tag_type);
|
|
}
|
|
|
|
pub fn isNonexhaustiveEnum(ty: Type, zcu: *const Zcu) bool {
|
|
const ip = &zcu.intern_pool;
|
|
return switch (ip.indexToKey(ty.toIntern())) {
|
|
.enum_type => ip.loadEnumType(ty.toIntern()).nonexhaustive,
|
|
else => false,
|
|
};
|
|
}
|
|
|
|
// Asserts that `ty` is an error set and not `anyerror`.
|
|
// Asserts that `ty` is resolved if it is an inferred error set.
|
|
pub fn errorSetNames(ty: Type, zcu: *const Zcu) InternPool.NullTerminatedString.Slice {
|
|
const ip = &zcu.intern_pool;
|
|
return switch (ip.indexToKey(ty.toIntern())) {
|
|
.error_set_type => |x| x.names,
|
|
.inferred_error_set_type => |i| switch (ip.funcIesResolvedUnordered(i)) {
|
|
.none => unreachable, // unresolved inferred error set
|
|
.anyerror_type => unreachable,
|
|
else => |t| ip.indexToKey(t).error_set_type.names,
|
|
},
|
|
else => unreachable,
|
|
};
|
|
}
|
|
|
|
pub fn enumFields(ty: Type, zcu: *const Zcu) InternPool.NullTerminatedString.Slice {
|
|
assertHasLayout(ty, zcu);
|
|
return zcu.intern_pool.loadEnumType(ty.toIntern()).field_names;
|
|
}
|
|
|
|
pub fn enumFieldCount(ty: Type, zcu: *const Zcu) usize {
|
|
assertHasLayout(ty, zcu);
|
|
return zcu.intern_pool.loadEnumType(ty.toIntern()).field_names.len;
|
|
}
|
|
|
|
pub fn enumFieldName(ty: Type, field_index: usize, zcu: *const Zcu) InternPool.NullTerminatedString {
|
|
assertHasLayout(ty, zcu);
|
|
const ip = &zcu.intern_pool;
|
|
return ip.loadEnumType(ty.toIntern()).field_names.get(ip)[field_index];
|
|
}
|
|
|
|
pub fn enumFieldIndex(ty: Type, field_name: InternPool.NullTerminatedString, zcu: *const Zcu) ?u32 {
|
|
assertHasLayout(ty, zcu);
|
|
const ip = &zcu.intern_pool;
|
|
const enum_type = ip.loadEnumType(ty.toIntern());
|
|
return enum_type.nameIndex(ip, field_name);
|
|
}
|
|
|
|
/// Asserts `ty` is an enum. `enum_tag` can either be `enum_field_index` or
|
|
/// an integer which represents the enum value. Returns the field index in
|
|
/// declaration order, or `null` if `enum_tag` does not match any field.
|
|
pub fn enumTagFieldIndex(ty: Type, enum_tag: Value, zcu: *const Zcu) ?u32 {
|
|
assertHasLayout(ty, zcu);
|
|
const ip = &zcu.intern_pool;
|
|
const enum_type = ip.loadEnumType(ty.toIntern());
|
|
const int_tag = switch (ip.indexToKey(enum_tag.toIntern())) {
|
|
.int => enum_tag.toIntern(),
|
|
.enum_tag => |info| info.int,
|
|
else => unreachable,
|
|
};
|
|
assert(ip.typeOf(int_tag) == enum_type.int_tag_type);
|
|
return enum_type.tagValueIndex(ip, int_tag);
|
|
}
|
|
|
|
/// Returns none in the case of a tuple which uses the integer index as the field name.
|
|
pub fn structFieldName(ty: Type, index: usize, zcu: *const Zcu) InternPool.OptionalNullTerminatedString {
|
|
const ip = &zcu.intern_pool;
|
|
switch (ip.indexToKey(ty.toIntern())) {
|
|
.struct_type => {
|
|
assertHasLayout(ty, zcu);
|
|
return ip.loadStructType(ty.toIntern()).field_names.get(ip)[index].toOptional();
|
|
},
|
|
.tuple_type => return .none,
|
|
else => unreachable,
|
|
}
|
|
}
|
|
|
|
pub fn structFieldCount(ty: Type, zcu: *const Zcu) u32 {
|
|
const ip = &zcu.intern_pool;
|
|
switch (ip.indexToKey(ty.toIntern())) {
|
|
.struct_type => {
|
|
assertHasLayout(ty, zcu);
|
|
return ip.loadStructType(ty.toIntern()).field_types.len;
|
|
},
|
|
.tuple_type => |tuple| return tuple.types.len,
|
|
else => unreachable,
|
|
}
|
|
}
|
|
|
|
/// Returns the field type. Supports structs and unions.
|
|
pub fn fieldType(ty: Type, index: usize, zcu: *const Zcu) Type {
|
|
const ip = &zcu.intern_pool;
|
|
const types = switch (ip.indexToKey(ty.toIntern())) {
|
|
.struct_type => types: {
|
|
assertHasLayout(ty, zcu);
|
|
break :types ip.loadStructType(ty.toIntern()).field_types;
|
|
},
|
|
.union_type => types: {
|
|
assertHasLayout(ty, zcu);
|
|
break :types ip.loadUnionType(ty.toIntern()).field_types;
|
|
},
|
|
.tuple_type => |tuple| tuple.types,
|
|
else => unreachable,
|
|
};
|
|
return .fromInterned(types.get(ip)[index]);
|
|
}
|
|
|
|
// TODO MLUGG: clean up doc comments and usages of `{resolved,explicit}FieldAlignment`
|
|
|
|
/// Returns the alignment of the given struct, tuple, or union field.
|
|
/// Asserts that the layout of `ty` is resolved. Asserts that `ty` is not packed.
|
|
/// Never returns `.none`, even if the field's alignment was not specified.
|
|
pub fn resolvedFieldAlignment(ty: Type, index: usize, zcu: *const Zcu) Alignment {
|
|
switch (ty.explicitFieldAlignment(index, zcu)) {
|
|
.none => {},
|
|
else => |explicit| return explicit,
|
|
}
|
|
const ip = &zcu.intern_pool;
|
|
return switch (ip.indexToKey(ty.toIntern())) {
|
|
.tuple_type => |tuple| Type.fromInterned(tuple.types.get(ip)[index]).abiAlignment(zcu),
|
|
.struct_type => {
|
|
assertHasLayout(ty, zcu);
|
|
const struct_obj = ip.loadStructType(ty.toIntern());
|
|
const field_ty: Type = .fromInterned(struct_obj.field_types.get(ip)[index]);
|
|
return field_ty.defaultStructFieldAlignment(struct_obj.layout, zcu);
|
|
},
|
|
.union_type => {
|
|
assertHasLayout(ty, zcu);
|
|
const union_obj = ip.loadUnionType(ty.toIntern());
|
|
const field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[index]);
|
|
return field_ty.abiAlignment(zcu);
|
|
},
|
|
else => unreachable,
|
|
};
|
|
}
|
|
|
|
pub fn explicitFieldAlignment(ty: Type, index: usize, zcu: *const Zcu) Alignment {
|
|
const ip = &zcu.intern_pool;
|
|
return switch (ip.indexToKey(ty.toIntern())) {
|
|
.tuple_type => .none,
|
|
.struct_type => {
|
|
assertHasLayout(ty, zcu);
|
|
const struct_obj = ip.loadStructType(ty.toIntern());
|
|
assert(struct_obj.layout != .@"packed");
|
|
if (struct_obj.field_aligns.len == 0) return .none;
|
|
return struct_obj.field_aligns.get(ip)[index];
|
|
},
|
|
.union_type => {
|
|
assertHasLayout(ty, zcu);
|
|
const union_obj = ip.loadUnionType(ty.toIntern());
|
|
assert(union_obj.layout != .@"packed");
|
|
if (union_obj.field_aligns.len == 0) return .none;
|
|
return union_obj.field_aligns.get(ip)[index];
|
|
},
|
|
else => unreachable,
|
|
};
|
|
}
|
|
|
|
/// Returns the alignment a struct field will have if not explicitly specified.
|
|
/// Asserts that the layout of `field_ty` is resolved. Asserts that `layout` is not `.@"packed"`.
|
|
pub fn defaultStructFieldAlignment(
|
|
field_ty: Type,
|
|
layout: std.builtin.Type.ContainerLayout,
|
|
zcu: *const Zcu,
|
|
) Alignment {
|
|
const overalign_big_int = switch (layout) {
|
|
.@"packed" => unreachable,
|
|
.auto => zcu.getTarget().ofmt == .c,
|
|
.@"extern" => true,
|
|
};
|
|
const abi_align = field_ty.abiAlignment(zcu);
|
|
assert(abi_align != .none);
|
|
if (overalign_big_int and field_ty.isAbiInt(zcu) and field_ty.intInfo(zcu).bits >= 128) {
|
|
return abi_align.maxStrict(.@"16");
|
|
}
|
|
return abi_align;
|
|
}
|
|
|
|
pub fn structFieldDefaultValue(ty: Type, index: usize, zcu: *const Zcu) ?Value {
|
|
const ip = &zcu.intern_pool;
|
|
switch (ip.indexToKey(ty.toIntern())) {
|
|
.struct_type => {
|
|
const field_defaults = ip.loadStructType(ty.toIntern()).field_defaults.get(ip);
|
|
if (field_defaults.len == 0) return null;
|
|
if (field_defaults[index] == .none) return null;
|
|
return .fromInterned(field_defaults[index]);
|
|
},
|
|
.tuple_type => |tuple| {
|
|
const val = tuple.values.get(ip)[index];
|
|
if (val == .none) return null;
|
|
return .fromInterned(val);
|
|
},
|
|
else => unreachable,
|
|
}
|
|
}
|
|
|
|
pub fn structFieldValueComptime(ty: Type, pt: Zcu.PerThread, index: usize) !?Value {
|
|
const zcu = pt.zcu;
|
|
const ip = &zcu.intern_pool;
|
|
switch (ip.indexToKey(ty.toIntern())) {
|
|
.struct_type => {
|
|
const struct_type = ip.loadStructType(ty.toIntern());
|
|
if (struct_type.field_is_comptime_bits.get(ip, index)) {
|
|
return .fromInterned(struct_type.field_defaults.get(ip)[index]);
|
|
} else {
|
|
return Type.fromInterned(struct_type.field_types.get(ip)[index]).onePossibleValue(pt);
|
|
}
|
|
},
|
|
.tuple_type => |tuple| {
|
|
const val = tuple.values.get(ip)[index];
|
|
if (val == .none) {
|
|
return Type.fromInterned(tuple.types.get(ip)[index]).onePossibleValue(pt);
|
|
} else {
|
|
return .fromInterned(val);
|
|
}
|
|
},
|
|
else => unreachable,
|
|
}
|
|
}
|
|
|
|
pub fn structFieldIsComptime(ty: Type, index: usize, zcu: *const Zcu) bool {
|
|
const ip = &zcu.intern_pool;
|
|
switch (ip.indexToKey(ty.toIntern())) {
|
|
.struct_type => {
|
|
assertHasLayout(ty, zcu);
|
|
return ip.loadStructType(ty.toIntern()).field_is_comptime_bits.get(ip, index);
|
|
},
|
|
.tuple_type => |tuple| return tuple.values.get(ip)[index] != .none,
|
|
else => unreachable,
|
|
}
|
|
}
|
|
|
|
pub const FieldOffset = struct {
|
|
field: usize,
|
|
offset: u64,
|
|
};
|
|
|
|
/// Supports structs, tuples, and unions.
|
|
pub fn structFieldOffset(ty: Type, index: usize, zcu: *const Zcu) u64 {
|
|
assertHasLayout(ty, zcu);
|
|
const ip = &zcu.intern_pool;
|
|
switch (ip.indexToKey(ty.toIntern())) {
|
|
.struct_type => {
|
|
const struct_type = ip.loadStructType(ty.toIntern());
|
|
assert(struct_type.layout != .@"packed");
|
|
return struct_type.field_offsets.get(ip)[index];
|
|
},
|
|
|
|
.tuple_type => |tuple| {
|
|
var offset: u64 = 0;
|
|
var big_align: Alignment = .none;
|
|
|
|
for (tuple.types.get(ip), tuple.values.get(ip), 0..) |field_ty, field_val, i| {
|
|
if (field_val != .none or !Type.fromInterned(field_ty).hasRuntimeBits(zcu)) {
|
|
// comptime field
|
|
if (i == index) return offset;
|
|
continue;
|
|
}
|
|
|
|
const field_align = Type.fromInterned(field_ty).abiAlignment(zcu);
|
|
big_align = big_align.max(field_align);
|
|
offset = field_align.forward(offset);
|
|
if (i == index) return offset;
|
|
offset += Type.fromInterned(field_ty).abiSize(zcu);
|
|
}
|
|
offset = big_align.max(.@"1").forward(offset);
|
|
return offset;
|
|
},
|
|
|
|
.union_type => {
|
|
const union_type = ip.loadUnionType(ty.toIntern());
|
|
if (union_type.runtime_tag == .none)
|
|
return 0;
|
|
const layout = Type.getUnionLayout(union_type, zcu);
|
|
if (layout.tag_align.compare(.gte, layout.payload_align)) {
|
|
// {Tag, Payload}
|
|
return layout.payload_align.forward(layout.tag_size);
|
|
} else {
|
|
// {Payload, Tag}
|
|
return 0;
|
|
}
|
|
},
|
|
|
|
else => unreachable,
|
|
}
|
|
}
|
|
|
|
pub fn srcLocOrNull(ty: Type, zcu: *Zcu) ?Zcu.LazySrcLoc {
|
|
const ip = &zcu.intern_pool;
|
|
return .{
|
|
.base_node_inst = switch (ip.indexToKey(ty.toIntern())) {
|
|
.struct_type, .union_type, .opaque_type, .enum_type => |info| switch (info) {
|
|
.declared => |d| d.zir_index,
|
|
.reified => |r| r.zir_index,
|
|
.generated_union_tag => |union_ty| ip.loadUnionType(union_ty).zir_index,
|
|
},
|
|
else => return null,
|
|
},
|
|
.offset = Zcu.LazySrcLoc.Offset.nodeOffset(.zero),
|
|
};
|
|
}
|
|
|
|
pub fn srcLoc(ty: Type, zcu: *Zcu) Zcu.LazySrcLoc {
|
|
return ty.srcLocOrNull(zcu).?;
|
|
}
|
|
|
|
pub fn isGenericPoison(ty: Type) bool {
|
|
return ty.toIntern() == .generic_poison_type;
|
|
}
|
|
|
|
pub fn isTuple(ty: Type, zcu: *const Zcu) bool {
|
|
const ip = &zcu.intern_pool;
|
|
return switch (ip.indexToKey(ty.toIntern())) {
|
|
.tuple_type => true,
|
|
else => false,
|
|
};
|
|
}
|
|
|
|
/// Traverses optional child types and error union payloads until the type is neither of those.
|
|
/// For `E!?u32`, returns `u32`; for `*u8`, returns `*u8`.
|
|
pub fn optEuBaseType(ty: Type, zcu: *const Zcu) Type {
|
|
var cur = ty;
|
|
while (true) switch (cur.zigTypeTag(zcu)) {
|
|
.optional => cur = cur.optionalChild(zcu),
|
|
.error_union => cur = cur.errorUnionPayload(zcu),
|
|
else => return cur,
|
|
};
|
|
}
|
|
|
|
pub fn toUnsigned(ty: Type, pt: Zcu.PerThread) !Type {
|
|
const zcu = pt.zcu;
|
|
return switch (ty.toIntern()) {
|
|
// zig fmt: off
|
|
.usize_type, .isize_type => .usize,
|
|
.c_ushort_type, .c_short_type => .c_ushort,
|
|
.c_uint_type, .c_int_type => .c_uint,
|
|
.c_ulong_type, .c_long_type => .c_ulong,
|
|
.c_ulonglong_type, .c_longlong_type => .c_ulonglong,
|
|
// zig fmt: on
|
|
else => switch (ty.zigTypeTag(zcu)) {
|
|
.int => pt.intType(.unsigned, ty.intInfo(zcu).bits),
|
|
.vector => try pt.vectorType(.{
|
|
.len = ty.vectorLen(zcu),
|
|
.child = (try ty.childType(zcu).toUnsigned(pt)).toIntern(),
|
|
}),
|
|
else => unreachable,
|
|
},
|
|
};
|
|
}
|
|
|
|
pub fn typeDeclInst(ty: Type, zcu: *const Zcu) ?InternPool.TrackedInst.Index {
|
|
const ip = &zcu.intern_pool;
|
|
return switch (ip.indexToKey(ty.toIntern())) {
|
|
.struct_type => ip.loadStructType(ty.toIntern()).zir_index,
|
|
.union_type => ip.loadUnionType(ty.toIntern()).zir_index,
|
|
.enum_type => ip.loadEnumType(ty.toIntern()).zir_index.unwrap(),
|
|
.opaque_type => ip.loadOpaqueType(ty.toIntern()).zir_index,
|
|
else => null,
|
|
};
|
|
}
|
|
|
|
pub fn typeDeclInstAllowGeneratedTag(ty: Type, zcu: *const Zcu) ?InternPool.TrackedInst.Index {
|
|
const ip = &zcu.intern_pool;
|
|
return switch (ip.indexToKey(ty.toIntern())) {
|
|
.struct_type => ip.loadStructType(ty.toIntern()).zir_index,
|
|
.union_type => ip.loadUnionType(ty.toIntern()).zir_index,
|
|
.enum_type => |e| switch (e) {
|
|
.declared, .reified => ip.loadEnumType(ty.toIntern()).zir_index.unwrap().?,
|
|
.generated_union_tag => |union_ty| ip.loadUnionType(union_ty).zir_index,
|
|
},
|
|
.opaque_type => ip.loadOpaqueType(ty.toIntern()).zir_index,
|
|
else => null,
|
|
};
|
|
}
|
|
|
|
pub fn typeDeclSrcLine(ty: Type, zcu: *Zcu) ?u32 {
|
|
// Note that changes to ZIR instruction tracking only need to update this code
|
|
// if a newly-tracked instruction can be a type's owner `zir_index`.
|
|
comptime assert(Zir.inst_tracking_version == 0);
|
|
|
|
const ip = &zcu.intern_pool;
|
|
const tracked = switch (ip.indexToKey(ty.toIntern())) {
|
|
.struct_type, .union_type, .opaque_type, .enum_type => |info| switch (info) {
|
|
.declared => |d| d.zir_index,
|
|
.reified => |r| r.zir_index,
|
|
.generated_union_tag => |union_ty| ip.loadUnionType(union_ty).zir_index,
|
|
},
|
|
else => return null,
|
|
};
|
|
const info = tracked.resolveFull(&zcu.intern_pool) orelse return null;
|
|
const file = zcu.fileByIndex(info.file);
|
|
const zir = switch (file.getMode()) {
|
|
.zig => file.zir.?,
|
|
.zon => return 0,
|
|
};
|
|
const inst = zir.instructions.get(@intFromEnum(info.inst));
|
|
return switch (inst.tag) {
|
|
.struct_init, .struct_init_ref => zir.extraData(Zir.Inst.StructInit, inst.data.pl_node.payload_index).data.abs_line,
|
|
.struct_init_anon => zir.extraData(Zir.Inst.StructInitAnon, inst.data.pl_node.payload_index).data.abs_line,
|
|
.extended => switch (inst.data.extended.opcode) {
|
|
.struct_decl => zir.getStructDecl(info.inst).src_line,
|
|
.union_decl => zir.getUnionDecl(info.inst).src_line,
|
|
.enum_decl => zir.getEnumDecl(info.inst).src_line,
|
|
.opaque_decl => zir.getOpaqueDecl(info.inst).src_line,
|
|
.reify_enum => zir.extraData(Zir.Inst.ReifyEnum, inst.data.extended.operand).data.src_line,
|
|
.reify_struct => zir.extraData(Zir.Inst.ReifyStruct, inst.data.extended.operand).data.src_line,
|
|
.reify_union => zir.extraData(Zir.Inst.ReifyUnion, inst.data.extended.operand).data.src_line,
|
|
else => unreachable,
|
|
},
|
|
else => unreachable,
|
|
};
|
|
}
|
|
|
|
/// Given a namespace type, returns its list of captured values.
|
|
pub fn getCaptures(ty: Type, zcu: *const Zcu) InternPool.CaptureValue.Slice {
|
|
const ip = &zcu.intern_pool;
|
|
return switch (ip.indexToKey(ty.toIntern())) {
|
|
.struct_type => ip.loadStructType(ty.toIntern()).captures,
|
|
.union_type => ip.loadUnionType(ty.toIntern()).captures,
|
|
.enum_type => ip.loadEnumType(ty.toIntern()).captures,
|
|
.opaque_type => ip.loadOpaqueType(ty.toIntern()).captures,
|
|
else => unreachable,
|
|
};
|
|
}
|
|
|
|
pub fn arrayBase(ty: Type, zcu: *const Zcu) struct { Type, u64 } {
|
|
var cur_ty: Type = ty;
|
|
var cur_len: u64 = 1;
|
|
while (cur_ty.zigTypeTag(zcu) == .array) {
|
|
cur_len *= cur_ty.arrayLenIncludingSentinel(zcu);
|
|
cur_ty = cur_ty.childType(zcu);
|
|
}
|
|
return .{ cur_ty, cur_len };
|
|
}
|
|
|
|
/// Returns a bit-pointer with the same value and a new packed offset.
|
|
pub fn packedStructFieldPtrInfo(
|
|
struct_ty: Type,
|
|
parent_ptr_ty: Type,
|
|
field_idx: u32,
|
|
pt: Zcu.PerThread,
|
|
) InternPool.Key.PtrType.PackedOffset {
|
|
comptime assert(Type.packed_struct_layout_version == 2);
|
|
|
|
const zcu = pt.zcu;
|
|
const parent_ptr_info = parent_ptr_ty.ptrInfo(zcu);
|
|
|
|
var bit_offset: u16 = 0;
|
|
var running_bits: u16 = 0;
|
|
for (0..struct_ty.structFieldCount(zcu)) |i| {
|
|
const f_ty = struct_ty.fieldType(i, zcu);
|
|
if (i == field_idx) {
|
|
bit_offset = running_bits;
|
|
}
|
|
running_bits += @intCast(f_ty.bitSize(zcu));
|
|
}
|
|
|
|
const res_host_size: u16, const res_bit_offset: u16 = if (parent_ptr_info.packed_offset.host_size != 0) .{
|
|
parent_ptr_info.packed_offset.host_size,
|
|
parent_ptr_info.packed_offset.bit_offset + bit_offset,
|
|
} else .{
|
|
switch (zcu.comp.getZigBackend()) {
|
|
else => (running_bits + 7) / 8,
|
|
.stage2_x86_64, .stage2_c => @intCast(struct_ty.abiSize(zcu)),
|
|
},
|
|
bit_offset,
|
|
};
|
|
|
|
return .{
|
|
.host_size = res_host_size,
|
|
.bit_offset = res_bit_offset,
|
|
};
|
|
}
|
|
|
|
pub fn getUnionLayout(loaded_union: InternPool.LoadedUnionType, zcu: *const Zcu) Zcu.UnionLayout {
|
|
const ip = &zcu.intern_pool;
|
|
var most_aligned_field: u32 = 0;
|
|
var most_aligned_field_align: InternPool.Alignment = .@"1";
|
|
var most_aligned_field_size: u64 = 0;
|
|
var biggest_field: u32 = 0;
|
|
var payload_size: u64 = 0;
|
|
var payload_align: InternPool.Alignment = .@"1";
|
|
for (loaded_union.field_types.get(ip), 0..) |field_ty_ip_index, field_index| {
|
|
const field_ty: Type = .fromInterned(field_ty_ip_index);
|
|
if (field_ty.isNoReturn(zcu)) continue;
|
|
|
|
const field_align: InternPool.Alignment = a: {
|
|
const explicit_aligns = loaded_union.field_aligns.get(ip);
|
|
if (explicit_aligns.len > 0) {
|
|
const a = explicit_aligns[field_index];
|
|
if (a != .none) break :a a;
|
|
}
|
|
break :a field_ty.abiAlignment(zcu);
|
|
};
|
|
if (field_ty.hasRuntimeBits(zcu)) {
|
|
const field_size = field_ty.abiSize(zcu);
|
|
if (field_size > payload_size) {
|
|
payload_size = field_size;
|
|
biggest_field = @intCast(field_index);
|
|
}
|
|
if (field_size > 0 and field_align.compare(.gte, most_aligned_field_align)) {
|
|
most_aligned_field = @intCast(field_index);
|
|
most_aligned_field_align = field_align;
|
|
most_aligned_field_size = field_size;
|
|
}
|
|
}
|
|
payload_align = payload_align.max(field_align);
|
|
}
|
|
if (loaded_union.runtime_tag == .none or
|
|
!Type.fromInterned(loaded_union.enum_tag_type).hasRuntimeBits(zcu))
|
|
{
|
|
return .{
|
|
.abi_size = payload_align.forward(payload_size),
|
|
.abi_align = payload_align,
|
|
.most_aligned_field = most_aligned_field,
|
|
.most_aligned_field_size = most_aligned_field_size,
|
|
.biggest_field = biggest_field,
|
|
.payload_size = payload_size,
|
|
.payload_align = payload_align,
|
|
.tag_align = .none,
|
|
.tag_size = 0,
|
|
.padding = 0,
|
|
};
|
|
}
|
|
|
|
const tag_size = Type.fromInterned(loaded_union.enum_tag_type).abiSize(zcu);
|
|
const tag_align = Type.fromInterned(loaded_union.enum_tag_type).abiAlignment(zcu).max(.@"1");
|
|
return .{
|
|
.abi_size = loaded_union.size,
|
|
.abi_align = tag_align.max(payload_align),
|
|
.most_aligned_field = most_aligned_field,
|
|
.most_aligned_field_size = most_aligned_field_size,
|
|
.biggest_field = biggest_field,
|
|
.payload_size = payload_size,
|
|
.payload_align = payload_align,
|
|
.tag_align = tag_align,
|
|
.tag_size = tag_size,
|
|
.padding = loaded_union.padding,
|
|
};
|
|
}
|
|
|
|
/// Returns the type of a pointer to an element.
|
|
/// Asserts that the type is a pointer, and that the element type is indexable.
|
|
/// If the element index is comptime-known, it must be passed in `offset`.
|
|
/// For *@Vector(n, T), return *align(a:b:h:v) T
|
|
/// For *[N]T, return *T
|
|
/// For [*]T, returns *T
|
|
/// For []T, returns *T
|
|
/// Handles const-ness and address spaces in particular.
|
|
/// This code is duplicated in `Sema.analyzePtrArithmetic`.
|
|
/// May perform type resolution and return a transitive `error.AnalysisFail`.
|
|
/// MLUGG TODO audit this shit
|
|
pub fn elemPtrType(ptr_ty: Type, offset: ?usize, pt: Zcu.PerThread) !Type {
|
|
const zcu = pt.zcu;
|
|
const ptr_info = ptr_ty.ptrInfo(zcu);
|
|
const elem_ty: Type = switch (ptr_info.flags.size) {
|
|
.one => switch (Type.fromInterned(ptr_info.child).zigTypeTag(zcu)) {
|
|
.array, .vector => Type.fromInterned(ptr_info.child).childType(zcu),
|
|
else => .fromInterned(ptr_info.child),
|
|
},
|
|
.many, .c, .slice => .fromInterned(ptr_info.child),
|
|
};
|
|
const is_allowzero = ptr_info.flags.is_allowzero and (offset orelse 0) == 0;
|
|
const parent_ty = ptr_ty.childType(zcu);
|
|
|
|
const VI = InternPool.Key.PtrType.VectorIndex;
|
|
|
|
const vector_info: struct {
|
|
host_size: u16 = 0,
|
|
alignment: Alignment = .none,
|
|
vector_index: VI = .none,
|
|
} = if (parent_ty.isVector(zcu) and ptr_info.flags.size == .one) blk: {
|
|
const elem_bits = elem_ty.bitSize(zcu);
|
|
if (elem_bits == 0) break :blk .{};
|
|
const is_packed = elem_bits < 8 or !std.math.isPowerOfTwo(elem_bits);
|
|
if (!is_packed) break :blk .{};
|
|
|
|
break :blk .{
|
|
.host_size = @intCast(parent_ty.arrayLen(zcu)),
|
|
.alignment = parent_ty.abiAlignment(zcu),
|
|
.vector_index = @enumFromInt(offset.?),
|
|
};
|
|
} else .{};
|
|
|
|
const alignment: Alignment = a: {
|
|
// Calculate the new pointer alignment.
|
|
if (ptr_info.flags.alignment == .none) {
|
|
// In case of an ABI-aligned pointer, any pointer arithmetic
|
|
// maintains the same ABI-alignedness.
|
|
break :a vector_info.alignment;
|
|
}
|
|
// If the addend is not a comptime-known value we can still count on
|
|
// it being a multiple of the type size.
|
|
const elem_size = elem_ty.abiSize(zcu);
|
|
const addend = if (offset) |off| elem_size * off else elem_size;
|
|
|
|
// The resulting pointer is aligned to the lcd between the offset (an
|
|
// arbitrary number) and the alignment factor (always a power of two,
|
|
// non zero).
|
|
const new_align: Alignment = @enumFromInt(@min(
|
|
@ctz(addend),
|
|
ptr_info.flags.alignment.toLog2Units(),
|
|
));
|
|
assert(new_align != .none);
|
|
break :a new_align;
|
|
};
|
|
return pt.ptrType(.{
|
|
.child = elem_ty.toIntern(),
|
|
.flags = .{
|
|
.alignment = alignment,
|
|
.is_const = ptr_info.flags.is_const,
|
|
.is_volatile = ptr_info.flags.is_volatile,
|
|
.is_allowzero = is_allowzero,
|
|
.address_space = ptr_info.flags.address_space,
|
|
.vector_index = vector_info.vector_index,
|
|
},
|
|
.packed_offset = .{
|
|
.host_size = vector_info.host_size,
|
|
.bit_offset = 0,
|
|
},
|
|
});
|
|
}
|
|
|
|
pub fn containerTypeName(ty: Type, ip: *const InternPool) InternPool.NullTerminatedString {
|
|
return switch (ip.indexToKey(ty.toIntern())) {
|
|
.struct_type => ip.loadStructType(ty.toIntern()).name,
|
|
.union_type => ip.loadUnionType(ty.toIntern()).name,
|
|
.enum_type => ip.loadEnumType(ty.toIntern()).name,
|
|
.opaque_type => ip.loadOpaqueType(ty.toIntern()).name,
|
|
else => unreachable,
|
|
};
|
|
}
|
|
|
|
/// Returns `true` if a value of this type is always `null`.
|
|
/// Returns `false` if a value of this type is neve `null`.
|
|
/// Returns `null` otherwise.
|
|
pub fn isNullFromType(ty: Type, zcu: *const Zcu) ?bool {
|
|
if (ty.zigTypeTag(zcu) != .optional and !ty.isCPtr(zcu)) return false;
|
|
if (ty.optionalChild(zcu).isNoReturn(zcu)) return true; // `?noreturn` is always null
|
|
return null;
|
|
}
|
|
|
|
pub const UnpackableReason = union(enum) {
|
|
comptime_only,
|
|
pointer,
|
|
enum_inferred_int_tag: Type,
|
|
non_packed_struct: Type,
|
|
non_packed_union: Type,
|
|
other,
|
|
};
|
|
|
|
/// Returns `null` iff `ty` is allowed in packed types.
|
|
pub fn unpackable(ty: Type, zcu: *const Zcu) ?UnpackableReason {
|
|
return switch (ty.zigTypeTag(zcu)) {
|
|
.void,
|
|
.bool,
|
|
.float,
|
|
.int,
|
|
=> null,
|
|
|
|
.type,
|
|
.comptime_float,
|
|
.comptime_int,
|
|
.enum_literal,
|
|
.undefined,
|
|
.null,
|
|
=> .comptime_only,
|
|
|
|
.noreturn,
|
|
.@"opaque",
|
|
.error_union,
|
|
.error_set,
|
|
.frame,
|
|
.@"anyframe",
|
|
.@"fn",
|
|
.array,
|
|
.vector,
|
|
=> .other,
|
|
|
|
.optional => if (ty.isPtrLikeOptional(zcu))
|
|
.pointer
|
|
else
|
|
.other,
|
|
|
|
.pointer => .pointer,
|
|
|
|
.@"enum" => switch (zcu.intern_pool.loadEnumType(ty.toIntern()).int_tag_mode) {
|
|
.explicit => null,
|
|
.auto => .{ .enum_inferred_int_tag = ty },
|
|
},
|
|
|
|
.@"struct" => switch (ty.containerLayout(zcu)) {
|
|
.@"packed" => null,
|
|
.auto, .@"extern" => .{ .non_packed_struct = ty },
|
|
},
|
|
.@"union" => switch (ty.containerLayout(zcu)) {
|
|
.@"packed" => null,
|
|
.auto, .@"extern" => .{ .non_packed_union = ty },
|
|
},
|
|
};
|
|
}
|
|
|
|
pub const ExternPosition = enum {
|
|
ret_ty,
|
|
param_ty,
|
|
union_field,
|
|
struct_field,
|
|
element,
|
|
other,
|
|
};
|
|
|
|
/// Returns true if `ty` is allowed in extern types.
|
|
/// Does not require `ty` to be resolved in any way.
|
|
/// Keep in sync with `Sema.explainWhyTypeIsNotExtern`.
|
|
pub fn validateExtern(ty: Type, position: ExternPosition, zcu: *const Zcu) bool {
|
|
return switch (ty.zigTypeTag(zcu)) {
|
|
.type,
|
|
.comptime_float,
|
|
.comptime_int,
|
|
.enum_literal,
|
|
.undefined,
|
|
.null,
|
|
.error_union,
|
|
.error_set,
|
|
.frame,
|
|
=> false,
|
|
|
|
.void => switch (position) {
|
|
.ret_ty,
|
|
.union_field,
|
|
.struct_field,
|
|
.element,
|
|
=> true,
|
|
.param_ty,
|
|
.other,
|
|
=> false,
|
|
},
|
|
|
|
.noreturn => position == .ret_ty,
|
|
|
|
.@"opaque",
|
|
.bool,
|
|
.float,
|
|
.@"anyframe",
|
|
=> true,
|
|
|
|
.pointer => {
|
|
if (ty.isSlice(zcu)) return false;
|
|
const child_ty = ty.childType(zcu);
|
|
if (child_ty.zigTypeTag(zcu) == .@"fn") {
|
|
return ty.isConstPtr(zcu) and child_ty.validateExtern(.other, zcu);
|
|
}
|
|
return true;
|
|
},
|
|
.int => switch (ty.intInfo(zcu).bits) {
|
|
0, 8, 16, 32, 64, 128 => true,
|
|
else => false,
|
|
},
|
|
.@"fn" => {
|
|
if (position != .other) return false;
|
|
// For now we want to authorize PTX kernel to use zig objects, even if we end up exposing the ABI.
|
|
// The goal is to experiment with more integrated CPU/GPU code.
|
|
if (ty.fnCallingConvention(zcu) == .nvptx_kernel) {
|
|
return true;
|
|
}
|
|
return !target_util.fnCallConvAllowsZigTypes(ty.fnCallingConvention(zcu));
|
|
},
|
|
.@"enum" => {
|
|
const enum_obj = zcu.intern_pool.loadEnumType(ty.toIntern());
|
|
return switch (enum_obj.int_tag_mode) {
|
|
.auto => false,
|
|
.explicit => Type.fromInterned(enum_obj.int_tag_type).validateExtern(position, zcu),
|
|
};
|
|
},
|
|
.@"struct" => {
|
|
const struct_obj = zcu.intern_pool.loadStructType(ty.toIntern());
|
|
return switch (struct_obj.layout) {
|
|
.auto => false,
|
|
.@"extern" => true,
|
|
.@"packed" => switch (struct_obj.packed_backing_mode) {
|
|
.auto => false,
|
|
.explicit => Type.fromInterned(struct_obj.packed_backing_int_type).validateExtern(position, zcu),
|
|
},
|
|
};
|
|
},
|
|
.@"union" => {
|
|
const union_obj = zcu.intern_pool.loadUnionType(ty.toIntern());
|
|
return switch (union_obj.layout) {
|
|
.auto => false,
|
|
.@"extern" => true,
|
|
.@"packed" => switch (union_obj.packed_backing_mode) {
|
|
.auto => false,
|
|
.explicit => Type.fromInterned(union_obj.packed_backing_int_type).validateExtern(position, zcu),
|
|
},
|
|
};
|
|
},
|
|
.array => {
|
|
if (position == .ret_ty or position == .param_ty) return false;
|
|
return ty.childType(zcu).validateExtern(.element, zcu);
|
|
},
|
|
.vector => ty.childType(zcu).validateExtern(.element, zcu),
|
|
.optional => ty.isPtrLikeOptional(zcu),
|
|
};
|
|
}
|
|
|
|
/// Asserts that `ty` has resolved layout.
|
|
pub fn assertHasLayout(ty: Type, zcu: *const Zcu) void {
|
|
switch (zcu.intern_pool.indexToKey(ty.toIntern())) {
|
|
.int_type,
|
|
.ptr_type,
|
|
.anyframe_type,
|
|
.simple_type,
|
|
.opaque_type,
|
|
.error_set_type,
|
|
.inferred_error_set_type,
|
|
=> {},
|
|
.func_type => |func_type| {
|
|
for (func_type.param_types.get(&zcu.intern_pool)) |param_ty| {
|
|
assertHasLayout(.fromInterned(param_ty), zcu);
|
|
}
|
|
assertHasLayout(.fromInterned(func_type.return_type), zcu);
|
|
},
|
|
.array_type => |arr| assertHasLayout(.fromInterned(arr.child), zcu),
|
|
.vector_type => |vec| assertHasLayout(.fromInterned(vec.child), zcu),
|
|
.opt_type => |child| assertHasLayout(.fromInterned(child), zcu),
|
|
.error_union_type => |eu| assertHasLayout(.fromInterned(eu.payload_type), zcu),
|
|
.tuple_type => |tuple| for (tuple.types.get(&zcu.intern_pool)) |field_ty| {
|
|
assertHasLayout(.fromInterned(field_ty), zcu);
|
|
},
|
|
.struct_type => {
|
|
assert(zcu.intern_pool.loadStructType(ty.toIntern()).want_layout);
|
|
const unit: InternPool.AnalUnit = .wrap(.{ .type_layout = ty.toIntern() });
|
|
assert(!zcu.outdated.contains(unit));
|
|
assert(!zcu.potentially_outdated.contains(unit));
|
|
},
|
|
.union_type => {
|
|
assert(zcu.intern_pool.loadUnionType(ty.toIntern()).want_layout);
|
|
const unit: InternPool.AnalUnit = .wrap(.{ .type_layout = ty.toIntern() });
|
|
assert(!zcu.outdated.contains(unit));
|
|
assert(!zcu.potentially_outdated.contains(unit));
|
|
},
|
|
.enum_type => {
|
|
assert(zcu.intern_pool.loadEnumType(ty.toIntern()).want_layout);
|
|
const unit: InternPool.AnalUnit = .wrap(.{ .type_layout = ty.toIntern() });
|
|
assert(!zcu.outdated.contains(unit));
|
|
assert(!zcu.potentially_outdated.contains(unit));
|
|
},
|
|
|
|
// values, not types
|
|
.simple_value,
|
|
.variable,
|
|
.@"extern",
|
|
.func,
|
|
.int,
|
|
.err,
|
|
.error_union,
|
|
.enum_literal,
|
|
.enum_tag,
|
|
.empty_enum_value,
|
|
.float,
|
|
.ptr,
|
|
.slice,
|
|
.opt,
|
|
.aggregate,
|
|
.un,
|
|
.bitpack,
|
|
.undef,
|
|
// memoization, not types
|
|
.memoized_call,
|
|
=> unreachable,
|
|
}
|
|
}
|
|
|
|
/// Recursively walks the type and marks for each subtype how many times it has been seen
|
|
fn collectSubtypes(ty: Type, pt: Zcu.PerThread, visited: *std.AutoArrayHashMapUnmanaged(Type, u16)) error{OutOfMemory}!void {
|
|
const zcu = pt.zcu;
|
|
const ip = &zcu.intern_pool;
|
|
|
|
const gop = try visited.getOrPut(zcu.gpa, ty);
|
|
if (gop.found_existing) {
|
|
gop.value_ptr.* += 1;
|
|
} else {
|
|
gop.value_ptr.* = 1;
|
|
}
|
|
|
|
switch (ip.indexToKey(ty.toIntern())) {
|
|
.ptr_type => try collectSubtypes(Type.fromInterned(ty.ptrInfo(zcu).child), pt, visited),
|
|
.array_type => |array_type| try collectSubtypes(Type.fromInterned(array_type.child), pt, visited),
|
|
.vector_type => |vector_type| try collectSubtypes(Type.fromInterned(vector_type.child), pt, visited),
|
|
.opt_type => |child| try collectSubtypes(Type.fromInterned(child), pt, visited),
|
|
.error_union_type => |error_union_type| {
|
|
try collectSubtypes(Type.fromInterned(error_union_type.error_set_type), pt, visited);
|
|
if (error_union_type.payload_type != .generic_poison_type) {
|
|
try collectSubtypes(Type.fromInterned(error_union_type.payload_type), pt, visited);
|
|
}
|
|
},
|
|
.tuple_type => |tuple| {
|
|
for (tuple.types.get(ip)) |field_ty| {
|
|
try collectSubtypes(Type.fromInterned(field_ty), pt, visited);
|
|
}
|
|
},
|
|
.func_type => |fn_info| {
|
|
const param_types = fn_info.param_types.get(&zcu.intern_pool);
|
|
for (param_types) |param_ty| {
|
|
if (param_ty != .generic_poison_type) {
|
|
try collectSubtypes(Type.fromInterned(param_ty), pt, visited);
|
|
}
|
|
}
|
|
|
|
if (fn_info.return_type != .generic_poison_type) {
|
|
try collectSubtypes(Type.fromInterned(fn_info.return_type), pt, visited);
|
|
}
|
|
},
|
|
.anyframe_type => |child| try collectSubtypes(Type.fromInterned(child), pt, visited),
|
|
|
|
// leaf types
|
|
.undef,
|
|
.inferred_error_set_type,
|
|
.error_set_type,
|
|
.struct_type,
|
|
.union_type,
|
|
.opaque_type,
|
|
.enum_type,
|
|
.simple_type,
|
|
.int_type,
|
|
=> {},
|
|
|
|
// values, not types
|
|
.simple_value,
|
|
.variable,
|
|
.@"extern",
|
|
.func,
|
|
.int,
|
|
.err,
|
|
.error_union,
|
|
.enum_literal,
|
|
.enum_tag,
|
|
.empty_enum_value,
|
|
.float,
|
|
.ptr,
|
|
.slice,
|
|
.opt,
|
|
.aggregate,
|
|
.un,
|
|
.bitpack,
|
|
// memoization, not types
|
|
.memoized_call,
|
|
=> unreachable,
|
|
}
|
|
}
|
|
|
|
fn shouldDedupeType(ty: Type, ctx: *Comparison, pt: Zcu.PerThread) error{OutOfMemory}!Comparison.DedupeEntry {
|
|
if (ctx.type_occurrences.get(ty)) |occ| {
|
|
if (ctx.type_dedupe_cache.get(ty)) |cached| {
|
|
return cached;
|
|
}
|
|
|
|
var discarding: std.Io.Writer.Discarding = .init(&.{});
|
|
|
|
print(ty, &discarding.writer, pt, null) catch
|
|
unreachable; // we are writing into a discarding writer, it should never fail
|
|
|
|
const type_len: i32 = @intCast(discarding.count);
|
|
|
|
const placeholder_len: i32 = 1;
|
|
const min_saved_bytes: i32 = 20;
|
|
|
|
const saved_bytes = (type_len - placeholder_len) * (occ - 1);
|
|
const max_placeholders = 7; // T to Z
|
|
const should_dedupe = saved_bytes >= min_saved_bytes and ctx.placeholder_index < max_placeholders;
|
|
|
|
const entry: Comparison.DedupeEntry = if (should_dedupe) b: {
|
|
ctx.placeholder_index += 1;
|
|
break :b .{ .dedupe = .{ .index = ctx.placeholder_index - 1 } };
|
|
} else .dont_dedupe;
|
|
|
|
try ctx.type_dedupe_cache.put(pt.zcu.gpa, ty, entry);
|
|
|
|
return entry;
|
|
} else {
|
|
return .{ .dont_dedupe = {} };
|
|
}
|
|
}
|
|
|
|
/// The comparison recursively walks all types given and notes how many times
|
|
/// each subtype occurs. It then while recursively printing decides for each
|
|
/// subtype whether to print the type inline or create a placeholder based on
|
|
/// the subtype length and number of occurences. Placeholders are then found by
|
|
/// iterating `type_dedupe_cache` which caches the inline/placeholder decisions.
|
|
pub const Comparison = struct {
|
|
type_occurrences: std.AutoArrayHashMapUnmanaged(Type, u16),
|
|
type_dedupe_cache: std.AutoArrayHashMapUnmanaged(Type, DedupeEntry),
|
|
placeholder_index: u8,
|
|
|
|
pub const Placeholder = struct {
|
|
index: u8,
|
|
|
|
pub fn format(p: Placeholder, writer: *std.Io.Writer) error{WriteFailed}!void {
|
|
return writer.print("{c}", .{p.index + 'T'});
|
|
}
|
|
};
|
|
|
|
pub const DedupeEntry = union(enum) {
|
|
dont_dedupe: void,
|
|
dedupe: Placeholder,
|
|
};
|
|
|
|
pub fn init(types: []const Type, pt: Zcu.PerThread) error{OutOfMemory}!Comparison {
|
|
var cmp: Comparison = .{
|
|
.type_occurrences = .empty,
|
|
.type_dedupe_cache = .empty,
|
|
.placeholder_index = 0,
|
|
};
|
|
|
|
errdefer cmp.deinit(pt);
|
|
|
|
for (types) |ty| {
|
|
try collectSubtypes(ty, pt, &cmp.type_occurrences);
|
|
}
|
|
|
|
return cmp;
|
|
}
|
|
|
|
pub fn deinit(cmp: *Comparison, pt: Zcu.PerThread) void {
|
|
const gpa = pt.zcu.gpa;
|
|
cmp.type_occurrences.deinit(gpa);
|
|
cmp.type_dedupe_cache.deinit(gpa);
|
|
}
|
|
|
|
pub fn fmtType(ctx: *Comparison, ty: Type, pt: Zcu.PerThread) Comparison.Formatter {
|
|
return .{ .ty = ty, .ctx = ctx, .pt = pt };
|
|
}
|
|
pub const Formatter = struct {
|
|
ty: Type,
|
|
ctx: *Comparison,
|
|
pt: Zcu.PerThread,
|
|
|
|
pub fn format(self: Comparison.Formatter, writer: anytype) error{WriteFailed}!void {
|
|
print(self.ty, writer, self.pt, self.ctx) catch return error.WriteFailed;
|
|
}
|
|
};
|
|
};
|
|
|
|
pub const @"u0": Type = .{ .ip_index = .u0_type };
|
|
pub const @"u1": Type = .{ .ip_index = .u1_type };
|
|
pub const @"u8": Type = .{ .ip_index = .u8_type };
|
|
pub const @"u16": Type = .{ .ip_index = .u16_type };
|
|
pub const @"u29": Type = .{ .ip_index = .u29_type };
|
|
pub const @"u32": Type = .{ .ip_index = .u32_type };
|
|
pub const @"u64": Type = .{ .ip_index = .u64_type };
|
|
pub const @"u80": Type = .{ .ip_index = .u80_type };
|
|
pub const @"u128": Type = .{ .ip_index = .u128_type };
|
|
pub const @"u256": Type = .{ .ip_index = .u256_type };
|
|
|
|
pub const @"i8": Type = .{ .ip_index = .i8_type };
|
|
pub const @"i16": Type = .{ .ip_index = .i16_type };
|
|
pub const @"i32": Type = .{ .ip_index = .i32_type };
|
|
pub const @"i64": Type = .{ .ip_index = .i64_type };
|
|
pub const @"i128": Type = .{ .ip_index = .i128_type };
|
|
|
|
pub const @"f16": Type = .{ .ip_index = .f16_type };
|
|
pub const @"f32": Type = .{ .ip_index = .f32_type };
|
|
pub const @"f64": Type = .{ .ip_index = .f64_type };
|
|
pub const @"f80": Type = .{ .ip_index = .f80_type };
|
|
pub const @"f128": Type = .{ .ip_index = .f128_type };
|
|
|
|
pub const @"bool": Type = .{ .ip_index = .bool_type };
|
|
pub const @"usize": Type = .{ .ip_index = .usize_type };
|
|
pub const @"isize": Type = .{ .ip_index = .isize_type };
|
|
pub const @"comptime_int": Type = .{ .ip_index = .comptime_int_type };
|
|
pub const @"comptime_float": Type = .{ .ip_index = .comptime_float_type };
|
|
pub const @"void": Type = .{ .ip_index = .void_type };
|
|
pub const @"type": Type = .{ .ip_index = .type_type };
|
|
pub const @"anyerror": Type = .{ .ip_index = .anyerror_type };
|
|
pub const @"anyopaque": Type = .{ .ip_index = .anyopaque_type };
|
|
pub const @"anyframe": Type = .{ .ip_index = .anyframe_type };
|
|
pub const @"null": Type = .{ .ip_index = .null_type };
|
|
pub const @"undefined": Type = .{ .ip_index = .undefined_type };
|
|
pub const @"noreturn": Type = .{ .ip_index = .noreturn_type };
|
|
pub const enum_literal: Type = .{ .ip_index = .enum_literal_type };
|
|
|
|
pub const @"c_char": Type = .{ .ip_index = .c_char_type };
|
|
pub const @"c_short": Type = .{ .ip_index = .c_short_type };
|
|
pub const @"c_ushort": Type = .{ .ip_index = .c_ushort_type };
|
|
pub const @"c_int": Type = .{ .ip_index = .c_int_type };
|
|
pub const @"c_uint": Type = .{ .ip_index = .c_uint_type };
|
|
pub const @"c_long": Type = .{ .ip_index = .c_long_type };
|
|
pub const @"c_ulong": Type = .{ .ip_index = .c_ulong_type };
|
|
pub const @"c_longlong": Type = .{ .ip_index = .c_longlong_type };
|
|
pub const @"c_ulonglong": Type = .{ .ip_index = .c_ulonglong_type };
|
|
pub const @"c_longdouble": Type = .{ .ip_index = .c_longdouble_type };
|
|
|
|
pub const ptr_usize: Type = .{ .ip_index = .ptr_usize_type };
|
|
pub const ptr_const_comptime_int: Type = .{ .ip_index = .ptr_const_comptime_int_type };
|
|
pub const manyptr_u8: Type = .{ .ip_index = .manyptr_u8_type };
|
|
pub const manyptr_const_u8: Type = .{ .ip_index = .manyptr_const_u8_type };
|
|
pub const manyptr_const_u8_sentinel_0: Type = .{ .ip_index = .manyptr_const_u8_sentinel_0_type };
|
|
pub const slice_const_u8: Type = .{ .ip_index = .slice_const_u8_type };
|
|
pub const slice_const_u8_sentinel_0: Type = .{ .ip_index = .slice_const_u8_sentinel_0_type };
|
|
pub const slice_const_slice_const_u8: Type = .{ .ip_index = .slice_const_slice_const_u8_type };
|
|
pub const slice_const_type: Type = .{ .ip_index = .slice_const_type_type };
|
|
pub const optional_type: Type = .{ .ip_index = .optional_type_type };
|
|
pub const optional_noreturn: Type = .{ .ip_index = .optional_noreturn_type };
|
|
|
|
pub const vector_8_i8: Type = .{ .ip_index = .vector_8_i8_type };
|
|
pub const vector_16_i8: Type = .{ .ip_index = .vector_16_i8_type };
|
|
pub const vector_32_i8: Type = .{ .ip_index = .vector_32_i8_type };
|
|
pub const vector_64_i8: Type = .{ .ip_index = .vector_64_i8_type };
|
|
pub const vector_1_u8: Type = .{ .ip_index = .vector_1_u8_type };
|
|
pub const vector_2_u8: Type = .{ .ip_index = .vector_2_u8_type };
|
|
pub const vector_4_u8: Type = .{ .ip_index = .vector_4_u8_type };
|
|
pub const vector_8_u8: Type = .{ .ip_index = .vector_8_u8_type };
|
|
pub const vector_16_u8: Type = .{ .ip_index = .vector_16_u8_type };
|
|
pub const vector_32_u8: Type = .{ .ip_index = .vector_32_u8_type };
|
|
pub const vector_64_u8: Type = .{ .ip_index = .vector_64_u8_type };
|
|
pub const vector_2_i16: Type = .{ .ip_index = .vector_2_i16_type };
|
|
pub const vector_4_i16: Type = .{ .ip_index = .vector_4_i16_type };
|
|
pub const vector_8_i16: Type = .{ .ip_index = .vector_8_i16_type };
|
|
pub const vector_16_i16: Type = .{ .ip_index = .vector_16_i16_type };
|
|
pub const vector_32_i16: Type = .{ .ip_index = .vector_32_i16_type };
|
|
pub const vector_4_u16: Type = .{ .ip_index = .vector_4_u16_type };
|
|
pub const vector_8_u16: Type = .{ .ip_index = .vector_8_u16_type };
|
|
pub const vector_16_u16: Type = .{ .ip_index = .vector_16_u16_type };
|
|
pub const vector_32_u16: Type = .{ .ip_index = .vector_32_u16_type };
|
|
pub const vector_2_i32: Type = .{ .ip_index = .vector_2_i32_type };
|
|
pub const vector_4_i32: Type = .{ .ip_index = .vector_4_i32_type };
|
|
pub const vector_8_i32: Type = .{ .ip_index = .vector_8_i32_type };
|
|
pub const vector_16_i32: Type = .{ .ip_index = .vector_16_i32_type };
|
|
pub const vector_4_u32: Type = .{ .ip_index = .vector_4_u32_type };
|
|
pub const vector_8_u32: Type = .{ .ip_index = .vector_8_u32_type };
|
|
pub const vector_16_u32: Type = .{ .ip_index = .vector_16_u32_type };
|
|
pub const vector_2_i64: Type = .{ .ip_index = .vector_2_i64_type };
|
|
pub const vector_4_i64: Type = .{ .ip_index = .vector_4_i64_type };
|
|
pub const vector_8_i64: Type = .{ .ip_index = .vector_8_i64_type };
|
|
pub const vector_2_u64: Type = .{ .ip_index = .vector_2_u64_type };
|
|
pub const vector_4_u64: Type = .{ .ip_index = .vector_4_u64_type };
|
|
pub const vector_8_u64: Type = .{ .ip_index = .vector_8_u64_type };
|
|
pub const vector_1_u128: Type = .{ .ip_index = .vector_1_u128_type };
|
|
pub const vector_2_u128: Type = .{ .ip_index = .vector_2_u128_type };
|
|
pub const vector_1_u256: Type = .{ .ip_index = .vector_1_u256_type };
|
|
pub const vector_4_f16: Type = .{ .ip_index = .vector_4_f16_type };
|
|
pub const vector_8_f16: Type = .{ .ip_index = .vector_8_f16_type };
|
|
pub const vector_16_f16: Type = .{ .ip_index = .vector_16_f16_type };
|
|
pub const vector_32_f16: Type = .{ .ip_index = .vector_32_f16_type };
|
|
pub const vector_2_f32: Type = .{ .ip_index = .vector_2_f32_type };
|
|
pub const vector_4_f32: Type = .{ .ip_index = .vector_4_f32_type };
|
|
pub const vector_8_f32: Type = .{ .ip_index = .vector_8_f32_type };
|
|
pub const vector_16_f32: Type = .{ .ip_index = .vector_16_f32_type };
|
|
pub const vector_2_f64: Type = .{ .ip_index = .vector_2_f64_type };
|
|
pub const vector_4_f64: Type = .{ .ip_index = .vector_4_f64_type };
|
|
pub const vector_8_f64: Type = .{ .ip_index = .vector_8_f64_type };
|
|
|
|
pub const empty_tuple: Type = .{ .ip_index = .empty_tuple_type };
|
|
|
|
pub const generic_poison: Type = .{ .ip_index = .generic_poison_type };
|
|
|
|
pub fn smallestUnsignedBits(max: u64) u16 {
|
|
return switch (max) {
|
|
0 => 0,
|
|
else => @as(u16, 1) + std.math.log2_int(u64, max),
|
|
};
|
|
}
|
|
|
|
/// This is only used for comptime asserts. Bump this number when you make a change
|
|
/// to packed struct layout to find out all the places in the codebase you need to edit!
|
|
pub const packed_struct_layout_version = 2;
|
|
|
|
fn cTypeAlign(target: *const Target, c_type: Target.CType) Alignment {
|
|
return Alignment.fromByteUnits(target.cTypeAlignment(c_type));
|
|
}
|