| author | |
| committer | |
| log | 2f0f1efa6fa50ca27a44d5f7a0c38a6cafbbfb7c |
| tree | 0d47a70b6df13e5024f02b3bed2bc2cc1ef95d9f |
| parent | ded5c759f83a4da355a128dd4d7f5e22cbd3cabe |
| signature |
44 files changed, 3659 insertions(+), 3659 deletions(-)
CMakeLists.txt+1-1| ... | @@ -522,6 +522,7 @@ set(ZIG_STAGE2_SOURCES | ... | @@ -522,6 +522,7 @@ set(ZIG_STAGE2_SOURCES |
| 522 | src/Sema.zig | 522 | src/Sema.zig |
| 523 | src/Sema/bitcast.zig | 523 | src/Sema/bitcast.zig |
| 524 | src/Sema/comptime_ptr_access.zig | 524 | src/Sema/comptime_ptr_access.zig |
| 525 | src/Type.zig | ||
| 525 | src/Value.zig | 526 | src/Value.zig |
| 526 | src/Zcu.zig | 527 | src/Zcu.zig |
| 527 | src/arch/aarch64/CodeGen.zig | 528 | src/arch/aarch64/CodeGen.zig |
| ... | @@ -673,7 +674,6 @@ set(ZIG_STAGE2_SOURCES | ... | @@ -673,7 +674,6 @@ set(ZIG_STAGE2_SOURCES |
| 673 | src/target.zig | 674 | src/target.zig |
| 674 | src/tracy.zig | 675 | src/tracy.zig |
| 675 | src/translate_c.zig | 676 | src/translate_c.zig |
| 676 | src/type.zig | ||
| 677 | src/wasi_libc.zig | 677 | src/wasi_libc.zig |
| 678 | ) | 678 | ) |
| 679 | 679 |
src/Air.zig+1-1| ... | @@ -9,7 +9,7 @@ const assert = std.debug.assert; | ... | @@ -9,7 +9,7 @@ const assert = std.debug.assert; |
| 9 | 9 | ||
| 10 | const Air = @This(); | 10 | const Air = @This(); |
| 11 | const Value = @import("Value.zig"); | 11 | const Value = @import("Value.zig"); |
| 12 | const Type = @import("type.zig").Type; | 12 | const Type = @import("Type.zig"); |
| 13 | const InternPool = @import("InternPool.zig"); | 13 | const InternPool = @import("InternPool.zig"); |
| 14 | const Zcu = @import("Zcu.zig"); | 14 | const Zcu = @import("Zcu.zig"); |
| 15 | /// Deprecated. | 15 | /// Deprecated. |
src/Compilation.zig+1-1| ... | @@ -12,7 +12,7 @@ const WaitGroup = std.Thread.WaitGroup; | ... | @@ -12,7 +12,7 @@ const WaitGroup = std.Thread.WaitGroup; |
| 12 | const ErrorBundle = std.zig.ErrorBundle; | 12 | const ErrorBundle = std.zig.ErrorBundle; |
| 13 | 13 | ||
| 14 | const Value = @import("Value.zig"); | 14 | const Value = @import("Value.zig"); |
| 15 | const Type = @import("type.zig").Type; | 15 | const Type = @import("Type.zig"); |
| 16 | const target_util = @import("target.zig"); | 16 | const target_util = @import("target.zig"); |
| 17 | const Package = @import("Package.zig"); | 17 | const Package = @import("Package.zig"); |
| 18 | const link = @import("link.zig"); | 18 | const link = @import("link.zig"); |
src/RangeSet.zig+1-1| ... | @@ -3,7 +3,7 @@ const assert = std.debug.assert; | ... | @@ -3,7 +3,7 @@ const assert = std.debug.assert; |
| 3 | const Order = std.math.Order; | 3 | const Order = std.math.Order; |
| 4 | 4 | ||
| 5 | const InternPool = @import("InternPool.zig"); | 5 | const InternPool = @import("InternPool.zig"); |
| 6 | const Type = @import("type.zig").Type; | 6 | const Type = @import("Type.zig"); |
| 7 | const Value = @import("Value.zig"); | 7 | const Value = @import("Value.zig"); |
| 8 | const Zcu = @import("Zcu.zig"); | 8 | const Zcu = @import("Zcu.zig"); |
| 9 | /// Deprecated. | 9 | /// Deprecated. |
src/Sema.zig+1-1| ... | @@ -176,7 +176,7 @@ const log = std.log.scoped(.sema); | ... | @@ -176,7 +176,7 @@ const log = std.log.scoped(.sema); |
| 176 | const Sema = @This(); | 176 | const Sema = @This(); |
| 177 | const Value = @import("Value.zig"); | 177 | const Value = @import("Value.zig"); |
| 178 | const MutableValue = @import("mutable_value.zig").MutableValue; | 178 | const MutableValue = @import("mutable_value.zig").MutableValue; |
| 179 | const Type = @import("type.zig").Type; | 179 | const Type = @import("Type.zig"); |
| 180 | const Air = @import("Air.zig"); | 180 | const Air = @import("Air.zig"); |
| 181 | const Zir = std.zig.Zir; | 181 | const Zir = std.zig.Zir; |
| 182 | const Zcu = @import("Zcu.zig"); | 182 | const Zcu = @import("Zcu.zig"); |
src/Sema/bitcast.zig+1-1| ... | @@ -767,6 +767,6 @@ const assert = std.debug.assert; | ... | @@ -767,6 +767,6 @@ const assert = std.debug.assert; |
| 767 | const Sema = @import("../Sema.zig"); | 767 | const Sema = @import("../Sema.zig"); |
| 768 | const Zcu = @import("../Zcu.zig"); | 768 | const Zcu = @import("../Zcu.zig"); |
| 769 | const InternPool = @import("../InternPool.zig"); | 769 | const InternPool = @import("../InternPool.zig"); |
| 770 | const Type = @import("../type.zig").Type; | 770 | const Type = @import("../Type.zig"); |
| 771 | const Value = @import("../Value.zig"); | 771 | const Value = @import("../Value.zig"); |
| 772 | const CompileError = Zcu.CompileError; | 772 | const CompileError = Zcu.CompileError; |
src/Sema/comptime_ptr_access.zig+1-1| ... | @@ -1054,7 +1054,7 @@ const ComptimeAllocIndex = InternPool.ComptimeAllocIndex; | ... | @@ -1054,7 +1054,7 @@ const ComptimeAllocIndex = InternPool.ComptimeAllocIndex; |
| 1054 | const Sema = @import("../Sema.zig"); | 1054 | const Sema = @import("../Sema.zig"); |
| 1055 | const Block = Sema.Block; | 1055 | const Block = Sema.Block; |
| 1056 | const MutableValue = @import("../mutable_value.zig").MutableValue; | 1056 | const MutableValue = @import("../mutable_value.zig").MutableValue; |
| 1057 | const Type = @import("../type.zig").Type; | 1057 | const Type = @import("../Type.zig"); |
| 1058 | const Value = @import("../Value.zig"); | 1058 | const Value = @import("../Value.zig"); |
| 1059 | const Zcu = @import("../Zcu.zig"); | 1059 | const Zcu = @import("../Zcu.zig"); |
| 1060 | const LazySrcLoc = Zcu.LazySrcLoc; | 1060 | const LazySrcLoc = Zcu.LazySrcLoc; |
src/Type.zig created+3617| ... | @@ -0,0 +1,3617 @@ | ||
| 1 | //! Both types and values are canonically represented by a single 32-bit integer | ||
| 2 | //! which is an index into an `InternPool` data structure. | ||
| 3 | //! This struct abstracts around this storage by providing methods only | ||
| 4 | //! applicable to types rather than values in general. | ||
| 5 | |||
| 6 | const std = @import("std"); | ||
| 7 | const builtin = @import("builtin"); | ||
| 8 | const Value = @import("Value.zig"); | ||
| 9 | const assert = std.debug.assert; | ||
| 10 | const Target = std.Target; | ||
| 11 | const Zcu = @import("Zcu.zig"); | ||
| 12 | /// Deprecated. | ||
| 13 | const Module = Zcu; | ||
| 14 | const log = std.log.scoped(.Type); | ||
| 15 | const target_util = @import("target.zig"); | ||
| 16 | const Sema = @import("Sema.zig"); | ||
| 17 | const InternPool = @import("InternPool.zig"); | ||
| 18 | const Alignment = InternPool.Alignment; | ||
| 19 | const Zir = std.zig.Zir; | ||
| 20 | const Type = @This(); | ||
| 21 | |||
| 22 | ip_index: InternPool.Index, | ||
| 23 | |||
| 24 | pub fn zigTypeTag(ty: Type, mod: *const Module) std.builtin.TypeId { | ||
| 25 | return ty.zigTypeTagOrPoison(mod) catch unreachable; | ||
| 26 | } | ||
| 27 | |||
| 28 | pub fn zigTypeTagOrPoison(ty: Type, mod: *const Module) error{GenericPoison}!std.builtin.TypeId { | ||
| 29 | return mod.intern_pool.zigTypeTagOrPoison(ty.toIntern()); | ||
| 30 | } | ||
| 31 | |||
| 32 | pub fn baseZigTypeTag(self: Type, mod: *Module) std.builtin.TypeId { | ||
| 33 | return switch (self.zigTypeTag(mod)) { | ||
| 34 | .ErrorUnion => self.errorUnionPayload(mod).baseZigTypeTag(mod), | ||
| 35 | .Optional => { | ||
| 36 | return self.optionalChild(mod).baseZigTypeTag(mod); | ||
| 37 | }, | ||
| 38 | else => |t| t, | ||
| 39 | }; | ||
| 40 | } | ||
| 41 | |||
| 42 | pub fn isSelfComparable(ty: Type, mod: *const Module, is_equality_cmp: bool) bool { | ||
| 43 | return switch (ty.zigTypeTag(mod)) { | ||
| 44 | .Int, | ||
| 45 | .Float, | ||
| 46 | .ComptimeFloat, | ||
| 47 | .ComptimeInt, | ||
| 48 | => true, | ||
| 49 | |||
| 50 | .Vector => ty.elemType2(mod).isSelfComparable(mod, is_equality_cmp), | ||
| 51 | |||
| 52 | .Bool, | ||
| 53 | .Type, | ||
| 54 | .Void, | ||
| 55 | .ErrorSet, | ||
| 56 | .Fn, | ||
| 57 | .Opaque, | ||
| 58 | .AnyFrame, | ||
| 59 | .Enum, | ||
| 60 | .EnumLiteral, | ||
| 61 | => is_equality_cmp, | ||
| 62 | |||
| 63 | .NoReturn, | ||
| 64 | .Array, | ||
| 65 | .Struct, | ||
| 66 | .Undefined, | ||
| 67 | .Null, | ||
| 68 | .ErrorUnion, | ||
| 69 | .Union, | ||
| 70 | .Frame, | ||
| 71 | => false, | ||
| 72 | |||
| 73 | .Pointer => !ty.isSlice(mod) and (is_equality_cmp or ty.isCPtr(mod)), | ||
| 74 | .Optional => { | ||
| 75 | if (!is_equality_cmp) return false; | ||
| 76 | return ty.optionalChild(mod).isSelfComparable(mod, is_equality_cmp); | ||
| 77 | }, | ||
| 78 | }; | ||
| 79 | } | ||
| 80 | |||
| 81 | /// If it is a function pointer, returns the function type. Otherwise returns null. | ||
| 82 | pub fn castPtrToFn(ty: Type, mod: *const Module) ?Type { | ||
| 83 | if (ty.zigTypeTag(mod) != .Pointer) return null; | ||
| 84 | const elem_ty = ty.childType(mod); | ||
| 85 | if (elem_ty.zigTypeTag(mod) != .Fn) return null; | ||
| 86 | return elem_ty; | ||
| 87 | } | ||
| 88 | |||
| 89 | /// Asserts the type is a pointer. | ||
| 90 | pub fn ptrIsMutable(ty: Type, mod: *const Module) bool { | ||
| 91 | return !mod.intern_pool.indexToKey(ty.toIntern()).ptr_type.flags.is_const; | ||
| 92 | } | ||
| 93 | |||
| 94 | pub const ArrayInfo = struct { | ||
| 95 | elem_type: Type, | ||
| 96 | sentinel: ?Value = null, | ||
| 97 | len: u64, | ||
| 98 | }; | ||
| 99 | |||
| 100 | pub fn arrayInfo(self: Type, mod: *const Module) ArrayInfo { | ||
| 101 | return .{ | ||
| 102 | .len = self.arrayLen(mod), | ||
| 103 | .sentinel = self.sentinel(mod), | ||
| 104 | .elem_type = self.childType(mod), | ||
| 105 | }; | ||
| 106 | } | ||
| 107 | |||
| 108 | pub fn ptrInfo(ty: Type, mod: *const Module) InternPool.Key.PtrType { | ||
| 109 | return switch (mod.intern_pool.indexToKey(ty.toIntern())) { | ||
| 110 | .ptr_type => |p| p, | ||
| 111 | .opt_type => |child| switch (mod.intern_pool.indexToKey(child)) { | ||
| 112 | .ptr_type => |p| p, | ||
| 113 | else => unreachable, | ||
| 114 | }, | ||
| 115 | else => unreachable, | ||
| 116 | }; | ||
| 117 | } | ||
| 118 | |||
| 119 | pub fn eql(a: Type, b: Type, mod: *const Module) bool { | ||
| 120 | _ = mod; // TODO: remove this parameter | ||
| 121 | // The InternPool data structure hashes based on Key to make interned objects | ||
| 122 | // unique. An Index can be treated simply as u32 value for the | ||
| 123 | // purpose of Type/Value hashing and equality. | ||
| 124 | return a.toIntern() == b.toIntern(); | ||
| 125 | } | ||
| 126 | |||
| 127 | pub fn format(ty: Type, comptime unused_fmt_string: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void { | ||
| 128 | _ = ty; | ||
| 129 | _ = unused_fmt_string; | ||
| 130 | _ = options; | ||
| 131 | _ = writer; | ||
| 132 | @compileError("do not format types directly; use either ty.fmtDebug() or ty.fmt()"); | ||
| 133 | } | ||
| 134 | |||
| 135 | pub const Formatter = std.fmt.Formatter(format2); | ||
| 136 | |||
| 137 | pub fn fmt(ty: Type, module: *Module) Formatter { | ||
| 138 | return .{ .data = .{ | ||
| 139 | .ty = ty, | ||
| 140 | .module = module, | ||
| 141 | } }; | ||
| 142 | } | ||
| 143 | |||
| 144 | const FormatContext = struct { | ||
| 145 | ty: Type, | ||
| 146 | module: *Module, | ||
| 147 | }; | ||
| 148 | |||
| 149 | fn format2( | ||
| 150 | ctx: FormatContext, | ||
| 151 | comptime unused_format_string: []const u8, | ||
| 152 | options: std.fmt.FormatOptions, | ||
| 153 | writer: anytype, | ||
| 154 | ) !void { | ||
| 155 | comptime assert(unused_format_string.len == 0); | ||
| 156 | _ = options; | ||
| 157 | return print(ctx.ty, writer, ctx.module); | ||
| 158 | } | ||
| 159 | |||
| 160 | pub fn fmtDebug(ty: Type) std.fmt.Formatter(dump) { | ||
| 161 | return .{ .data = ty }; | ||
| 162 | } | ||
| 163 | |||
| 164 | /// This is a debug function. In order to print types in a meaningful way | ||
| 165 | /// we also need access to the module. | ||
| 166 | pub fn dump( | ||
| 167 | start_type: Type, | ||
| 168 | comptime unused_format_string: []const u8, | ||
| 169 | options: std.fmt.FormatOptions, | ||
| 170 | writer: anytype, | ||
| 171 | ) @TypeOf(writer).Error!void { | ||
| 172 | _ = options; | ||
| 173 | comptime assert(unused_format_string.len == 0); | ||
| 174 | return writer.print("{any}", .{start_type.ip_index}); | ||
| 175 | } | ||
| 176 | |||
| 177 | /// Prints a name suitable for `@typeName`. | ||
| 178 | /// TODO: take an `opt_sema` to pass to `fmtValue` when printing sentinels. | ||
| 179 | pub fn print(ty: Type, writer: anytype, mod: *Module) @TypeOf(writer).Error!void { | ||
| 180 | const ip = &mod.intern_pool; | ||
| 181 | switch (ip.indexToKey(ty.toIntern())) { | ||
| 182 | .int_type => |int_type| { | ||
| 183 | const sign_char: u8 = switch (int_type.signedness) { | ||
| 184 | .signed => 'i', | ||
| 185 | .unsigned => 'u', | ||
| 186 | }; | ||
| 187 | return writer.print("{c}{d}", .{ sign_char, int_type.bits }); | ||
| 188 | }, | ||
| 189 | .ptr_type => { | ||
| 190 | const info = ty.ptrInfo(mod); | ||
| 191 | |||
| 192 | if (info.sentinel != .none) switch (info.flags.size) { | ||
| 193 | .One, .C => unreachable, | ||
| 194 | .Many => try writer.print("[*:{}]", .{Value.fromInterned(info.sentinel).fmtValue(mod, null)}), | ||
| 195 | .Slice => try writer.print("[:{}]", .{Value.fromInterned(info.sentinel).fmtValue(mod, null)}), | ||
| 196 | } else switch (info.flags.size) { | ||
| 197 | .One => try writer.writeAll("*"), | ||
| 198 | .Many => try writer.writeAll("[*]"), | ||
| 199 | .C => try writer.writeAll("[*c]"), | ||
| 200 | .Slice => try writer.writeAll("[]"), | ||
| 201 | } | ||
| 202 | if (info.flags.alignment != .none or | ||
| 203 | info.packed_offset.host_size != 0 or | ||
| 204 | info.flags.vector_index != .none) | ||
| 205 | { | ||
| 206 | const alignment = if (info.flags.alignment != .none) | ||
| 207 | info.flags.alignment | ||
| 208 | else | ||
| 209 | Type.fromInterned(info.child).abiAlignment(mod); | ||
| 210 | try writer.print("align({d}", .{alignment.toByteUnits() orelse 0}); | ||
| 211 | |||
| 212 | if (info.packed_offset.bit_offset != 0 or info.packed_offset.host_size != 0) { | ||
| 213 | try writer.print(":{d}:{d}", .{ | ||
| 214 | info.packed_offset.bit_offset, info.packed_offset.host_size, | ||
| 215 | }); | ||
| 216 | } | ||
| 217 | if (info.flags.vector_index == .runtime) { | ||
| 218 | try writer.writeAll(":?"); | ||
| 219 | } else if (info.flags.vector_index != .none) { | ||
| 220 | try writer.print(":{d}", .{@intFromEnum(info.flags.vector_index)}); | ||
| 221 | } | ||
| 222 | try writer.writeAll(") "); | ||
| 223 | } | ||
| 224 | if (info.flags.address_space != .generic) { | ||
| 225 | try writer.print("addrspace(.{s}) ", .{@tagName(info.flags.address_space)}); | ||
| 226 | } | ||
| 227 | if (info.flags.is_const) try writer.writeAll("const "); | ||
| 228 | if (info.flags.is_volatile) try writer.writeAll("volatile "); | ||
| 229 | if (info.flags.is_allowzero and info.flags.size != .C) try writer.writeAll("allowzero "); | ||
| 230 | |||
| 231 | try print(Type.fromInterned(info.child), writer, mod); | ||
| 232 | return; | ||
| 233 | }, | ||
| 234 | .array_type => |array_type| { | ||
| 235 | if (array_type.sentinel == .none) { | ||
| 236 | try writer.print("[{d}]", .{array_type.len}); | ||
| 237 | try print(Type.fromInterned(array_type.child), writer, mod); | ||
| 238 | } else { | ||
| 239 | try writer.print("[{d}:{}]", .{ | ||
| 240 | array_type.len, | ||
| 241 | Value.fromInterned(array_type.sentinel).fmtValue(mod, null), | ||
| 242 | }); | ||
| 243 | try print(Type.fromInterned(array_type.child), writer, mod); | ||
| 244 | } | ||
| 245 | return; | ||
| 246 | }, | ||
| 247 | .vector_type => |vector_type| { | ||
| 248 | try writer.print("@Vector({d}, ", .{vector_type.len}); | ||
| 249 | try print(Type.fromInterned(vector_type.child), writer, mod); | ||
| 250 | try writer.writeAll(")"); | ||
| 251 | return; | ||
| 252 | }, | ||
| 253 | .opt_type => |child| { | ||
| 254 | try writer.writeByte('?'); | ||
| 255 | return print(Type.fromInterned(child), writer, mod); | ||
| 256 | }, | ||
| 257 | .error_union_type => |error_union_type| { | ||
| 258 | try print(Type.fromInterned(error_union_type.error_set_type), writer, mod); | ||
| 259 | try writer.writeByte('!'); | ||
| 260 | if (error_union_type.payload_type == .generic_poison_type) { | ||
| 261 | try writer.writeAll("anytype"); | ||
| 262 | } else { | ||
| 263 | try print(Type.fromInterned(error_union_type.payload_type), writer, mod); | ||
| 264 | } | ||
| 265 | return; | ||
| 266 | }, | ||
| 267 | .inferred_error_set_type => |func_index| { | ||
| 268 | try writer.writeAll("@typeInfo(@typeInfo(@TypeOf("); | ||
| 269 | const owner_decl = mod.funcOwnerDeclPtr(func_index); | ||
| 270 | try owner_decl.renderFullyQualifiedName(mod, writer); | ||
| 271 | try writer.writeAll(")).Fn.return_type.?).ErrorUnion.error_set"); | ||
| 272 | }, | ||
| 273 | .error_set_type => |error_set_type| { | ||
| 274 | const names = error_set_type.names; | ||
| 275 | try writer.writeAll("error{"); | ||
| 276 | for (names.get(ip), 0..) |name, i| { | ||
| 277 | if (i != 0) try writer.writeByte(','); | ||
| 278 | try writer.print("{}", .{name.fmt(ip)}); | ||
| 279 | } | ||
| 280 | try writer.writeAll("}"); | ||
| 281 | }, | ||
| 282 | .simple_type => |s| switch (s) { | ||
| 283 | .f16, | ||
| 284 | .f32, | ||
| 285 | .f64, | ||
| 286 | .f80, | ||
| 287 | .f128, | ||
| 288 | .usize, | ||
| 289 | .isize, | ||
| 290 | .c_char, | ||
| 291 | .c_short, | ||
| 292 | .c_ushort, | ||
| 293 | .c_int, | ||
| 294 | .c_uint, | ||
| 295 | .c_long, | ||
| 296 | .c_ulong, | ||
| 297 | .c_longlong, | ||
| 298 | .c_ulonglong, | ||
| 299 | .c_longdouble, | ||
| 300 | .anyopaque, | ||
| 301 | .bool, | ||
| 302 | .void, | ||
| 303 | .type, | ||
| 304 | .anyerror, | ||
| 305 | .comptime_int, | ||
| 306 | .comptime_float, | ||
| 307 | .noreturn, | ||
| 308 | .adhoc_inferred_error_set, | ||
| 309 | => return writer.writeAll(@tagName(s)), | ||
| 310 | |||
| 311 | .null, | ||
| 312 | .undefined, | ||
| 313 | => try writer.print("@TypeOf({s})", .{@tagName(s)}), | ||
| 314 | |||
| 315 | .enum_literal => try writer.print("@TypeOf(.{s})", .{@tagName(s)}), | ||
| 316 | .atomic_order => try writer.writeAll("std.builtin.AtomicOrder"), | ||
| 317 | .atomic_rmw_op => try writer.writeAll("std.builtin.AtomicRmwOp"), | ||
| 318 | .calling_convention => try writer.writeAll("std.builtin.CallingConvention"), | ||
| 319 | .address_space => try writer.writeAll("std.builtin.AddressSpace"), | ||
| 320 | .float_mode => try writer.writeAll("std.builtin.FloatMode"), | ||
| 321 | .reduce_op => try writer.writeAll("std.builtin.ReduceOp"), | ||
| 322 | .call_modifier => try writer.writeAll("std.builtin.CallModifier"), | ||
| 323 | .prefetch_options => try writer.writeAll("std.builtin.PrefetchOptions"), | ||
| 324 | .export_options => try writer.writeAll("std.builtin.ExportOptions"), | ||
| 325 | .extern_options => try writer.writeAll("std.builtin.ExternOptions"), | ||
| 326 | .type_info => try writer.writeAll("std.builtin.Type"), | ||
| 327 | |||
| 328 | .generic_poison => unreachable, | ||
| 329 | }, | ||
| 330 | .struct_type => { | ||
| 331 | const struct_type = ip.loadStructType(ty.toIntern()); | ||
| 332 | if (struct_type.decl.unwrap()) |decl_index| { | ||
| 333 | const decl = mod.declPtr(decl_index); | ||
| 334 | try decl.renderFullyQualifiedName(mod, writer); | ||
| 335 | } else if (ip.loadStructType(ty.toIntern()).namespace.unwrap()) |namespace_index| { | ||
| 336 | const namespace = mod.namespacePtr(namespace_index); | ||
| 337 | try namespace.renderFullyQualifiedName(mod, .empty, writer); | ||
| 338 | } else { | ||
| 339 | try writer.writeAll("@TypeOf(.{})"); | ||
| 340 | } | ||
| 341 | }, | ||
| 342 | .anon_struct_type => |anon_struct| { | ||
| 343 | if (anon_struct.types.len == 0) { | ||
| 344 | return writer.writeAll("@TypeOf(.{})"); | ||
| 345 | } | ||
| 346 | try writer.writeAll("struct{"); | ||
| 347 | for (anon_struct.types.get(ip), anon_struct.values.get(ip), 0..) |field_ty, val, i| { | ||
| 348 | if (i != 0) try writer.writeAll(", "); | ||
| 349 | if (val != .none) { | ||
| 350 | try writer.writeAll("comptime "); | ||
| 351 | } | ||
| 352 | if (anon_struct.names.len != 0) { | ||
| 353 | try writer.print("{}: ", .{anon_struct.names.get(ip)[i].fmt(&mod.intern_pool)}); | ||
| 354 | } | ||
| 355 | |||
| 356 | try print(Type.fromInterned(field_ty), writer, mod); | ||
| 357 | |||
| 358 | if (val != .none) { | ||
| 359 | try writer.print(" = {}", .{Value.fromInterned(val).fmtValue(mod, null)}); | ||
| 360 | } | ||
| 361 | } | ||
| 362 | try writer.writeAll("}"); | ||
| 363 | }, | ||
| 364 | |||
| 365 | .union_type => { | ||
| 366 | const decl = mod.declPtr(ip.loadUnionType(ty.toIntern()).decl); | ||
| 367 | try decl.renderFullyQualifiedName(mod, writer); | ||
| 368 | }, | ||
| 369 | .opaque_type => { | ||
| 370 | const decl = mod.declPtr(ip.loadOpaqueType(ty.toIntern()).decl); | ||
| 371 | try decl.renderFullyQualifiedName(mod, writer); | ||
| 372 | }, | ||
| 373 | .enum_type => { | ||
| 374 | const decl = mod.declPtr(ip.loadEnumType(ty.toIntern()).decl); | ||
| 375 | try decl.renderFullyQualifiedName(mod, writer); | ||
| 376 | }, | ||
| 377 | .func_type => |fn_info| { | ||
| 378 | if (fn_info.is_noinline) { | ||
| 379 | try writer.writeAll("noinline "); | ||
| 380 | } | ||
| 381 | try writer.writeAll("fn ("); | ||
| 382 | const param_types = fn_info.param_types.get(&mod.intern_pool); | ||
| 383 | for (param_types, 0..) |param_ty, i| { | ||
| 384 | if (i != 0) try writer.writeAll(", "); | ||
| 385 | if (std.math.cast(u5, i)) |index| { | ||
| 386 | if (fn_info.paramIsComptime(index)) { | ||
| 387 | try writer.writeAll("comptime "); | ||
| 388 | } | ||
| 389 | if (fn_info.paramIsNoalias(index)) { | ||
| 390 | try writer.writeAll("noalias "); | ||
| 391 | } | ||
| 392 | } | ||
| 393 | if (param_ty == .generic_poison_type) { | ||
| 394 | try writer.writeAll("anytype"); | ||
| 395 | } else { | ||
| 396 | try print(Type.fromInterned(param_ty), writer, mod); | ||
| 397 | } | ||
| 398 | } | ||
| 399 | if (fn_info.is_var_args) { | ||
| 400 | if (param_types.len != 0) { | ||
| 401 | try writer.writeAll(", "); | ||
| 402 | } | ||
| 403 | try writer.writeAll("..."); | ||
| 404 | } | ||
| 405 | try writer.writeAll(") "); | ||
| 406 | if (fn_info.cc != .Unspecified) { | ||
| 407 | try writer.writeAll("callconv(."); | ||
| 408 | try writer.writeAll(@tagName(fn_info.cc)); | ||
| 409 | try writer.writeAll(") "); | ||
| 410 | } | ||
| 411 | if (fn_info.return_type == .generic_poison_type) { | ||
| 412 | try writer.writeAll("anytype"); | ||
| 413 | } else { | ||
| 414 | try print(Type.fromInterned(fn_info.return_type), writer, mod); | ||
| 415 | } | ||
| 416 | }, | ||
| 417 | .anyframe_type => |child| { | ||
| 418 | if (child == .none) return writer.writeAll("anyframe"); | ||
| 419 | try writer.writeAll("anyframe->"); | ||
| 420 | return print(Type.fromInterned(child), writer, mod); | ||
| 421 | }, | ||
| 422 | |||
| 423 | // values, not types | ||
| 424 | .undef, | ||
| 425 | .simple_value, | ||
| 426 | .variable, | ||
| 427 | .extern_func, | ||
| 428 | .func, | ||
| 429 | .int, | ||
| 430 | .err, | ||
| 431 | .error_union, | ||
| 432 | .enum_literal, | ||
| 433 | .enum_tag, | ||
| 434 | .empty_enum_value, | ||
| 435 | .float, | ||
| 436 | .ptr, | ||
| 437 | .slice, | ||
| 438 | .opt, | ||
| 439 | .aggregate, | ||
| 440 | .un, | ||
| 441 | // memoization, not types | ||
| 442 | .memoized_call, | ||
| 443 | => unreachable, | ||
| 444 | } | ||
| 445 | } | ||
| 446 | |||
| 447 | pub fn fromInterned(i: InternPool.Index) Type { | ||
| 448 | assert(i != .none); | ||
| 449 | return .{ .ip_index = i }; | ||
| 450 | } | ||
| 451 | |||
| 452 | pub fn toIntern(ty: Type) InternPool.Index { | ||
| 453 | assert(ty.ip_index != .none); | ||
| 454 | return ty.ip_index; | ||
| 455 | } | ||
| 456 | |||
| 457 | pub fn toValue(self: Type) Value { | ||
| 458 | return Value.fromInterned(self.toIntern()); | ||
| 459 | } | ||
| 460 | |||
| 461 | const RuntimeBitsError = Module.CompileError || error{NeedLazy}; | ||
| 462 | |||
| 463 | /// true if and only if the type takes up space in memory at runtime. | ||
| 464 | /// There are two reasons a type will return false: | ||
| 465 | /// * the type is a comptime-only type. For example, the type `type` itself. | ||
| 466 | /// - note, however, that a struct can have mixed fields and only the non-comptime-only | ||
| 467 | /// fields will count towards the ABI size. For example, `struct {T: type, x: i32}` | ||
| 468 | /// hasRuntimeBits()=true and abiSize()=4 | ||
| 469 | /// * the type has only one possible value, making its ABI size 0. | ||
| 470 | /// - an enum with an explicit tag type has the ABI size of the integer tag type, | ||
| 471 | /// making it one-possible-value only if the integer tag type has 0 bits. | ||
| 472 | /// When `ignore_comptime_only` is true, then types that are comptime-only | ||
| 473 | /// may return false positives. | ||
| 474 | pub fn hasRuntimeBitsAdvanced( | ||
| 475 | ty: Type, | ||
| 476 | mod: *Module, | ||
| 477 | ignore_comptime_only: bool, | ||
| 478 | strat: AbiAlignmentAdvancedStrat, | ||
| 479 | ) RuntimeBitsError!bool { | ||
| 480 | const ip = &mod.intern_pool; | ||
| 481 | return switch (ty.toIntern()) { | ||
| 482 | // False because it is a comptime-only type. | ||
| 483 | .empty_struct_type => false, | ||
| 484 | else => switch (ip.indexToKey(ty.toIntern())) { | ||
| 485 | .int_type => |int_type| int_type.bits != 0, | ||
| 486 | .ptr_type => { | ||
| 487 | // Pointers to zero-bit types still have a runtime address; however, pointers | ||
| 488 | // to comptime-only types do not, with the exception of function pointers. | ||
| 489 | if (ignore_comptime_only) return true; | ||
| 490 | return switch (strat) { | ||
| 491 | .sema => |sema| !(try sema.typeRequiresComptime(ty)), | ||
| 492 | .eager => !comptimeOnly(ty, mod), | ||
| 493 | .lazy => error.NeedLazy, | ||
| 494 | }; | ||
| 495 | }, | ||
| 496 | .anyframe_type => true, | ||
| 497 | .array_type => |array_type| return array_type.lenIncludingSentinel() > 0 and | ||
| 498 | try Type.fromInterned(array_type.child).hasRuntimeBitsAdvanced(mod, ignore_comptime_only, strat), | ||
| 499 | .vector_type => |vector_type| return vector_type.len > 0 and | ||
| 500 | try Type.fromInterned(vector_type.child).hasRuntimeBitsAdvanced(mod, ignore_comptime_only, strat), | ||
| 501 | .opt_type => |child| { | ||
| 502 | const child_ty = Type.fromInterned(child); | ||
| 503 | if (child_ty.isNoReturn(mod)) { | ||
| 504 | // Then the optional is comptime-known to be null. | ||
| 505 | return false; | ||
| 506 | } | ||
| 507 | if (ignore_comptime_only) return true; | ||
| 508 | return switch (strat) { | ||
| 509 | .sema => |sema| !(try sema.typeRequiresComptime(child_ty)), | ||
| 510 | .eager => !comptimeOnly(child_ty, mod), | ||
| 511 | .lazy => error.NeedLazy, | ||
| 512 | }; | ||
| 513 | }, | ||
| 514 | .error_union_type, | ||
| 515 | .error_set_type, | ||
| 516 | .inferred_error_set_type, | ||
| 517 | => true, | ||
| 518 | |||
| 519 | // These are function *bodies*, not pointers. | ||
| 520 | // They return false here because they are comptime-only types. | ||
| 521 | // Special exceptions have to be made when emitting functions due to | ||
| 522 | // this returning false. | ||
| 523 | .func_type => false, | ||
| 524 | |||
| 525 | .simple_type => |t| switch (t) { | ||
| 526 | .f16, | ||
| 527 | .f32, | ||
| 528 | .f64, | ||
| 529 | .f80, | ||
| 530 | .f128, | ||
| 531 | .usize, | ||
| 532 | .isize, | ||
| 533 | .c_char, | ||
| 534 | .c_short, | ||
| 535 | .c_ushort, | ||
| 536 | .c_int, | ||
| 537 | .c_uint, | ||
| 538 | .c_long, | ||
| 539 | .c_ulong, | ||
| 540 | .c_longlong, | ||
| 541 | .c_ulonglong, | ||
| 542 | .c_longdouble, | ||
| 543 | .bool, | ||
| 544 | .anyerror, | ||
| 545 | .adhoc_inferred_error_set, | ||
| 546 | .anyopaque, | ||
| 547 | .atomic_order, | ||
| 548 | .atomic_rmw_op, | ||
| 549 | .calling_convention, | ||
| 550 | .address_space, | ||
| 551 | .float_mode, | ||
| 552 | .reduce_op, | ||
| 553 | .call_modifier, | ||
| 554 | .prefetch_options, | ||
| 555 | .export_options, | ||
| 556 | .extern_options, | ||
| 557 | => true, | ||
| 558 | |||
| 559 | // These are false because they are comptime-only types. | ||
| 560 | .void, | ||
| 561 | .type, | ||
| 562 | .comptime_int, | ||
| 563 | .comptime_float, | ||
| 564 | .noreturn, | ||
| 565 | .null, | ||
| 566 | .undefined, | ||
| 567 | .enum_literal, | ||
| 568 | .type_info, | ||
| 569 | => false, | ||
| 570 | |||
| 571 | .generic_poison => unreachable, | ||
| 572 | }, | ||
| 573 | .struct_type => { | ||
| 574 | const struct_type = ip.loadStructType(ty.toIntern()); | ||
| 575 | if (struct_type.assumeRuntimeBitsIfFieldTypesWip(ip)) { | ||
| 576 | // In this case, we guess that hasRuntimeBits() for this type is true, | ||
| 577 | // and then later if our guess was incorrect, we emit a compile error. | ||
| 578 | return true; | ||
| 579 | } | ||
| 580 | switch (strat) { | ||
| 581 | .sema => |sema| _ = try sema.resolveTypeFields(ty), | ||
| 582 | .eager => assert(struct_type.haveFieldTypes(ip)), | ||
| 583 | .lazy => if (!struct_type.haveFieldTypes(ip)) return error.NeedLazy, | ||
| 584 | } | ||
| 585 | for (0..struct_type.field_types.len) |i| { | ||
| 586 | if (struct_type.comptime_bits.getBit(ip, i)) continue; | ||
| 587 | const field_ty = Type.fromInterned(struct_type.field_types.get(ip)[i]); | ||
| 588 | if (try field_ty.hasRuntimeBitsAdvanced(mod, ignore_comptime_only, strat)) | ||
| 589 | return true; | ||
| 590 | } else { | ||
| 591 | return false; | ||
| 592 | } | ||
| 593 | }, | ||
| 594 | .anon_struct_type => |tuple| { | ||
| 595 | for (tuple.types.get(ip), tuple.values.get(ip)) |field_ty, val| { | ||
| 596 | if (val != .none) continue; // comptime field | ||
| 597 | if (try Type.fromInterned(field_ty).hasRuntimeBitsAdvanced(mod, ignore_comptime_only, strat)) return true; | ||
| 598 | } | ||
| 599 | return false; | ||
| 600 | }, | ||
| 601 | |||
| 602 | .union_type => { | ||
| 603 | const union_type = ip.loadUnionType(ty.toIntern()); | ||
| 604 | switch (union_type.flagsPtr(ip).runtime_tag) { | ||
| 605 | .none => { | ||
| 606 | if (union_type.flagsPtr(ip).status == .field_types_wip) { | ||
| 607 | // In this case, we guess that hasRuntimeBits() for this type is true, | ||
| 608 | // and then later if our guess was incorrect, we emit a compile error. | ||
| 609 | union_type.flagsPtr(ip).assumed_runtime_bits = true; | ||
| 610 | return true; | ||
| 611 | } | ||
| 612 | }, | ||
| 613 | .safety, .tagged => { | ||
| 614 | const tag_ty = union_type.tagTypePtr(ip).*; | ||
| 615 | // tag_ty will be `none` if this union's tag type is not resolved yet, | ||
| 616 | // in which case we want control flow to continue down below. | ||
| 617 | if (tag_ty != .none and | ||
| 618 | try Type.fromInterned(tag_ty).hasRuntimeBitsAdvanced(mod, ignore_comptime_only, strat)) | ||
| 619 | { | ||
| 620 | return true; | ||
| 621 | } | ||
| 622 | }, | ||
| 623 | } | ||
| 624 | switch (strat) { | ||
| 625 | .sema => |sema| _ = try sema.resolveTypeFields(ty), | ||
| 626 | .eager => assert(union_type.flagsPtr(ip).status.haveFieldTypes()), | ||
| 627 | .lazy => if (!union_type.flagsPtr(ip).status.haveFieldTypes()) | ||
| 628 | return error.NeedLazy, | ||
| 629 | } | ||
| 630 | for (0..union_type.field_types.len) |field_index| { | ||
| 631 | const field_ty = Type.fromInterned(union_type.field_types.get(ip)[field_index]); | ||
| 632 | if (try field_ty.hasRuntimeBitsAdvanced(mod, ignore_comptime_only, strat)) | ||
| 633 | return true; | ||
| 634 | } else { | ||
| 635 | return false; | ||
| 636 | } | ||
| 637 | }, | ||
| 638 | |||
| 639 | .opaque_type => true, | ||
| 640 | .enum_type => Type.fromInterned(ip.loadEnumType(ty.toIntern()).tag_ty).hasRuntimeBitsAdvanced(mod, ignore_comptime_only, strat), | ||
| 641 | |||
| 642 | // values, not types | ||
| 643 | .undef, | ||
| 644 | .simple_value, | ||
| 645 | .variable, | ||
| 646 | .extern_func, | ||
| 647 | .func, | ||
| 648 | .int, | ||
| 649 | .err, | ||
| 650 | .error_union, | ||
| 651 | .enum_literal, | ||
| 652 | .enum_tag, | ||
| 653 | .empty_enum_value, | ||
| 654 | .float, | ||
| 655 | .ptr, | ||
| 656 | .slice, | ||
| 657 | .opt, | ||
| 658 | .aggregate, | ||
| 659 | .un, | ||
| 660 | // memoization, not types | ||
| 661 | .memoized_call, | ||
| 662 | => unreachable, | ||
| 663 | }, | ||
| 664 | }; | ||
| 665 | } | ||
| 666 | |||
| 667 | /// true if and only if the type has a well-defined memory layout | ||
| 668 | /// readFrom/writeToMemory are supported only for types with a well- | ||
| 669 | /// defined memory layout | ||
| 670 | pub fn hasWellDefinedLayout(ty: Type, mod: *Module) bool { | ||
| 671 | const ip = &mod.intern_pool; | ||
| 672 | return switch (ip.indexToKey(ty.toIntern())) { | ||
| 673 | .int_type, | ||
| 674 | .vector_type, | ||
| 675 | => true, | ||
| 676 | |||
| 677 | .error_union_type, | ||
| 678 | .error_set_type, | ||
| 679 | .inferred_error_set_type, | ||
| 680 | .anon_struct_type, | ||
| 681 | .opaque_type, | ||
| 682 | .anyframe_type, | ||
| 683 | // These are function bodies, not function pointers. | ||
| 684 | .func_type, | ||
| 685 | => false, | ||
| 686 | |||
| 687 | .array_type => |array_type| Type.fromInterned(array_type.child).hasWellDefinedLayout(mod), | ||
| 688 | .opt_type => ty.isPtrLikeOptional(mod), | ||
| 689 | .ptr_type => |ptr_type| ptr_type.flags.size != .Slice, | ||
| 690 | |||
| 691 | .simple_type => |t| switch (t) { | ||
| 692 | .f16, | ||
| 693 | .f32, | ||
| 694 | .f64, | ||
| 695 | .f80, | ||
| 696 | .f128, | ||
| 697 | .usize, | ||
| 698 | .isize, | ||
| 699 | .c_char, | ||
| 700 | .c_short, | ||
| 701 | .c_ushort, | ||
| 702 | .c_int, | ||
| 703 | .c_uint, | ||
| 704 | .c_long, | ||
| 705 | .c_ulong, | ||
| 706 | .c_longlong, | ||
| 707 | .c_ulonglong, | ||
| 708 | .c_longdouble, | ||
| 709 | .bool, | ||
| 710 | .void, | ||
| 711 | => true, | ||
| 712 | |||
| 713 | .anyerror, | ||
| 714 | .adhoc_inferred_error_set, | ||
| 715 | .anyopaque, | ||
| 716 | .atomic_order, | ||
| 717 | .atomic_rmw_op, | ||
| 718 | .calling_convention, | ||
| 719 | .address_space, | ||
| 720 | .float_mode, | ||
| 721 | .reduce_op, | ||
| 722 | .call_modifier, | ||
| 723 | .prefetch_options, | ||
| 724 | .export_options, | ||
| 725 | .extern_options, | ||
| 726 | .type, | ||
| 727 | .comptime_int, | ||
| 728 | .comptime_float, | ||
| 729 | .noreturn, | ||
| 730 | .null, | ||
| 731 | .undefined, | ||
| 732 | .enum_literal, | ||
| 733 | .type_info, | ||
| 734 | .generic_poison, | ||
| 735 | => false, | ||
| 736 | }, | ||
| 737 | .struct_type => { | ||
| 738 | const struct_type = ip.loadStructType(ty.toIntern()); | ||
| 739 | // Struct with no fields have a well-defined layout of no bits. | ||
| 740 | return struct_type.layout != .auto or struct_type.field_types.len == 0; | ||
| 741 | }, | ||
| 742 | .union_type => { | ||
| 743 | const union_type = ip.loadUnionType(ty.toIntern()); | ||
| 744 | return switch (union_type.flagsPtr(ip).runtime_tag) { | ||
| 745 | .none, .safety => union_type.flagsPtr(ip).layout != .auto, | ||
| 746 | .tagged => false, | ||
| 747 | }; | ||
| 748 | }, | ||
| 749 | .enum_type => switch (ip.loadEnumType(ty.toIntern()).tag_mode) { | ||
| 750 | .auto => false, | ||
| 751 | .explicit, .nonexhaustive => true, | ||
| 752 | }, | ||
| 753 | |||
| 754 | // values, not types | ||
| 755 | .undef, | ||
| 756 | .simple_value, | ||
| 757 | .variable, | ||
| 758 | .extern_func, | ||
| 759 | .func, | ||
| 760 | .int, | ||
| 761 | .err, | ||
| 762 | .error_union, | ||
| 763 | .enum_literal, | ||
| 764 | .enum_tag, | ||
| 765 | .empty_enum_value, | ||
| 766 | .float, | ||
| 767 | .ptr, | ||
| 768 | .slice, | ||
| 769 | .opt, | ||
| 770 | .aggregate, | ||
| 771 | .un, | ||
| 772 | // memoization, not types | ||
| 773 | .memoized_call, | ||
| 774 | => unreachable, | ||
| 775 | }; | ||
| 776 | } | ||
| 777 | |||
| 778 | pub fn hasRuntimeBits(ty: Type, mod: *Module) bool { | ||
| 779 | return hasRuntimeBitsAdvanced(ty, mod, false, .eager) catch unreachable; | ||
| 780 | } | ||
| 781 | |||
| 782 | pub fn hasRuntimeBitsIgnoreComptime(ty: Type, mod: *Module) bool { | ||
| 783 | return hasRuntimeBitsAdvanced(ty, mod, true, .eager) catch unreachable; | ||
| 784 | } | ||
| 785 | |||
| 786 | pub fn fnHasRuntimeBits(ty: Type, mod: *Module) bool { | ||
| 787 | return ty.fnHasRuntimeBitsAdvanced(mod, null) catch unreachable; | ||
| 788 | } | ||
| 789 | |||
| 790 | /// Determines whether a function type has runtime bits, i.e. whether a | ||
| 791 | /// function with this type can exist at runtime. | ||
| 792 | /// Asserts that `ty` is a function type. | ||
| 793 | /// If `opt_sema` is not provided, asserts that the return type is sufficiently resolved. | ||
| 794 | pub fn fnHasRuntimeBitsAdvanced(ty: Type, mod: *Module, opt_sema: ?*Sema) Module.CompileError!bool { | ||
| 795 | const fn_info = mod.typeToFunc(ty).?; | ||
| 796 | if (fn_info.is_generic) return false; | ||
| 797 | if (fn_info.is_var_args) return true; | ||
| 798 | if (fn_info.cc == .Inline) return false; | ||
| 799 | return !try Type.fromInterned(fn_info.return_type).comptimeOnlyAdvanced(mod, opt_sema); | ||
| 800 | } | ||
| 801 | |||
| 802 | pub fn isFnOrHasRuntimeBits(ty: Type, mod: *Module) bool { | ||
| 803 | switch (ty.zigTypeTag(mod)) { | ||
| 804 | .Fn => return ty.fnHasRuntimeBits(mod), | ||
| 805 | else => return ty.hasRuntimeBits(mod), | ||
| 806 | } | ||
| 807 | } | ||
| 808 | |||
| 809 | /// Same as `isFnOrHasRuntimeBits` but comptime-only types may return a false positive. | ||
| 810 | pub fn isFnOrHasRuntimeBitsIgnoreComptime(ty: Type, mod: *Module) bool { | ||
| 811 | return switch (ty.zigTypeTag(mod)) { | ||
| 812 | .Fn => true, | ||
| 813 | else => return ty.hasRuntimeBitsIgnoreComptime(mod), | ||
| 814 | }; | ||
| 815 | } | ||
| 816 | |||
| 817 | pub fn isNoReturn(ty: Type, mod: *Module) bool { | ||
| 818 | return mod.intern_pool.isNoReturn(ty.toIntern()); | ||
| 819 | } | ||
| 820 | |||
| 821 | /// Returns `none` if the pointer is naturally aligned and the element type is 0-bit. | ||
| 822 | pub fn ptrAlignment(ty: Type, mod: *Module) Alignment { | ||
| 823 | return ptrAlignmentAdvanced(ty, mod, null) catch unreachable; | ||
| 824 | } | ||
| 825 | |||
| 826 | pub fn ptrAlignmentAdvanced(ty: Type, mod: *Module, opt_sema: ?*Sema) !Alignment { | ||
| 827 | return switch (mod.intern_pool.indexToKey(ty.toIntern())) { | ||
| 828 | .ptr_type => |ptr_type| { | ||
| 829 | if (ptr_type.flags.alignment != .none) | ||
| 830 | return ptr_type.flags.alignment; | ||
| 831 | |||
| 832 | if (opt_sema) |sema| { | ||
| 833 | const res = try Type.fromInterned(ptr_type.child).abiAlignmentAdvanced(mod, .{ .sema = sema }); | ||
| 834 | return res.scalar; | ||
| 835 | } | ||
| 836 | |||
| 837 | return (Type.fromInterned(ptr_type.child).abiAlignmentAdvanced(mod, .eager) catch unreachable).scalar; | ||
| 838 | }, | ||
| 839 | .opt_type => |child| Type.fromInterned(child).ptrAlignmentAdvanced(mod, opt_sema), | ||
| 840 | else => unreachable, | ||
| 841 | }; | ||
| 842 | } | ||
| 843 | |||
| 844 | pub fn ptrAddressSpace(ty: Type, mod: *const Module) std.builtin.AddressSpace { | ||
| 845 | return switch (mod.intern_pool.indexToKey(ty.toIntern())) { | ||
| 846 | .ptr_type => |ptr_type| ptr_type.flags.address_space, | ||
| 847 | .opt_type => |child| mod.intern_pool.indexToKey(child).ptr_type.flags.address_space, | ||
| 848 | else => unreachable, | ||
| 849 | }; | ||
| 850 | } | ||
| 851 | |||
| 852 | /// Never returns `none`. Asserts that all necessary type resolution is already done. | ||
| 853 | pub fn abiAlignment(ty: Type, mod: *Module) Alignment { | ||
| 854 | return (ty.abiAlignmentAdvanced(mod, .eager) catch unreachable).scalar; | ||
| 855 | } | ||
| 856 | |||
| 857 | /// May capture a reference to `ty`. | ||
| 858 | /// Returned value has type `comptime_int`. | ||
| 859 | pub fn lazyAbiAlignment(ty: Type, mod: *Module) !Value { | ||
| 860 | switch (try ty.abiAlignmentAdvanced(mod, .lazy)) { | ||
| 861 | .val => |val| return val, | ||
| 862 | .scalar => |x| return mod.intValue(Type.comptime_int, x.toByteUnits() orelse 0), | ||
| 863 | } | ||
| 864 | } | ||
| 865 | |||
| 866 | pub const AbiAlignmentAdvanced = union(enum) { | ||
| 867 | scalar: Alignment, | ||
| 868 | val: Value, | ||
| 869 | }; | ||
| 870 | |||
| 871 | pub const AbiAlignmentAdvancedStrat = union(enum) { | ||
| 872 | eager, | ||
| 873 | lazy, | ||
| 874 | sema: *Sema, | ||
| 875 | }; | ||
| 876 | |||
| 877 | /// If you pass `eager` you will get back `scalar` and assert the type is resolved. | ||
| 878 | /// In this case there will be no error, guaranteed. | ||
| 879 | /// If you pass `lazy` you may get back `scalar` or `val`. | ||
| 880 | /// If `val` is returned, a reference to `ty` has been captured. | ||
| 881 | /// If you pass `sema` you will get back `scalar` and resolve the type if | ||
| 882 | /// necessary, possibly returning a CompileError. | ||
| 883 | pub fn abiAlignmentAdvanced( | ||
| 884 | ty: Type, | ||
| 885 | mod: *Module, | ||
| 886 | strat: AbiAlignmentAdvancedStrat, | ||
| 887 | ) Module.CompileError!AbiAlignmentAdvanced { | ||
| 888 | const target = mod.getTarget(); | ||
| 889 | const use_llvm = mod.comp.config.use_llvm; | ||
| 890 | const ip = &mod.intern_pool; | ||
| 891 | |||
| 892 | const opt_sema = switch (strat) { | ||
| 893 | .sema => |sema| sema, | ||
| 894 | else => null, | ||
| 895 | }; | ||
| 896 | |||
| 897 | switch (ty.toIntern()) { | ||
| 898 | .empty_struct_type => return AbiAlignmentAdvanced{ .scalar = .@"1" }, | ||
| 899 | else => switch (ip.indexToKey(ty.toIntern())) { | ||
| 900 | .int_type => |int_type| { | ||
| 901 | if (int_type.bits == 0) return AbiAlignmentAdvanced{ .scalar = .@"1" }; | ||
| 902 | return .{ .scalar = intAbiAlignment(int_type.bits, target, use_llvm) }; | ||
| 903 | }, | ||
| 904 | .ptr_type, .anyframe_type => { | ||
| 905 | return .{ .scalar = ptrAbiAlignment(target) }; | ||
| 906 | }, | ||
| 907 | .array_type => |array_type| { | ||
| 908 | return Type.fromInterned(array_type.child).abiAlignmentAdvanced(mod, strat); | ||
| 909 | }, | ||
| 910 | .vector_type => |vector_type| { | ||
| 911 | if (vector_type.len == 0) return .{ .scalar = .@"1" }; | ||
| 912 | switch (mod.comp.getZigBackend()) { | ||
| 913 | else => { | ||
| 914 | const elem_bits: u32 = @intCast(try Type.fromInterned(vector_type.child).bitSizeAdvanced(mod, opt_sema)); | ||
| 915 | if (elem_bits == 0) return .{ .scalar = .@"1" }; | ||
| 916 | const bytes = ((elem_bits * vector_type.len) + 7) / 8; | ||
| 917 | const alignment = std.math.ceilPowerOfTwoAssert(u32, bytes); | ||
| 918 | return .{ .scalar = Alignment.fromByteUnits(alignment) }; | ||
| 919 | }, | ||
| 920 | .stage2_c => { | ||
| 921 | return Type.fromInterned(vector_type.child).abiAlignmentAdvanced(mod, strat); | ||
| 922 | }, | ||
| 923 | .stage2_x86_64 => { | ||
| 924 | if (vector_type.child == .bool_type) { | ||
| 925 | if (vector_type.len > 256 and std.Target.x86.featureSetHas(target.cpu.features, .avx512f)) return .{ .scalar = .@"64" }; | ||
| 926 | if (vector_type.len > 128 and std.Target.x86.featureSetHas(target.cpu.features, .avx2)) return .{ .scalar = .@"32" }; | ||
| 927 | if (vector_type.len > 64) return .{ .scalar = .@"16" }; | ||
| 928 | const bytes = std.math.divCeil(u32, vector_type.len, 8) catch unreachable; | ||
| 929 | const alignment = std.math.ceilPowerOfTwoAssert(u32, bytes); | ||
| 930 | return .{ .scalar = Alignment.fromByteUnits(alignment) }; | ||
| 931 | } | ||
| 932 | const elem_bytes: u32 = @intCast((try Type.fromInterned(vector_type.child).abiSizeAdvanced(mod, strat)).scalar); | ||
| 933 | if (elem_bytes == 0) return .{ .scalar = .@"1" }; | ||
| 934 | const bytes = elem_bytes * vector_type.len; | ||
| 935 | if (bytes > 32 and std.Target.x86.featureSetHas(target.cpu.features, .avx512f)) return .{ .scalar = .@"64" }; | ||
| 936 | if (bytes > 16 and std.Target.x86.featureSetHas(target.cpu.features, .avx)) return .{ .scalar = .@"32" }; | ||
| 937 | return .{ .scalar = .@"16" }; | ||
| 938 | }, | ||
| 939 | } | ||
| 940 | }, | ||
| 941 | |||
| 942 | .opt_type => return abiAlignmentAdvancedOptional(ty, mod, strat), | ||
| 943 | .error_union_type => |info| return abiAlignmentAdvancedErrorUnion(ty, mod, strat, Type.fromInterned(info.payload_type)), | ||
| 944 | |||
| 945 | .error_set_type, .inferred_error_set_type => { | ||
| 946 | const bits = mod.errorSetBits(); | ||
| 947 | if (bits == 0) return AbiAlignmentAdvanced{ .scalar = .@"1" }; | ||
| 948 | return .{ .scalar = intAbiAlignment(bits, target, use_llvm) }; | ||
| 949 | }, | ||
| 950 | |||
| 951 | // represents machine code; not a pointer | ||
| 952 | .func_type => return .{ .scalar = target_util.defaultFunctionAlignment(target) }, | ||
| 953 | |||
| 954 | .simple_type => |t| switch (t) { | ||
| 955 | .bool, | ||
| 956 | .atomic_order, | ||
| 957 | .atomic_rmw_op, | ||
| 958 | .calling_convention, | ||
| 959 | .address_space, | ||
| 960 | .float_mode, | ||
| 961 | .reduce_op, | ||
| 962 | .call_modifier, | ||
| 963 | .prefetch_options, | ||
| 964 | .anyopaque, | ||
| 965 | => return .{ .scalar = .@"1" }, | ||
| 966 | |||
| 967 | .usize, | ||
| 968 | .isize, | ||
| 969 | => return .{ .scalar = intAbiAlignment(target.ptrBitWidth(), target, use_llvm) }, | ||
| 970 | |||
| 971 | .export_options, | ||
| 972 | .extern_options, | ||
| 973 | .type_info, | ||
| 974 | => return .{ .scalar = ptrAbiAlignment(target) }, | ||
| 975 | |||
| 976 | .c_char => return .{ .scalar = cTypeAlign(target, .char) }, | ||
| 977 | .c_short => return .{ .scalar = cTypeAlign(target, .short) }, | ||
| 978 | .c_ushort => return .{ .scalar = cTypeAlign(target, .ushort) }, | ||
| 979 | .c_int => return .{ .scalar = cTypeAlign(target, .int) }, | ||
| 980 | .c_uint => return .{ .scalar = cTypeAlign(target, .uint) }, | ||
| 981 | .c_long => return .{ .scalar = cTypeAlign(target, .long) }, | ||
| 982 | .c_ulong => return .{ .scalar = cTypeAlign(target, .ulong) }, | ||
| 983 | .c_longlong => return .{ .scalar = cTypeAlign(target, .longlong) }, | ||
| 984 | .c_ulonglong => return .{ .scalar = cTypeAlign(target, .ulonglong) }, | ||
| 985 | .c_longdouble => return .{ .scalar = cTypeAlign(target, .longdouble) }, | ||
| 986 | |||
| 987 | .f16 => return .{ .scalar = .@"2" }, | ||
| 988 | .f32 => return .{ .scalar = cTypeAlign(target, .float) }, | ||
| 989 | .f64 => switch (target.c_type_bit_size(.double)) { | ||
| 990 | 64 => return .{ .scalar = cTypeAlign(target, .double) }, | ||
| 991 | else => return .{ .scalar = .@"8" }, | ||
| 992 | }, | ||
| 993 | .f80 => switch (target.c_type_bit_size(.longdouble)) { | ||
| 994 | 80 => return .{ .scalar = cTypeAlign(target, .longdouble) }, | ||
| 995 | else => { | ||
| 996 | const u80_ty: Type = .{ .ip_index = .u80_type }; | ||
| 997 | return .{ .scalar = abiAlignment(u80_ty, mod) }; | ||
| 998 | }, | ||
| 999 | }, | ||
| 1000 | .f128 => switch (target.c_type_bit_size(.longdouble)) { | ||
| 1001 | 128 => return .{ .scalar = cTypeAlign(target, .longdouble) }, | ||
| 1002 | else => return .{ .scalar = .@"16" }, | ||
| 1003 | }, | ||
| 1004 | |||
| 1005 | .anyerror, .adhoc_inferred_error_set => { | ||
| 1006 | const bits = mod.errorSetBits(); | ||
| 1007 | if (bits == 0) return AbiAlignmentAdvanced{ .scalar = .@"1" }; | ||
| 1008 | return .{ .scalar = intAbiAlignment(bits, target, use_llvm) }; | ||
| 1009 | }, | ||
| 1010 | |||
| 1011 | .void, | ||
| 1012 | .type, | ||
| 1013 | .comptime_int, | ||
| 1014 | .comptime_float, | ||
| 1015 | .null, | ||
| 1016 | .undefined, | ||
| 1017 | .enum_literal, | ||
| 1018 | => return .{ .scalar = .@"1" }, | ||
| 1019 | |||
| 1020 | .noreturn => unreachable, | ||
| 1021 | .generic_poison => unreachable, | ||
| 1022 | }, | ||
| 1023 | .struct_type => { | ||
| 1024 | const struct_type = ip.loadStructType(ty.toIntern()); | ||
| 1025 | if (struct_type.layout == .@"packed") { | ||
| 1026 | switch (strat) { | ||
| 1027 | .sema => |sema| try sema.resolveTypeLayout(ty), | ||
| 1028 | .lazy => if (struct_type.backingIntType(ip).* == .none) return .{ | ||
| 1029 | .val = Value.fromInterned((try mod.intern(.{ .int = .{ | ||
| 1030 | .ty = .comptime_int_type, | ||
| 1031 | .storage = .{ .lazy_align = ty.toIntern() }, | ||
| 1032 | } }))), | ||
| 1033 | }, | ||
| 1034 | .eager => {}, | ||
| 1035 | } | ||
| 1036 | return .{ .scalar = Type.fromInterned(struct_type.backingIntType(ip).*).abiAlignment(mod) }; | ||
| 1037 | } | ||
| 1038 | |||
| 1039 | const flags = struct_type.flagsPtr(ip).*; | ||
| 1040 | if (flags.alignment != .none) return .{ .scalar = flags.alignment }; | ||
| 1041 | |||
| 1042 | return switch (strat) { | ||
| 1043 | .eager => unreachable, // struct alignment not resolved | ||
| 1044 | .sema => |sema| .{ | ||
| 1045 | .scalar = try sema.resolveStructAlignment(ty.toIntern(), struct_type), | ||
| 1046 | }, | ||
| 1047 | .lazy => .{ .val = Value.fromInterned((try mod.intern(.{ .int = .{ | ||
| 1048 | .ty = .comptime_int_type, | ||
| 1049 | .storage = .{ .lazy_align = ty.toIntern() }, | ||
| 1050 | } }))) }, | ||
| 1051 | }; | ||
| 1052 | }, | ||
| 1053 | .anon_struct_type => |tuple| { | ||
| 1054 | var big_align: Alignment = .@"1"; | ||
| 1055 | for (tuple.types.get(ip), tuple.values.get(ip)) |field_ty, val| { | ||
| 1056 | if (val != .none) continue; // comptime field | ||
| 1057 | switch (try Type.fromInterned(field_ty).abiAlignmentAdvanced(mod, strat)) { | ||
| 1058 | .scalar => |field_align| big_align = big_align.max(field_align), | ||
| 1059 | .val => switch (strat) { | ||
| 1060 | .eager => unreachable, // field type alignment not resolved | ||
| 1061 | .sema => unreachable, // passed to abiAlignmentAdvanced above | ||
| 1062 | .lazy => return .{ .val = Value.fromInterned((try mod.intern(.{ .int = .{ | ||
| 1063 | .ty = .comptime_int_type, | ||
| 1064 | .storage = .{ .lazy_align = ty.toIntern() }, | ||
| 1065 | } }))) }, | ||
| 1066 | }, | ||
| 1067 | } | ||
| 1068 | } | ||
| 1069 | return .{ .scalar = big_align }; | ||
| 1070 | }, | ||
| 1071 | .union_type => { | ||
| 1072 | const union_type = ip.loadUnionType(ty.toIntern()); | ||
| 1073 | const flags = union_type.flagsPtr(ip).*; | ||
| 1074 | if (flags.alignment != .none) return .{ .scalar = flags.alignment }; | ||
| 1075 | |||
| 1076 | if (!union_type.haveLayout(ip)) switch (strat) { | ||
| 1077 | .eager => unreachable, // union layout not resolved | ||
| 1078 | .sema => |sema| return .{ .scalar = try sema.resolveUnionAlignment(ty, union_type) }, | ||
| 1079 | .lazy => return .{ .val = Value.fromInterned((try mod.intern(.{ .int = .{ | ||
| 1080 | .ty = .comptime_int_type, | ||
| 1081 | .storage = .{ .lazy_align = ty.toIntern() }, | ||
| 1082 | } }))) }, | ||
| 1083 | }; | ||
| 1084 | |||
| 1085 | return .{ .scalar = union_type.flagsPtr(ip).alignment }; | ||
| 1086 | }, | ||
| 1087 | .opaque_type => return .{ .scalar = .@"1" }, | ||
| 1088 | .enum_type => return .{ | ||
| 1089 | .scalar = Type.fromInterned(ip.loadEnumType(ty.toIntern()).tag_ty).abiAlignment(mod), | ||
| 1090 | }, | ||
| 1091 | |||
| 1092 | // values, not types | ||
| 1093 | .undef, | ||
| 1094 | .simple_value, | ||
| 1095 | .variable, | ||
| 1096 | .extern_func, | ||
| 1097 | .func, | ||
| 1098 | .int, | ||
| 1099 | .err, | ||
| 1100 | .error_union, | ||
| 1101 | .enum_literal, | ||
| 1102 | .enum_tag, | ||
| 1103 | .empty_enum_value, | ||
| 1104 | .float, | ||
| 1105 | .ptr, | ||
| 1106 | .slice, | ||
| 1107 | .opt, | ||
| 1108 | .aggregate, | ||
| 1109 | .un, | ||
| 1110 | // memoization, not types | ||
| 1111 | .memoized_call, | ||
| 1112 | => unreachable, | ||
| 1113 | }, | ||
| 1114 | } | ||
| 1115 | } | ||
| 1116 | |||
| 1117 | fn abiAlignmentAdvancedErrorUnion( | ||
| 1118 | ty: Type, | ||
| 1119 | mod: *Module, | ||
| 1120 | strat: AbiAlignmentAdvancedStrat, | ||
| 1121 | payload_ty: Type, | ||
| 1122 | ) Module.CompileError!AbiAlignmentAdvanced { | ||
| 1123 | // This code needs to be kept in sync with the equivalent switch prong | ||
| 1124 | // in abiSizeAdvanced. | ||
| 1125 | const code_align = abiAlignment(Type.anyerror, mod); | ||
| 1126 | switch (strat) { | ||
| 1127 | .eager, .sema => { | ||
| 1128 | if (!(payload_ty.hasRuntimeBitsAdvanced(mod, false, strat) catch |err| switch (err) { | ||
| 1129 | error.NeedLazy => return .{ .val = Value.fromInterned((try mod.intern(.{ .int = .{ | ||
| 1130 | .ty = .comptime_int_type, | ||
| 1131 | .storage = .{ .lazy_align = ty.toIntern() }, | ||
| 1132 | } }))) }, | ||
| 1133 | else => |e| return e, | ||
| 1134 | })) { | ||
| 1135 | return .{ .scalar = code_align }; | ||
| 1136 | } | ||
| 1137 | return .{ .scalar = code_align.max( | ||
| 1138 | (try payload_ty.abiAlignmentAdvanced(mod, strat)).scalar, | ||
| 1139 | ) }; | ||
| 1140 | }, | ||
| 1141 | .lazy => { | ||
| 1142 | switch (try payload_ty.abiAlignmentAdvanced(mod, strat)) { | ||
| 1143 | .scalar => |payload_align| return .{ .scalar = code_align.max(payload_align) }, | ||
| 1144 | .val => {}, | ||
| 1145 | } | ||
| 1146 | return .{ .val = Value.fromInterned((try mod.intern(.{ .int = .{ | ||
| 1147 | .ty = .comptime_int_type, | ||
| 1148 | .storage = .{ .lazy_align = ty.toIntern() }, | ||
| 1149 | } }))) }; | ||
| 1150 | }, | ||
| 1151 | } | ||
| 1152 | } | ||
| 1153 | |||
| 1154 | fn abiAlignmentAdvancedOptional( | ||
| 1155 | ty: Type, | ||
| 1156 | mod: *Module, | ||
| 1157 | strat: AbiAlignmentAdvancedStrat, | ||
| 1158 | ) Module.CompileError!AbiAlignmentAdvanced { | ||
| 1159 | const target = mod.getTarget(); | ||
| 1160 | const child_type = ty.optionalChild(mod); | ||
| 1161 | |||
| 1162 | switch (child_type.zigTypeTag(mod)) { | ||
| 1163 | .Pointer => return .{ .scalar = ptrAbiAlignment(target) }, | ||
| 1164 | .ErrorSet => return abiAlignmentAdvanced(Type.anyerror, mod, strat), | ||
| 1165 | .NoReturn => return .{ .scalar = .@"1" }, | ||
| 1166 | else => {}, | ||
| 1167 | } | ||
| 1168 | |||
| 1169 | switch (strat) { | ||
| 1170 | .eager, .sema => { | ||
| 1171 | if (!(child_type.hasRuntimeBitsAdvanced(mod, false, strat) catch |err| switch (err) { | ||
| 1172 | error.NeedLazy => return .{ .val = Value.fromInterned((try mod.intern(.{ .int = .{ | ||
| 1173 | .ty = .comptime_int_type, | ||
| 1174 | .storage = .{ .lazy_align = ty.toIntern() }, | ||
| 1175 | } }))) }, | ||
| 1176 | else => |e| return e, | ||
| 1177 | })) { | ||
| 1178 | return .{ .scalar = .@"1" }; | ||
| 1179 | } | ||
| 1180 | return child_type.abiAlignmentAdvanced(mod, strat); | ||
| 1181 | }, | ||
| 1182 | .lazy => switch (try child_type.abiAlignmentAdvanced(mod, strat)) { | ||
| 1183 | .scalar => |x| return .{ .scalar = x.max(.@"1") }, | ||
| 1184 | .val => return .{ .val = Value.fromInterned((try mod.intern(.{ .int = .{ | ||
| 1185 | .ty = .comptime_int_type, | ||
| 1186 | .storage = .{ .lazy_align = ty.toIntern() }, | ||
| 1187 | } }))) }, | ||
| 1188 | }, | ||
| 1189 | } | ||
| 1190 | } | ||
| 1191 | |||
| 1192 | /// May capture a reference to `ty`. | ||
| 1193 | pub fn lazyAbiSize(ty: Type, mod: *Module) !Value { | ||
| 1194 | switch (try ty.abiSizeAdvanced(mod, .lazy)) { | ||
| 1195 | .val => |val| return val, | ||
| 1196 | .scalar => |x| return mod.intValue(Type.comptime_int, x), | ||
| 1197 | } | ||
| 1198 | } | ||
| 1199 | |||
| 1200 | /// Asserts the type has the ABI size already resolved. | ||
| 1201 | /// Types that return false for hasRuntimeBits() return 0. | ||
| 1202 | pub fn abiSize(ty: Type, mod: *Module) u64 { | ||
| 1203 | return (abiSizeAdvanced(ty, mod, .eager) catch unreachable).scalar; | ||
| 1204 | } | ||
| 1205 | |||
| 1206 | const AbiSizeAdvanced = union(enum) { | ||
| 1207 | scalar: u64, | ||
| 1208 | val: Value, | ||
| 1209 | }; | ||
| 1210 | |||
| 1211 | /// If you pass `eager` you will get back `scalar` and assert the type is resolved. | ||
| 1212 | /// In this case there will be no error, guaranteed. | ||
| 1213 | /// If you pass `lazy` you may get back `scalar` or `val`. | ||
| 1214 | /// If `val` is returned, a reference to `ty` has been captured. | ||
| 1215 | /// If you pass `sema` you will get back `scalar` and resolve the type if | ||
| 1216 | /// necessary, possibly returning a CompileError. | ||
| 1217 | pub fn abiSizeAdvanced( | ||
| 1218 | ty: Type, | ||
| 1219 | mod: *Module, | ||
| 1220 | strat: AbiAlignmentAdvancedStrat, | ||
| 1221 | ) Module.CompileError!AbiSizeAdvanced { | ||
| 1222 | const target = mod.getTarget(); | ||
| 1223 | const use_llvm = mod.comp.config.use_llvm; | ||
| 1224 | const ip = &mod.intern_pool; | ||
| 1225 | |||
| 1226 | switch (ty.toIntern()) { | ||
| 1227 | .empty_struct_type => return AbiSizeAdvanced{ .scalar = 0 }, | ||
| 1228 | |||
| 1229 | else => switch (ip.indexToKey(ty.toIntern())) { | ||
| 1230 | .int_type => |int_type| { | ||
| 1231 | if (int_type.bits == 0) return AbiSizeAdvanced{ .scalar = 0 }; | ||
| 1232 | return AbiSizeAdvanced{ .scalar = intAbiSize(int_type.bits, target, use_llvm) }; | ||
| 1233 | }, | ||
| 1234 | .ptr_type => |ptr_type| switch (ptr_type.flags.size) { | ||
| 1235 | .Slice => return .{ .scalar = @divExact(target.ptrBitWidth(), 8) * 2 }, | ||
| 1236 | else => return .{ .scalar = @divExact(target.ptrBitWidth(), 8) }, | ||
| 1237 | }, | ||
| 1238 | .anyframe_type => return AbiSizeAdvanced{ .scalar = @divExact(target.ptrBitWidth(), 8) }, | ||
| 1239 | |||
| 1240 | .array_type => |array_type| { | ||
| 1241 | const len = array_type.lenIncludingSentinel(); | ||
| 1242 | if (len == 0) return .{ .scalar = 0 }; | ||
| 1243 | switch (try Type.fromInterned(array_type.child).abiSizeAdvanced(mod, strat)) { | ||
| 1244 | .scalar => |elem_size| return .{ .scalar = len * elem_size }, | ||
| 1245 | .val => switch (strat) { | ||
| 1246 | .sema, .eager => unreachable, | ||
| 1247 | .lazy => return .{ .val = Value.fromInterned((try mod.intern(.{ .int = .{ | ||
| 1248 | .ty = .comptime_int_type, | ||
| 1249 | .storage = .{ .lazy_size = ty.toIntern() }, | ||
| 1250 | } }))) }, | ||
| 1251 | }, | ||
| 1252 | } | ||
| 1253 | }, | ||
| 1254 | .vector_type => |vector_type| { | ||
| 1255 | const opt_sema = switch (strat) { | ||
| 1256 | .sema => |sema| sema, | ||
| 1257 | .eager => null, | ||
| 1258 | .lazy => return .{ .val = Value.fromInterned((try mod.intern(.{ .int = .{ | ||
| 1259 | .ty = .comptime_int_type, | ||
| 1260 | .storage = .{ .lazy_size = ty.toIntern() }, | ||
| 1261 | } }))) }, | ||
| 1262 | }; | ||
| 1263 | const alignment = switch (try ty.abiAlignmentAdvanced(mod, strat)) { | ||
| 1264 | .scalar => |x| x, | ||
| 1265 | .val => return .{ .val = Value.fromInterned((try mod.intern(.{ .int = .{ | ||
| 1266 | .ty = .comptime_int_type, | ||
| 1267 | .storage = .{ .lazy_size = ty.toIntern() }, | ||
| 1268 | } }))) }, | ||
| 1269 | }; | ||
| 1270 | const total_bytes = switch (mod.comp.getZigBackend()) { | ||
| 1271 | else => total_bytes: { | ||
| 1272 | const elem_bits = try Type.fromInterned(vector_type.child).bitSizeAdvanced(mod, opt_sema); | ||
| 1273 | const total_bits = elem_bits * vector_type.len; | ||
| 1274 | break :total_bytes (total_bits + 7) / 8; | ||
| 1275 | }, | ||
| 1276 | .stage2_c => total_bytes: { | ||
| 1277 | const elem_bytes: u32 = @intCast((try Type.fromInterned(vector_type.child).abiSizeAdvanced(mod, strat)).scalar); | ||
| 1278 | break :total_bytes elem_bytes * vector_type.len; | ||
| 1279 | }, | ||
| 1280 | .stage2_x86_64 => total_bytes: { | ||
| 1281 | if (vector_type.child == .bool_type) break :total_bytes std.math.divCeil(u32, vector_type.len, 8) catch unreachable; | ||
| 1282 | const elem_bytes: u32 = @intCast((try Type.fromInterned(vector_type.child).abiSizeAdvanced(mod, strat)).scalar); | ||
| 1283 | break :total_bytes elem_bytes * vector_type.len; | ||
| 1284 | }, | ||
| 1285 | }; | ||
| 1286 | return AbiSizeAdvanced{ .scalar = alignment.forward(total_bytes) }; | ||
| 1287 | }, | ||
| 1288 | |||
| 1289 | .opt_type => return ty.abiSizeAdvancedOptional(mod, strat), | ||
| 1290 | |||
| 1291 | .error_set_type, .inferred_error_set_type => { | ||
| 1292 | const bits = mod.errorSetBits(); | ||
| 1293 | if (bits == 0) return AbiSizeAdvanced{ .scalar = 0 }; | ||
| 1294 | return AbiSizeAdvanced{ .scalar = intAbiSize(bits, target, use_llvm) }; | ||
| 1295 | }, | ||
| 1296 | |||
| 1297 | .error_union_type => |error_union_type| { | ||
| 1298 | const payload_ty = Type.fromInterned(error_union_type.payload_type); | ||
| 1299 | // This code needs to be kept in sync with the equivalent switch prong | ||
| 1300 | // in abiAlignmentAdvanced. | ||
| 1301 | const code_size = abiSize(Type.anyerror, mod); | ||
| 1302 | if (!(payload_ty.hasRuntimeBitsAdvanced(mod, false, strat) catch |err| switch (err) { | ||
| 1303 | error.NeedLazy => return .{ .val = Value.fromInterned((try mod.intern(.{ .int = .{ | ||
| 1304 | .ty = .comptime_int_type, | ||
| 1305 | .storage = .{ .lazy_size = ty.toIntern() }, | ||
| 1306 | } }))) }, | ||
| 1307 | else => |e| return e, | ||
| 1308 | })) { | ||
| 1309 | // Same as anyerror. | ||
| 1310 | return AbiSizeAdvanced{ .scalar = code_size }; | ||
| 1311 | } | ||
| 1312 | const code_align = abiAlignment(Type.anyerror, mod); | ||
| 1313 | const payload_align = abiAlignment(payload_ty, mod); | ||
| 1314 | const payload_size = switch (try payload_ty.abiSizeAdvanced(mod, strat)) { | ||
| 1315 | .scalar => |elem_size| elem_size, | ||
| 1316 | .val => switch (strat) { | ||
| 1317 | .sema => unreachable, | ||
| 1318 | .eager => unreachable, | ||
| 1319 | .lazy => return .{ .val = Value.fromInterned((try mod.intern(.{ .int = .{ | ||
| 1320 | .ty = .comptime_int_type, | ||
| 1321 | .storage = .{ .lazy_size = ty.toIntern() }, | ||
| 1322 | } }))) }, | ||
| 1323 | }, | ||
| 1324 | }; | ||
| 1325 | |||
| 1326 | var size: u64 = 0; | ||
| 1327 | if (code_align.compare(.gt, payload_align)) { | ||
| 1328 | size += code_size; | ||
| 1329 | size = payload_align.forward(size); | ||
| 1330 | size += payload_size; | ||
| 1331 | size = code_align.forward(size); | ||
| 1332 | } else { | ||
| 1333 | size += payload_size; | ||
| 1334 | size = code_align.forward(size); | ||
| 1335 | size += code_size; | ||
| 1336 | size = payload_align.forward(size); | ||
| 1337 | } | ||
| 1338 | return AbiSizeAdvanced{ .scalar = size }; | ||
| 1339 | }, | ||
| 1340 | .func_type => unreachable, // represents machine code; not a pointer | ||
| 1341 | .simple_type => |t| switch (t) { | ||
| 1342 | .bool, | ||
| 1343 | .atomic_order, | ||
| 1344 | .atomic_rmw_op, | ||
| 1345 | .calling_convention, | ||
| 1346 | .address_space, | ||
| 1347 | .float_mode, | ||
| 1348 | .reduce_op, | ||
| 1349 | .call_modifier, | ||
| 1350 | => return AbiSizeAdvanced{ .scalar = 1 }, | ||
| 1351 | |||
| 1352 | .f16 => return AbiSizeAdvanced{ .scalar = 2 }, | ||
| 1353 | .f32 => return AbiSizeAdvanced{ .scalar = 4 }, | ||
| 1354 | .f64 => return AbiSizeAdvanced{ .scalar = 8 }, | ||
| 1355 | .f128 => return AbiSizeAdvanced{ .scalar = 16 }, | ||
| 1356 | .f80 => switch (target.c_type_bit_size(.longdouble)) { | ||
| 1357 | 80 => return AbiSizeAdvanced{ .scalar = target.c_type_byte_size(.longdouble) }, | ||
| 1358 | else => { | ||
| 1359 | const u80_ty: Type = .{ .ip_index = .u80_type }; | ||
| 1360 | return AbiSizeAdvanced{ .scalar = abiSize(u80_ty, mod) }; | ||
| 1361 | }, | ||
| 1362 | }, | ||
| 1363 | |||
| 1364 | .usize, | ||
| 1365 | .isize, | ||
| 1366 | => return AbiSizeAdvanced{ .scalar = @divExact(target.ptrBitWidth(), 8) }, | ||
| 1367 | |||
| 1368 | .c_char => return AbiSizeAdvanced{ .scalar = target.c_type_byte_size(.char) }, | ||
| 1369 | .c_short => return AbiSizeAdvanced{ .scalar = target.c_type_byte_size(.short) }, | ||
| 1370 | .c_ushort => return AbiSizeAdvanced{ .scalar = target.c_type_byte_size(.ushort) }, | ||
| 1371 | .c_int => return AbiSizeAdvanced{ .scalar = target.c_type_byte_size(.int) }, | ||
| 1372 | .c_uint => return AbiSizeAdvanced{ .scalar = target.c_type_byte_size(.uint) }, | ||
| 1373 | .c_long => return AbiSizeAdvanced{ .scalar = target.c_type_byte_size(.long) }, | ||
| 1374 | .c_ulong => return AbiSizeAdvanced{ .scalar = target.c_type_byte_size(.ulong) }, | ||
| 1375 | .c_longlong => return AbiSizeAdvanced{ .scalar = target.c_type_byte_size(.longlong) }, | ||
| 1376 | .c_ulonglong => return AbiSizeAdvanced{ .scalar = target.c_type_byte_size(.ulonglong) }, | ||
| 1377 | .c_longdouble => return AbiSizeAdvanced{ .scalar = target.c_type_byte_size(.longdouble) }, | ||
| 1378 | |||
| 1379 | .anyopaque, | ||
| 1380 | .void, | ||
| 1381 | .type, | ||
| 1382 | .comptime_int, | ||
| 1383 | .comptime_float, | ||
| 1384 | .null, | ||
| 1385 | .undefined, | ||
| 1386 | .enum_literal, | ||
| 1387 | => return AbiSizeAdvanced{ .scalar = 0 }, | ||
| 1388 | |||
| 1389 | .anyerror, .adhoc_inferred_error_set => { | ||
| 1390 | const bits = mod.errorSetBits(); | ||
| 1391 | if (bits == 0) return AbiSizeAdvanced{ .scalar = 0 }; | ||
| 1392 | return AbiSizeAdvanced{ .scalar = intAbiSize(bits, target, use_llvm) }; | ||
| 1393 | }, | ||
| 1394 | |||
| 1395 | .prefetch_options => unreachable, // missing call to resolveTypeFields | ||
| 1396 | .export_options => unreachable, // missing call to resolveTypeFields | ||
| 1397 | .extern_options => unreachable, // missing call to resolveTypeFields | ||
| 1398 | |||
| 1399 | .type_info => unreachable, | ||
| 1400 | .noreturn => unreachable, | ||
| 1401 | .generic_poison => unreachable, | ||
| 1402 | }, | ||
| 1403 | .struct_type => { | ||
| 1404 | const struct_type = ip.loadStructType(ty.toIntern()); | ||
| 1405 | switch (strat) { | ||
| 1406 | .sema => |sema| try sema.resolveTypeLayout(ty), | ||
| 1407 | .lazy => switch (struct_type.layout) { | ||
| 1408 | .@"packed" => { | ||
| 1409 | if (struct_type.backingIntType(ip).* == .none) return .{ | ||
| 1410 | .val = Value.fromInterned((try mod.intern(.{ .int = .{ | ||
| 1411 | .ty = .comptime_int_type, | ||
| 1412 | .storage = .{ .lazy_size = ty.toIntern() }, | ||
| 1413 | } }))), | ||
| 1414 | }; | ||
| 1415 | }, | ||
| 1416 | .auto, .@"extern" => { | ||
| 1417 | if (!struct_type.haveLayout(ip)) return .{ | ||
| 1418 | .val = Value.fromInterned((try mod.intern(.{ .int = .{ | ||
| 1419 | .ty = .comptime_int_type, | ||
| 1420 | .storage = .{ .lazy_size = ty.toIntern() }, | ||
| 1421 | } }))), | ||
| 1422 | }; | ||
| 1423 | }, | ||
| 1424 | }, | ||
| 1425 | .eager => {}, | ||
| 1426 | } | ||
| 1427 | switch (struct_type.layout) { | ||
| 1428 | .@"packed" => return .{ | ||
| 1429 | .scalar = Type.fromInterned(struct_type.backingIntType(ip).*).abiSize(mod), | ||
| 1430 | }, | ||
| 1431 | .auto, .@"extern" => { | ||
| 1432 | assert(struct_type.haveLayout(ip)); | ||
| 1433 | return .{ .scalar = struct_type.size(ip).* }; | ||
| 1434 | }, | ||
| 1435 | } | ||
| 1436 | }, | ||
| 1437 | .anon_struct_type => |tuple| { | ||
| 1438 | switch (strat) { | ||
| 1439 | .sema => |sema| try sema.resolveTypeLayout(ty), | ||
| 1440 | .lazy, .eager => {}, | ||
| 1441 | } | ||
| 1442 | const field_count = tuple.types.len; | ||
| 1443 | if (field_count == 0) { | ||
| 1444 | return AbiSizeAdvanced{ .scalar = 0 }; | ||
| 1445 | } | ||
| 1446 | return AbiSizeAdvanced{ .scalar = ty.structFieldOffset(field_count, mod) }; | ||
| 1447 | }, | ||
| 1448 | |||
| 1449 | .union_type => { | ||
| 1450 | const union_type = ip.loadUnionType(ty.toIntern()); | ||
| 1451 | switch (strat) { | ||
| 1452 | .sema => |sema| try sema.resolveTypeLayout(ty), | ||
| 1453 | .lazy => if (!union_type.flagsPtr(ip).status.haveLayout()) return .{ | ||
| 1454 | .val = Value.fromInterned((try mod.intern(.{ .int = .{ | ||
| 1455 | .ty = .comptime_int_type, | ||
| 1456 | .storage = .{ .lazy_size = ty.toIntern() }, | ||
| 1457 | } }))), | ||
| 1458 | }, | ||
| 1459 | .eager => {}, | ||
| 1460 | } | ||
| 1461 | |||
| 1462 | assert(union_type.haveLayout(ip)); | ||
| 1463 | return .{ .scalar = union_type.size(ip).* }; | ||
| 1464 | }, | ||
| 1465 | .opaque_type => unreachable, // no size available | ||
| 1466 | .enum_type => return .{ .scalar = Type.fromInterned(ip.loadEnumType(ty.toIntern()).tag_ty).abiSize(mod) }, | ||
| 1467 | |||
| 1468 | // values, not types | ||
| 1469 | .undef, | ||
| 1470 | .simple_value, | ||
| 1471 | .variable, | ||
| 1472 | .extern_func, | ||
| 1473 | .func, | ||
| 1474 | .int, | ||
| 1475 | .err, | ||
| 1476 | .error_union, | ||
| 1477 | .enum_literal, | ||
| 1478 | .enum_tag, | ||
| 1479 | .empty_enum_value, | ||
| 1480 | .float, | ||
| 1481 | .ptr, | ||
| 1482 | .slice, | ||
| 1483 | .opt, | ||
| 1484 | .aggregate, | ||
| 1485 | .un, | ||
| 1486 | // memoization, not types | ||
| 1487 | .memoized_call, | ||
| 1488 | => unreachable, | ||
| 1489 | }, | ||
| 1490 | } | ||
| 1491 | } | ||
| 1492 | |||
| 1493 | fn abiSizeAdvancedOptional( | ||
| 1494 | ty: Type, | ||
| 1495 | mod: *Module, | ||
| 1496 | strat: AbiAlignmentAdvancedStrat, | ||
| 1497 | ) Module.CompileError!AbiSizeAdvanced { | ||
| 1498 | const child_ty = ty.optionalChild(mod); | ||
| 1499 | |||
| 1500 | if (child_ty.isNoReturn(mod)) { | ||
| 1501 | return AbiSizeAdvanced{ .scalar = 0 }; | ||
| 1502 | } | ||
| 1503 | |||
| 1504 | if (!(child_ty.hasRuntimeBitsAdvanced(mod, false, strat) catch |err| switch (err) { | ||
| 1505 | error.NeedLazy => return .{ .val = Value.fromInterned((try mod.intern(.{ .int = .{ | ||
| 1506 | .ty = .comptime_int_type, | ||
| 1507 | .storage = .{ .lazy_size = ty.toIntern() }, | ||
| 1508 | } }))) }, | ||
| 1509 | else => |e| return e, | ||
| 1510 | })) return AbiSizeAdvanced{ .scalar = 1 }; | ||
| 1511 | |||
| 1512 | if (ty.optionalReprIsPayload(mod)) { | ||
| 1513 | return abiSizeAdvanced(child_ty, mod, strat); | ||
| 1514 | } | ||
| 1515 | |||
| 1516 | const payload_size = switch (try child_ty.abiSizeAdvanced(mod, strat)) { | ||
| 1517 | .scalar => |elem_size| elem_size, | ||
| 1518 | .val => switch (strat) { | ||
| 1519 | .sema => unreachable, | ||
| 1520 | .eager => unreachable, | ||
| 1521 | .lazy => return .{ .val = Value.fromInterned((try mod.intern(.{ .int = .{ | ||
| 1522 | .ty = .comptime_int_type, | ||
| 1523 | .storage = .{ .lazy_size = ty.toIntern() }, | ||
| 1524 | } }))) }, | ||
| 1525 | }, | ||
| 1526 | }; | ||
| 1527 | |||
| 1528 | // Optional types are represented as a struct with the child type as the first | ||
| 1529 | // field and a boolean as the second. Since the child type's abi alignment is | ||
| 1530 | // guaranteed to be >= that of bool's (1 byte) the added size is exactly equal | ||
| 1531 | // to the child type's ABI alignment. | ||
| 1532 | return AbiSizeAdvanced{ | ||
| 1533 | .scalar = (child_ty.abiAlignment(mod).toByteUnits() orelse 0) + payload_size, | ||
| 1534 | }; | ||
| 1535 | } | ||
| 1536 | |||
| 1537 | pub fn ptrAbiAlignment(target: Target) Alignment { | ||
| 1538 | return Alignment.fromNonzeroByteUnits(@divExact(target.ptrBitWidth(), 8)); | ||
| 1539 | } | ||
| 1540 | |||
| 1541 | pub fn intAbiSize(bits: u16, target: Target, use_llvm: bool) u64 { | ||
| 1542 | return intAbiAlignment(bits, target, use_llvm).forward(@as(u16, @intCast((@as(u17, bits) + 7) / 8))); | ||
| 1543 | } | ||
| 1544 | |||
| 1545 | pub fn intAbiAlignment(bits: u16, target: Target, use_llvm: bool) Alignment { | ||
| 1546 | return switch (target.cpu.arch) { | ||
| 1547 | .x86 => switch (bits) { | ||
| 1548 | 0 => .none, | ||
| 1549 | 1...8 => .@"1", | ||
| 1550 | 9...16 => .@"2", | ||
| 1551 | 17...64 => .@"4", | ||
| 1552 | else => .@"16", | ||
| 1553 | }, | ||
| 1554 | .x86_64 => switch (bits) { | ||
| 1555 | 0 => .none, | ||
| 1556 | 1...8 => .@"1", | ||
| 1557 | 9...16 => .@"2", | ||
| 1558 | 17...32 => .@"4", | ||
| 1559 | 33...64 => .@"8", | ||
| 1560 | else => switch (target_util.zigBackend(target, use_llvm)) { | ||
| 1561 | .stage2_x86_64 => .@"8", | ||
| 1562 | else => .@"16", | ||
| 1563 | }, | ||
| 1564 | }, | ||
| 1565 | else => return Alignment.fromByteUnits(@min( | ||
| 1566 | std.math.ceilPowerOfTwoPromote(u16, @as(u16, @intCast((@as(u17, bits) + 7) / 8))), | ||
| 1567 | maxIntAlignment(target, use_llvm), | ||
| 1568 | )), | ||
| 1569 | }; | ||
| 1570 | } | ||
| 1571 | |||
| 1572 | pub fn maxIntAlignment(target: std.Target, use_llvm: bool) u16 { | ||
| 1573 | return switch (target.cpu.arch) { | ||
| 1574 | .avr => 1, | ||
| 1575 | .msp430 => 2, | ||
| 1576 | .xcore => 4, | ||
| 1577 | |||
| 1578 | .arm, | ||
| 1579 | .armeb, | ||
| 1580 | .thumb, | ||
| 1581 | .thumbeb, | ||
| 1582 | .hexagon, | ||
| 1583 | .mips, | ||
| 1584 | .mipsel, | ||
| 1585 | .powerpc, | ||
| 1586 | .powerpcle, | ||
| 1587 | .r600, | ||
| 1588 | .amdgcn, | ||
| 1589 | .riscv32, | ||
| 1590 | .sparc, | ||
| 1591 | .sparcel, | ||
| 1592 | .s390x, | ||
| 1593 | .lanai, | ||
| 1594 | .wasm32, | ||
| 1595 | .wasm64, | ||
| 1596 | => 8, | ||
| 1597 | |||
| 1598 | // For these, LLVMABIAlignmentOfType(i128) reports 8. Note that 16 | ||
| 1599 | // is a relevant number in three cases: | ||
| 1600 | // 1. Different machine code instruction when loading into SIMD register. | ||
| 1601 | // 2. The C ABI wants 16 for extern structs. | ||
| 1602 | // 3. 16-byte cmpxchg needs 16-byte alignment. | ||
| 1603 | // Same logic for powerpc64, mips64, sparc64. | ||
| 1604 | .powerpc64, | ||
| 1605 | .powerpc64le, | ||
| 1606 | .mips64, | ||
| 1607 | .mips64el, | ||
| 1608 | .sparc64, | ||
| 1609 | => switch (target.ofmt) { | ||
| 1610 | .c => 16, | ||
| 1611 | else => 8, | ||
| 1612 | }, | ||
| 1613 | |||
| 1614 | .x86_64 => switch (target_util.zigBackend(target, use_llvm)) { | ||
| 1615 | .stage2_x86_64 => 8, | ||
| 1616 | else => 16, | ||
| 1617 | }, | ||
| 1618 | |||
| 1619 | // Even LLVMABIAlignmentOfType(i128) agrees on these targets. | ||
| 1620 | .x86, | ||
| 1621 | .aarch64, | ||
| 1622 | .aarch64_be, | ||
| 1623 | .aarch64_32, | ||
| 1624 | .riscv64, | ||
| 1625 | .bpfel, | ||
| 1626 | .bpfeb, | ||
| 1627 | .nvptx, | ||
| 1628 | .nvptx64, | ||
| 1629 | => 16, | ||
| 1630 | |||
| 1631 | // Below this comment are unverified but based on the fact that C requires | ||
| 1632 | // int128_t to be 16 bytes aligned, it's a safe default. | ||
| 1633 | .spu_2, | ||
| 1634 | .csky, | ||
| 1635 | .arc, | ||
| 1636 | .m68k, | ||
| 1637 | .tce, | ||
| 1638 | .tcele, | ||
| 1639 | .le32, | ||
| 1640 | .amdil, | ||
| 1641 | .hsail, | ||
| 1642 | .spir, | ||
| 1643 | .kalimba, | ||
| 1644 | .renderscript32, | ||
| 1645 | .spirv, | ||
| 1646 | .spirv32, | ||
| 1647 | .shave, | ||
| 1648 | .le64, | ||
| 1649 | .amdil64, | ||
| 1650 | .hsail64, | ||
| 1651 | .spir64, | ||
| 1652 | .renderscript64, | ||
| 1653 | .ve, | ||
| 1654 | .spirv64, | ||
| 1655 | .dxil, | ||
| 1656 | .loongarch32, | ||
| 1657 | .loongarch64, | ||
| 1658 | .xtensa, | ||
| 1659 | => 16, | ||
| 1660 | }; | ||
| 1661 | } | ||
| 1662 | |||
| 1663 | pub fn bitSize(ty: Type, mod: *Module) u64 { | ||
| 1664 | return bitSizeAdvanced(ty, mod, null) catch unreachable; | ||
| 1665 | } | ||
| 1666 | |||
| 1667 | /// If you pass `opt_sema`, any recursive type resolutions will happen if | ||
| 1668 | /// necessary, possibly returning a CompileError. Passing `null` instead asserts | ||
| 1669 | /// the type is fully resolved, and there will be no error, guaranteed. | ||
| 1670 | pub fn bitSizeAdvanced( | ||
| 1671 | ty: Type, | ||
| 1672 | mod: *Module, | ||
| 1673 | opt_sema: ?*Sema, | ||
| 1674 | ) Module.CompileError!u64 { | ||
| 1675 | const target = mod.getTarget(); | ||
| 1676 | const ip = &mod.intern_pool; | ||
| 1677 | |||
| 1678 | const strat: AbiAlignmentAdvancedStrat = if (opt_sema) |sema| .{ .sema = sema } else .eager; | ||
| 1679 | |||
| 1680 | switch (ip.indexToKey(ty.toIntern())) { | ||
| 1681 | .int_type => |int_type| return int_type.bits, | ||
| 1682 | .ptr_type => |ptr_type| switch (ptr_type.flags.size) { | ||
| 1683 | .Slice => return target.ptrBitWidth() * 2, | ||
| 1684 | else => return target.ptrBitWidth(), | ||
| 1685 | }, | ||
| 1686 | .anyframe_type => return target.ptrBitWidth(), | ||
| 1687 | |||
| 1688 | .array_type => |array_type| { | ||
| 1689 | const len = array_type.lenIncludingSentinel(); | ||
| 1690 | if (len == 0) return 0; | ||
| 1691 | const elem_ty = Type.fromInterned(array_type.child); | ||
| 1692 | const elem_size = @max( | ||
| 1693 | (try elem_ty.abiAlignmentAdvanced(mod, strat)).scalar.toByteUnits() orelse 0, | ||
| 1694 | (try elem_ty.abiSizeAdvanced(mod, strat)).scalar, | ||
| 1695 | ); | ||
| 1696 | if (elem_size == 0) return 0; | ||
| 1697 | const elem_bit_size = try bitSizeAdvanced(elem_ty, mod, opt_sema); | ||
| 1698 | return (len - 1) * 8 * elem_size + elem_bit_size; | ||
| 1699 | }, | ||
| 1700 | .vector_type => |vector_type| { | ||
| 1701 | const child_ty = Type.fromInterned(vector_type.child); | ||
| 1702 | const elem_bit_size = try bitSizeAdvanced(child_ty, mod, opt_sema); | ||
| 1703 | return elem_bit_size * vector_type.len; | ||
| 1704 | }, | ||
| 1705 | .opt_type => { | ||
| 1706 | // Optionals and error unions are not packed so their bitsize | ||
| 1707 | // includes padding bits. | ||
| 1708 | return (try abiSizeAdvanced(ty, mod, strat)).scalar * 8; | ||
| 1709 | }, | ||
| 1710 | |||
| 1711 | .error_set_type, .inferred_error_set_type => return mod.errorSetBits(), | ||
| 1712 | |||
| 1713 | .error_union_type => { | ||
| 1714 | // Optionals and error unions are not packed so their bitsize | ||
| 1715 | // includes padding bits. | ||
| 1716 | return (try abiSizeAdvanced(ty, mod, strat)).scalar * 8; | ||
| 1717 | }, | ||
| 1718 | .func_type => unreachable, // represents machine code; not a pointer | ||
| 1719 | .simple_type => |t| switch (t) { | ||
| 1720 | .f16 => return 16, | ||
| 1721 | .f32 => return 32, | ||
| 1722 | .f64 => return 64, | ||
| 1723 | .f80 => return 80, | ||
| 1724 | .f128 => return 128, | ||
| 1725 | |||
| 1726 | .usize, | ||
| 1727 | .isize, | ||
| 1728 | => return target.ptrBitWidth(), | ||
| 1729 | |||
| 1730 | .c_char => return target.c_type_bit_size(.char), | ||
| 1731 | .c_short => return target.c_type_bit_size(.short), | ||
| 1732 | .c_ushort => return target.c_type_bit_size(.ushort), | ||
| 1733 | .c_int => return target.c_type_bit_size(.int), | ||
| 1734 | .c_uint => return target.c_type_bit_size(.uint), | ||
| 1735 | .c_long => return target.c_type_bit_size(.long), | ||
| 1736 | .c_ulong => return target.c_type_bit_size(.ulong), | ||
| 1737 | .c_longlong => return target.c_type_bit_size(.longlong), | ||
| 1738 | .c_ulonglong => return target.c_type_bit_size(.ulonglong), | ||
| 1739 | .c_longdouble => return target.c_type_bit_size(.longdouble), | ||
| 1740 | |||
| 1741 | .bool => return 1, | ||
| 1742 | .void => return 0, | ||
| 1743 | |||
| 1744 | .anyerror, | ||
| 1745 | .adhoc_inferred_error_set, | ||
| 1746 | => return mod.errorSetBits(), | ||
| 1747 | |||
| 1748 | .anyopaque => unreachable, | ||
| 1749 | .type => unreachable, | ||
| 1750 | .comptime_int => unreachable, | ||
| 1751 | .comptime_float => unreachable, | ||
| 1752 | .noreturn => unreachable, | ||
| 1753 | .null => unreachable, | ||
| 1754 | .undefined => unreachable, | ||
| 1755 | .enum_literal => unreachable, | ||
| 1756 | .generic_poison => unreachable, | ||
| 1757 | |||
| 1758 | .atomic_order => unreachable, | ||
| 1759 | .atomic_rmw_op => unreachable, | ||
| 1760 | .calling_convention => unreachable, | ||
| 1761 | .address_space => unreachable, | ||
| 1762 | .float_mode => unreachable, | ||
| 1763 | .reduce_op => unreachable, | ||
| 1764 | .call_modifier => unreachable, | ||
| 1765 | .prefetch_options => unreachable, | ||
| 1766 | .export_options => unreachable, | ||
| 1767 | .extern_options => unreachable, | ||
| 1768 | .type_info => unreachable, | ||
| 1769 | }, | ||
| 1770 | .struct_type => { | ||
| 1771 | const struct_type = ip.loadStructType(ty.toIntern()); | ||
| 1772 | const is_packed = struct_type.layout == .@"packed"; | ||
| 1773 | if (opt_sema) |sema| { | ||
| 1774 | try sema.resolveTypeFields(ty); | ||
| 1775 | if (is_packed) try sema.resolveTypeLayout(ty); | ||
| 1776 | } | ||
| 1777 | if (is_packed) { | ||
| 1778 | return try Type.fromInterned(struct_type.backingIntType(ip).*).bitSizeAdvanced(mod, opt_sema); | ||
| 1779 | } | ||
| 1780 | return (try ty.abiSizeAdvanced(mod, strat)).scalar * 8; | ||
| 1781 | }, | ||
| 1782 | |||
| 1783 | .anon_struct_type => { | ||
| 1784 | if (opt_sema) |sema| try sema.resolveTypeFields(ty); | ||
| 1785 | return (try ty.abiSizeAdvanced(mod, strat)).scalar * 8; | ||
| 1786 | }, | ||
| 1787 | |||
| 1788 | .union_type => { | ||
| 1789 | const union_type = ip.loadUnionType(ty.toIntern()); | ||
| 1790 | const is_packed = ty.containerLayout(mod) == .@"packed"; | ||
| 1791 | if (opt_sema) |sema| { | ||
| 1792 | try sema.resolveTypeFields(ty); | ||
| 1793 | if (is_packed) try sema.resolveTypeLayout(ty); | ||
| 1794 | } | ||
| 1795 | if (!is_packed) { | ||
| 1796 | return (try ty.abiSizeAdvanced(mod, strat)).scalar * 8; | ||
| 1797 | } | ||
| 1798 | assert(union_type.flagsPtr(ip).status.haveFieldTypes()); | ||
| 1799 | |||
| 1800 | var size: u64 = 0; | ||
| 1801 | for (0..union_type.field_types.len) |field_index| { | ||
| 1802 | const field_ty = union_type.field_types.get(ip)[field_index]; | ||
| 1803 | size = @max(size, try bitSizeAdvanced(Type.fromInterned(field_ty), mod, opt_sema)); | ||
| 1804 | } | ||
| 1805 | |||
| 1806 | return size; | ||
| 1807 | }, | ||
| 1808 | .opaque_type => unreachable, | ||
| 1809 | .enum_type => return bitSizeAdvanced(Type.fromInterned(ip.loadEnumType(ty.toIntern()).tag_ty), mod, opt_sema), | ||
| 1810 | |||
| 1811 | // values, not types | ||
| 1812 | .undef, | ||
| 1813 | .simple_value, | ||
| 1814 | .variable, | ||
| 1815 | .extern_func, | ||
| 1816 | .func, | ||
| 1817 | .int, | ||
| 1818 | .err, | ||
| 1819 | .error_union, | ||
| 1820 | .enum_literal, | ||
| 1821 | .enum_tag, | ||
| 1822 | .empty_enum_value, | ||
| 1823 | .float, | ||
| 1824 | .ptr, | ||
| 1825 | .slice, | ||
| 1826 | .opt, | ||
| 1827 | .aggregate, | ||
| 1828 | .un, | ||
| 1829 | // memoization, not types | ||
| 1830 | .memoized_call, | ||
| 1831 | => unreachable, | ||
| 1832 | } | ||
| 1833 | } | ||
| 1834 | |||
| 1835 | /// Returns true if the type's layout is already resolved and it is safe | ||
| 1836 | /// to use `abiSize`, `abiAlignment` and `bitSize` on it. | ||
| 1837 | pub fn layoutIsResolved(ty: Type, mod: *Module) bool { | ||
| 1838 | const ip = &mod.intern_pool; | ||
| 1839 | return switch (ip.indexToKey(ty.toIntern())) { | ||
| 1840 | .struct_type => ip.loadStructType(ty.toIntern()).haveLayout(ip), | ||
| 1841 | .union_type => ip.loadUnionType(ty.toIntern()).haveLayout(ip), | ||
| 1842 | .array_type => |array_type| { | ||
| 1843 | if (array_type.lenIncludingSentinel() == 0) return true; | ||
| 1844 | return Type.fromInterned(array_type.child).layoutIsResolved(mod); | ||
| 1845 | }, | ||
| 1846 | .opt_type => |child| Type.fromInterned(child).layoutIsResolved(mod), | ||
| 1847 | .error_union_type => |k| Type.fromInterned(k.payload_type).layoutIsResolved(mod), | ||
| 1848 | else => true, | ||
| 1849 | }; | ||
| 1850 | } | ||
| 1851 | |||
| 1852 | pub fn isSinglePointer(ty: Type, mod: *const Module) bool { | ||
| 1853 | return switch (mod.intern_pool.indexToKey(ty.toIntern())) { | ||
| 1854 | .ptr_type => |ptr_info| ptr_info.flags.size == .One, | ||
| 1855 | else => false, | ||
| 1856 | }; | ||
| 1857 | } | ||
| 1858 | |||
| 1859 | /// Asserts `ty` is a pointer. | ||
| 1860 | pub fn ptrSize(ty: Type, mod: *const Module) std.builtin.Type.Pointer.Size { | ||
| 1861 | return ptrSizeOrNull(ty, mod).?; | ||
| 1862 | } | ||
| 1863 | |||
| 1864 | /// Returns `null` if `ty` is not a pointer. | ||
| 1865 | pub fn ptrSizeOrNull(ty: Type, mod: *const Module) ?std.builtin.Type.Pointer.Size { | ||
| 1866 | return switch (mod.intern_pool.indexToKey(ty.toIntern())) { | ||
| 1867 | .ptr_type => |ptr_info| ptr_info.flags.size, | ||
| 1868 | else => null, | ||
| 1869 | }; | ||
| 1870 | } | ||
| 1871 | |||
| 1872 | pub fn isSlice(ty: Type, mod: *const Module) bool { | ||
| 1873 | return switch (mod.intern_pool.indexToKey(ty.toIntern())) { | ||
| 1874 | .ptr_type => |ptr_type| ptr_type.flags.size == .Slice, | ||
| 1875 | else => false, | ||
| 1876 | }; | ||
| 1877 | } | ||
| 1878 | |||
| 1879 | pub fn slicePtrFieldType(ty: Type, mod: *const Module) Type { | ||
| 1880 | return Type.fromInterned(mod.intern_pool.slicePtrType(ty.toIntern())); | ||
| 1881 | } | ||
| 1882 | |||
| 1883 | pub fn isConstPtr(ty: Type, mod: *const Module) bool { | ||
| 1884 | return switch (mod.intern_pool.indexToKey(ty.toIntern())) { | ||
| 1885 | .ptr_type => |ptr_type| ptr_type.flags.is_const, | ||
| 1886 | else => false, | ||
| 1887 | }; | ||
| 1888 | } | ||
| 1889 | |||
| 1890 | pub fn isVolatilePtr(ty: Type, mod: *const Module) bool { | ||
| 1891 | return isVolatilePtrIp(ty, &mod.intern_pool); | ||
| 1892 | } | ||
| 1893 | |||
| 1894 | pub fn isVolatilePtrIp(ty: Type, ip: *const InternPool) bool { | ||
| 1895 | return switch (ip.indexToKey(ty.toIntern())) { | ||
| 1896 | .ptr_type => |ptr_type| ptr_type.flags.is_volatile, | ||
| 1897 | else => false, | ||
| 1898 | }; | ||
| 1899 | } | ||
| 1900 | |||
| 1901 | pub fn isAllowzeroPtr(ty: Type, mod: *const Module) bool { | ||
| 1902 | return switch (mod.intern_pool.indexToKey(ty.toIntern())) { | ||
| 1903 | .ptr_type => |ptr_type| ptr_type.flags.is_allowzero, | ||
| 1904 | .opt_type => true, | ||
| 1905 | else => false, | ||
| 1906 | }; | ||
| 1907 | } | ||
| 1908 | |||
| 1909 | pub fn isCPtr(ty: Type, mod: *const Module) bool { | ||
| 1910 | return switch (mod.intern_pool.indexToKey(ty.toIntern())) { | ||
| 1911 | .ptr_type => |ptr_type| ptr_type.flags.size == .C, | ||
| 1912 | else => false, | ||
| 1913 | }; | ||
| 1914 | } | ||
| 1915 | |||
| 1916 | pub fn isPtrAtRuntime(ty: Type, mod: *const Module) bool { | ||
| 1917 | return switch (mod.intern_pool.indexToKey(ty.toIntern())) { | ||
| 1918 | .ptr_type => |ptr_type| switch (ptr_type.flags.size) { | ||
| 1919 | .Slice => false, | ||
| 1920 | .One, .Many, .C => true, | ||
| 1921 | }, | ||
| 1922 | .opt_type => |child| switch (mod.intern_pool.indexToKey(child)) { | ||
| 1923 | .ptr_type => |p| switch (p.flags.size) { | ||
| 1924 | .Slice, .C => false, | ||
| 1925 | .Many, .One => !p.flags.is_allowzero, | ||
| 1926 | }, | ||
| 1927 | else => false, | ||
| 1928 | }, | ||
| 1929 | else => false, | ||
| 1930 | }; | ||
| 1931 | } | ||
| 1932 | |||
| 1933 | /// For pointer-like optionals, returns true, otherwise returns the allowzero property | ||
| 1934 | /// of pointers. | ||
| 1935 | pub fn ptrAllowsZero(ty: Type, mod: *const Module) bool { | ||
| 1936 | if (ty.isPtrLikeOptional(mod)) { | ||
| 1937 | return true; | ||
| 1938 | } | ||
| 1939 | return ty.ptrInfo(mod).flags.is_allowzero; | ||
| 1940 | } | ||
| 1941 | |||
| 1942 | /// See also `isPtrLikeOptional`. | ||
| 1943 | pub fn optionalReprIsPayload(ty: Type, mod: *const Module) bool { | ||
| 1944 | return switch (mod.intern_pool.indexToKey(ty.toIntern())) { | ||
| 1945 | .opt_type => |child_type| child_type == .anyerror_type or switch (mod.intern_pool.indexToKey(child_type)) { | ||
| 1946 | .ptr_type => |ptr_type| ptr_type.flags.size != .C and !ptr_type.flags.is_allowzero, | ||
| 1947 | .error_set_type, .inferred_error_set_type => true, | ||
| 1948 | else => false, | ||
| 1949 | }, | ||
| 1950 | .ptr_type => |ptr_type| ptr_type.flags.size == .C, | ||
| 1951 | else => false, | ||
| 1952 | }; | ||
| 1953 | } | ||
| 1954 | |||
| 1955 | /// Returns true if the type is optional and would be lowered to a single pointer | ||
| 1956 | /// address value, using 0 for null. Note that this returns true for C pointers. | ||
| 1957 | /// This function must be kept in sync with `Sema.typePtrOrOptionalPtrTy`. | ||
| 1958 | pub fn isPtrLikeOptional(ty: Type, mod: *const Module) bool { | ||
| 1959 | return switch (mod.intern_pool.indexToKey(ty.toIntern())) { | ||
| 1960 | .ptr_type => |ptr_type| ptr_type.flags.size == .C, | ||
| 1961 | .opt_type => |child| switch (mod.intern_pool.indexToKey(child)) { | ||
| 1962 | .ptr_type => |ptr_type| switch (ptr_type.flags.size) { | ||
| 1963 | .Slice, .C => false, | ||
| 1964 | .Many, .One => !ptr_type.flags.is_allowzero, | ||
| 1965 | }, | ||
| 1966 | else => false, | ||
| 1967 | }, | ||
| 1968 | else => false, | ||
| 1969 | }; | ||
| 1970 | } | ||
| 1971 | |||
| 1972 | /// For *[N]T, returns [N]T. | ||
| 1973 | /// For *T, returns T. | ||
| 1974 | /// For [*]T, returns T. | ||
| 1975 | pub fn childType(ty: Type, mod: *const Module) Type { | ||
| 1976 | return childTypeIp(ty, &mod.intern_pool); | ||
| 1977 | } | ||
| 1978 | |||
| 1979 | pub fn childTypeIp(ty: Type, ip: *const InternPool) Type { | ||
| 1980 | return Type.fromInterned(ip.childType(ty.toIntern())); | ||
| 1981 | } | ||
| 1982 | |||
| 1983 | /// For *[N]T, returns T. | ||
| 1984 | /// For ?*T, returns T. | ||
| 1985 | /// For ?*[N]T, returns T. | ||
| 1986 | /// For ?[*]T, returns T. | ||
| 1987 | /// For *T, returns T. | ||
| 1988 | /// For [*]T, returns T. | ||
| 1989 | /// For [N]T, returns T. | ||
| 1990 | /// For []T, returns T. | ||
| 1991 | /// For anyframe->T, returns T. | ||
| 1992 | pub fn elemType2(ty: Type, mod: *const Module) Type { | ||
| 1993 | return switch (mod.intern_pool.indexToKey(ty.toIntern())) { | ||
| 1994 | .ptr_type => |ptr_type| switch (ptr_type.flags.size) { | ||
| 1995 | .One => Type.fromInterned(ptr_type.child).shallowElemType(mod), | ||
| 1996 | .Many, .C, .Slice => Type.fromInterned(ptr_type.child), | ||
| 1997 | }, | ||
| 1998 | .anyframe_type => |child| { | ||
| 1999 | assert(child != .none); | ||
| 2000 | return Type.fromInterned(child); | ||
| 2001 | }, | ||
| 2002 | .vector_type => |vector_type| Type.fromInterned(vector_type.child), | ||
| 2003 | .array_type => |array_type| Type.fromInterned(array_type.child), | ||
| 2004 | .opt_type => |child| Type.fromInterned(mod.intern_pool.childType(child)), | ||
| 2005 | else => unreachable, | ||
| 2006 | }; | ||
| 2007 | } | ||
| 2008 | |||
| 2009 | fn shallowElemType(child_ty: Type, mod: *const Module) Type { | ||
| 2010 | return switch (child_ty.zigTypeTag(mod)) { | ||
| 2011 | .Array, .Vector => child_ty.childType(mod), | ||
| 2012 | else => child_ty, | ||
| 2013 | }; | ||
| 2014 | } | ||
| 2015 | |||
| 2016 | /// For vectors, returns the element type. Otherwise returns self. | ||
| 2017 | pub fn scalarType(ty: Type, mod: *Module) Type { | ||
| 2018 | return switch (ty.zigTypeTag(mod)) { | ||
| 2019 | .Vector => ty.childType(mod), | ||
| 2020 | else => ty, | ||
| 2021 | }; | ||
| 2022 | } | ||
| 2023 | |||
| 2024 | /// Asserts that the type is an optional. | ||
| 2025 | /// Note that for C pointers this returns the type unmodified. | ||
| 2026 | pub fn optionalChild(ty: Type, mod: *const Module) Type { | ||
| 2027 | return switch (mod.intern_pool.indexToKey(ty.toIntern())) { | ||
| 2028 | .opt_type => |child| Type.fromInterned(child), | ||
| 2029 | .ptr_type => |ptr_type| b: { | ||
| 2030 | assert(ptr_type.flags.size == .C); | ||
| 2031 | break :b ty; | ||
| 2032 | }, | ||
| 2033 | else => unreachable, | ||
| 2034 | }; | ||
| 2035 | } | ||
| 2036 | |||
| 2037 | /// Returns the tag type of a union, if the type is a union and it has a tag type. | ||
| 2038 | /// Otherwise, returns `null`. | ||
| 2039 | pub fn unionTagType(ty: Type, mod: *Module) ?Type { | ||
| 2040 | const ip = &mod.intern_pool; | ||
| 2041 | switch (ip.indexToKey(ty.toIntern())) { | ||
| 2042 | .union_type => {}, | ||
| 2043 | else => return null, | ||
| 2044 | } | ||
| 2045 | const union_type = ip.loadUnionType(ty.toIntern()); | ||
| 2046 | switch (union_type.flagsPtr(ip).runtime_tag) { | ||
| 2047 | .tagged => { | ||
| 2048 | assert(union_type.flagsPtr(ip).status.haveFieldTypes()); | ||
| 2049 | return Type.fromInterned(union_type.enum_tag_ty); | ||
| 2050 | }, | ||
| 2051 | else => return null, | ||
| 2052 | } | ||
| 2053 | } | ||
| 2054 | |||
| 2055 | /// Same as `unionTagType` but includes safety tag. | ||
| 2056 | /// Codegen should use this version. | ||
| 2057 | pub fn unionTagTypeSafety(ty: Type, mod: *Module) ?Type { | ||
| 2058 | const ip = &mod.intern_pool; | ||
| 2059 | return switch (ip.indexToKey(ty.toIntern())) { | ||
| 2060 | .union_type => { | ||
| 2061 | const union_type = ip.loadUnionType(ty.toIntern()); | ||
| 2062 | if (!union_type.hasTag(ip)) return null; | ||
| 2063 | assert(union_type.haveFieldTypes(ip)); | ||
| 2064 | return Type.fromInterned(union_type.enum_tag_ty); | ||
| 2065 | }, | ||
| 2066 | else => null, | ||
| 2067 | }; | ||
| 2068 | } | ||
| 2069 | |||
| 2070 | /// Asserts the type is a union; returns the tag type, even if the tag will | ||
| 2071 | /// not be stored at runtime. | ||
| 2072 | pub fn unionTagTypeHypothetical(ty: Type, mod: *Module) Type { | ||
| 2073 | const union_obj = mod.typeToUnion(ty).?; | ||
| 2074 | return Type.fromInterned(union_obj.enum_tag_ty); | ||
| 2075 | } | ||
| 2076 | |||
| 2077 | pub fn unionFieldType(ty: Type, enum_tag: Value, mod: *Module) ?Type { | ||
| 2078 | const ip = &mod.intern_pool; | ||
| 2079 | const union_obj = mod.typeToUnion(ty).?; | ||
| 2080 | const union_fields = union_obj.field_types.get(ip); | ||
| 2081 | const index = mod.unionTagFieldIndex(union_obj, enum_tag) orelse return null; | ||
| 2082 | return Type.fromInterned(union_fields[index]); | ||
| 2083 | } | ||
| 2084 | |||
| 2085 | pub fn unionFieldTypeByIndex(ty: Type, index: usize, mod: *Module) Type { | ||
| 2086 | const ip = &mod.intern_pool; | ||
| 2087 | const union_obj = mod.typeToUnion(ty).?; | ||
| 2088 | return Type.fromInterned(union_obj.field_types.get(ip)[index]); | ||
| 2089 | } | ||
| 2090 | |||
| 2091 | pub fn unionTagFieldIndex(ty: Type, enum_tag: Value, mod: *Module) ?u32 { | ||
| 2092 | const union_obj = mod.typeToUnion(ty).?; | ||
| 2093 | return mod.unionTagFieldIndex(union_obj, enum_tag); | ||
| 2094 | } | ||
| 2095 | |||
| 2096 | pub fn unionHasAllZeroBitFieldTypes(ty: Type, mod: *Module) bool { | ||
| 2097 | const ip = &mod.intern_pool; | ||
| 2098 | const union_obj = mod.typeToUnion(ty).?; | ||
| 2099 | for (union_obj.field_types.get(ip)) |field_ty| { | ||
| 2100 | if (Type.fromInterned(field_ty).hasRuntimeBits(mod)) return false; | ||
| 2101 | } | ||
| 2102 | return true; | ||
| 2103 | } | ||
| 2104 | |||
| 2105 | /// Returns the type used for backing storage of this union during comptime operations. | ||
| 2106 | /// Asserts the type is either an extern or packed union. | ||
| 2107 | pub fn unionBackingType(ty: Type, mod: *Module) !Type { | ||
| 2108 | return switch (ty.containerLayout(mod)) { | ||
| 2109 | .@"extern" => try mod.arrayType(.{ .len = ty.abiSize(mod), .child = .u8_type }), | ||
| 2110 | .@"packed" => try mod.intType(.unsigned, @intCast(ty.bitSize(mod))), | ||
| 2111 | .auto => unreachable, | ||
| 2112 | }; | ||
| 2113 | } | ||
| 2114 | |||
| 2115 | pub fn unionGetLayout(ty: Type, mod: *Module) Module.UnionLayout { | ||
| 2116 | const ip = &mod.intern_pool; | ||
| 2117 | const union_obj = ip.loadUnionType(ty.toIntern()); | ||
| 2118 | return mod.getUnionLayout(union_obj); | ||
| 2119 | } | ||
| 2120 | |||
| 2121 | pub fn containerLayout(ty: Type, mod: *Module) std.builtin.Type.ContainerLayout { | ||
| 2122 | const ip = &mod.intern_pool; | ||
| 2123 | return switch (ip.indexToKey(ty.toIntern())) { | ||
| 2124 | .struct_type => ip.loadStructType(ty.toIntern()).layout, | ||
| 2125 | .anon_struct_type => .auto, | ||
| 2126 | .union_type => ip.loadUnionType(ty.toIntern()).flagsPtr(ip).layout, | ||
| 2127 | else => unreachable, | ||
| 2128 | }; | ||
| 2129 | } | ||
| 2130 | |||
| 2131 | /// Asserts that the type is an error union. | ||
| 2132 | pub fn errorUnionPayload(ty: Type, mod: *Module) Type { | ||
| 2133 | return Type.fromInterned(mod.intern_pool.indexToKey(ty.toIntern()).error_union_type.payload_type); | ||
| 2134 | } | ||
| 2135 | |||
| 2136 | /// Asserts that the type is an error union. | ||
| 2137 | pub fn errorUnionSet(ty: Type, mod: *Module) Type { | ||
| 2138 | return Type.fromInterned(mod.intern_pool.errorUnionSet(ty.toIntern())); | ||
| 2139 | } | ||
| 2140 | |||
| 2141 | /// Returns false for unresolved inferred error sets. | ||
| 2142 | pub fn errorSetIsEmpty(ty: Type, mod: *Module) bool { | ||
| 2143 | const ip = &mod.intern_pool; | ||
| 2144 | return switch (ty.toIntern()) { | ||
| 2145 | .anyerror_type, .adhoc_inferred_error_set_type => false, | ||
| 2146 | else => switch (ip.indexToKey(ty.toIntern())) { | ||
| 2147 | .error_set_type => |error_set_type| error_set_type.names.len == 0, | ||
| 2148 | .inferred_error_set_type => |i| switch (ip.funcIesResolved(i).*) { | ||
| 2149 | .none, .anyerror_type => false, | ||
| 2150 | else => |t| ip.indexToKey(t).error_set_type.names.len == 0, | ||
| 2151 | }, | ||
| 2152 | else => unreachable, | ||
| 2153 | }, | ||
| 2154 | }; | ||
| 2155 | } | ||
| 2156 | |||
| 2157 | /// Returns true if it is an error set that includes anyerror, false otherwise. | ||
| 2158 | /// Note that the result may be a false negative if the type did not get error set | ||
| 2159 | /// resolution prior to this call. | ||
| 2160 | pub fn isAnyError(ty: Type, mod: *Module) bool { | ||
| 2161 | const ip = &mod.intern_pool; | ||
| 2162 | return switch (ty.toIntern()) { | ||
| 2163 | .anyerror_type => true, | ||
| 2164 | .adhoc_inferred_error_set_type => false, | ||
| 2165 | else => switch (mod.intern_pool.indexToKey(ty.toIntern())) { | ||
| 2166 | .inferred_error_set_type => |i| ip.funcIesResolved(i).* == .anyerror_type, | ||
| 2167 | else => false, | ||
| 2168 | }, | ||
| 2169 | }; | ||
| 2170 | } | ||
| 2171 | |||
| 2172 | pub fn isError(ty: Type, mod: *const Module) bool { | ||
| 2173 | return switch (ty.zigTypeTag(mod)) { | ||
| 2174 | .ErrorUnion, .ErrorSet => true, | ||
| 2175 | else => false, | ||
| 2176 | }; | ||
| 2177 | } | ||
| 2178 | |||
| 2179 | /// Returns whether ty, which must be an error set, includes an error `name`. | ||
| 2180 | /// Might return a false negative if `ty` is an inferred error set and not fully | ||
| 2181 | /// resolved yet. | ||
| 2182 | pub fn errorSetHasFieldIp( | ||
| 2183 | ip: *const InternPool, | ||
| 2184 | ty: InternPool.Index, | ||
| 2185 | name: InternPool.NullTerminatedString, | ||
| 2186 | ) bool { | ||
| 2187 | return switch (ty) { | ||
| 2188 | .anyerror_type => true, | ||
| 2189 | else => switch (ip.indexToKey(ty)) { | ||
| 2190 | .error_set_type => |error_set_type| error_set_type.nameIndex(ip, name) != null, | ||
| 2191 | .inferred_error_set_type => |i| switch (ip.funcIesResolved(i).*) { | ||
| 2192 | .anyerror_type => true, | ||
| 2193 | .none => false, | ||
| 2194 | else => |t| ip.indexToKey(t).error_set_type.nameIndex(ip, name) != null, | ||
| 2195 | }, | ||
| 2196 | else => unreachable, | ||
| 2197 | }, | ||
| 2198 | }; | ||
| 2199 | } | ||
| 2200 | |||
| 2201 | /// Returns whether ty, which must be an error set, includes an error `name`. | ||
| 2202 | /// Might return a false negative if `ty` is an inferred error set and not fully | ||
| 2203 | /// resolved yet. | ||
| 2204 | pub fn errorSetHasField(ty: Type, name: []const u8, mod: *Module) bool { | ||
| 2205 | const ip = &mod.intern_pool; | ||
| 2206 | return switch (ty.toIntern()) { | ||
| 2207 | .anyerror_type => true, | ||
| 2208 | else => switch (ip.indexToKey(ty.toIntern())) { | ||
| 2209 | .error_set_type => |error_set_type| { | ||
| 2210 | // If the string is not interned, then the field certainly is not present. | ||
| 2211 | const field_name_interned = ip.getString(name).unwrap() orelse return false; | ||
| 2212 | return error_set_type.nameIndex(ip, field_name_interned) != null; | ||
| 2213 | }, | ||
| 2214 | .inferred_error_set_type => |i| switch (ip.funcIesResolved(i).*) { | ||
| 2215 | .anyerror_type => true, | ||
| 2216 | .none => false, | ||
| 2217 | else => |t| { | ||
| 2218 | // If the string is not interned, then the field certainly is not present. | ||
| 2219 | const field_name_interned = ip.getString(name).unwrap() orelse return false; | ||
| 2220 | return ip.indexToKey(t).error_set_type.nameIndex(ip, field_name_interned) != null; | ||
| 2221 | }, | ||
| 2222 | }, | ||
| 2223 | else => unreachable, | ||
| 2224 | }, | ||
| 2225 | }; | ||
| 2226 | } | ||
| 2227 | |||
| 2228 | /// Asserts the type is an array or vector or struct. | ||
| 2229 | pub fn arrayLen(ty: Type, mod: *const Module) u64 { | ||
| 2230 | return ty.arrayLenIp(&mod.intern_pool); | ||
| 2231 | } | ||
| 2232 | |||
| 2233 | pub fn arrayLenIp(ty: Type, ip: *const InternPool) u64 { | ||
| 2234 | return ip.aggregateTypeLen(ty.toIntern()); | ||
| 2235 | } | ||
| 2236 | |||
| 2237 | pub fn arrayLenIncludingSentinel(ty: Type, mod: *const Module) u64 { | ||
| 2238 | return mod.intern_pool.aggregateTypeLenIncludingSentinel(ty.toIntern()); | ||
| 2239 | } | ||
| 2240 | |||
| 2241 | pub fn vectorLen(ty: Type, mod: *const Module) u32 { | ||
| 2242 | return switch (mod.intern_pool.indexToKey(ty.toIntern())) { | ||
| 2243 | .vector_type => |vector_type| vector_type.len, | ||
| 2244 | .anon_struct_type => |tuple| @intCast(tuple.types.len), | ||
| 2245 | else => unreachable, | ||
| 2246 | }; | ||
| 2247 | } | ||
| 2248 | |||
| 2249 | /// Asserts the type is an array, pointer or vector. | ||
| 2250 | pub fn sentinel(ty: Type, mod: *const Module) ?Value { | ||
| 2251 | return switch (mod.intern_pool.indexToKey(ty.toIntern())) { | ||
| 2252 | .vector_type, | ||
| 2253 | .struct_type, | ||
| 2254 | .anon_struct_type, | ||
| 2255 | => null, | ||
| 2256 | |||
| 2257 | .array_type => |t| if (t.sentinel != .none) Value.fromInterned(t.sentinel) else null, | ||
| 2258 | .ptr_type => |t| if (t.sentinel != .none) Value.fromInterned(t.sentinel) else null, | ||
| 2259 | |||
| 2260 | else => unreachable, | ||
| 2261 | }; | ||
| 2262 | } | ||
| 2263 | |||
| 2264 | /// Returns true if and only if the type is a fixed-width integer. | ||
| 2265 | pub fn isInt(self: Type, mod: *const Module) bool { | ||
| 2266 | return self.toIntern() != .comptime_int_type and | ||
| 2267 | mod.intern_pool.isIntegerType(self.toIntern()); | ||
| 2268 | } | ||
| 2269 | |||
| 2270 | /// Returns true if and only if the type is a fixed-width, signed integer. | ||
| 2271 | pub fn isSignedInt(ty: Type, mod: *const Module) bool { | ||
| 2272 | return switch (ty.toIntern()) { | ||
| 2273 | .c_char_type => mod.getTarget().charSignedness() == .signed, | ||
| 2274 | .isize_type, .c_short_type, .c_int_type, .c_long_type, .c_longlong_type => true, | ||
| 2275 | else => switch (mod.intern_pool.indexToKey(ty.toIntern())) { | ||
| 2276 | .int_type => |int_type| int_type.signedness == .signed, | ||
| 2277 | else => false, | ||
| 2278 | }, | ||
| 2279 | }; | ||
| 2280 | } | ||
| 2281 | |||
| 2282 | /// Returns true if and only if the type is a fixed-width, unsigned integer. | ||
| 2283 | pub fn isUnsignedInt(ty: Type, mod: *const Module) bool { | ||
| 2284 | return switch (ty.toIntern()) { | ||
| 2285 | .c_char_type => mod.getTarget().charSignedness() == .unsigned, | ||
| 2286 | .usize_type, .c_ushort_type, .c_uint_type, .c_ulong_type, .c_ulonglong_type => true, | ||
| 2287 | else => switch (mod.intern_pool.indexToKey(ty.toIntern())) { | ||
| 2288 | .int_type => |int_type| int_type.signedness == .unsigned, | ||
| 2289 | else => false, | ||
| 2290 | }, | ||
| 2291 | }; | ||
| 2292 | } | ||
| 2293 | |||
| 2294 | /// Returns true for integers, enums, error sets, and packed structs. | ||
| 2295 | /// If this function returns true, then intInfo() can be called on the type. | ||
| 2296 | pub fn isAbiInt(ty: Type, mod: *Module) bool { | ||
| 2297 | return switch (ty.zigTypeTag(mod)) { | ||
| 2298 | .Int, .Enum, .ErrorSet => true, | ||
| 2299 | .Struct => ty.containerLayout(mod) == .@"packed", | ||
| 2300 | else => false, | ||
| 2301 | }; | ||
| 2302 | } | ||
| 2303 | |||
| 2304 | /// Asserts the type is an integer, enum, error set, or vector of one of them. | ||
| 2305 | pub fn intInfo(starting_ty: Type, mod: *Module) InternPool.Key.IntType { | ||
| 2306 | const ip = &mod.intern_pool; | ||
| 2307 | const target = mod.getTarget(); | ||
| 2308 | var ty = starting_ty; | ||
| 2309 | |||
| 2310 | while (true) switch (ty.toIntern()) { | ||
| 2311 | .anyerror_type, .adhoc_inferred_error_set_type => { | ||
| 2312 | return .{ .signedness = .unsigned, .bits = mod.errorSetBits() }; | ||
| 2313 | }, | ||
| 2314 | .usize_type => return .{ .signedness = .unsigned, .bits = target.ptrBitWidth() }, | ||
| 2315 | .isize_type => return .{ .signedness = .signed, .bits = target.ptrBitWidth() }, | ||
| 2316 | .c_char_type => return .{ .signedness = mod.getTarget().charSignedness(), .bits = target.c_type_bit_size(.char) }, | ||
| 2317 | .c_short_type => return .{ .signedness = .signed, .bits = target.c_type_bit_size(.short) }, | ||
| 2318 | .c_ushort_type => return .{ .signedness = .unsigned, .bits = target.c_type_bit_size(.ushort) }, | ||
| 2319 | .c_int_type => return .{ .signedness = .signed, .bits = target.c_type_bit_size(.int) }, | ||
| 2320 | .c_uint_type => return .{ .signedness = .unsigned, .bits = target.c_type_bit_size(.uint) }, | ||
| 2321 | .c_long_type => return .{ .signedness = .signed, .bits = target.c_type_bit_size(.long) }, | ||
| 2322 | .c_ulong_type => return .{ .signedness = .unsigned, .bits = target.c_type_bit_size(.ulong) }, | ||
| 2323 | .c_longlong_type => return .{ .signedness = .signed, .bits = target.c_type_bit_size(.longlong) }, | ||
| 2324 | .c_ulonglong_type => return .{ .signedness = .unsigned, .bits = target.c_type_bit_size(.ulonglong) }, | ||
| 2325 | else => switch (ip.indexToKey(ty.toIntern())) { | ||
| 2326 | .int_type => |int_type| return int_type, | ||
| 2327 | .struct_type => ty = Type.fromInterned(ip.loadStructType(ty.toIntern()).backingIntType(ip).*), | ||
| 2328 | .enum_type => ty = Type.fromInterned(ip.loadEnumType(ty.toIntern()).tag_ty), | ||
| 2329 | .vector_type => |vector_type| ty = Type.fromInterned(vector_type.child), | ||
| 2330 | |||
| 2331 | .error_set_type, .inferred_error_set_type => { | ||
| 2332 | return .{ .signedness = .unsigned, .bits = mod.errorSetBits() }; | ||
| 2333 | }, | ||
| 2334 | |||
| 2335 | .anon_struct_type => unreachable, | ||
| 2336 | |||
| 2337 | .ptr_type => unreachable, | ||
| 2338 | .anyframe_type => unreachable, | ||
| 2339 | .array_type => unreachable, | ||
| 2340 | |||
| 2341 | .opt_type => unreachable, | ||
| 2342 | .error_union_type => unreachable, | ||
| 2343 | .func_type => unreachable, | ||
| 2344 | .simple_type => unreachable, // handled via Index enum tag above | ||
| 2345 | |||
| 2346 | .union_type => unreachable, | ||
| 2347 | .opaque_type => unreachable, | ||
| 2348 | |||
| 2349 | // values, not types | ||
| 2350 | .undef, | ||
| 2351 | .simple_value, | ||
| 2352 | .variable, | ||
| 2353 | .extern_func, | ||
| 2354 | .func, | ||
| 2355 | .int, | ||
| 2356 | .err, | ||
| 2357 | .error_union, | ||
| 2358 | .enum_literal, | ||
| 2359 | .enum_tag, | ||
| 2360 | .empty_enum_value, | ||
| 2361 | .float, | ||
| 2362 | .ptr, | ||
| 2363 | .slice, | ||
| 2364 | .opt, | ||
| 2365 | .aggregate, | ||
| 2366 | .un, | ||
| 2367 | // memoization, not types | ||
| 2368 | .memoized_call, | ||
| 2369 | => unreachable, | ||
| 2370 | }, | ||
| 2371 | }; | ||
| 2372 | } | ||
| 2373 | |||
| 2374 | pub fn isNamedInt(ty: Type) bool { | ||
| 2375 | return switch (ty.toIntern()) { | ||
| 2376 | .usize_type, | ||
| 2377 | .isize_type, | ||
| 2378 | .c_char_type, | ||
| 2379 | .c_short_type, | ||
| 2380 | .c_ushort_type, | ||
| 2381 | .c_int_type, | ||
| 2382 | .c_uint_type, | ||
| 2383 | .c_long_type, | ||
| 2384 | .c_ulong_type, | ||
| 2385 | .c_longlong_type, | ||
| 2386 | .c_ulonglong_type, | ||
| 2387 | => true, | ||
| 2388 | |||
| 2389 | else => false, | ||
| 2390 | }; | ||
| 2391 | } | ||
| 2392 | |||
| 2393 | /// Returns `false` for `comptime_float`. | ||
| 2394 | pub fn isRuntimeFloat(ty: Type) bool { | ||
| 2395 | return switch (ty.toIntern()) { | ||
| 2396 | .f16_type, | ||
| 2397 | .f32_type, | ||
| 2398 | .f64_type, | ||
| 2399 | .f80_type, | ||
| 2400 | .f128_type, | ||
| 2401 | .c_longdouble_type, | ||
| 2402 | => true, | ||
| 2403 | |||
| 2404 | else => false, | ||
| 2405 | }; | ||
| 2406 | } | ||
| 2407 | |||
| 2408 | /// Returns `true` for `comptime_float`. | ||
| 2409 | pub fn isAnyFloat(ty: Type) bool { | ||
| 2410 | return switch (ty.toIntern()) { | ||
| 2411 | .f16_type, | ||
| 2412 | .f32_type, | ||
| 2413 | .f64_type, | ||
| 2414 | .f80_type, | ||
| 2415 | .f128_type, | ||
| 2416 | .c_longdouble_type, | ||
| 2417 | .comptime_float_type, | ||
| 2418 | => true, | ||
| 2419 | |||
| 2420 | else => false, | ||
| 2421 | }; | ||
| 2422 | } | ||
| 2423 | |||
| 2424 | /// Asserts the type is a fixed-size float or comptime_float. | ||
| 2425 | /// Returns 128 for comptime_float types. | ||
| 2426 | pub fn floatBits(ty: Type, target: Target) u16 { | ||
| 2427 | return switch (ty.toIntern()) { | ||
| 2428 | .f16_type => 16, | ||
| 2429 | .f32_type => 32, | ||
| 2430 | .f64_type => 64, | ||
| 2431 | .f80_type => 80, | ||
| 2432 | .f128_type, .comptime_float_type => 128, | ||
| 2433 | .c_longdouble_type => target.c_type_bit_size(.longdouble), | ||
| 2434 | |||
| 2435 | else => unreachable, | ||
| 2436 | }; | ||
| 2437 | } | ||
| 2438 | |||
| 2439 | /// Asserts the type is a function or a function pointer. | ||
| 2440 | pub fn fnReturnType(ty: Type, mod: *Module) Type { | ||
| 2441 | return Type.fromInterned(mod.intern_pool.funcTypeReturnType(ty.toIntern())); | ||
| 2442 | } | ||
| 2443 | |||
| 2444 | /// Asserts the type is a function. | ||
| 2445 | pub fn fnCallingConvention(ty: Type, mod: *Module) std.builtin.CallingConvention { | ||
| 2446 | return mod.intern_pool.indexToKey(ty.toIntern()).func_type.cc; | ||
| 2447 | } | ||
| 2448 | |||
| 2449 | pub fn isValidParamType(self: Type, mod: *const Module) bool { | ||
| 2450 | return switch (self.zigTypeTagOrPoison(mod) catch return true) { | ||
| 2451 | .Opaque, .NoReturn => false, | ||
| 2452 | else => true, | ||
| 2453 | }; | ||
| 2454 | } | ||
| 2455 | |||
| 2456 | pub fn isValidReturnType(self: Type, mod: *const Module) bool { | ||
| 2457 | return switch (self.zigTypeTagOrPoison(mod) catch return true) { | ||
| 2458 | .Opaque => false, | ||
| 2459 | else => true, | ||
| 2460 | }; | ||
| 2461 | } | ||
| 2462 | |||
| 2463 | /// Asserts the type is a function. | ||
| 2464 | pub fn fnIsVarArgs(ty: Type, mod: *Module) bool { | ||
| 2465 | return mod.intern_pool.indexToKey(ty.toIntern()).func_type.is_var_args; | ||
| 2466 | } | ||
| 2467 | |||
| 2468 | pub fn isNumeric(ty: Type, mod: *const Module) bool { | ||
| 2469 | return switch (ty.toIntern()) { | ||
| 2470 | .f16_type, | ||
| 2471 | .f32_type, | ||
| 2472 | .f64_type, | ||
| 2473 | .f80_type, | ||
| 2474 | .f128_type, | ||
| 2475 | .c_longdouble_type, | ||
| 2476 | .comptime_int_type, | ||
| 2477 | .comptime_float_type, | ||
| 2478 | .usize_type, | ||
| 2479 | .isize_type, | ||
| 2480 | .c_char_type, | ||
| 2481 | .c_short_type, | ||
| 2482 | .c_ushort_type, | ||
| 2483 | .c_int_type, | ||
| 2484 | .c_uint_type, | ||
| 2485 | .c_long_type, | ||
| 2486 | .c_ulong_type, | ||
| 2487 | .c_longlong_type, | ||
| 2488 | .c_ulonglong_type, | ||
| 2489 | => true, | ||
| 2490 | |||
| 2491 | else => switch (mod.intern_pool.indexToKey(ty.toIntern())) { | ||
| 2492 | .int_type => true, | ||
| 2493 | else => false, | ||
| 2494 | }, | ||
| 2495 | }; | ||
| 2496 | } | ||
| 2497 | |||
| 2498 | /// During semantic analysis, instead call `Sema.typeHasOnePossibleValue` which | ||
| 2499 | /// resolves field types rather than asserting they are already resolved. | ||
| 2500 | pub fn onePossibleValue(starting_type: Type, mod: *Module) !?Value { | ||
| 2501 | var ty = starting_type; | ||
| 2502 | const ip = &mod.intern_pool; | ||
| 2503 | while (true) switch (ty.toIntern()) { | ||
| 2504 | .empty_struct_type => return Value.empty_struct, | ||
| 2505 | |||
| 2506 | else => switch (ip.indexToKey(ty.toIntern())) { | ||
| 2507 | .int_type => |int_type| { | ||
| 2508 | if (int_type.bits == 0) { | ||
| 2509 | return try mod.intValue(ty, 0); | ||
| 2510 | } else { | ||
| 2511 | return null; | ||
| 2512 | } | ||
| 2513 | }, | ||
| 2514 | |||
| 2515 | .ptr_type, | ||
| 2516 | .error_union_type, | ||
| 2517 | .func_type, | ||
| 2518 | .anyframe_type, | ||
| 2519 | .error_set_type, | ||
| 2520 | .inferred_error_set_type, | ||
| 2521 | => return null, | ||
| 2522 | |||
| 2523 | inline .array_type, .vector_type => |seq_type, seq_tag| { | ||
| 2524 | const has_sentinel = seq_tag == .array_type and seq_type.sentinel != .none; | ||
| 2525 | if (seq_type.len + @intFromBool(has_sentinel) == 0) return Value.fromInterned((try mod.intern(.{ .aggregate = .{ | ||
| 2526 | .ty = ty.toIntern(), | ||
| 2527 | .storage = .{ .elems = &.{} }, | ||
| 2528 | } }))); | ||
| 2529 | if (try Type.fromInterned(seq_type.child).onePossibleValue(mod)) |opv| { | ||
| 2530 | return Value.fromInterned((try mod.intern(.{ .aggregate = .{ | ||
| 2531 | .ty = ty.toIntern(), | ||
| 2532 | .storage = .{ .repeated_elem = opv.toIntern() }, | ||
| 2533 | } }))); | ||
| 2534 | } | ||
| 2535 | return null; | ||
| 2536 | }, | ||
| 2537 | .opt_type => |child| { | ||
| 2538 | if (child == .noreturn_type) { | ||
| 2539 | return try mod.nullValue(ty); | ||
| 2540 | } else { | ||
| 2541 | return null; | ||
| 2542 | } | ||
| 2543 | }, | ||
| 2544 | |||
| 2545 | .simple_type => |t| switch (t) { | ||
| 2546 | .f16, | ||
| 2547 | .f32, | ||
| 2548 | .f64, | ||
| 2549 | .f80, | ||
| 2550 | .f128, | ||
| 2551 | .usize, | ||
| 2552 | .isize, | ||
| 2553 | .c_char, | ||
| 2554 | .c_short, | ||
| 2555 | .c_ushort, | ||
| 2556 | .c_int, | ||
| 2557 | .c_uint, | ||
| 2558 | .c_long, | ||
| 2559 | .c_ulong, | ||
| 2560 | .c_longlong, | ||
| 2561 | .c_ulonglong, | ||
| 2562 | .c_longdouble, | ||
| 2563 | .anyopaque, | ||
| 2564 | .bool, | ||
| 2565 | .type, | ||
| 2566 | .anyerror, | ||
| 2567 | .comptime_int, | ||
| 2568 | .comptime_float, | ||
| 2569 | .enum_literal, | ||
| 2570 | .atomic_order, | ||
| 2571 | .atomic_rmw_op, | ||
| 2572 | .calling_convention, | ||
| 2573 | .address_space, | ||
| 2574 | .float_mode, | ||
| 2575 | .reduce_op, | ||
| 2576 | .call_modifier, | ||
| 2577 | .prefetch_options, | ||
| 2578 | .export_options, | ||
| 2579 | .extern_options, | ||
| 2580 | .type_info, | ||
| 2581 | .adhoc_inferred_error_set, | ||
| 2582 | => return null, | ||
| 2583 | |||
| 2584 | .void => return Value.void, | ||
| 2585 | .noreturn => return Value.@"unreachable", | ||
| 2586 | .null => return Value.null, | ||
| 2587 | .undefined => return Value.undef, | ||
| 2588 | |||
| 2589 | .generic_poison => unreachable, | ||
| 2590 | }, | ||
| 2591 | .struct_type => { | ||
| 2592 | const struct_type = ip.loadStructType(ty.toIntern()); | ||
| 2593 | assert(struct_type.haveFieldTypes(ip)); | ||
| 2594 | if (struct_type.knownNonOpv(ip)) | ||
| 2595 | return null; | ||
| 2596 | const field_vals = try mod.gpa.alloc(InternPool.Index, struct_type.field_types.len); | ||
| 2597 | defer mod.gpa.free(field_vals); | ||
| 2598 | for (field_vals, 0..) |*field_val, i_usize| { | ||
| 2599 | const i: u32 = @intCast(i_usize); | ||
| 2600 | if (struct_type.fieldIsComptime(ip, i)) { | ||
| 2601 | assert(struct_type.haveFieldInits(ip)); | ||
| 2602 | field_val.* = struct_type.field_inits.get(ip)[i]; | ||
| 2603 | continue; | ||
| 2604 | } | ||
| 2605 | const field_ty = Type.fromInterned(struct_type.field_types.get(ip)[i]); | ||
| 2606 | if (try field_ty.onePossibleValue(mod)) |field_opv| { | ||
| 2607 | field_val.* = field_opv.toIntern(); | ||
| 2608 | } else return null; | ||
| 2609 | } | ||
| 2610 | |||
| 2611 | // In this case the struct has no runtime-known fields and | ||
| 2612 | // therefore has one possible value. | ||
| 2613 | return Value.fromInterned((try mod.intern(.{ .aggregate = .{ | ||
| 2614 | .ty = ty.toIntern(), | ||
| 2615 | .storage = .{ .elems = field_vals }, | ||
| 2616 | } }))); | ||
| 2617 | }, | ||
| 2618 | |||
| 2619 | .anon_struct_type => |tuple| { | ||
| 2620 | for (tuple.values.get(ip)) |val| { | ||
| 2621 | if (val == .none) return null; | ||
| 2622 | } | ||
| 2623 | // In this case the struct has all comptime-known fields and | ||
| 2624 | // therefore has one possible value. | ||
| 2625 | // TODO: write something like getCoercedInts to avoid needing to dupe | ||
| 2626 | const duped_values = try mod.gpa.dupe(InternPool.Index, tuple.values.get(ip)); | ||
| 2627 | defer mod.gpa.free(duped_values); | ||
| 2628 | return Value.fromInterned((try mod.intern(.{ .aggregate = .{ | ||
| 2629 | .ty = ty.toIntern(), | ||
| 2630 | .storage = .{ .elems = duped_values }, | ||
| 2631 | } }))); | ||
| 2632 | }, | ||
| 2633 | |||
| 2634 | .union_type => { | ||
| 2635 | const union_obj = ip.loadUnionType(ty.toIntern()); | ||
| 2636 | const tag_val = (try Type.fromInterned(union_obj.enum_tag_ty).onePossibleValue(mod)) orelse | ||
| 2637 | return null; | ||
| 2638 | if (union_obj.field_types.len == 0) { | ||
| 2639 | const only = try mod.intern(.{ .empty_enum_value = ty.toIntern() }); | ||
| 2640 | return Value.fromInterned(only); | ||
| 2641 | } | ||
| 2642 | const only_field_ty = union_obj.field_types.get(ip)[0]; | ||
| 2643 | const val_val = (try Type.fromInterned(only_field_ty).onePossibleValue(mod)) orelse | ||
| 2644 | return null; | ||
| 2645 | const only = try mod.intern(.{ .un = .{ | ||
| 2646 | .ty = ty.toIntern(), | ||
| 2647 | .tag = tag_val.toIntern(), | ||
| 2648 | .val = val_val.toIntern(), | ||
| 2649 | } }); | ||
| 2650 | return Value.fromInterned(only); | ||
| 2651 | }, | ||
| 2652 | .opaque_type => return null, | ||
| 2653 | .enum_type => { | ||
| 2654 | const enum_type = ip.loadEnumType(ty.toIntern()); | ||
| 2655 | switch (enum_type.tag_mode) { | ||
| 2656 | .nonexhaustive => { | ||
| 2657 | if (enum_type.tag_ty == .comptime_int_type) return null; | ||
| 2658 | |||
| 2659 | if (try Type.fromInterned(enum_type.tag_ty).onePossibleValue(mod)) |int_opv| { | ||
| 2660 | const only = try mod.intern(.{ .enum_tag = .{ | ||
| 2661 | .ty = ty.toIntern(), | ||
| 2662 | .int = int_opv.toIntern(), | ||
| 2663 | } }); | ||
| 2664 | return Value.fromInterned(only); | ||
| 2665 | } | ||
| 2666 | |||
| 2667 | return null; | ||
| 2668 | }, | ||
| 2669 | .auto, .explicit => { | ||
| 2670 | if (Type.fromInterned(enum_type.tag_ty).hasRuntimeBits(mod)) return null; | ||
| 2671 | |||
| 2672 | switch (enum_type.names.len) { | ||
| 2673 | 0 => { | ||
| 2674 | const only = try mod.intern(.{ .empty_enum_value = ty.toIntern() }); | ||
| 2675 | return Value.fromInterned(only); | ||
| 2676 | }, | ||
| 2677 | 1 => { | ||
| 2678 | if (enum_type.values.len == 0) { | ||
| 2679 | const only = try mod.intern(.{ .enum_tag = .{ | ||
| 2680 | .ty = ty.toIntern(), | ||
| 2681 | .int = try mod.intern(.{ .int = .{ | ||
| 2682 | .ty = enum_type.tag_ty, | ||
| 2683 | .storage = .{ .u64 = 0 }, | ||
| 2684 | } }), | ||
| 2685 | } }); | ||
| 2686 | return Value.fromInterned(only); | ||
| 2687 | } else { | ||
| 2688 | return Value.fromInterned(enum_type.values.get(ip)[0]); | ||
| 2689 | } | ||
| 2690 | }, | ||
| 2691 | else => return null, | ||
| 2692 | } | ||
| 2693 | }, | ||
| 2694 | } | ||
| 2695 | }, | ||
| 2696 | |||
| 2697 | // values, not types | ||
| 2698 | .undef, | ||
| 2699 | .simple_value, | ||
| 2700 | .variable, | ||
| 2701 | .extern_func, | ||
| 2702 | .func, | ||
| 2703 | .int, | ||
| 2704 | .err, | ||
| 2705 | .error_union, | ||
| 2706 | .enum_literal, | ||
| 2707 | .enum_tag, | ||
| 2708 | .empty_enum_value, | ||
| 2709 | .float, | ||
| 2710 | .ptr, | ||
| 2711 | .slice, | ||
| 2712 | .opt, | ||
| 2713 | .aggregate, | ||
| 2714 | .un, | ||
| 2715 | // memoization, not types | ||
| 2716 | .memoized_call, | ||
| 2717 | => unreachable, | ||
| 2718 | }, | ||
| 2719 | }; | ||
| 2720 | } | ||
| 2721 | |||
| 2722 | /// During semantic analysis, instead call `Sema.typeRequiresComptime` which | ||
| 2723 | /// resolves field types rather than asserting they are already resolved. | ||
| 2724 | pub fn comptimeOnly(ty: Type, mod: *Module) bool { | ||
| 2725 | return ty.comptimeOnlyAdvanced(mod, null) catch unreachable; | ||
| 2726 | } | ||
| 2727 | |||
| 2728 | /// `generic_poison` will return false. | ||
| 2729 | /// May return false negatives when structs and unions are having their field types resolved. | ||
| 2730 | /// If `opt_sema` is not provided, asserts that the type is sufficiently resolved. | ||
| 2731 | pub fn comptimeOnlyAdvanced(ty: Type, mod: *Module, opt_sema: ?*Sema) Module.CompileError!bool { | ||
| 2732 | const ip = &mod.intern_pool; | ||
| 2733 | return switch (ty.toIntern()) { | ||
| 2734 | .empty_struct_type => false, | ||
| 2735 | |||
| 2736 | else => switch (ip.indexToKey(ty.toIntern())) { | ||
| 2737 | .int_type => false, | ||
| 2738 | .ptr_type => |ptr_type| { | ||
| 2739 | const child_ty = Type.fromInterned(ptr_type.child); | ||
| 2740 | switch (child_ty.zigTypeTag(mod)) { | ||
| 2741 | .Fn => return !try child_ty.fnHasRuntimeBitsAdvanced(mod, opt_sema), | ||
| 2742 | .Opaque => return false, | ||
| 2743 | else => return child_ty.comptimeOnlyAdvanced(mod, opt_sema), | ||
| 2744 | } | ||
| 2745 | }, | ||
| 2746 | .anyframe_type => |child| { | ||
| 2747 | if (child == .none) return false; | ||
| 2748 | return Type.fromInterned(child).comptimeOnlyAdvanced(mod, opt_sema); | ||
| 2749 | }, | ||
| 2750 | .array_type => |array_type| return Type.fromInterned(array_type.child).comptimeOnlyAdvanced(mod, opt_sema), | ||
| 2751 | .vector_type => |vector_type| return Type.fromInterned(vector_type.child).comptimeOnlyAdvanced(mod, opt_sema), | ||
| 2752 | .opt_type => |child| return Type.fromInterned(child).comptimeOnlyAdvanced(mod, opt_sema), | ||
| 2753 | .error_union_type => |error_union_type| return Type.fromInterned(error_union_type.payload_type).comptimeOnlyAdvanced(mod, opt_sema), | ||
| 2754 | |||
| 2755 | .error_set_type, | ||
| 2756 | .inferred_error_set_type, | ||
| 2757 | => false, | ||
| 2758 | |||
| 2759 | // These are function bodies, not function pointers. | ||
| 2760 | .func_type => true, | ||
| 2761 | |||
| 2762 | .simple_type => |t| switch (t) { | ||
| 2763 | .f16, | ||
| 2764 | .f32, | ||
| 2765 | .f64, | ||
| 2766 | .f80, | ||
| 2767 | .f128, | ||
| 2768 | .usize, | ||
| 2769 | .isize, | ||
| 2770 | .c_char, | ||
| 2771 | .c_short, | ||
| 2772 | .c_ushort, | ||
| 2773 | .c_int, | ||
| 2774 | .c_uint, | ||
| 2775 | .c_long, | ||
| 2776 | .c_ulong, | ||
| 2777 | .c_longlong, | ||
| 2778 | .c_ulonglong, | ||
| 2779 | .c_longdouble, | ||
| 2780 | .anyopaque, | ||
| 2781 | .bool, | ||
| 2782 | .void, | ||
| 2783 | .anyerror, | ||
| 2784 | .adhoc_inferred_error_set, | ||
| 2785 | .noreturn, | ||
| 2786 | .generic_poison, | ||
| 2787 | .atomic_order, | ||
| 2788 | .atomic_rmw_op, | ||
| 2789 | .calling_convention, | ||
| 2790 | .address_space, | ||
| 2791 | .float_mode, | ||
| 2792 | .reduce_op, | ||
| 2793 | .call_modifier, | ||
| 2794 | .prefetch_options, | ||
| 2795 | .export_options, | ||
| 2796 | .extern_options, | ||
| 2797 | => false, | ||
| 2798 | |||
| 2799 | .type, | ||
| 2800 | .comptime_int, | ||
| 2801 | .comptime_float, | ||
| 2802 | .null, | ||
| 2803 | .undefined, | ||
| 2804 | .enum_literal, | ||
| 2805 | .type_info, | ||
| 2806 | => true, | ||
| 2807 | }, | ||
| 2808 | .struct_type => { | ||
| 2809 | const struct_type = ip.loadStructType(ty.toIntern()); | ||
| 2810 | // packed structs cannot be comptime-only because they have a well-defined | ||
| 2811 | // memory layout and every field has a well-defined bit pattern. | ||
| 2812 | if (struct_type.layout == .@"packed") | ||
| 2813 | return false; | ||
| 2814 | |||
| 2815 | // A struct with no fields is not comptime-only. | ||
| 2816 | return switch (struct_type.flagsPtr(ip).requires_comptime) { | ||
| 2817 | .no, .wip => false, | ||
| 2818 | .yes => true, | ||
| 2819 | .unknown => { | ||
| 2820 | // The type is not resolved; assert that we have a Sema. | ||
| 2821 | const sema = opt_sema.?; | ||
| 2822 | |||
| 2823 | if (struct_type.flagsPtr(ip).field_types_wip) | ||
| 2824 | return false; | ||
| 2825 | |||
| 2826 | struct_type.flagsPtr(ip).requires_comptime = .wip; | ||
| 2827 | errdefer struct_type.flagsPtr(ip).requires_comptime = .unknown; | ||
| 2828 | |||
| 2829 | try sema.resolveTypeFieldsStruct(ty.toIntern(), struct_type); | ||
| 2830 | |||
| 2831 | for (0..struct_type.field_types.len) |i_usize| { | ||
| 2832 | const i: u32 = @intCast(i_usize); | ||
| 2833 | if (struct_type.fieldIsComptime(ip, i)) continue; | ||
| 2834 | const field_ty = struct_type.field_types.get(ip)[i]; | ||
| 2835 | if (try Type.fromInterned(field_ty).comptimeOnlyAdvanced(mod, opt_sema)) { | ||
| 2836 | // Note that this does not cause the layout to | ||
| 2837 | // be considered resolved. Comptime-only types | ||
| 2838 | // still maintain a layout of their | ||
| 2839 | // runtime-known fields. | ||
| 2840 | struct_type.flagsPtr(ip).requires_comptime = .yes; | ||
| 2841 | return true; | ||
| 2842 | } | ||
| 2843 | } | ||
| 2844 | |||
| 2845 | struct_type.flagsPtr(ip).requires_comptime = .no; | ||
| 2846 | return false; | ||
| 2847 | }, | ||
| 2848 | }; | ||
| 2849 | }, | ||
| 2850 | |||
| 2851 | .anon_struct_type => |tuple| { | ||
| 2852 | for (tuple.types.get(ip), tuple.values.get(ip)) |field_ty, val| { | ||
| 2853 | const have_comptime_val = val != .none; | ||
| 2854 | if (!have_comptime_val and try Type.fromInterned(field_ty).comptimeOnlyAdvanced(mod, opt_sema)) return true; | ||
| 2855 | } | ||
| 2856 | return false; | ||
| 2857 | }, | ||
| 2858 | |||
| 2859 | .union_type => { | ||
| 2860 | const union_type = ip.loadUnionType(ty.toIntern()); | ||
| 2861 | switch (union_type.flagsPtr(ip).requires_comptime) { | ||
| 2862 | .no, .wip => return false, | ||
| 2863 | .yes => return true, | ||
| 2864 | .unknown => { | ||
| 2865 | // The type is not resolved; assert that we have a Sema. | ||
| 2866 | const sema = opt_sema.?; | ||
| 2867 | |||
| 2868 | if (union_type.flagsPtr(ip).status == .field_types_wip) | ||
| 2869 | return false; | ||
| 2870 | |||
| 2871 | union_type.flagsPtr(ip).requires_comptime = .wip; | ||
| 2872 | errdefer union_type.flagsPtr(ip).requires_comptime = .unknown; | ||
| 2873 | |||
| 2874 | try sema.resolveTypeFieldsUnion(ty, union_type); | ||
| 2875 | |||
| 2876 | for (0..union_type.field_types.len) |field_idx| { | ||
| 2877 | const field_ty = union_type.field_types.get(ip)[field_idx]; | ||
| 2878 | if (try Type.fromInterned(field_ty).comptimeOnlyAdvanced(mod, opt_sema)) { | ||
| 2879 | union_type.flagsPtr(ip).requires_comptime = .yes; | ||
| 2880 | return true; | ||
| 2881 | } | ||
| 2882 | } | ||
| 2883 | |||
| 2884 | union_type.flagsPtr(ip).requires_comptime = .no; | ||
| 2885 | return false; | ||
| 2886 | }, | ||
| 2887 | } | ||
| 2888 | }, | ||
| 2889 | |||
| 2890 | .opaque_type => false, | ||
| 2891 | |||
| 2892 | .enum_type => return Type.fromInterned(ip.loadEnumType(ty.toIntern()).tag_ty).comptimeOnlyAdvanced(mod, opt_sema), | ||
| 2893 | |||
| 2894 | // values, not types | ||
| 2895 | .undef, | ||
| 2896 | .simple_value, | ||
| 2897 | .variable, | ||
| 2898 | .extern_func, | ||
| 2899 | .func, | ||
| 2900 | .int, | ||
| 2901 | .err, | ||
| 2902 | .error_union, | ||
| 2903 | .enum_literal, | ||
| 2904 | .enum_tag, | ||
| 2905 | .empty_enum_value, | ||
| 2906 | .float, | ||
| 2907 | .ptr, | ||
| 2908 | .slice, | ||
| 2909 | .opt, | ||
| 2910 | .aggregate, | ||
| 2911 | .un, | ||
| 2912 | // memoization, not types | ||
| 2913 | .memoized_call, | ||
| 2914 | => unreachable, | ||
| 2915 | }, | ||
| 2916 | }; | ||
| 2917 | } | ||
| 2918 | |||
| 2919 | pub fn isVector(ty: Type, mod: *const Module) bool { | ||
| 2920 | return ty.zigTypeTag(mod) == .Vector; | ||
| 2921 | } | ||
| 2922 | |||
| 2923 | /// Returns 0 if not a vector, otherwise returns @bitSizeOf(Element) * vector_len. | ||
| 2924 | pub fn totalVectorBits(ty: Type, zcu: *Zcu) u64 { | ||
| 2925 | if (!ty.isVector(zcu)) return 0; | ||
| 2926 | const v = zcu.intern_pool.indexToKey(ty.toIntern()).vector_type; | ||
| 2927 | return v.len * Type.fromInterned(v.child).bitSize(zcu); | ||
| 2928 | } | ||
| 2929 | |||
| 2930 | pub fn isArrayOrVector(ty: Type, mod: *const Module) bool { | ||
| 2931 | return switch (ty.zigTypeTag(mod)) { | ||
| 2932 | .Array, .Vector => true, | ||
| 2933 | else => false, | ||
| 2934 | }; | ||
| 2935 | } | ||
| 2936 | |||
| 2937 | pub fn isIndexable(ty: Type, mod: *Module) bool { | ||
| 2938 | return switch (ty.zigTypeTag(mod)) { | ||
| 2939 | .Array, .Vector => true, | ||
| 2940 | .Pointer => switch (ty.ptrSize(mod)) { | ||
| 2941 | .Slice, .Many, .C => true, | ||
| 2942 | .One => switch (ty.childType(mod).zigTypeTag(mod)) { | ||
| 2943 | .Array, .Vector => true, | ||
| 2944 | .Struct => ty.childType(mod).isTuple(mod), | ||
| 2945 | else => false, | ||
| 2946 | }, | ||
| 2947 | }, | ||
| 2948 | .Struct => ty.isTuple(mod), | ||
| 2949 | else => false, | ||
| 2950 | }; | ||
| 2951 | } | ||
| 2952 | |||
| 2953 | pub fn indexableHasLen(ty: Type, mod: *Module) bool { | ||
| 2954 | return switch (ty.zigTypeTag(mod)) { | ||
| 2955 | .Array, .Vector => true, | ||
| 2956 | .Pointer => switch (ty.ptrSize(mod)) { | ||
| 2957 | .Many, .C => false, | ||
| 2958 | .Slice => true, | ||
| 2959 | .One => switch (ty.childType(mod).zigTypeTag(mod)) { | ||
| 2960 | .Array, .Vector => true, | ||
| 2961 | .Struct => ty.childType(mod).isTuple(mod), | ||
| 2962 | else => false, | ||
| 2963 | }, | ||
| 2964 | }, | ||
| 2965 | .Struct => ty.isTuple(mod), | ||
| 2966 | else => false, | ||
| 2967 | }; | ||
| 2968 | } | ||
| 2969 | |||
| 2970 | /// Asserts that the type can have a namespace. | ||
| 2971 | pub fn getNamespaceIndex(ty: Type, zcu: *Zcu) InternPool.OptionalNamespaceIndex { | ||
| 2972 | return ty.getNamespace(zcu).?; | ||
| 2973 | } | ||
| 2974 | |||
| 2975 | /// Returns null if the type has no namespace. | ||
| 2976 | pub fn getNamespace(ty: Type, zcu: *Zcu) ?InternPool.OptionalNamespaceIndex { | ||
| 2977 | const ip = &zcu.intern_pool; | ||
| 2978 | return switch (ip.indexToKey(ty.toIntern())) { | ||
| 2979 | .opaque_type => ip.loadOpaqueType(ty.toIntern()).namespace, | ||
| 2980 | .struct_type => ip.loadStructType(ty.toIntern()).namespace, | ||
| 2981 | .union_type => ip.loadUnionType(ty.toIntern()).namespace, | ||
| 2982 | .enum_type => ip.loadEnumType(ty.toIntern()).namespace, | ||
| 2983 | |||
| 2984 | .anon_struct_type => .none, | ||
| 2985 | .simple_type => |s| switch (s) { | ||
| 2986 | .anyopaque, | ||
| 2987 | .atomic_order, | ||
| 2988 | .atomic_rmw_op, | ||
| 2989 | .calling_convention, | ||
| 2990 | .address_space, | ||
| 2991 | .float_mode, | ||
| 2992 | .reduce_op, | ||
| 2993 | .call_modifier, | ||
| 2994 | .prefetch_options, | ||
| 2995 | .export_options, | ||
| 2996 | .extern_options, | ||
| 2997 | .type_info, | ||
| 2998 | => .none, | ||
| 2999 | else => null, | ||
| 3000 | }, | ||
| 3001 | |||
| 3002 | else => null, | ||
| 3003 | }; | ||
| 3004 | } | ||
| 3005 | |||
| 3006 | // Works for vectors and vectors of integers. | ||
| 3007 | pub fn minInt(ty: Type, mod: *Module, dest_ty: Type) !Value { | ||
| 3008 | const scalar = try minIntScalar(ty.scalarType(mod), mod, dest_ty.scalarType(mod)); | ||
| 3009 | return if (ty.zigTypeTag(mod) == .Vector) Value.fromInterned((try mod.intern(.{ .aggregate = .{ | ||
| 3010 | .ty = dest_ty.toIntern(), | ||
| 3011 | .storage = .{ .repeated_elem = scalar.toIntern() }, | ||
| 3012 | } }))) else scalar; | ||
| 3013 | } | ||
| 3014 | |||
| 3015 | /// Asserts that the type is an integer. | ||
| 3016 | pub fn minIntScalar(ty: Type, mod: *Module, dest_ty: Type) !Value { | ||
| 3017 | const info = ty.intInfo(mod); | ||
| 3018 | if (info.signedness == .unsigned) return mod.intValue(dest_ty, 0); | ||
| 3019 | if (info.bits == 0) return mod.intValue(dest_ty, -1); | ||
| 3020 | |||
| 3021 | if (std.math.cast(u6, info.bits - 1)) |shift| { | ||
| 3022 | const n = @as(i64, std.math.minInt(i64)) >> (63 - shift); | ||
| 3023 | return mod.intValue(dest_ty, n); | ||
| 3024 | } | ||
| 3025 | |||
| 3026 | var res = try std.math.big.int.Managed.init(mod.gpa); | ||
| 3027 | defer res.deinit(); | ||
| 3028 | |||
| 3029 | try res.setTwosCompIntLimit(.min, info.signedness, info.bits); | ||
| 3030 | |||
| 3031 | return mod.intValue_big(dest_ty, res.toConst()); | ||
| 3032 | } | ||
| 3033 | |||
| 3034 | // Works for vectors and vectors of integers. | ||
| 3035 | /// The returned Value will have type dest_ty. | ||
| 3036 | pub fn maxInt(ty: Type, mod: *Module, dest_ty: Type) !Value { | ||
| 3037 | const scalar = try maxIntScalar(ty.scalarType(mod), mod, dest_ty.scalarType(mod)); | ||
| 3038 | return if (ty.zigTypeTag(mod) == .Vector) Value.fromInterned((try mod.intern(.{ .aggregate = .{ | ||
| 3039 | .ty = dest_ty.toIntern(), | ||
| 3040 | .storage = .{ .repeated_elem = scalar.toIntern() }, | ||
| 3041 | } }))) else scalar; | ||
| 3042 | } | ||
| 3043 | |||
| 3044 | /// The returned Value will have type dest_ty. | ||
| 3045 | pub fn maxIntScalar(ty: Type, mod: *Module, dest_ty: Type) !Value { | ||
| 3046 | const info = ty.intInfo(mod); | ||
| 3047 | |||
| 3048 | switch (info.bits) { | ||
| 3049 | 0 => return switch (info.signedness) { | ||
| 3050 | .signed => try mod.intValue(dest_ty, -1), | ||
| 3051 | .unsigned => try mod.intValue(dest_ty, 0), | ||
| 3052 | }, | ||
| 3053 | 1 => return switch (info.signedness) { | ||
| 3054 | .signed => try mod.intValue(dest_ty, 0), | ||
| 3055 | .unsigned => try mod.intValue(dest_ty, 1), | ||
| 3056 | }, | ||
| 3057 | else => {}, | ||
| 3058 | } | ||
| 3059 | |||
| 3060 | if (std.math.cast(u6, info.bits - 1)) |shift| switch (info.signedness) { | ||
| 3061 | .signed => { | ||
| 3062 | const n = @as(i64, std.math.maxInt(i64)) >> (63 - shift); | ||
| 3063 | return mod.intValue(dest_ty, n); | ||
| 3064 | }, | ||
| 3065 | .unsigned => { | ||
| 3066 | const n = @as(u64, std.math.maxInt(u64)) >> (63 - shift); | ||
| 3067 | return mod.intValue(dest_ty, n); | ||
| 3068 | }, | ||
| 3069 | }; | ||
| 3070 | |||
| 3071 | var res = try std.math.big.int.Managed.init(mod.gpa); | ||
| 3072 | defer res.deinit(); | ||
| 3073 | |||
| 3074 | try res.setTwosCompIntLimit(.max, info.signedness, info.bits); | ||
| 3075 | |||
| 3076 | return mod.intValue_big(dest_ty, res.toConst()); | ||
| 3077 | } | ||
| 3078 | |||
| 3079 | /// Asserts the type is an enum or a union. | ||
| 3080 | pub fn intTagType(ty: Type, mod: *Module) Type { | ||
| 3081 | const ip = &mod.intern_pool; | ||
| 3082 | return switch (ip.indexToKey(ty.toIntern())) { | ||
| 3083 | .union_type => Type.fromInterned(ip.loadUnionType(ty.toIntern()).enum_tag_ty).intTagType(mod), | ||
| 3084 | .enum_type => Type.fromInterned(ip.loadEnumType(ty.toIntern()).tag_ty), | ||
| 3085 | else => unreachable, | ||
| 3086 | }; | ||
| 3087 | } | ||
| 3088 | |||
| 3089 | pub fn isNonexhaustiveEnum(ty: Type, mod: *Module) bool { | ||
| 3090 | const ip = &mod.intern_pool; | ||
| 3091 | return switch (ip.indexToKey(ty.toIntern())) { | ||
| 3092 | .enum_type => switch (ip.loadEnumType(ty.toIntern()).tag_mode) { | ||
| 3093 | .nonexhaustive => true, | ||
| 3094 | .auto, .explicit => false, | ||
| 3095 | }, | ||
| 3096 | else => false, | ||
| 3097 | }; | ||
| 3098 | } | ||
| 3099 | |||
| 3100 | // Asserts that `ty` is an error set and not `anyerror`. | ||
| 3101 | // Asserts that `ty` is resolved if it is an inferred error set. | ||
| 3102 | pub fn errorSetNames(ty: Type, mod: *Module) InternPool.NullTerminatedString.Slice { | ||
| 3103 | const ip = &mod.intern_pool; | ||
| 3104 | return switch (ip.indexToKey(ty.toIntern())) { | ||
| 3105 | .error_set_type => |x| x.names, | ||
| 3106 | .inferred_error_set_type => |i| switch (ip.funcIesResolved(i).*) { | ||
| 3107 | .none => unreachable, // unresolved inferred error set | ||
| 3108 | .anyerror_type => unreachable, | ||
| 3109 | else => |t| ip.indexToKey(t).error_set_type.names, | ||
| 3110 | }, | ||
| 3111 | else => unreachable, | ||
| 3112 | }; | ||
| 3113 | } | ||
| 3114 | |||
| 3115 | pub fn enumFields(ty: Type, mod: *Module) InternPool.NullTerminatedString.Slice { | ||
| 3116 | return mod.intern_pool.loadEnumType(ty.toIntern()).names; | ||
| 3117 | } | ||
| 3118 | |||
| 3119 | pub fn enumFieldCount(ty: Type, mod: *Module) usize { | ||
| 3120 | return mod.intern_pool.loadEnumType(ty.toIntern()).names.len; | ||
| 3121 | } | ||
| 3122 | |||
| 3123 | pub fn enumFieldName(ty: Type, field_index: usize, mod: *Module) InternPool.NullTerminatedString { | ||
| 3124 | const ip = &mod.intern_pool; | ||
| 3125 | return ip.loadEnumType(ty.toIntern()).names.get(ip)[field_index]; | ||
| 3126 | } | ||
| 3127 | |||
| 3128 | pub fn enumFieldIndex(ty: Type, field_name: InternPool.NullTerminatedString, mod: *Module) ?u32 { | ||
| 3129 | const ip = &mod.intern_pool; | ||
| 3130 | const enum_type = ip.loadEnumType(ty.toIntern()); | ||
| 3131 | return enum_type.nameIndex(ip, field_name); | ||
| 3132 | } | ||
| 3133 | |||
| 3134 | /// Asserts `ty` is an enum. `enum_tag` can either be `enum_field_index` or | ||
| 3135 | /// an integer which represents the enum value. Returns the field index in | ||
| 3136 | /// declaration order, or `null` if `enum_tag` does not match any field. | ||
| 3137 | pub fn enumTagFieldIndex(ty: Type, enum_tag: Value, mod: *Module) ?u32 { | ||
| 3138 | const ip = &mod.intern_pool; | ||
| 3139 | const enum_type = ip.loadEnumType(ty.toIntern()); | ||
| 3140 | const int_tag = switch (ip.indexToKey(enum_tag.toIntern())) { | ||
| 3141 | .int => enum_tag.toIntern(), | ||
| 3142 | .enum_tag => |info| info.int, | ||
| 3143 | else => unreachable, | ||
| 3144 | }; | ||
| 3145 | assert(ip.typeOf(int_tag) == enum_type.tag_ty); | ||
| 3146 | return enum_type.tagValueIndex(ip, int_tag); | ||
| 3147 | } | ||
| 3148 | |||
| 3149 | /// Returns none in the case of a tuple which uses the integer index as the field name. | ||
| 3150 | pub fn structFieldName(ty: Type, index: usize, mod: *Module) InternPool.OptionalNullTerminatedString { | ||
| 3151 | const ip = &mod.intern_pool; | ||
| 3152 | return switch (ip.indexToKey(ty.toIntern())) { | ||
| 3153 | .struct_type => ip.loadStructType(ty.toIntern()).fieldName(ip, index), | ||
| 3154 | .anon_struct_type => |anon_struct| anon_struct.fieldName(ip, index), | ||
| 3155 | else => unreachable, | ||
| 3156 | }; | ||
| 3157 | } | ||
| 3158 | |||
| 3159 | pub fn structFieldCount(ty: Type, mod: *Module) u32 { | ||
| 3160 | const ip = &mod.intern_pool; | ||
| 3161 | return switch (ip.indexToKey(ty.toIntern())) { | ||
| 3162 | .struct_type => ip.loadStructType(ty.toIntern()).field_types.len, | ||
| 3163 | .anon_struct_type => |anon_struct| anon_struct.types.len, | ||
| 3164 | else => unreachable, | ||
| 3165 | }; | ||
| 3166 | } | ||
| 3167 | |||
| 3168 | /// Supports structs and unions. | ||
| 3169 | pub fn structFieldType(ty: Type, index: usize, mod: *Module) Type { | ||
| 3170 | const ip = &mod.intern_pool; | ||
| 3171 | return switch (ip.indexToKey(ty.toIntern())) { | ||
| 3172 | .struct_type => Type.fromInterned(ip.loadStructType(ty.toIntern()).field_types.get(ip)[index]), | ||
| 3173 | .union_type => { | ||
| 3174 | const union_obj = ip.loadUnionType(ty.toIntern()); | ||
| 3175 | return Type.fromInterned(union_obj.field_types.get(ip)[index]); | ||
| 3176 | }, | ||
| 3177 | .anon_struct_type => |anon_struct| Type.fromInterned(anon_struct.types.get(ip)[index]), | ||
| 3178 | else => unreachable, | ||
| 3179 | }; | ||
| 3180 | } | ||
| 3181 | |||
| 3182 | pub fn structFieldAlign(ty: Type, index: usize, zcu: *Zcu) Alignment { | ||
| 3183 | return ty.structFieldAlignAdvanced(index, zcu, null) catch unreachable; | ||
| 3184 | } | ||
| 3185 | |||
| 3186 | pub fn structFieldAlignAdvanced(ty: Type, index: usize, zcu: *Zcu, opt_sema: ?*Sema) !Alignment { | ||
| 3187 | const ip = &zcu.intern_pool; | ||
| 3188 | switch (ip.indexToKey(ty.toIntern())) { | ||
| 3189 | .struct_type => { | ||
| 3190 | const struct_type = ip.loadStructType(ty.toIntern()); | ||
| 3191 | assert(struct_type.layout != .@"packed"); | ||
| 3192 | const explicit_align = struct_type.fieldAlign(ip, index); | ||
| 3193 | const field_ty = Type.fromInterned(struct_type.field_types.get(ip)[index]); | ||
| 3194 | if (opt_sema) |sema| { | ||
| 3195 | return sema.structFieldAlignment(explicit_align, field_ty, struct_type.layout); | ||
| 3196 | } else { | ||
| 3197 | return zcu.structFieldAlignment(explicit_align, field_ty, struct_type.layout); | ||
| 3198 | } | ||
| 3199 | }, | ||
| 3200 | .anon_struct_type => |anon_struct| { | ||
| 3201 | return (try Type.fromInterned(anon_struct.types.get(ip)[index]).abiAlignmentAdvanced(zcu, if (opt_sema) |sema| .{ .sema = sema } else .eager)).scalar; | ||
| 3202 | }, | ||
| 3203 | .union_type => { | ||
| 3204 | const union_obj = ip.loadUnionType(ty.toIntern()); | ||
| 3205 | if (opt_sema) |sema| { | ||
| 3206 | return sema.unionFieldAlignment(union_obj, @intCast(index)); | ||
| 3207 | } else { | ||
| 3208 | return zcu.unionFieldNormalAlignment(union_obj, @intCast(index)); | ||
| 3209 | } | ||
| 3210 | }, | ||
| 3211 | else => unreachable, | ||
| 3212 | } | ||
| 3213 | } | ||
| 3214 | |||
| 3215 | pub fn structFieldDefaultValue(ty: Type, index: usize, mod: *Module) Value { | ||
| 3216 | const ip = &mod.intern_pool; | ||
| 3217 | switch (ip.indexToKey(ty.toIntern())) { | ||
| 3218 | .struct_type => { | ||
| 3219 | const struct_type = ip.loadStructType(ty.toIntern()); | ||
| 3220 | const val = struct_type.fieldInit(ip, index); | ||
| 3221 | // TODO: avoid using `unreachable` to indicate this. | ||
| 3222 | if (val == .none) return Value.@"unreachable"; | ||
| 3223 | return Value.fromInterned(val); | ||
| 3224 | }, | ||
| 3225 | .anon_struct_type => |anon_struct| { | ||
| 3226 | const val = anon_struct.values.get(ip)[index]; | ||
| 3227 | // TODO: avoid using `unreachable` to indicate this. | ||
| 3228 | if (val == .none) return Value.@"unreachable"; | ||
| 3229 | return Value.fromInterned(val); | ||
| 3230 | }, | ||
| 3231 | else => unreachable, | ||
| 3232 | } | ||
| 3233 | } | ||
| 3234 | |||
| 3235 | pub fn structFieldValueComptime(ty: Type, mod: *Module, index: usize) !?Value { | ||
| 3236 | const ip = &mod.intern_pool; | ||
| 3237 | switch (ip.indexToKey(ty.toIntern())) { | ||
| 3238 | .struct_type => { | ||
| 3239 | const struct_type = ip.loadStructType(ty.toIntern()); | ||
| 3240 | if (struct_type.fieldIsComptime(ip, index)) { | ||
| 3241 | assert(struct_type.haveFieldInits(ip)); | ||
| 3242 | return Value.fromInterned(struct_type.field_inits.get(ip)[index]); | ||
| 3243 | } else { | ||
| 3244 | return Type.fromInterned(struct_type.field_types.get(ip)[index]).onePossibleValue(mod); | ||
| 3245 | } | ||
| 3246 | }, | ||
| 3247 | .anon_struct_type => |tuple| { | ||
| 3248 | const val = tuple.values.get(ip)[index]; | ||
| 3249 | if (val == .none) { | ||
| 3250 | return Type.fromInterned(tuple.types.get(ip)[index]).onePossibleValue(mod); | ||
| 3251 | } else { | ||
| 3252 | return Value.fromInterned(val); | ||
| 3253 | } | ||
| 3254 | }, | ||
| 3255 | else => unreachable, | ||
| 3256 | } | ||
| 3257 | } | ||
| 3258 | |||
| 3259 | pub fn structFieldIsComptime(ty: Type, index: usize, mod: *Module) bool { | ||
| 3260 | const ip = &mod.intern_pool; | ||
| 3261 | return switch (ip.indexToKey(ty.toIntern())) { | ||
| 3262 | .struct_type => ip.loadStructType(ty.toIntern()).fieldIsComptime(ip, index), | ||
| 3263 | .anon_struct_type => |anon_struct| anon_struct.values.get(ip)[index] != .none, | ||
| 3264 | else => unreachable, | ||
| 3265 | }; | ||
| 3266 | } | ||
| 3267 | |||
| 3268 | pub const FieldOffset = struct { | ||
| 3269 | field: usize, | ||
| 3270 | offset: u64, | ||
| 3271 | }; | ||
| 3272 | |||
| 3273 | /// Supports structs and unions. | ||
| 3274 | pub fn structFieldOffset(ty: Type, index: usize, mod: *Module) u64 { | ||
| 3275 | const ip = &mod.intern_pool; | ||
| 3276 | switch (ip.indexToKey(ty.toIntern())) { | ||
| 3277 | .struct_type => { | ||
| 3278 | const struct_type = ip.loadStructType(ty.toIntern()); | ||
| 3279 | assert(struct_type.haveLayout(ip)); | ||
| 3280 | assert(struct_type.layout != .@"packed"); | ||
| 3281 | return struct_type.offsets.get(ip)[index]; | ||
| 3282 | }, | ||
| 3283 | |||
| 3284 | .anon_struct_type => |tuple| { | ||
| 3285 | var offset: u64 = 0; | ||
| 3286 | var big_align: Alignment = .none; | ||
| 3287 | |||
| 3288 | for (tuple.types.get(ip), tuple.values.get(ip), 0..) |field_ty, field_val, i| { | ||
| 3289 | if (field_val != .none or !Type.fromInterned(field_ty).hasRuntimeBits(mod)) { | ||
| 3290 | // comptime field | ||
| 3291 | if (i == index) return offset; | ||
| 3292 | continue; | ||
| 3293 | } | ||
| 3294 | |||
| 3295 | const field_align = Type.fromInterned(field_ty).abiAlignment(mod); | ||
| 3296 | big_align = big_align.max(field_align); | ||
| 3297 | offset = field_align.forward(offset); | ||
| 3298 | if (i == index) return offset; | ||
| 3299 | offset += Type.fromInterned(field_ty).abiSize(mod); | ||
| 3300 | } | ||
| 3301 | offset = big_align.max(.@"1").forward(offset); | ||
| 3302 | return offset; | ||
| 3303 | }, | ||
| 3304 | |||
| 3305 | .union_type => { | ||
| 3306 | const union_type = ip.loadUnionType(ty.toIntern()); | ||
| 3307 | if (!union_type.hasTag(ip)) | ||
| 3308 | return 0; | ||
| 3309 | const layout = mod.getUnionLayout(union_type); | ||
| 3310 | if (layout.tag_align.compare(.gte, layout.payload_align)) { | ||
| 3311 | // {Tag, Payload} | ||
| 3312 | return layout.payload_align.forward(layout.tag_size); | ||
| 3313 | } else { | ||
| 3314 | // {Payload, Tag} | ||
| 3315 | return 0; | ||
| 3316 | } | ||
| 3317 | }, | ||
| 3318 | |||
| 3319 | else => unreachable, | ||
| 3320 | } | ||
| 3321 | } | ||
| 3322 | |||
| 3323 | pub fn getOwnerDecl(ty: Type, mod: *Module) InternPool.DeclIndex { | ||
| 3324 | return ty.getOwnerDeclOrNull(mod) orelse unreachable; | ||
| 3325 | } | ||
| 3326 | |||
| 3327 | pub fn getOwnerDeclOrNull(ty: Type, mod: *Module) ?InternPool.DeclIndex { | ||
| 3328 | const ip = &mod.intern_pool; | ||
| 3329 | return switch (ip.indexToKey(ty.toIntern())) { | ||
| 3330 | .struct_type => ip.loadStructType(ty.toIntern()).decl.unwrap(), | ||
| 3331 | .union_type => ip.loadUnionType(ty.toIntern()).decl, | ||
| 3332 | .opaque_type => ip.loadOpaqueType(ty.toIntern()).decl, | ||
| 3333 | .enum_type => ip.loadEnumType(ty.toIntern()).decl, | ||
| 3334 | else => null, | ||
| 3335 | }; | ||
| 3336 | } | ||
| 3337 | |||
| 3338 | pub fn srcLocOrNull(ty: Type, zcu: *Zcu) ?Module.LazySrcLoc { | ||
| 3339 | const ip = &zcu.intern_pool; | ||
| 3340 | return .{ | ||
| 3341 | .base_node_inst = switch (ip.indexToKey(ty.toIntern())) { | ||
| 3342 | .struct_type, .union_type, .opaque_type, .enum_type => |info| switch (info) { | ||
| 3343 | .declared => |d| d.zir_index, | ||
| 3344 | .reified => |r| r.zir_index, | ||
| 3345 | .generated_tag => |gt| ip.loadUnionType(gt.union_type).zir_index, | ||
| 3346 | .empty_struct => return null, | ||
| 3347 | }, | ||
| 3348 | else => return null, | ||
| 3349 | }, | ||
| 3350 | .offset = Module.LazySrcLoc.Offset.nodeOffset(0), | ||
| 3351 | }; | ||
| 3352 | } | ||
| 3353 | |||
| 3354 | pub fn srcLoc(ty: Type, zcu: *Zcu) Module.LazySrcLoc { | ||
| 3355 | return ty.srcLocOrNull(zcu).?; | ||
| 3356 | } | ||
| 3357 | |||
| 3358 | pub fn isGenericPoison(ty: Type) bool { | ||
| 3359 | return ty.toIntern() == .generic_poison_type; | ||
| 3360 | } | ||
| 3361 | |||
| 3362 | pub fn isTuple(ty: Type, mod: *Module) bool { | ||
| 3363 | const ip = &mod.intern_pool; | ||
| 3364 | return switch (ip.indexToKey(ty.toIntern())) { | ||
| 3365 | .struct_type => { | ||
| 3366 | const struct_type = ip.loadStructType(ty.toIntern()); | ||
| 3367 | if (struct_type.layout == .@"packed") return false; | ||
| 3368 | if (struct_type.decl == .none) return false; | ||
| 3369 | return struct_type.flagsPtr(ip).is_tuple; | ||
| 3370 | }, | ||
| 3371 | .anon_struct_type => |anon_struct| anon_struct.names.len == 0, | ||
| 3372 | else => false, | ||
| 3373 | }; | ||
| 3374 | } | ||
| 3375 | |||
| 3376 | pub fn isAnonStruct(ty: Type, mod: *Module) bool { | ||
| 3377 | if (ty.toIntern() == .empty_struct_type) return true; | ||
| 3378 | return switch (mod.intern_pool.indexToKey(ty.toIntern())) { | ||
| 3379 | .anon_struct_type => |anon_struct_type| anon_struct_type.names.len > 0, | ||
| 3380 | else => false, | ||
| 3381 | }; | ||
| 3382 | } | ||
| 3383 | |||
| 3384 | pub fn isTupleOrAnonStruct(ty: Type, mod: *Module) bool { | ||
| 3385 | const ip = &mod.intern_pool; | ||
| 3386 | return switch (ip.indexToKey(ty.toIntern())) { | ||
| 3387 | .struct_type => { | ||
| 3388 | const struct_type = ip.loadStructType(ty.toIntern()); | ||
| 3389 | if (struct_type.layout == .@"packed") return false; | ||
| 3390 | if (struct_type.decl == .none) return false; | ||
| 3391 | return struct_type.flagsPtr(ip).is_tuple; | ||
| 3392 | }, | ||
| 3393 | .anon_struct_type => true, | ||
| 3394 | else => false, | ||
| 3395 | }; | ||
| 3396 | } | ||
| 3397 | |||
| 3398 | pub fn isSimpleTuple(ty: Type, mod: *Module) bool { | ||
| 3399 | return switch (mod.intern_pool.indexToKey(ty.toIntern())) { | ||
| 3400 | .anon_struct_type => |anon_struct_type| anon_struct_type.names.len == 0, | ||
| 3401 | else => false, | ||
| 3402 | }; | ||
| 3403 | } | ||
| 3404 | |||
| 3405 | pub fn isSimpleTupleOrAnonStruct(ty: Type, mod: *Module) bool { | ||
| 3406 | return switch (mod.intern_pool.indexToKey(ty.toIntern())) { | ||
| 3407 | .anon_struct_type => true, | ||
| 3408 | else => false, | ||
| 3409 | }; | ||
| 3410 | } | ||
| 3411 | |||
| 3412 | /// Traverses optional child types and error union payloads until the type | ||
| 3413 | /// is not a pointer. For `E!?u32`, returns `u32`; for `*u8`, returns `*u8`. | ||
| 3414 | pub fn optEuBaseType(ty: Type, mod: *Module) Type { | ||
| 3415 | var cur = ty; | ||
| 3416 | while (true) switch (cur.zigTypeTag(mod)) { | ||
| 3417 | .Optional => cur = cur.optionalChild(mod), | ||
| 3418 | .ErrorUnion => cur = cur.errorUnionPayload(mod), | ||
| 3419 | else => return cur, | ||
| 3420 | }; | ||
| 3421 | } | ||
| 3422 | |||
| 3423 | pub fn toUnsigned(ty: Type, mod: *Module) !Type { | ||
| 3424 | return switch (ty.zigTypeTag(mod)) { | ||
| 3425 | .Int => mod.intType(.unsigned, ty.intInfo(mod).bits), | ||
| 3426 | .Vector => try mod.vectorType(.{ | ||
| 3427 | .len = ty.vectorLen(mod), | ||
| 3428 | .child = (try ty.childType(mod).toUnsigned(mod)).toIntern(), | ||
| 3429 | }), | ||
| 3430 | else => unreachable, | ||
| 3431 | }; | ||
| 3432 | } | ||
| 3433 | |||
| 3434 | pub fn typeDeclInst(ty: Type, zcu: *const Zcu) ?InternPool.TrackedInst.Index { | ||
| 3435 | const ip = &zcu.intern_pool; | ||
| 3436 | return switch (ip.indexToKey(ty.toIntern())) { | ||
| 3437 | .struct_type => ip.loadStructType(ty.toIntern()).zir_index.unwrap(), | ||
| 3438 | .union_type => ip.loadUnionType(ty.toIntern()).zir_index, | ||
| 3439 | .enum_type => ip.loadEnumType(ty.toIntern()).zir_index.unwrap(), | ||
| 3440 | .opaque_type => ip.loadOpaqueType(ty.toIntern()).zir_index, | ||
| 3441 | else => null, | ||
| 3442 | }; | ||
| 3443 | } | ||
| 3444 | |||
| 3445 | pub fn typeDeclSrcLine(ty: Type, zcu: *const Zcu) ?u32 { | ||
| 3446 | const ip = &zcu.intern_pool; | ||
| 3447 | const tracked = switch (ip.indexToKey(ty.toIntern())) { | ||
| 3448 | .struct_type, .union_type, .opaque_type, .enum_type => |info| switch (info) { | ||
| 3449 | .declared => |d| d.zir_index, | ||
| 3450 | .reified => |r| r.zir_index, | ||
| 3451 | .generated_tag => |gt| ip.loadUnionType(gt.union_type).zir_index, | ||
| 3452 | .empty_struct => return null, | ||
| 3453 | }, | ||
| 3454 | else => return null, | ||
| 3455 | }; | ||
| 3456 | const info = tracked.resolveFull(&zcu.intern_pool); | ||
| 3457 | const file = zcu.import_table.values()[zcu.path_digest_map.getIndex(info.path_digest).?]; | ||
| 3458 | assert(file.zir_loaded); | ||
| 3459 | const zir = file.zir; | ||
| 3460 | const inst = zir.instructions.get(@intFromEnum(info.inst)); | ||
| 3461 | assert(inst.tag == .extended); | ||
| 3462 | return switch (inst.data.extended.opcode) { | ||
| 3463 | .struct_decl => zir.extraData(Zir.Inst.StructDecl, inst.data.extended.operand).data.src_line, | ||
| 3464 | .union_decl => zir.extraData(Zir.Inst.UnionDecl, inst.data.extended.operand).data.src_line, | ||
| 3465 | .enum_decl => zir.extraData(Zir.Inst.EnumDecl, inst.data.extended.operand).data.src_line, | ||
| 3466 | .opaque_decl => zir.extraData(Zir.Inst.OpaqueDecl, inst.data.extended.operand).data.src_line, | ||
| 3467 | .reify => zir.extraData(Zir.Inst.Reify, inst.data.extended.operand).data.src_line, | ||
| 3468 | else => unreachable, | ||
| 3469 | }; | ||
| 3470 | } | ||
| 3471 | |||
| 3472 | /// Given a namespace type, returns its list of caotured values. | ||
| 3473 | pub fn getCaptures(ty: Type, zcu: *const Zcu) InternPool.CaptureValue.Slice { | ||
| 3474 | const ip = &zcu.intern_pool; | ||
| 3475 | return switch (ip.indexToKey(ty.toIntern())) { | ||
| 3476 | .struct_type => ip.loadStructType(ty.toIntern()).captures, | ||
| 3477 | .union_type => ip.loadUnionType(ty.toIntern()).captures, | ||
| 3478 | .enum_type => ip.loadEnumType(ty.toIntern()).captures, | ||
| 3479 | .opaque_type => ip.loadOpaqueType(ty.toIntern()).captures, | ||
| 3480 | else => unreachable, | ||
| 3481 | }; | ||
| 3482 | } | ||
| 3483 | |||
| 3484 | pub fn arrayBase(ty: Type, zcu: *const Zcu) struct { Type, u64 } { | ||
| 3485 | var cur_ty: Type = ty; | ||
| 3486 | var cur_len: u64 = 1; | ||
| 3487 | while (cur_ty.zigTypeTag(zcu) == .Array) { | ||
| 3488 | cur_len *= cur_ty.arrayLenIncludingSentinel(zcu); | ||
| 3489 | cur_ty = cur_ty.childType(zcu); | ||
| 3490 | } | ||
| 3491 | return .{ cur_ty, cur_len }; | ||
| 3492 | } | ||
| 3493 | |||
| 3494 | pub fn packedStructFieldPtrInfo(struct_ty: Type, parent_ptr_ty: Type, field_idx: u32, zcu: *Zcu) union(enum) { | ||
| 3495 | /// The result is a bit-pointer with the same value and a new packed offset. | ||
| 3496 | bit_ptr: InternPool.Key.PtrType.PackedOffset, | ||
| 3497 | /// The result is a standard pointer. | ||
| 3498 | byte_ptr: struct { | ||
| 3499 | /// The byte offset of the field pointer from the parent pointer value. | ||
| 3500 | offset: u64, | ||
| 3501 | /// The alignment of the field pointer type. | ||
| 3502 | alignment: InternPool.Alignment, | ||
| 3503 | }, | ||
| 3504 | } { | ||
| 3505 | comptime assert(Type.packed_struct_layout_version == 2); | ||
| 3506 | |||
| 3507 | const parent_ptr_info = parent_ptr_ty.ptrInfo(zcu); | ||
| 3508 | const field_ty = struct_ty.structFieldType(field_idx, zcu); | ||
| 3509 | |||
| 3510 | var bit_offset: u16 = 0; | ||
| 3511 | var running_bits: u16 = 0; | ||
| 3512 | for (0..struct_ty.structFieldCount(zcu)) |i| { | ||
| 3513 | const f_ty = struct_ty.structFieldType(i, zcu); | ||
| 3514 | if (i == field_idx) { | ||
| 3515 | bit_offset = running_bits; | ||
| 3516 | } | ||
| 3517 | running_bits += @intCast(f_ty.bitSize(zcu)); | ||
| 3518 | } | ||
| 3519 | |||
| 3520 | const res_host_size: u16, const res_bit_offset: u16 = if (parent_ptr_info.packed_offset.host_size != 0) | ||
| 3521 | .{ parent_ptr_info.packed_offset.host_size, parent_ptr_info.packed_offset.bit_offset + bit_offset } | ||
| 3522 | else | ||
| 3523 | .{ (running_bits + 7) / 8, bit_offset }; | ||
| 3524 | |||
| 3525 | // If the field happens to be byte-aligned, simplify the pointer type. | ||
| 3526 | // We can only do this if the pointee's bit size matches its ABI byte size, | ||
| 3527 | // so that loads and stores do not interfere with surrounding packed bits. | ||
| 3528 | // | ||
| 3529 | // TODO: we do not attempt this with big-endian targets yet because of nested | ||
| 3530 | // structs and floats. I need to double-check the desired behavior for big endian | ||
| 3531 | // targets before adding the necessary complications to this code. This will not | ||
| 3532 | // cause miscompilations; it only means the field pointer uses bit masking when it | ||
| 3533 | // might not be strictly necessary. | ||
| 3534 | if (res_bit_offset % 8 == 0 and field_ty.bitSize(zcu) == field_ty.abiSize(zcu) * 8 and zcu.getTarget().cpu.arch.endian() == .little) { | ||
| 3535 | const byte_offset = res_bit_offset / 8; | ||
| 3536 | const new_align = Alignment.fromLog2Units(@ctz(byte_offset | parent_ptr_ty.ptrAlignment(zcu).toByteUnits().?)); | ||
| 3537 | return .{ .byte_ptr = .{ | ||
| 3538 | .offset = byte_offset, | ||
| 3539 | .alignment = new_align, | ||
| 3540 | } }; | ||
| 3541 | } | ||
| 3542 | |||
| 3543 | return .{ .bit_ptr = .{ | ||
| 3544 | .host_size = res_host_size, | ||
| 3545 | .bit_offset = res_bit_offset, | ||
| 3546 | } }; | ||
| 3547 | } | ||
| 3548 | |||
| 3549 | pub const @"u1": Type = .{ .ip_index = .u1_type }; | ||
| 3550 | pub const @"u8": Type = .{ .ip_index = .u8_type }; | ||
| 3551 | pub const @"u16": Type = .{ .ip_index = .u16_type }; | ||
| 3552 | pub const @"u29": Type = .{ .ip_index = .u29_type }; | ||
| 3553 | pub const @"u32": Type = .{ .ip_index = .u32_type }; | ||
| 3554 | pub const @"u64": Type = .{ .ip_index = .u64_type }; | ||
| 3555 | pub const @"u128": Type = .{ .ip_index = .u128_type }; | ||
| 3556 | |||
| 3557 | pub const @"i8": Type = .{ .ip_index = .i8_type }; | ||
| 3558 | pub const @"i16": Type = .{ .ip_index = .i16_type }; | ||
| 3559 | pub const @"i32": Type = .{ .ip_index = .i32_type }; | ||
| 3560 | pub const @"i64": Type = .{ .ip_index = .i64_type }; | ||
| 3561 | pub const @"i128": Type = .{ .ip_index = .i128_type }; | ||
| 3562 | |||
| 3563 | pub const @"f16": Type = .{ .ip_index = .f16_type }; | ||
| 3564 | pub const @"f32": Type = .{ .ip_index = .f32_type }; | ||
| 3565 | pub const @"f64": Type = .{ .ip_index = .f64_type }; | ||
| 3566 | pub const @"f80": Type = .{ .ip_index = .f80_type }; | ||
| 3567 | pub const @"f128": Type = .{ .ip_index = .f128_type }; | ||
| 3568 | |||
| 3569 | pub const @"bool": Type = .{ .ip_index = .bool_type }; | ||
| 3570 | pub const @"usize": Type = .{ .ip_index = .usize_type }; | ||
| 3571 | pub const @"isize": Type = .{ .ip_index = .isize_type }; | ||
| 3572 | pub const @"comptime_int": Type = .{ .ip_index = .comptime_int_type }; | ||
| 3573 | pub const @"comptime_float": Type = .{ .ip_index = .comptime_float_type }; | ||
| 3574 | pub const @"void": Type = .{ .ip_index = .void_type }; | ||
| 3575 | pub const @"type": Type = .{ .ip_index = .type_type }; | ||
| 3576 | pub const @"anyerror": Type = .{ .ip_index = .anyerror_type }; | ||
| 3577 | pub const @"anyopaque": Type = .{ .ip_index = .anyopaque_type }; | ||
| 3578 | pub const @"anyframe": Type = .{ .ip_index = .anyframe_type }; | ||
| 3579 | pub const @"null": Type = .{ .ip_index = .null_type }; | ||
| 3580 | pub const @"undefined": Type = .{ .ip_index = .undefined_type }; | ||
| 3581 | pub const @"noreturn": Type = .{ .ip_index = .noreturn_type }; | ||
| 3582 | |||
| 3583 | pub const @"c_char": Type = .{ .ip_index = .c_char_type }; | ||
| 3584 | pub const @"c_short": Type = .{ .ip_index = .c_short_type }; | ||
| 3585 | pub const @"c_ushort": Type = .{ .ip_index = .c_ushort_type }; | ||
| 3586 | pub const @"c_int": Type = .{ .ip_index = .c_int_type }; | ||
| 3587 | pub const @"c_uint": Type = .{ .ip_index = .c_uint_type }; | ||
| 3588 | pub const @"c_long": Type = .{ .ip_index = .c_long_type }; | ||
| 3589 | pub const @"c_ulong": Type = .{ .ip_index = .c_ulong_type }; | ||
| 3590 | pub const @"c_longlong": Type = .{ .ip_index = .c_longlong_type }; | ||
| 3591 | pub const @"c_ulonglong": Type = .{ .ip_index = .c_ulonglong_type }; | ||
| 3592 | pub const @"c_longdouble": Type = .{ .ip_index = .c_longdouble_type }; | ||
| 3593 | |||
| 3594 | pub const slice_const_u8: Type = .{ .ip_index = .slice_const_u8_type }; | ||
| 3595 | pub const manyptr_u8: Type = .{ .ip_index = .manyptr_u8_type }; | ||
| 3596 | pub const single_const_pointer_to_comptime_int: Type = .{ | ||
| 3597 | .ip_index = .single_const_pointer_to_comptime_int_type, | ||
| 3598 | }; | ||
| 3599 | pub const slice_const_u8_sentinel_0: Type = .{ .ip_index = .slice_const_u8_sentinel_0_type }; | ||
| 3600 | pub const empty_struct_literal: Type = .{ .ip_index = .empty_struct_type }; | ||
| 3601 | |||
| 3602 | pub const generic_poison: Type = .{ .ip_index = .generic_poison_type }; | ||
| 3603 | |||
| 3604 | pub fn smallestUnsignedBits(max: u64) u16 { | ||
| 3605 | if (max == 0) return 0; | ||
| 3606 | const base = std.math.log2(max); | ||
| 3607 | const upper = (@as(u64, 1) << @as(u6, @intCast(base))) - 1; | ||
| 3608 | return @as(u16, @intCast(base + @intFromBool(upper < max))); | ||
| 3609 | } | ||
| 3610 | |||
| 3611 | /// This is only used for comptime asserts. Bump this number when you make a change | ||
| 3612 | /// to packed struct layout to find out all the places in the codebase you need to edit! | ||
| 3613 | pub const packed_struct_layout_version = 2; | ||
| 3614 | |||
| 3615 | fn cTypeAlign(target: Target, c_type: Target.CType) Alignment { | ||
| 3616 | return Alignment.fromByteUnits(target.c_type_alignment(c_type)); | ||
| 3617 | } | ||
src/Value.zig+1-1| ... | @@ -1,6 +1,6 @@ | ... | @@ -1,6 +1,6 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | const builtin = @import("builtin"); | 2 | const builtin = @import("builtin"); |
| 3 | const Type = @import("type.zig").Type; | 3 | const Type = @import("Type.zig"); |
| 4 | const assert = std.debug.assert; | 4 | const assert = std.debug.assert; |
| 5 | const BigIntConst = std.math.big.int.Const; | 5 | const BigIntConst = std.math.big.int.Const; |
| 6 | const BigIntMutable = std.math.big.int.Mutable; | 6 | const BigIntMutable = std.math.big.int.Mutable; |
src/Zcu.zig+1-1| ... | @@ -20,7 +20,7 @@ const Zcu = @This(); | ... | @@ -20,7 +20,7 @@ const Zcu = @This(); |
| 20 | const Compilation = @import("Compilation.zig"); | 20 | const Compilation = @import("Compilation.zig"); |
| 21 | const Cache = std.Build.Cache; | 21 | const Cache = std.Build.Cache; |
| 22 | const Value = @import("Value.zig"); | 22 | const Value = @import("Value.zig"); |
| 23 | const Type = @import("type.zig").Type; | 23 | const Type = @import("Type.zig"); |
| 24 | const Package = @import("Package.zig"); | 24 | const Package = @import("Package.zig"); |
| 25 | const link = @import("link.zig"); | 25 | const link = @import("link.zig"); |
| 26 | const Air = @import("Air.zig"); | 26 | const Air = @import("Air.zig"); |
src/arch/aarch64/CodeGen.zig+1-1| ... | @@ -8,7 +8,7 @@ const Air = @import("../../Air.zig"); | ... | @@ -8,7 +8,7 @@ const Air = @import("../../Air.zig"); |
| 8 | const Mir = @import("Mir.zig"); | 8 | const Mir = @import("Mir.zig"); |
| 9 | const Emit = @import("Emit.zig"); | 9 | const Emit = @import("Emit.zig"); |
| 10 | const Liveness = @import("../../Liveness.zig"); | 10 | const Liveness = @import("../../Liveness.zig"); |
| 11 | const Type = @import("../../type.zig").Type; | 11 | const Type = @import("../../Type.zig"); |
| 12 | const Value = @import("../../Value.zig"); | 12 | const Value = @import("../../Value.zig"); |
| 13 | const link = @import("../../link.zig"); | 13 | const link = @import("../../link.zig"); |
| 14 | const Zcu = @import("../../Zcu.zig"); | 14 | const Zcu = @import("../../Zcu.zig"); |
src/arch/aarch64/abi.zig+1-1| ... | @@ -3,7 +3,7 @@ const builtin = @import("builtin"); | ... | @@ -3,7 +3,7 @@ const builtin = @import("builtin"); |
| 3 | const bits = @import("bits.zig"); | 3 | const bits = @import("bits.zig"); |
| 4 | const Register = bits.Register; | 4 | const Register = bits.Register; |
| 5 | const RegisterManagerFn = @import("../../register_manager.zig").RegisterManager; | 5 | const RegisterManagerFn = @import("../../register_manager.zig").RegisterManager; |
| 6 | const Type = @import("../../type.zig").Type; | 6 | const Type = @import("../../Type.zig"); |
| 7 | const Zcu = @import("../../Zcu.zig"); | 7 | const Zcu = @import("../../Zcu.zig"); |
| 8 | /// Deprecated. | 8 | /// Deprecated. |
| 9 | const Module = Zcu; | 9 | const Module = Zcu; |
src/arch/arm/CodeGen.zig+1-1| ... | @@ -8,7 +8,7 @@ const Air = @import("../../Air.zig"); | ... | @@ -8,7 +8,7 @@ const Air = @import("../../Air.zig"); |
| 8 | const Mir = @import("Mir.zig"); | 8 | const Mir = @import("Mir.zig"); |
| 9 | const Emit = @import("Emit.zig"); | 9 | const Emit = @import("Emit.zig"); |
| 10 | const Liveness = @import("../../Liveness.zig"); | 10 | const Liveness = @import("../../Liveness.zig"); |
| 11 | const Type = @import("../../type.zig").Type; | 11 | const Type = @import("../../Type.zig"); |
| 12 | const Value = @import("../../Value.zig"); | 12 | const Value = @import("../../Value.zig"); |
| 13 | const link = @import("../../link.zig"); | 13 | const link = @import("../../link.zig"); |
| 14 | const Zcu = @import("../../Zcu.zig"); | 14 | const Zcu = @import("../../Zcu.zig"); |
src/arch/arm/Emit.zig+1-1| ... | @@ -11,7 +11,7 @@ const link = @import("../../link.zig"); | ... | @@ -11,7 +11,7 @@ const link = @import("../../link.zig"); |
| 11 | const Zcu = @import("../../Zcu.zig"); | 11 | const Zcu = @import("../../Zcu.zig"); |
| 12 | /// Deprecated. | 12 | /// Deprecated. |
| 13 | const Module = Zcu; | 13 | const Module = Zcu; |
| 14 | const Type = @import("../../type.zig").Type; | 14 | const Type = @import("../../Type.zig"); |
| 15 | const ErrorMsg = Module.ErrorMsg; | 15 | const ErrorMsg = Module.ErrorMsg; |
| 16 | const Target = std.Target; | 16 | const Target = std.Target; |
| 17 | const assert = std.debug.assert; | 17 | const assert = std.debug.assert; |
src/arch/arm/abi.zig+1-1| ... | @@ -3,7 +3,7 @@ const assert = std.debug.assert; | ... | @@ -3,7 +3,7 @@ const assert = std.debug.assert; |
| 3 | const bits = @import("bits.zig"); | 3 | const bits = @import("bits.zig"); |
| 4 | const Register = bits.Register; | 4 | const Register = bits.Register; |
| 5 | const RegisterManagerFn = @import("../../register_manager.zig").RegisterManager; | 5 | const RegisterManagerFn = @import("../../register_manager.zig").RegisterManager; |
| 6 | const Type = @import("../../type.zig").Type; | 6 | const Type = @import("../../Type.zig"); |
| 7 | const Zcu = @import("../../Zcu.zig"); | 7 | const Zcu = @import("../../Zcu.zig"); |
| 8 | /// Deprecated. | 8 | /// Deprecated. |
| 9 | const Module = Zcu; | 9 | const Module = Zcu; |
src/arch/riscv64/CodeGen.zig+1-1| ... | @@ -7,7 +7,7 @@ const Air = @import("../../Air.zig"); | ... | @@ -7,7 +7,7 @@ const Air = @import("../../Air.zig"); |
| 7 | const Mir = @import("Mir.zig"); | 7 | const Mir = @import("Mir.zig"); |
| 8 | const Emit = @import("Emit.zig"); | 8 | const Emit = @import("Emit.zig"); |
| 9 | const Liveness = @import("../../Liveness.zig"); | 9 | const Liveness = @import("../../Liveness.zig"); |
| 10 | const Type = @import("../../type.zig").Type; | 10 | const Type = @import("../../Type.zig"); |
| 11 | const Value = @import("../../Value.zig"); | 11 | const Value = @import("../../Value.zig"); |
| 12 | const link = @import("../../link.zig"); | 12 | const link = @import("../../link.zig"); |
| 13 | const Zcu = @import("../../Zcu.zig"); | 13 | const Zcu = @import("../../Zcu.zig"); |
src/arch/riscv64/Mir.zig+1-1| ... | @@ -431,7 +431,7 @@ pub const RegisterList = struct { | ... | @@ -431,7 +431,7 @@ pub const RegisterList = struct { |
| 431 | const Mir = @This(); | 431 | const Mir = @This(); |
| 432 | const std = @import("std"); | 432 | const std = @import("std"); |
| 433 | const builtin = @import("builtin"); | 433 | const builtin = @import("builtin"); |
| 434 | const Type = @import("../../type.zig").Type; | 434 | const Type = @import("../../Type.zig"); |
| 435 | 435 | ||
| 436 | const assert = std.debug.assert; | 436 | const assert = std.debug.assert; |
| 437 | 437 |
src/arch/riscv64/abi.zig+1-1| ... | @@ -2,7 +2,7 @@ const std = @import("std"); | ... | @@ -2,7 +2,7 @@ const std = @import("std"); |
| 2 | const bits = @import("bits.zig"); | 2 | const bits = @import("bits.zig"); |
| 3 | const Register = bits.Register; | 3 | const Register = bits.Register; |
| 4 | const RegisterManagerFn = @import("../../register_manager.zig").RegisterManager; | 4 | const RegisterManagerFn = @import("../../register_manager.zig").RegisterManager; |
| 5 | const Type = @import("../../type.zig").Type; | 5 | const Type = @import("../../Type.zig"); |
| 6 | const InternPool = @import("../../InternPool.zig"); | 6 | const InternPool = @import("../../InternPool.zig"); |
| 7 | const Zcu = @import("../../Zcu.zig"); | 7 | const Zcu = @import("../../Zcu.zig"); |
| 8 | const assert = std.debug.assert; | 8 | const assert = std.debug.assert; |
src/arch/sparc64/CodeGen.zig+1-1| ... | @@ -21,7 +21,7 @@ const Air = @import("../../Air.zig"); | ... | @@ -21,7 +21,7 @@ const Air = @import("../../Air.zig"); |
| 21 | const Mir = @import("Mir.zig"); | 21 | const Mir = @import("Mir.zig"); |
| 22 | const Emit = @import("Emit.zig"); | 22 | const Emit = @import("Emit.zig"); |
| 23 | const Liveness = @import("../../Liveness.zig"); | 23 | const Liveness = @import("../../Liveness.zig"); |
| 24 | const Type = @import("../../type.zig").Type; | 24 | const Type = @import("../../Type.zig"); |
| 25 | const CodeGenError = codegen.CodeGenError; | 25 | const CodeGenError = codegen.CodeGenError; |
| 26 | const Result = @import("../../codegen.zig").Result; | 26 | const Result = @import("../../codegen.zig").Result; |
| 27 | const DebugInfoOutput = @import("../../codegen.zig").DebugInfoOutput; | 27 | const DebugInfoOutput = @import("../../codegen.zig").DebugInfoOutput; |
src/arch/wasm/CodeGen.zig+1-1| ... | @@ -13,7 +13,7 @@ const codegen = @import("../../codegen.zig"); | ... | @@ -13,7 +13,7 @@ const codegen = @import("../../codegen.zig"); |
| 13 | const Zcu = @import("../../Zcu.zig"); | 13 | const Zcu = @import("../../Zcu.zig"); |
| 14 | const InternPool = @import("../../InternPool.zig"); | 14 | const InternPool = @import("../../InternPool.zig"); |
| 15 | const Decl = Zcu.Decl; | 15 | const Decl = Zcu.Decl; |
| 16 | const Type = @import("../../type.zig").Type; | 16 | const Type = @import("../../Type.zig"); |
| 17 | const Value = @import("../../Value.zig"); | 17 | const Value = @import("../../Value.zig"); |
| 18 | const Compilation = @import("../../Compilation.zig"); | 18 | const Compilation = @import("../../Compilation.zig"); |
| 19 | const link = @import("../../link.zig"); | 19 | const link = @import("../../link.zig"); |
src/arch/wasm/abi.zig+1-1| ... | @@ -8,7 +8,7 @@ const std = @import("std"); | ... | @@ -8,7 +8,7 @@ const std = @import("std"); |
| 8 | const Target = std.Target; | 8 | const Target = std.Target; |
| 9 | const assert = std.debug.assert; | 9 | const assert = std.debug.assert; |
| 10 | 10 | ||
| 11 | const Type = @import("../../type.zig").Type; | 11 | const Type = @import("../../Type.zig"); |
| 12 | const Zcu = @import("../../Zcu.zig"); | 12 | const Zcu = @import("../../Zcu.zig"); |
| 13 | 13 | ||
| 14 | /// Defines how to pass a type as part of a function signature, | 14 | /// Defines how to pass a type as part of a function signature, |
src/arch/x86_64/CodeGen.zig+1-1| ... | @@ -32,7 +32,7 @@ const Module = Zcu; | ... | @@ -32,7 +32,7 @@ const Module = Zcu; |
| 32 | const InternPool = @import("../../InternPool.zig"); | 32 | const InternPool = @import("../../InternPool.zig"); |
| 33 | const Alignment = InternPool.Alignment; | 33 | const Alignment = InternPool.Alignment; |
| 34 | const Target = std.Target; | 34 | const Target = std.Target; |
| 35 | const Type = @import("../../type.zig").Type; | 35 | const Type = @import("../../Type.zig"); |
| 36 | const Value = @import("../../Value.zig"); | 36 | const Value = @import("../../Value.zig"); |
| 37 | const Instruction = @import("encoder.zig").Instruction; | 37 | const Instruction = @import("encoder.zig").Instruction; |
| 38 | 38 |
src/arch/x86_64/abi.zig+1-1| ... | @@ -537,6 +537,6 @@ const testing = std.testing; | ... | @@ -537,6 +537,6 @@ const testing = std.testing; |
| 537 | const InternPool = @import("../../InternPool.zig"); | 537 | const InternPool = @import("../../InternPool.zig"); |
| 538 | const Register = @import("bits.zig").Register; | 538 | const Register = @import("bits.zig").Register; |
| 539 | const RegisterManagerFn = @import("../../register_manager.zig").RegisterManager; | 539 | const RegisterManagerFn = @import("../../register_manager.zig").RegisterManager; |
| 540 | const Type = @import("../../type.zig").Type; | 540 | const Type = @import("../../Type.zig"); |
| 541 | const Value = @import("../../Value.zig"); | 541 | const Value = @import("../../Value.zig"); |
| 542 | const Zcu = @import("../../Zcu.zig"); | 542 | const Zcu = @import("../../Zcu.zig"); |
src/codegen.zig+1-1| ... | @@ -20,7 +20,7 @@ const Zcu = @import("Zcu.zig"); | ... | @@ -20,7 +20,7 @@ const Zcu = @import("Zcu.zig"); |
| 20 | /// Deprecated. | 20 | /// Deprecated. |
| 21 | const Module = Zcu; | 21 | const Module = Zcu; |
| 22 | const Target = std.Target; | 22 | const Target = std.Target; |
| 23 | const Type = @import("type.zig").Type; | 23 | const Type = @import("Type.zig"); |
| 24 | const Value = @import("Value.zig"); | 24 | const Value = @import("Value.zig"); |
| 25 | const Zir = std.zig.Zir; | 25 | const Zir = std.zig.Zir; |
| 26 | const Alignment = InternPool.Alignment; | 26 | const Alignment = InternPool.Alignment; |
src/codegen/c.zig+1-1| ... | @@ -9,7 +9,7 @@ const Zcu = @import("../Zcu.zig"); | ... | @@ -9,7 +9,7 @@ const Zcu = @import("../Zcu.zig"); |
| 9 | const Module = @import("../Package/Module.zig"); | 9 | const Module = @import("../Package/Module.zig"); |
| 10 | const Compilation = @import("../Compilation.zig"); | 10 | const Compilation = @import("../Compilation.zig"); |
| 11 | const Value = @import("../Value.zig"); | 11 | const Value = @import("../Value.zig"); |
| 12 | const Type = @import("../type.zig").Type; | 12 | const Type = @import("../Type.zig"); |
| 13 | const C = link.File.C; | 13 | const C = link.File.C; |
| 14 | const Decl = Zcu.Decl; | 14 | const Decl = Zcu.Decl; |
| 15 | const trace = @import("../tracy.zig").trace; | 15 | const trace = @import("../tracy.zig").trace; |
src/codegen/c/Type.zig+1-1| ... | @@ -2583,6 +2583,6 @@ const assert = std.debug.assert; | ... | @@ -2583,6 +2583,6 @@ const assert = std.debug.assert; |
| 2583 | const CType = @This(); | 2583 | const CType = @This(); |
| 2584 | const Module = @import("../../Package/Module.zig"); | 2584 | const Module = @import("../../Package/Module.zig"); |
| 2585 | const std = @import("std"); | 2585 | const std = @import("std"); |
| 2586 | const Type = @import("../../type.zig").Type; | 2586 | const Type = @import("../../Type.zig"); |
| 2587 | const Zcu = @import("../../Zcu.zig"); | 2587 | const Zcu = @import("../../Zcu.zig"); |
| 2588 | const DeclIndex = @import("../../InternPool.zig").DeclIndex; | 2588 | const DeclIndex = @import("../../InternPool.zig").DeclIndex; |
src/codegen/llvm.zig+1-1| ... | @@ -22,7 +22,7 @@ const Package = @import("../Package.zig"); | ... | @@ -22,7 +22,7 @@ const Package = @import("../Package.zig"); |
| 22 | const Air = @import("../Air.zig"); | 22 | const Air = @import("../Air.zig"); |
| 23 | const Liveness = @import("../Liveness.zig"); | 23 | const Liveness = @import("../Liveness.zig"); |
| 24 | const Value = @import("../Value.zig"); | 24 | const Value = @import("../Value.zig"); |
| 25 | const Type = @import("../type.zig").Type; | 25 | const Type = @import("../Type.zig"); |
| 26 | const x86_64_abi = @import("../arch/x86_64/abi.zig"); | 26 | const x86_64_abi = @import("../arch/x86_64/abi.zig"); |
| 27 | const wasm_c_abi = @import("../arch/wasm/abi.zig"); | 27 | const wasm_c_abi = @import("../arch/wasm/abi.zig"); |
| 28 | const aarch64_c_abi = @import("../arch/aarch64/abi.zig"); | 28 | const aarch64_c_abi = @import("../arch/aarch64/abi.zig"); |
src/codegen/spirv.zig+1-1| ... | @@ -9,7 +9,7 @@ const Zcu = @import("../Zcu.zig"); | ... | @@ -9,7 +9,7 @@ const Zcu = @import("../Zcu.zig"); |
| 9 | /// Deprecated. | 9 | /// Deprecated. |
| 10 | const Module = Zcu; | 10 | const Module = Zcu; |
| 11 | const Decl = Module.Decl; | 11 | const Decl = Module.Decl; |
| 12 | const Type = @import("../type.zig").Type; | 12 | const Type = @import("../Type.zig"); |
| 13 | const Value = @import("../Value.zig"); | 13 | const Value = @import("../Value.zig"); |
| 14 | const Air = @import("../Air.zig"); | 14 | const Air = @import("../Air.zig"); |
| 15 | const Liveness = @import("../Liveness.zig"); | 15 | const Liveness = @import("../Liveness.zig"); |
src/link.zig+1-1| ... | @@ -18,7 +18,7 @@ const Zcu = @import("Zcu.zig"); | ... | @@ -18,7 +18,7 @@ const Zcu = @import("Zcu.zig"); |
| 18 | /// Deprecated. | 18 | /// Deprecated. |
| 19 | const Module = Zcu; | 19 | const Module = Zcu; |
| 20 | const InternPool = @import("InternPool.zig"); | 20 | const InternPool = @import("InternPool.zig"); |
| 21 | const Type = @import("type.zig").Type; | 21 | const Type = @import("Type.zig"); |
| 22 | const Value = @import("Value.zig"); | 22 | const Value = @import("Value.zig"); |
| 23 | const LlvmObject = @import("codegen/llvm.zig").Object; | 23 | const LlvmObject = @import("codegen/llvm.zig").Object; |
| 24 | const lldMain = @import("main.zig").lldMain; | 24 | const lldMain = @import("main.zig").lldMain; |
src/link/C.zig+1-1| ... | @@ -14,7 +14,7 @@ const Compilation = @import("../Compilation.zig"); | ... | @@ -14,7 +14,7 @@ const Compilation = @import("../Compilation.zig"); |
| 14 | const codegen = @import("../codegen/c.zig"); | 14 | const codegen = @import("../codegen/c.zig"); |
| 15 | const link = @import("../link.zig"); | 15 | const link = @import("../link.zig"); |
| 16 | const trace = @import("../tracy.zig").trace; | 16 | const trace = @import("../tracy.zig").trace; |
| 17 | const Type = @import("../type.zig").Type; | 17 | const Type = @import("../Type.zig"); |
| 18 | const Value = @import("../Value.zig"); | 18 | const Value = @import("../Value.zig"); |
| 19 | const Air = @import("../Air.zig"); | 19 | const Air = @import("../Air.zig"); |
| 20 | const Liveness = @import("../Liveness.zig"); | 20 | const Liveness = @import("../Liveness.zig"); |
src/link/Coff.zig+1-1| ... | @@ -2742,7 +2742,7 @@ const Object = @import("Coff/Object.zig"); | ... | @@ -2742,7 +2742,7 @@ const Object = @import("Coff/Object.zig"); |
| 2742 | const Relocation = @import("Coff/Relocation.zig"); | 2742 | const Relocation = @import("Coff/Relocation.zig"); |
| 2743 | const TableSection = @import("table_section.zig").TableSection; | 2743 | const TableSection = @import("table_section.zig").TableSection; |
| 2744 | const StringTable = @import("StringTable.zig"); | 2744 | const StringTable = @import("StringTable.zig"); |
| 2745 | const Type = @import("../type.zig").Type; | 2745 | const Type = @import("../Type.zig"); |
| 2746 | const Value = @import("../Value.zig"); | 2746 | const Value = @import("../Value.zig"); |
| 2747 | const AnalUnit = InternPool.AnalUnit; | 2747 | const AnalUnit = InternPool.AnalUnit; |
| 2748 | 2748 |
src/link/Dwarf.zig+1-1| ... | @@ -2969,5 +2969,5 @@ const Zcu = @import("../Zcu.zig"); | ... | @@ -2969,5 +2969,5 @@ const Zcu = @import("../Zcu.zig"); |
| 2969 | const Module = Zcu; | 2969 | const Module = Zcu; |
| 2970 | const InternPool = @import("../InternPool.zig"); | 2970 | const InternPool = @import("../InternPool.zig"); |
| 2971 | const StringTable = @import("StringTable.zig"); | 2971 | const StringTable = @import("StringTable.zig"); |
| 2972 | const Type = @import("../type.zig").Type; | 2972 | const Type = @import("../Type.zig"); |
| 2973 | const Value = @import("../Value.zig"); | 2973 | const Value = @import("../Value.zig"); |
src/link/Elf/ZigObject.zig+1-1| ... | @@ -1647,7 +1647,7 @@ const Module = Zcu; | ... | @@ -1647,7 +1647,7 @@ const Module = Zcu; |
| 1647 | const Object = @import("Object.zig"); | 1647 | const Object = @import("Object.zig"); |
| 1648 | const Symbol = @import("Symbol.zig"); | 1648 | const Symbol = @import("Symbol.zig"); |
| 1649 | const StringTable = @import("../StringTable.zig"); | 1649 | const StringTable = @import("../StringTable.zig"); |
| 1650 | const Type = @import("../../type.zig").Type; | 1650 | const Type = @import("../../Type.zig"); |
| 1651 | const Value = @import("../../Value.zig"); | 1651 | const Value = @import("../../Value.zig"); |
| 1652 | const AnalUnit = InternPool.AnalUnit; | 1652 | const AnalUnit = InternPool.AnalUnit; |
| 1653 | const ZigObject = @This(); | 1653 | const ZigObject = @This(); |
src/link/MachO/DebugSymbols.zig+1-1| ... | @@ -459,4 +459,4 @@ const trace = @import("../../tracy.zig").trace; | ... | @@ -459,4 +459,4 @@ const trace = @import("../../tracy.zig").trace; |
| 459 | const Allocator = mem.Allocator; | 459 | const Allocator = mem.Allocator; |
| 460 | const MachO = @import("../MachO.zig"); | 460 | const MachO = @import("../MachO.zig"); |
| 461 | const StringTable = @import("../StringTable.zig"); | 461 | const StringTable = @import("../StringTable.zig"); |
| 462 | const Type = @import("../../type.zig").Type; | 462 | const Type = @import("../../Type.zig"); |
src/link/MachO/ZigObject.zig+1-1| ... | @@ -1587,7 +1587,7 @@ const Object = @import("Object.zig"); | ... | @@ -1587,7 +1587,7 @@ const Object = @import("Object.zig"); |
| 1587 | const Relocation = @import("Relocation.zig"); | 1587 | const Relocation = @import("Relocation.zig"); |
| 1588 | const Symbol = @import("Symbol.zig"); | 1588 | const Symbol = @import("Symbol.zig"); |
| 1589 | const StringTable = @import("../StringTable.zig"); | 1589 | const StringTable = @import("../StringTable.zig"); |
| 1590 | const Type = @import("../../type.zig").Type; | 1590 | const Type = @import("../../Type.zig"); |
| 1591 | const Value = @import("../../Value.zig"); | 1591 | const Value = @import("../../Value.zig"); |
| 1592 | const AnalUnit = InternPool.AnalUnit; | 1592 | const AnalUnit = InternPool.AnalUnit; |
| 1593 | const ZigObject = @This(); | 1593 | const ZigObject = @This(); |
src/link/Plan9.zig+1-1| ... | @@ -15,7 +15,7 @@ const File = link.File; | ... | @@ -15,7 +15,7 @@ const File = link.File; |
| 15 | const build_options = @import("build_options"); | 15 | const build_options = @import("build_options"); |
| 16 | const Air = @import("../Air.zig"); | 16 | const Air = @import("../Air.zig"); |
| 17 | const Liveness = @import("../Liveness.zig"); | 17 | const Liveness = @import("../Liveness.zig"); |
| 18 | const Type = @import("../type.zig").Type; | 18 | const Type = @import("../Type.zig"); |
| 19 | const Value = @import("../Value.zig"); | 19 | const Value = @import("../Value.zig"); |
| 20 | const AnalUnit = InternPool.AnalUnit; | 20 | const AnalUnit = InternPool.AnalUnit; |
| 21 | 21 |
src/link/Wasm.zig+1-1| ... | @@ -33,7 +33,7 @@ const Zcu = @import("../Zcu.zig"); | ... | @@ -33,7 +33,7 @@ const Zcu = @import("../Zcu.zig"); |
| 33 | const Module = Zcu; | 33 | const Module = Zcu; |
| 34 | const Object = @import("Wasm/Object.zig"); | 34 | const Object = @import("Wasm/Object.zig"); |
| 35 | const Symbol = @import("Wasm/Symbol.zig"); | 35 | const Symbol = @import("Wasm/Symbol.zig"); |
| 36 | const Type = @import("../type.zig").Type; | 36 | const Type = @import("../Type.zig"); |
| 37 | const Value = @import("../Value.zig"); | 37 | const Value = @import("../Value.zig"); |
| 38 | const ZigObject = @import("Wasm/ZigObject.zig"); | 38 | const ZigObject = @import("Wasm/ZigObject.zig"); |
| 39 | 39 |
src/link/Wasm/ZigObject.zig+1-1| ... | @@ -1252,7 +1252,7 @@ const Zcu = @import("../../Zcu.zig"); | ... | @@ -1252,7 +1252,7 @@ const Zcu = @import("../../Zcu.zig"); |
| 1252 | const Module = Zcu; | 1252 | const Module = Zcu; |
| 1253 | const StringTable = @import("../StringTable.zig"); | 1253 | const StringTable = @import("../StringTable.zig"); |
| 1254 | const Symbol = @import("Symbol.zig"); | 1254 | const Symbol = @import("Symbol.zig"); |
| 1255 | const Type = @import("../../type.zig").Type; | 1255 | const Type = @import("../../Type.zig"); |
| 1256 | const Value = @import("../../Value.zig"); | 1256 | const Value = @import("../../Value.zig"); |
| 1257 | const Wasm = @import("../Wasm.zig"); | 1257 | const Wasm = @import("../Wasm.zig"); |
| 1258 | const AnalUnit = InternPool.AnalUnit; | 1258 | const AnalUnit = InternPool.AnalUnit; |
src/mutable_value.zig+1-1| ... | @@ -3,7 +3,7 @@ const assert = std.debug.assert; | ... | @@ -3,7 +3,7 @@ const assert = std.debug.assert; |
| 3 | const Allocator = std.mem.Allocator; | 3 | const Allocator = std.mem.Allocator; |
| 4 | const Zcu = @import("Zcu.zig"); | 4 | const Zcu = @import("Zcu.zig"); |
| 5 | const InternPool = @import("InternPool.zig"); | 5 | const InternPool = @import("InternPool.zig"); |
| 6 | const Type = @import("type.zig").Type; | 6 | const Type = @import("Type.zig"); |
| 7 | const Value = @import("Value.zig"); | 7 | const Value = @import("Value.zig"); |
| 8 | 8 | ||
| 9 | /// We use a tagged union here because while it wastes a few bytes for some tags, having a fixed | 9 | /// We use a tagged union here because while it wastes a few bytes for some tags, having a fixed |
src/print_air.zig+1-1| ... | @@ -4,7 +4,7 @@ const fmtIntSizeBin = std.fmt.fmtIntSizeBin; | ... | @@ -4,7 +4,7 @@ const fmtIntSizeBin = std.fmt.fmtIntSizeBin; |
| 4 | 4 | ||
| 5 | const Zcu = @import("Zcu.zig"); | 5 | const Zcu = @import("Zcu.zig"); |
| 6 | const Value = @import("Value.zig"); | 6 | const Value = @import("Value.zig"); |
| 7 | const Type = @import("type.zig").Type; | 7 | const Type = @import("Type.zig"); |
| 8 | const Air = @import("Air.zig"); | 8 | const Air = @import("Air.zig"); |
| 9 | const Liveness = @import("Liveness.zig"); | 9 | const Liveness = @import("Liveness.zig"); |
| 10 | const InternPool = @import("InternPool.zig"); | 10 | const InternPool = @import("InternPool.zig"); |
src/print_value.zig+1-1| ... | @@ -2,7 +2,7 @@ | ... | @@ -2,7 +2,7 @@ |
| 2 | //! It is a thin wrapper around a `Value` which also, redundantly, stores its `Type`. | 2 | //! It is a thin wrapper around a `Value` which also, redundantly, stores its `Type`. |
| 3 | 3 | ||
| 4 | const std = @import("std"); | 4 | const std = @import("std"); |
| 5 | const Type = @import("type.zig").Type; | 5 | const Type = @import("Type.zig"); |
| 6 | const Value = @import("Value.zig"); | 6 | const Value = @import("Value.zig"); |
| 7 | const Zcu = @import("Zcu.zig"); | 7 | const Zcu = @import("Zcu.zig"); |
| 8 | /// Deprecated. | 8 | /// Deprecated. |
src/register_manager.zig+1-1| ... | @@ -5,7 +5,7 @@ const assert = std.debug.assert; | ... | @@ -5,7 +5,7 @@ const assert = std.debug.assert; |
| 5 | const Allocator = std.mem.Allocator; | 5 | const Allocator = std.mem.Allocator; |
| 6 | const Air = @import("Air.zig"); | 6 | const Air = @import("Air.zig"); |
| 7 | const StaticBitSet = std.bit_set.StaticBitSet; | 7 | const StaticBitSet = std.bit_set.StaticBitSet; |
| 8 | const Type = @import("type.zig").Type; | 8 | const Type = @import("Type.zig"); |
| 9 | const Zcu = @import("Zcu.zig"); | 9 | const Zcu = @import("Zcu.zig"); |
| 10 | /// Deprecated. | 10 | /// Deprecated. |
| 11 | const Module = Zcu; | 11 | const Module = Zcu; |
src/target.zig+1-1| ... | @@ -1,5 +1,5 @@ | ... | @@ -1,5 +1,5 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | const Type = @import("type.zig").Type; | 2 | const Type = @import("Type.zig"); |
| 3 | const AddressSpace = std.builtin.AddressSpace; | 3 | const AddressSpace = std.builtin.AddressSpace; |
| 4 | const Alignment = @import("InternPool.zig").Alignment; | 4 | const Alignment = @import("InternPool.zig").Alignment; |
| 5 | const Feature = @import("Zcu.zig").Feature; | 5 | const Feature = @import("Zcu.zig").Feature; |
src/type.zig deleted-3617| ... | @@ -1,3617 +0,0 @@ | ||
| 1 | const std = @import("std"); | ||
| 2 | const builtin = @import("builtin"); | ||
| 3 | const Value = @import("Value.zig"); | ||
| 4 | const assert = std.debug.assert; | ||
| 5 | const Target = std.Target; | ||
| 6 | const Zcu = @import("Zcu.zig"); | ||
| 7 | /// Deprecated. | ||
| 8 | const Module = Zcu; | ||
| 9 | const log = std.log.scoped(.Type); | ||
| 10 | const target_util = @import("target.zig"); | ||
| 11 | const Sema = @import("Sema.zig"); | ||
| 12 | const InternPool = @import("InternPool.zig"); | ||
| 13 | const Alignment = InternPool.Alignment; | ||
| 14 | const Zir = std.zig.Zir; | ||
| 15 | |||
| 16 | /// Both types and values are canonically represented by a single 32-bit integer | ||
| 17 | /// which is an index into an `InternPool` data structure. | ||
| 18 | /// This struct abstracts around this storage by providing methods only | ||
| 19 | /// applicable to types rather than values in general. | ||
| 20 | pub const Type = struct { | ||
| 21 | ip_index: InternPool.Index, | ||
| 22 | |||
| 23 | pub fn zigTypeTag(ty: Type, mod: *const Module) std.builtin.TypeId { | ||
| 24 | return ty.zigTypeTagOrPoison(mod) catch unreachable; | ||
| 25 | } | ||
| 26 | |||
| 27 | pub fn zigTypeTagOrPoison(ty: Type, mod: *const Module) error{GenericPoison}!std.builtin.TypeId { | ||
| 28 | return mod.intern_pool.zigTypeTagOrPoison(ty.toIntern()); | ||
| 29 | } | ||
| 30 | |||
| 31 | pub fn baseZigTypeTag(self: Type, mod: *Module) std.builtin.TypeId { | ||
| 32 | return switch (self.zigTypeTag(mod)) { | ||
| 33 | .ErrorUnion => self.errorUnionPayload(mod).baseZigTypeTag(mod), | ||
| 34 | .Optional => { | ||
| 35 | return self.optionalChild(mod).baseZigTypeTag(mod); | ||
| 36 | }, | ||
| 37 | else => |t| t, | ||
| 38 | }; | ||
| 39 | } | ||
| 40 | |||
| 41 | pub fn isSelfComparable(ty: Type, mod: *const Module, is_equality_cmp: bool) bool { | ||
| 42 | return switch (ty.zigTypeTag(mod)) { | ||
| 43 | .Int, | ||
| 44 | .Float, | ||
| 45 | .ComptimeFloat, | ||
| 46 | .ComptimeInt, | ||
| 47 | => true, | ||
| 48 | |||
| 49 | .Vector => ty.elemType2(mod).isSelfComparable(mod, is_equality_cmp), | ||
| 50 | |||
| 51 | .Bool, | ||
| 52 | .Type, | ||
| 53 | .Void, | ||
| 54 | .ErrorSet, | ||
| 55 | .Fn, | ||
| 56 | .Opaque, | ||
| 57 | .AnyFrame, | ||
| 58 | .Enum, | ||
| 59 | .EnumLiteral, | ||
| 60 | => is_equality_cmp, | ||
| 61 | |||
| 62 | .NoReturn, | ||
| 63 | .Array, | ||
| 64 | .Struct, | ||
| 65 | .Undefined, | ||
| 66 | .Null, | ||
| 67 | .ErrorUnion, | ||
| 68 | .Union, | ||
| 69 | .Frame, | ||
| 70 | => false, | ||
| 71 | |||
| 72 | .Pointer => !ty.isSlice(mod) and (is_equality_cmp or ty.isCPtr(mod)), | ||
| 73 | .Optional => { | ||
| 74 | if (!is_equality_cmp) return false; | ||
| 75 | return ty.optionalChild(mod).isSelfComparable(mod, is_equality_cmp); | ||
| 76 | }, | ||
| 77 | }; | ||
| 78 | } | ||
| 79 | |||
| 80 | /// If it is a function pointer, returns the function type. Otherwise returns null. | ||
| 81 | pub fn castPtrToFn(ty: Type, mod: *const Module) ?Type { | ||
| 82 | if (ty.zigTypeTag(mod) != .Pointer) return null; | ||
| 83 | const elem_ty = ty.childType(mod); | ||
| 84 | if (elem_ty.zigTypeTag(mod) != .Fn) return null; | ||
| 85 | return elem_ty; | ||
| 86 | } | ||
| 87 | |||
| 88 | /// Asserts the type is a pointer. | ||
| 89 | pub fn ptrIsMutable(ty: Type, mod: *const Module) bool { | ||
| 90 | return !mod.intern_pool.indexToKey(ty.toIntern()).ptr_type.flags.is_const; | ||
| 91 | } | ||
| 92 | |||
| 93 | pub const ArrayInfo = struct { | ||
| 94 | elem_type: Type, | ||
| 95 | sentinel: ?Value = null, | ||
| 96 | len: u64, | ||
| 97 | }; | ||
| 98 | |||
| 99 | pub fn arrayInfo(self: Type, mod: *const Module) ArrayInfo { | ||
| 100 | return .{ | ||
| 101 | .len = self.arrayLen(mod), | ||
| 102 | .sentinel = self.sentinel(mod), | ||
| 103 | .elem_type = self.childType(mod), | ||
| 104 | }; | ||
| 105 | } | ||
| 106 | |||
| 107 | pub fn ptrInfo(ty: Type, mod: *const Module) InternPool.Key.PtrType { | ||
| 108 | return switch (mod.intern_pool.indexToKey(ty.toIntern())) { | ||
| 109 | .ptr_type => |p| p, | ||
| 110 | .opt_type => |child| switch (mod.intern_pool.indexToKey(child)) { | ||
| 111 | .ptr_type => |p| p, | ||
| 112 | else => unreachable, | ||
| 113 | }, | ||
| 114 | else => unreachable, | ||
| 115 | }; | ||
| 116 | } | ||
| 117 | |||
| 118 | pub fn eql(a: Type, b: Type, mod: *const Module) bool { | ||
| 119 | _ = mod; // TODO: remove this parameter | ||
| 120 | // The InternPool data structure hashes based on Key to make interned objects | ||
| 121 | // unique. An Index can be treated simply as u32 value for the | ||
| 122 | // purpose of Type/Value hashing and equality. | ||
| 123 | return a.toIntern() == b.toIntern(); | ||
| 124 | } | ||
| 125 | |||
| 126 | pub fn format(ty: Type, comptime unused_fmt_string: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void { | ||
| 127 | _ = ty; | ||
| 128 | _ = unused_fmt_string; | ||
| 129 | _ = options; | ||
| 130 | _ = writer; | ||
| 131 | @compileError("do not format types directly; use either ty.fmtDebug() or ty.fmt()"); | ||
| 132 | } | ||
| 133 | |||
| 134 | pub const Formatter = std.fmt.Formatter(format2); | ||
| 135 | |||
| 136 | pub fn fmt(ty: Type, module: *Module) Formatter { | ||
| 137 | return .{ .data = .{ | ||
| 138 | .ty = ty, | ||
| 139 | .module = module, | ||
| 140 | } }; | ||
| 141 | } | ||
| 142 | |||
| 143 | const FormatContext = struct { | ||
| 144 | ty: Type, | ||
| 145 | module: *Module, | ||
| 146 | }; | ||
| 147 | |||
| 148 | fn format2( | ||
| 149 | ctx: FormatContext, | ||
| 150 | comptime unused_format_string: []const u8, | ||
| 151 | options: std.fmt.FormatOptions, | ||
| 152 | writer: anytype, | ||
| 153 | ) !void { | ||
| 154 | comptime assert(unused_format_string.len == 0); | ||
| 155 | _ = options; | ||
| 156 | return print(ctx.ty, writer, ctx.module); | ||
| 157 | } | ||
| 158 | |||
| 159 | pub fn fmtDebug(ty: Type) std.fmt.Formatter(dump) { | ||
| 160 | return .{ .data = ty }; | ||
| 161 | } | ||
| 162 | |||
| 163 | /// This is a debug function. In order to print types in a meaningful way | ||
| 164 | /// we also need access to the module. | ||
| 165 | pub fn dump( | ||
| 166 | start_type: Type, | ||
| 167 | comptime unused_format_string: []const u8, | ||
| 168 | options: std.fmt.FormatOptions, | ||
| 169 | writer: anytype, | ||
| 170 | ) @TypeOf(writer).Error!void { | ||
| 171 | _ = options; | ||
| 172 | comptime assert(unused_format_string.len == 0); | ||
| 173 | return writer.print("{any}", .{start_type.ip_index}); | ||
| 174 | } | ||
| 175 | |||
| 176 | /// Prints a name suitable for `@typeName`. | ||
| 177 | /// TODO: take an `opt_sema` to pass to `fmtValue` when printing sentinels. | ||
| 178 | pub fn print(ty: Type, writer: anytype, mod: *Module) @TypeOf(writer).Error!void { | ||
| 179 | const ip = &mod.intern_pool; | ||
| 180 | switch (ip.indexToKey(ty.toIntern())) { | ||
| 181 | .int_type => |int_type| { | ||
| 182 | const sign_char: u8 = switch (int_type.signedness) { | ||
| 183 | .signed => 'i', | ||
| 184 | .unsigned => 'u', | ||
| 185 | }; | ||
| 186 | return writer.print("{c}{d}", .{ sign_char, int_type.bits }); | ||
| 187 | }, | ||
| 188 | .ptr_type => { | ||
| 189 | const info = ty.ptrInfo(mod); | ||
| 190 | |||
| 191 | if (info.sentinel != .none) switch (info.flags.size) { | ||
| 192 | .One, .C => unreachable, | ||
| 193 | .Many => try writer.print("[*:{}]", .{Value.fromInterned(info.sentinel).fmtValue(mod, null)}), | ||
| 194 | .Slice => try writer.print("[:{}]", .{Value.fromInterned(info.sentinel).fmtValue(mod, null)}), | ||
| 195 | } else switch (info.flags.size) { | ||
| 196 | .One => try writer.writeAll("*"), | ||
| 197 | .Many => try writer.writeAll("[*]"), | ||
| 198 | .C => try writer.writeAll("[*c]"), | ||
| 199 | .Slice => try writer.writeAll("[]"), | ||
| 200 | } | ||
| 201 | if (info.flags.alignment != .none or | ||
| 202 | info.packed_offset.host_size != 0 or | ||
| 203 | info.flags.vector_index != .none) | ||
| 204 | { | ||
| 205 | const alignment = if (info.flags.alignment != .none) | ||
| 206 | info.flags.alignment | ||
| 207 | else | ||
| 208 | Type.fromInterned(info.child).abiAlignment(mod); | ||
| 209 | try writer.print("align({d}", .{alignment.toByteUnits() orelse 0}); | ||
| 210 | |||
| 211 | if (info.packed_offset.bit_offset != 0 or info.packed_offset.host_size != 0) { | ||
| 212 | try writer.print(":{d}:{d}", .{ | ||
| 213 | info.packed_offset.bit_offset, info.packed_offset.host_size, | ||
| 214 | }); | ||
| 215 | } | ||
| 216 | if (info.flags.vector_index == .runtime) { | ||
| 217 | try writer.writeAll(":?"); | ||
| 218 | } else if (info.flags.vector_index != .none) { | ||
| 219 | try writer.print(":{d}", .{@intFromEnum(info.flags.vector_index)}); | ||
| 220 | } | ||
| 221 | try writer.writeAll(") "); | ||
| 222 | } | ||
| 223 | if (info.flags.address_space != .generic) { | ||
| 224 | try writer.print("addrspace(.{s}) ", .{@tagName(info.flags.address_space)}); | ||
| 225 | } | ||
| 226 | if (info.flags.is_const) try writer.writeAll("const "); | ||
| 227 | if (info.flags.is_volatile) try writer.writeAll("volatile "); | ||
| 228 | if (info.flags.is_allowzero and info.flags.size != .C) try writer.writeAll("allowzero "); | ||
| 229 | |||
| 230 | try print(Type.fromInterned(info.child), writer, mod); | ||
| 231 | return; | ||
| 232 | }, | ||
| 233 | .array_type => |array_type| { | ||
| 234 | if (array_type.sentinel == .none) { | ||
| 235 | try writer.print("[{d}]", .{array_type.len}); | ||
| 236 | try print(Type.fromInterned(array_type.child), writer, mod); | ||
| 237 | } else { | ||
| 238 | try writer.print("[{d}:{}]", .{ | ||
| 239 | array_type.len, | ||
| 240 | Value.fromInterned(array_type.sentinel).fmtValue(mod, null), | ||
| 241 | }); | ||
| 242 | try print(Type.fromInterned(array_type.child), writer, mod); | ||
| 243 | } | ||
| 244 | return; | ||
| 245 | }, | ||
| 246 | .vector_type => |vector_type| { | ||
| 247 | try writer.print("@Vector({d}, ", .{vector_type.len}); | ||
| 248 | try print(Type.fromInterned(vector_type.child), writer, mod); | ||
| 249 | try writer.writeAll(")"); | ||
| 250 | return; | ||
| 251 | }, | ||
| 252 | .opt_type => |child| { | ||
| 253 | try writer.writeByte('?'); | ||
| 254 | return print(Type.fromInterned(child), writer, mod); | ||
| 255 | }, | ||
| 256 | .error_union_type => |error_union_type| { | ||
| 257 | try print(Type.fromInterned(error_union_type.error_set_type), writer, mod); | ||
| 258 | try writer.writeByte('!'); | ||
| 259 | if (error_union_type.payload_type == .generic_poison_type) { | ||
| 260 | try writer.writeAll("anytype"); | ||
| 261 | } else { | ||
| 262 | try print(Type.fromInterned(error_union_type.payload_type), writer, mod); | ||
| 263 | } | ||
| 264 | return; | ||
| 265 | }, | ||
| 266 | .inferred_error_set_type => |func_index| { | ||
| 267 | try writer.writeAll("@typeInfo(@typeInfo(@TypeOf("); | ||
| 268 | const owner_decl = mod.funcOwnerDeclPtr(func_index); | ||
| 269 | try owner_decl.renderFullyQualifiedName(mod, writer); | ||
| 270 | try writer.writeAll(")).Fn.return_type.?).ErrorUnion.error_set"); | ||
| 271 | }, | ||
| 272 | .error_set_type => |error_set_type| { | ||
| 273 | const names = error_set_type.names; | ||
| 274 | try writer.writeAll("error{"); | ||
| 275 | for (names.get(ip), 0..) |name, i| { | ||
| 276 | if (i != 0) try writer.writeByte(','); | ||
| 277 | try writer.print("{}", .{name.fmt(ip)}); | ||
| 278 | } | ||
| 279 | try writer.writeAll("}"); | ||
| 280 | }, | ||
| 281 | .simple_type => |s| switch (s) { | ||
| 282 | .f16, | ||
| 283 | .f32, | ||
| 284 | .f64, | ||
| 285 | .f80, | ||
| 286 | .f128, | ||
| 287 | .usize, | ||
| 288 | .isize, | ||
| 289 | .c_char, | ||
| 290 | .c_short, | ||
| 291 | .c_ushort, | ||
| 292 | .c_int, | ||
| 293 | .c_uint, | ||
| 294 | .c_long, | ||
| 295 | .c_ulong, | ||
| 296 | .c_longlong, | ||
| 297 | .c_ulonglong, | ||
| 298 | .c_longdouble, | ||
| 299 | .anyopaque, | ||
| 300 | .bool, | ||
| 301 | .void, | ||
| 302 | .type, | ||
| 303 | .anyerror, | ||
| 304 | .comptime_int, | ||
| 305 | .comptime_float, | ||
| 306 | .noreturn, | ||
| 307 | .adhoc_inferred_error_set, | ||
| 308 | => return writer.writeAll(@tagName(s)), | ||
| 309 | |||
| 310 | .null, | ||
| 311 | .undefined, | ||
| 312 | => try writer.print("@TypeOf({s})", .{@tagName(s)}), | ||
| 313 | |||
| 314 | .enum_literal => try writer.print("@TypeOf(.{s})", .{@tagName(s)}), | ||
| 315 | .atomic_order => try writer.writeAll("std.builtin.AtomicOrder"), | ||
| 316 | .atomic_rmw_op => try writer.writeAll("std.builtin.AtomicRmwOp"), | ||
| 317 | .calling_convention => try writer.writeAll("std.builtin.CallingConvention"), | ||
| 318 | .address_space => try writer.writeAll("std.builtin.AddressSpace"), | ||
| 319 | .float_mode => try writer.writeAll("std.builtin.FloatMode"), | ||
| 320 | .reduce_op => try writer.writeAll("std.builtin.ReduceOp"), | ||
| 321 | .call_modifier => try writer.writeAll("std.builtin.CallModifier"), | ||
| 322 | .prefetch_options => try writer.writeAll("std.builtin.PrefetchOptions"), | ||
| 323 | .export_options => try writer.writeAll("std.builtin.ExportOptions"), | ||
| 324 | .extern_options => try writer.writeAll("std.builtin.ExternOptions"), | ||
| 325 | .type_info => try writer.writeAll("std.builtin.Type"), | ||
| 326 | |||
| 327 | .generic_poison => unreachable, | ||
| 328 | }, | ||
| 329 | .struct_type => { | ||
| 330 | const struct_type = ip.loadStructType(ty.toIntern()); | ||
| 331 | if (struct_type.decl.unwrap()) |decl_index| { | ||
| 332 | const decl = mod.declPtr(decl_index); | ||
| 333 | try decl.renderFullyQualifiedName(mod, writer); | ||
| 334 | } else if (ip.loadStructType(ty.toIntern()).namespace.unwrap()) |namespace_index| { | ||
| 335 | const namespace = mod.namespacePtr(namespace_index); | ||
| 336 | try namespace.renderFullyQualifiedName(mod, .empty, writer); | ||
| 337 | } else { | ||
| 338 | try writer.writeAll("@TypeOf(.{})"); | ||
| 339 | } | ||
| 340 | }, | ||
| 341 | .anon_struct_type => |anon_struct| { | ||
| 342 | if (anon_struct.types.len == 0) { | ||
| 343 | return writer.writeAll("@TypeOf(.{})"); | ||
| 344 | } | ||
| 345 | try writer.writeAll("struct{"); | ||
| 346 | for (anon_struct.types.get(ip), anon_struct.values.get(ip), 0..) |field_ty, val, i| { | ||
| 347 | if (i != 0) try writer.writeAll(", "); | ||
| 348 | if (val != .none) { | ||
| 349 | try writer.writeAll("comptime "); | ||
| 350 | } | ||
| 351 | if (anon_struct.names.len != 0) { | ||
| 352 | try writer.print("{}: ", .{anon_struct.names.get(ip)[i].fmt(&mod.intern_pool)}); | ||
| 353 | } | ||
| 354 | |||
| 355 | try print(Type.fromInterned(field_ty), writer, mod); | ||
| 356 | |||
| 357 | if (val != .none) { | ||
| 358 | try writer.print(" = {}", .{Value.fromInterned(val).fmtValue(mod, null)}); | ||
| 359 | } | ||
| 360 | } | ||
| 361 | try writer.writeAll("}"); | ||
| 362 | }, | ||
| 363 | |||
| 364 | .union_type => { | ||
| 365 | const decl = mod.declPtr(ip.loadUnionType(ty.toIntern()).decl); | ||
| 366 | try decl.renderFullyQualifiedName(mod, writer); | ||
| 367 | }, | ||
| 368 | .opaque_type => { | ||
| 369 | const decl = mod.declPtr(ip.loadOpaqueType(ty.toIntern()).decl); | ||
| 370 | try decl.renderFullyQualifiedName(mod, writer); | ||
| 371 | }, | ||
| 372 | .enum_type => { | ||
| 373 | const decl = mod.declPtr(ip.loadEnumType(ty.toIntern()).decl); | ||
| 374 | try decl.renderFullyQualifiedName(mod, writer); | ||
| 375 | }, | ||
| 376 | .func_type => |fn_info| { | ||
| 377 | if (fn_info.is_noinline) { | ||
| 378 | try writer.writeAll("noinline "); | ||
| 379 | } | ||
| 380 | try writer.writeAll("fn ("); | ||
| 381 | const param_types = fn_info.param_types.get(&mod.intern_pool); | ||
| 382 | for (param_types, 0..) |param_ty, i| { | ||
| 383 | if (i != 0) try writer.writeAll(", "); | ||
| 384 | if (std.math.cast(u5, i)) |index| { | ||
| 385 | if (fn_info.paramIsComptime(index)) { | ||
| 386 | try writer.writeAll("comptime "); | ||
| 387 | } | ||
| 388 | if (fn_info.paramIsNoalias(index)) { | ||
| 389 | try writer.writeAll("noalias "); | ||
| 390 | } | ||
| 391 | } | ||
| 392 | if (param_ty == .generic_poison_type) { | ||
| 393 | try writer.writeAll("anytype"); | ||
| 394 | } else { | ||
| 395 | try print(Type.fromInterned(param_ty), writer, mod); | ||
| 396 | } | ||
| 397 | } | ||
| 398 | if (fn_info.is_var_args) { | ||
| 399 | if (param_types.len != 0) { | ||
| 400 | try writer.writeAll(", "); | ||
| 401 | } | ||
| 402 | try writer.writeAll("..."); | ||
| 403 | } | ||
| 404 | try writer.writeAll(") "); | ||
| 405 | if (fn_info.cc != .Unspecified) { | ||
| 406 | try writer.writeAll("callconv(."); | ||
| 407 | try writer.writeAll(@tagName(fn_info.cc)); | ||
| 408 | try writer.writeAll(") "); | ||
| 409 | } | ||
| 410 | if (fn_info.return_type == .generic_poison_type) { | ||
| 411 | try writer.writeAll("anytype"); | ||
| 412 | } else { | ||
| 413 | try print(Type.fromInterned(fn_info.return_type), writer, mod); | ||
| 414 | } | ||
| 415 | }, | ||
| 416 | .anyframe_type => |child| { | ||
| 417 | if (child == .none) return writer.writeAll("anyframe"); | ||
| 418 | try writer.writeAll("anyframe->"); | ||
| 419 | return print(Type.fromInterned(child), writer, mod); | ||
| 420 | }, | ||
| 421 | |||
| 422 | // values, not types | ||
| 423 | .undef, | ||
| 424 | .simple_value, | ||
| 425 | .variable, | ||
| 426 | .extern_func, | ||
| 427 | .func, | ||
| 428 | .int, | ||
| 429 | .err, | ||
| 430 | .error_union, | ||
| 431 | .enum_literal, | ||
| 432 | .enum_tag, | ||
| 433 | .empty_enum_value, | ||
| 434 | .float, | ||
| 435 | .ptr, | ||
| 436 | .slice, | ||
| 437 | .opt, | ||
| 438 | .aggregate, | ||
| 439 | .un, | ||
| 440 | // memoization, not types | ||
| 441 | .memoized_call, | ||
| 442 | => unreachable, | ||
| 443 | } | ||
| 444 | } | ||
| 445 | |||
| 446 | pub fn fromInterned(i: InternPool.Index) Type { | ||
| 447 | assert(i != .none); | ||
| 448 | return .{ .ip_index = i }; | ||
| 449 | } | ||
| 450 | |||
| 451 | pub fn toIntern(ty: Type) InternPool.Index { | ||
| 452 | assert(ty.ip_index != .none); | ||
| 453 | return ty.ip_index; | ||
| 454 | } | ||
| 455 | |||
| 456 | pub fn toValue(self: Type) Value { | ||
| 457 | return Value.fromInterned(self.toIntern()); | ||
| 458 | } | ||
| 459 | |||
| 460 | const RuntimeBitsError = Module.CompileError || error{NeedLazy}; | ||
| 461 | |||
| 462 | /// true if and only if the type takes up space in memory at runtime. | ||
| 463 | /// There are two reasons a type will return false: | ||
| 464 | /// * the type is a comptime-only type. For example, the type `type` itself. | ||
| 465 | /// - note, however, that a struct can have mixed fields and only the non-comptime-only | ||
| 466 | /// fields will count towards the ABI size. For example, `struct {T: type, x: i32}` | ||
| 467 | /// hasRuntimeBits()=true and abiSize()=4 | ||
| 468 | /// * the type has only one possible value, making its ABI size 0. | ||
| 469 | /// - an enum with an explicit tag type has the ABI size of the integer tag type, | ||
| 470 | /// making it one-possible-value only if the integer tag type has 0 bits. | ||
| 471 | /// When `ignore_comptime_only` is true, then types that are comptime-only | ||
| 472 | /// may return false positives. | ||
| 473 | pub fn hasRuntimeBitsAdvanced( | ||
| 474 | ty: Type, | ||
| 475 | mod: *Module, | ||
| 476 | ignore_comptime_only: bool, | ||
| 477 | strat: AbiAlignmentAdvancedStrat, | ||
| 478 | ) RuntimeBitsError!bool { | ||
| 479 | const ip = &mod.intern_pool; | ||
| 480 | return switch (ty.toIntern()) { | ||
| 481 | // False because it is a comptime-only type. | ||
| 482 | .empty_struct_type => false, | ||
| 483 | else => switch (ip.indexToKey(ty.toIntern())) { | ||
| 484 | .int_type => |int_type| int_type.bits != 0, | ||
| 485 | .ptr_type => { | ||
| 486 | // Pointers to zero-bit types still have a runtime address; however, pointers | ||
| 487 | // to comptime-only types do not, with the exception of function pointers. | ||
| 488 | if (ignore_comptime_only) return true; | ||
| 489 | return switch (strat) { | ||
| 490 | .sema => |sema| !(try sema.typeRequiresComptime(ty)), | ||
| 491 | .eager => !comptimeOnly(ty, mod), | ||
| 492 | .lazy => error.NeedLazy, | ||
| 493 | }; | ||
| 494 | }, | ||
| 495 | .anyframe_type => true, | ||
| 496 | .array_type => |array_type| return array_type.lenIncludingSentinel() > 0 and | ||
| 497 | try Type.fromInterned(array_type.child).hasRuntimeBitsAdvanced(mod, ignore_comptime_only, strat), | ||
| 498 | .vector_type => |vector_type| return vector_type.len > 0 and | ||
| 499 | try Type.fromInterned(vector_type.child).hasRuntimeBitsAdvanced(mod, ignore_comptime_only, strat), | ||
| 500 | .opt_type => |child| { | ||
| 501 | const child_ty = Type.fromInterned(child); | ||
| 502 | if (child_ty.isNoReturn(mod)) { | ||
| 503 | // Then the optional is comptime-known to be null. | ||
| 504 | return false; | ||
| 505 | } | ||
| 506 | if (ignore_comptime_only) return true; | ||
| 507 | return switch (strat) { | ||
| 508 | .sema => |sema| !(try sema.typeRequiresComptime(child_ty)), | ||
| 509 | .eager => !comptimeOnly(child_ty, mod), | ||
| 510 | .lazy => error.NeedLazy, | ||
| 511 | }; | ||
| 512 | }, | ||
| 513 | .error_union_type, | ||
| 514 | .error_set_type, | ||
| 515 | .inferred_error_set_type, | ||
| 516 | => true, | ||
| 517 | |||
| 518 | // These are function *bodies*, not pointers. | ||
| 519 | // They return false here because they are comptime-only types. | ||
| 520 | // Special exceptions have to be made when emitting functions due to | ||
| 521 | // this returning false. | ||
| 522 | .func_type => false, | ||
| 523 | |||
| 524 | .simple_type => |t| switch (t) { | ||
| 525 | .f16, | ||
| 526 | .f32, | ||
| 527 | .f64, | ||
| 528 | .f80, | ||
| 529 | .f128, | ||
| 530 | .usize, | ||
| 531 | .isize, | ||
| 532 | .c_char, | ||
| 533 | .c_short, | ||
| 534 | .c_ushort, | ||
| 535 | .c_int, | ||
| 536 | .c_uint, | ||
| 537 | .c_long, | ||
| 538 | .c_ulong, | ||
| 539 | .c_longlong, | ||
| 540 | .c_ulonglong, | ||
| 541 | .c_longdouble, | ||
| 542 | .bool, | ||
| 543 | .anyerror, | ||
| 544 | .adhoc_inferred_error_set, | ||
| 545 | .anyopaque, | ||
| 546 | .atomic_order, | ||
| 547 | .atomic_rmw_op, | ||
| 548 | .calling_convention, | ||
| 549 | .address_space, | ||
| 550 | .float_mode, | ||
| 551 | .reduce_op, | ||
| 552 | .call_modifier, | ||
| 553 | .prefetch_options, | ||
| 554 | .export_options, | ||
| 555 | .extern_options, | ||
| 556 | => true, | ||
| 557 | |||
| 558 | // These are false because they are comptime-only types. | ||
| 559 | .void, | ||
| 560 | .type, | ||
| 561 | .comptime_int, | ||
| 562 | .comptime_float, | ||
| 563 | .noreturn, | ||
| 564 | .null, | ||
| 565 | .undefined, | ||
| 566 | .enum_literal, | ||
| 567 | .type_info, | ||
| 568 | => false, | ||
| 569 | |||
| 570 | .generic_poison => unreachable, | ||
| 571 | }, | ||
| 572 | .struct_type => { | ||
| 573 | const struct_type = ip.loadStructType(ty.toIntern()); | ||
| 574 | if (struct_type.assumeRuntimeBitsIfFieldTypesWip(ip)) { | ||
| 575 | // In this case, we guess that hasRuntimeBits() for this type is true, | ||
| 576 | // and then later if our guess was incorrect, we emit a compile error. | ||
| 577 | return true; | ||
| 578 | } | ||
| 579 | switch (strat) { | ||
| 580 | .sema => |sema| _ = try sema.resolveTypeFields(ty), | ||
| 581 | .eager => assert(struct_type.haveFieldTypes(ip)), | ||
| 582 | .lazy => if (!struct_type.haveFieldTypes(ip)) return error.NeedLazy, | ||
| 583 | } | ||
| 584 | for (0..struct_type.field_types.len) |i| { | ||
| 585 | if (struct_type.comptime_bits.getBit(ip, i)) continue; | ||
| 586 | const field_ty = Type.fromInterned(struct_type.field_types.get(ip)[i]); | ||
| 587 | if (try field_ty.hasRuntimeBitsAdvanced(mod, ignore_comptime_only, strat)) | ||
| 588 | return true; | ||
| 589 | } else { | ||
| 590 | return false; | ||
| 591 | } | ||
| 592 | }, | ||
| 593 | .anon_struct_type => |tuple| { | ||
| 594 | for (tuple.types.get(ip), tuple.values.get(ip)) |field_ty, val| { | ||
| 595 | if (val != .none) continue; // comptime field | ||
| 596 | if (try Type.fromInterned(field_ty).hasRuntimeBitsAdvanced(mod, ignore_comptime_only, strat)) return true; | ||
| 597 | } | ||
| 598 | return false; | ||
| 599 | }, | ||
| 600 | |||
| 601 | .union_type => { | ||
| 602 | const union_type = ip.loadUnionType(ty.toIntern()); | ||
| 603 | switch (union_type.flagsPtr(ip).runtime_tag) { | ||
| 604 | .none => { | ||
| 605 | if (union_type.flagsPtr(ip).status == .field_types_wip) { | ||
| 606 | // In this case, we guess that hasRuntimeBits() for this type is true, | ||
| 607 | // and then later if our guess was incorrect, we emit a compile error. | ||
| 608 | union_type.flagsPtr(ip).assumed_runtime_bits = true; | ||
| 609 | return true; | ||
| 610 | } | ||
| 611 | }, | ||
| 612 | .safety, .tagged => { | ||
| 613 | const tag_ty = union_type.tagTypePtr(ip).*; | ||
| 614 | // tag_ty will be `none` if this union's tag type is not resolved yet, | ||
| 615 | // in which case we want control flow to continue down below. | ||
| 616 | if (tag_ty != .none and | ||
| 617 | try Type.fromInterned(tag_ty).hasRuntimeBitsAdvanced(mod, ignore_comptime_only, strat)) | ||
| 618 | { | ||
| 619 | return true; | ||
| 620 | } | ||
| 621 | }, | ||
| 622 | } | ||
| 623 | switch (strat) { | ||
| 624 | .sema => |sema| _ = try sema.resolveTypeFields(ty), | ||
| 625 | .eager => assert(union_type.flagsPtr(ip).status.haveFieldTypes()), | ||
| 626 | .lazy => if (!union_type.flagsPtr(ip).status.haveFieldTypes()) | ||
| 627 | return error.NeedLazy, | ||
| 628 | } | ||
| 629 | for (0..union_type.field_types.len) |field_index| { | ||
| 630 | const field_ty = Type.fromInterned(union_type.field_types.get(ip)[field_index]); | ||
| 631 | if (try field_ty.hasRuntimeBitsAdvanced(mod, ignore_comptime_only, strat)) | ||
| 632 | return true; | ||
| 633 | } else { | ||
| 634 | return false; | ||
| 635 | } | ||
| 636 | }, | ||
| 637 | |||
| 638 | .opaque_type => true, | ||
| 639 | .enum_type => Type.fromInterned(ip.loadEnumType(ty.toIntern()).tag_ty).hasRuntimeBitsAdvanced(mod, ignore_comptime_only, strat), | ||
| 640 | |||
| 641 | // values, not types | ||
| 642 | .undef, | ||
| 643 | .simple_value, | ||
| 644 | .variable, | ||
| 645 | .extern_func, | ||
| 646 | .func, | ||
| 647 | .int, | ||
| 648 | .err, | ||
| 649 | .error_union, | ||
| 650 | .enum_literal, | ||
| 651 | .enum_tag, | ||
| 652 | .empty_enum_value, | ||
| 653 | .float, | ||
| 654 | .ptr, | ||
| 655 | .slice, | ||
| 656 | .opt, | ||
| 657 | .aggregate, | ||
| 658 | .un, | ||
| 659 | // memoization, not types | ||
| 660 | .memoized_call, | ||
| 661 | => unreachable, | ||
| 662 | }, | ||
| 663 | }; | ||
| 664 | } | ||
| 665 | |||
| 666 | /// true if and only if the type has a well-defined memory layout | ||
| 667 | /// readFrom/writeToMemory are supported only for types with a well- | ||
| 668 | /// defined memory layout | ||
| 669 | pub fn hasWellDefinedLayout(ty: Type, mod: *Module) bool { | ||
| 670 | const ip = &mod.intern_pool; | ||
| 671 | return switch (ip.indexToKey(ty.toIntern())) { | ||
| 672 | .int_type, | ||
| 673 | .vector_type, | ||
| 674 | => true, | ||
| 675 | |||
| 676 | .error_union_type, | ||
| 677 | .error_set_type, | ||
| 678 | .inferred_error_set_type, | ||
| 679 | .anon_struct_type, | ||
| 680 | .opaque_type, | ||
| 681 | .anyframe_type, | ||
| 682 | // These are function bodies, not function pointers. | ||
| 683 | .func_type, | ||
| 684 | => false, | ||
| 685 | |||
| 686 | .array_type => |array_type| Type.fromInterned(array_type.child).hasWellDefinedLayout(mod), | ||
| 687 | .opt_type => ty.isPtrLikeOptional(mod), | ||
| 688 | .ptr_type => |ptr_type| ptr_type.flags.size != .Slice, | ||
| 689 | |||
| 690 | .simple_type => |t| switch (t) { | ||
| 691 | .f16, | ||
| 692 | .f32, | ||
| 693 | .f64, | ||
| 694 | .f80, | ||
| 695 | .f128, | ||
| 696 | .usize, | ||
| 697 | .isize, | ||
| 698 | .c_char, | ||
| 699 | .c_short, | ||
| 700 | .c_ushort, | ||
| 701 | .c_int, | ||
| 702 | .c_uint, | ||
| 703 | .c_long, | ||
| 704 | .c_ulong, | ||
| 705 | .c_longlong, | ||
| 706 | .c_ulonglong, | ||
| 707 | .c_longdouble, | ||
| 708 | .bool, | ||
| 709 | .void, | ||
| 710 | => true, | ||
| 711 | |||
| 712 | .anyerror, | ||
| 713 | .adhoc_inferred_error_set, | ||
| 714 | .anyopaque, | ||
| 715 | .atomic_order, | ||
| 716 | .atomic_rmw_op, | ||
| 717 | .calling_convention, | ||
| 718 | .address_space, | ||
| 719 | .float_mode, | ||
| 720 | .reduce_op, | ||
| 721 | .call_modifier, | ||
| 722 | .prefetch_options, | ||
| 723 | .export_options, | ||
| 724 | .extern_options, | ||
| 725 | .type, | ||
| 726 | .comptime_int, | ||
| 727 | .comptime_float, | ||
| 728 | .noreturn, | ||
| 729 | .null, | ||
| 730 | .undefined, | ||
| 731 | .enum_literal, | ||
| 732 | .type_info, | ||
| 733 | .generic_poison, | ||
| 734 | => false, | ||
| 735 | }, | ||
| 736 | .struct_type => { | ||
| 737 | const struct_type = ip.loadStructType(ty.toIntern()); | ||
| 738 | // Struct with no fields have a well-defined layout of no bits. | ||
| 739 | return struct_type.layout != .auto or struct_type.field_types.len == 0; | ||
| 740 | }, | ||
| 741 | .union_type => { | ||
| 742 | const union_type = ip.loadUnionType(ty.toIntern()); | ||
| 743 | return switch (union_type.flagsPtr(ip).runtime_tag) { | ||
| 744 | .none, .safety => union_type.flagsPtr(ip).layout != .auto, | ||
| 745 | .tagged => false, | ||
| 746 | }; | ||
| 747 | }, | ||
| 748 | .enum_type => switch (ip.loadEnumType(ty.toIntern()).tag_mode) { | ||
| 749 | .auto => false, | ||
| 750 | .explicit, .nonexhaustive => true, | ||
| 751 | }, | ||
| 752 | |||
| 753 | // values, not types | ||
| 754 | .undef, | ||
| 755 | .simple_value, | ||
| 756 | .variable, | ||
| 757 | .extern_func, | ||
| 758 | .func, | ||
| 759 | .int, | ||
| 760 | .err, | ||
| 761 | .error_union, | ||
| 762 | .enum_literal, | ||
| 763 | .enum_tag, | ||
| 764 | .empty_enum_value, | ||
| 765 | .float, | ||
| 766 | .ptr, | ||
| 767 | .slice, | ||
| 768 | .opt, | ||
| 769 | .aggregate, | ||
| 770 | .un, | ||
| 771 | // memoization, not types | ||
| 772 | .memoized_call, | ||
| 773 | => unreachable, | ||
| 774 | }; | ||
| 775 | } | ||
| 776 | |||
| 777 | pub fn hasRuntimeBits(ty: Type, mod: *Module) bool { | ||
| 778 | return hasRuntimeBitsAdvanced(ty, mod, false, .eager) catch unreachable; | ||
| 779 | } | ||
| 780 | |||
| 781 | pub fn hasRuntimeBitsIgnoreComptime(ty: Type, mod: *Module) bool { | ||
| 782 | return hasRuntimeBitsAdvanced(ty, mod, true, .eager) catch unreachable; | ||
| 783 | } | ||
| 784 | |||
| 785 | pub fn fnHasRuntimeBits(ty: Type, mod: *Module) bool { | ||
| 786 | return ty.fnHasRuntimeBitsAdvanced(mod, null) catch unreachable; | ||
| 787 | } | ||
| 788 | |||
| 789 | /// Determines whether a function type has runtime bits, i.e. whether a | ||
| 790 | /// function with this type can exist at runtime. | ||
| 791 | /// Asserts that `ty` is a function type. | ||
| 792 | /// If `opt_sema` is not provided, asserts that the return type is sufficiently resolved. | ||
| 793 | pub fn fnHasRuntimeBitsAdvanced(ty: Type, mod: *Module, opt_sema: ?*Sema) Module.CompileError!bool { | ||
| 794 | const fn_info = mod.typeToFunc(ty).?; | ||
| 795 | if (fn_info.is_generic) return false; | ||
| 796 | if (fn_info.is_var_args) return true; | ||
| 797 | if (fn_info.cc == .Inline) return false; | ||
| 798 | return !try Type.fromInterned(fn_info.return_type).comptimeOnlyAdvanced(mod, opt_sema); | ||
| 799 | } | ||
| 800 | |||
| 801 | pub fn isFnOrHasRuntimeBits(ty: Type, mod: *Module) bool { | ||
| 802 | switch (ty.zigTypeTag(mod)) { | ||
| 803 | .Fn => return ty.fnHasRuntimeBits(mod), | ||
| 804 | else => return ty.hasRuntimeBits(mod), | ||
| 805 | } | ||
| 806 | } | ||
| 807 | |||
| 808 | /// Same as `isFnOrHasRuntimeBits` but comptime-only types may return a false positive. | ||
| 809 | pub fn isFnOrHasRuntimeBitsIgnoreComptime(ty: Type, mod: *Module) bool { | ||
| 810 | return switch (ty.zigTypeTag(mod)) { | ||
| 811 | .Fn => true, | ||
| 812 | else => return ty.hasRuntimeBitsIgnoreComptime(mod), | ||
| 813 | }; | ||
| 814 | } | ||
| 815 | |||
| 816 | pub fn isNoReturn(ty: Type, mod: *Module) bool { | ||
| 817 | return mod.intern_pool.isNoReturn(ty.toIntern()); | ||
| 818 | } | ||
| 819 | |||
| 820 | /// Returns `none` if the pointer is naturally aligned and the element type is 0-bit. | ||
| 821 | pub fn ptrAlignment(ty: Type, mod: *Module) Alignment { | ||
| 822 | return ptrAlignmentAdvanced(ty, mod, null) catch unreachable; | ||
| 823 | } | ||
| 824 | |||
| 825 | pub fn ptrAlignmentAdvanced(ty: Type, mod: *Module, opt_sema: ?*Sema) !Alignment { | ||
| 826 | return switch (mod.intern_pool.indexToKey(ty.toIntern())) { | ||
| 827 | .ptr_type => |ptr_type| { | ||
| 828 | if (ptr_type.flags.alignment != .none) | ||
| 829 | return ptr_type.flags.alignment; | ||
| 830 | |||
| 831 | if (opt_sema) |sema| { | ||
| 832 | const res = try Type.fromInterned(ptr_type.child).abiAlignmentAdvanced(mod, .{ .sema = sema }); | ||
| 833 | return res.scalar; | ||
| 834 | } | ||
| 835 | |||
| 836 | return (Type.fromInterned(ptr_type.child).abiAlignmentAdvanced(mod, .eager) catch unreachable).scalar; | ||
| 837 | }, | ||
| 838 | .opt_type => |child| Type.fromInterned(child).ptrAlignmentAdvanced(mod, opt_sema), | ||
| 839 | else => unreachable, | ||
| 840 | }; | ||
| 841 | } | ||
| 842 | |||
| 843 | pub fn ptrAddressSpace(ty: Type, mod: *const Module) std.builtin.AddressSpace { | ||
| 844 | return switch (mod.intern_pool.indexToKey(ty.toIntern())) { | ||
| 845 | .ptr_type => |ptr_type| ptr_type.flags.address_space, | ||
| 846 | .opt_type => |child| mod.intern_pool.indexToKey(child).ptr_type.flags.address_space, | ||
| 847 | else => unreachable, | ||
| 848 | }; | ||
| 849 | } | ||
| 850 | |||
| 851 | /// Never returns `none`. Asserts that all necessary type resolution is already done. | ||
| 852 | pub fn abiAlignment(ty: Type, mod: *Module) Alignment { | ||
| 853 | return (ty.abiAlignmentAdvanced(mod, .eager) catch unreachable).scalar; | ||
| 854 | } | ||
| 855 | |||
| 856 | /// May capture a reference to `ty`. | ||
| 857 | /// Returned value has type `comptime_int`. | ||
| 858 | pub fn lazyAbiAlignment(ty: Type, mod: *Module) !Value { | ||
| 859 | switch (try ty.abiAlignmentAdvanced(mod, .lazy)) { | ||
| 860 | .val => |val| return val, | ||
| 861 | .scalar => |x| return mod.intValue(Type.comptime_int, x.toByteUnits() orelse 0), | ||
| 862 | } | ||
| 863 | } | ||
| 864 | |||
| 865 | pub const AbiAlignmentAdvanced = union(enum) { | ||
| 866 | scalar: Alignment, | ||
| 867 | val: Value, | ||
| 868 | }; | ||
| 869 | |||
| 870 | pub const AbiAlignmentAdvancedStrat = union(enum) { | ||
| 871 | eager, | ||
| 872 | lazy, | ||
| 873 | sema: *Sema, | ||
| 874 | }; | ||
| 875 | |||
| 876 | /// If you pass `eager` you will get back `scalar` and assert the type is resolved. | ||
| 877 | /// In this case there will be no error, guaranteed. | ||
| 878 | /// If you pass `lazy` you may get back `scalar` or `val`. | ||
| 879 | /// If `val` is returned, a reference to `ty` has been captured. | ||
| 880 | /// If you pass `sema` you will get back `scalar` and resolve the type if | ||
| 881 | /// necessary, possibly returning a CompileError. | ||
| 882 | pub fn abiAlignmentAdvanced( | ||
| 883 | ty: Type, | ||
| 884 | mod: *Module, | ||
| 885 | strat: AbiAlignmentAdvancedStrat, | ||
| 886 | ) Module.CompileError!AbiAlignmentAdvanced { | ||
| 887 | const target = mod.getTarget(); | ||
| 888 | const use_llvm = mod.comp.config.use_llvm; | ||
| 889 | const ip = &mod.intern_pool; | ||
| 890 | |||
| 891 | const opt_sema = switch (strat) { | ||
| 892 | .sema => |sema| sema, | ||
| 893 | else => null, | ||
| 894 | }; | ||
| 895 | |||
| 896 | switch (ty.toIntern()) { | ||
| 897 | .empty_struct_type => return AbiAlignmentAdvanced{ .scalar = .@"1" }, | ||
| 898 | else => switch (ip.indexToKey(ty.toIntern())) { | ||
| 899 | .int_type => |int_type| { | ||
| 900 | if (int_type.bits == 0) return AbiAlignmentAdvanced{ .scalar = .@"1" }; | ||
| 901 | return .{ .scalar = intAbiAlignment(int_type.bits, target, use_llvm) }; | ||
| 902 | }, | ||
| 903 | .ptr_type, .anyframe_type => { | ||
| 904 | return .{ .scalar = ptrAbiAlignment(target) }; | ||
| 905 | }, | ||
| 906 | .array_type => |array_type| { | ||
| 907 | return Type.fromInterned(array_type.child).abiAlignmentAdvanced(mod, strat); | ||
| 908 | }, | ||
| 909 | .vector_type => |vector_type| { | ||
| 910 | if (vector_type.len == 0) return .{ .scalar = .@"1" }; | ||
| 911 | switch (mod.comp.getZigBackend()) { | ||
| 912 | else => { | ||
| 913 | const elem_bits: u32 = @intCast(try Type.fromInterned(vector_type.child).bitSizeAdvanced(mod, opt_sema)); | ||
| 914 | if (elem_bits == 0) return .{ .scalar = .@"1" }; | ||
| 915 | const bytes = ((elem_bits * vector_type.len) + 7) / 8; | ||
| 916 | const alignment = std.math.ceilPowerOfTwoAssert(u32, bytes); | ||
| 917 | return .{ .scalar = Alignment.fromByteUnits(alignment) }; | ||
| 918 | }, | ||
| 919 | .stage2_c => { | ||
| 920 | return Type.fromInterned(vector_type.child).abiAlignmentAdvanced(mod, strat); | ||
| 921 | }, | ||
| 922 | .stage2_x86_64 => { | ||
| 923 | if (vector_type.child == .bool_type) { | ||
| 924 | if (vector_type.len > 256 and std.Target.x86.featureSetHas(target.cpu.features, .avx512f)) return .{ .scalar = .@"64" }; | ||
| 925 | if (vector_type.len > 128 and std.Target.x86.featureSetHas(target.cpu.features, .avx2)) return .{ .scalar = .@"32" }; | ||
| 926 | if (vector_type.len > 64) return .{ .scalar = .@"16" }; | ||
| 927 | const bytes = std.math.divCeil(u32, vector_type.len, 8) catch unreachable; | ||
| 928 | const alignment = std.math.ceilPowerOfTwoAssert(u32, bytes); | ||
| 929 | return .{ .scalar = Alignment.fromByteUnits(alignment) }; | ||
| 930 | } | ||
| 931 | const elem_bytes: u32 = @intCast((try Type.fromInterned(vector_type.child).abiSizeAdvanced(mod, strat)).scalar); | ||
| 932 | if (elem_bytes == 0) return .{ .scalar = .@"1" }; | ||
| 933 | const bytes = elem_bytes * vector_type.len; | ||
| 934 | if (bytes > 32 and std.Target.x86.featureSetHas(target.cpu.features, .avx512f)) return .{ .scalar = .@"64" }; | ||
| 935 | if (bytes > 16 and std.Target.x86.featureSetHas(target.cpu.features, .avx)) return .{ .scalar = .@"32" }; | ||
| 936 | return .{ .scalar = .@"16" }; | ||
| 937 | }, | ||
| 938 | } | ||
| 939 | }, | ||
| 940 | |||
| 941 | .opt_type => return abiAlignmentAdvancedOptional(ty, mod, strat), | ||
| 942 | .error_union_type => |info| return abiAlignmentAdvancedErrorUnion(ty, mod, strat, Type.fromInterned(info.payload_type)), | ||
| 943 | |||
| 944 | .error_set_type, .inferred_error_set_type => { | ||
| 945 | const bits = mod.errorSetBits(); | ||
| 946 | if (bits == 0) return AbiAlignmentAdvanced{ .scalar = .@"1" }; | ||
| 947 | return .{ .scalar = intAbiAlignment(bits, target, use_llvm) }; | ||
| 948 | }, | ||
| 949 | |||
| 950 | // represents machine code; not a pointer | ||
| 951 | .func_type => return .{ .scalar = target_util.defaultFunctionAlignment(target) }, | ||
| 952 | |||
| 953 | .simple_type => |t| switch (t) { | ||
| 954 | .bool, | ||
| 955 | .atomic_order, | ||
| 956 | .atomic_rmw_op, | ||
| 957 | .calling_convention, | ||
| 958 | .address_space, | ||
| 959 | .float_mode, | ||
| 960 | .reduce_op, | ||
| 961 | .call_modifier, | ||
| 962 | .prefetch_options, | ||
| 963 | .anyopaque, | ||
| 964 | => return .{ .scalar = .@"1" }, | ||
| 965 | |||
| 966 | .usize, | ||
| 967 | .isize, | ||
| 968 | => return .{ .scalar = intAbiAlignment(target.ptrBitWidth(), target, use_llvm) }, | ||
| 969 | |||
| 970 | .export_options, | ||
| 971 | .extern_options, | ||
| 972 | .type_info, | ||
| 973 | => return .{ .scalar = ptrAbiAlignment(target) }, | ||
| 974 | |||
| 975 | .c_char => return .{ .scalar = cTypeAlign(target, .char) }, | ||
| 976 | .c_short => return .{ .scalar = cTypeAlign(target, .short) }, | ||
| 977 | .c_ushort => return .{ .scalar = cTypeAlign(target, .ushort) }, | ||
| 978 | .c_int => return .{ .scalar = cTypeAlign(target, .int) }, | ||
| 979 | .c_uint => return .{ .scalar = cTypeAlign(target, .uint) }, | ||
| 980 | .c_long => return .{ .scalar = cTypeAlign(target, .long) }, | ||
| 981 | .c_ulong => return .{ .scalar = cTypeAlign(target, .ulong) }, | ||
| 982 | .c_longlong => return .{ .scalar = cTypeAlign(target, .longlong) }, | ||
| 983 | .c_ulonglong => return .{ .scalar = cTypeAlign(target, .ulonglong) }, | ||
| 984 | .c_longdouble => return .{ .scalar = cTypeAlign(target, .longdouble) }, | ||
| 985 | |||
| 986 | .f16 => return .{ .scalar = .@"2" }, | ||
| 987 | .f32 => return .{ .scalar = cTypeAlign(target, .float) }, | ||
| 988 | .f64 => switch (target.c_type_bit_size(.double)) { | ||
| 989 | 64 => return .{ .scalar = cTypeAlign(target, .double) }, | ||
| 990 | else => return .{ .scalar = .@"8" }, | ||
| 991 | }, | ||
| 992 | .f80 => switch (target.c_type_bit_size(.longdouble)) { | ||
| 993 | 80 => return .{ .scalar = cTypeAlign(target, .longdouble) }, | ||
| 994 | else => { | ||
| 995 | const u80_ty: Type = .{ .ip_index = .u80_type }; | ||
| 996 | return .{ .scalar = abiAlignment(u80_ty, mod) }; | ||
| 997 | }, | ||
| 998 | }, | ||
| 999 | .f128 => switch (target.c_type_bit_size(.longdouble)) { | ||
| 1000 | 128 => return .{ .scalar = cTypeAlign(target, .longdouble) }, | ||
| 1001 | else => return .{ .scalar = .@"16" }, | ||
| 1002 | }, | ||
| 1003 | |||
| 1004 | .anyerror, .adhoc_inferred_error_set => { | ||
| 1005 | const bits = mod.errorSetBits(); | ||
| 1006 | if (bits == 0) return AbiAlignmentAdvanced{ .scalar = .@"1" }; | ||
| 1007 | return .{ .scalar = intAbiAlignment(bits, target, use_llvm) }; | ||
| 1008 | }, | ||
| 1009 | |||
| 1010 | .void, | ||
| 1011 | .type, | ||
| 1012 | .comptime_int, | ||
| 1013 | .comptime_float, | ||
| 1014 | .null, | ||
| 1015 | .undefined, | ||
| 1016 | .enum_literal, | ||
| 1017 | => return .{ .scalar = .@"1" }, | ||
| 1018 | |||
| 1019 | .noreturn => unreachable, | ||
| 1020 | .generic_poison => unreachable, | ||
| 1021 | }, | ||
| 1022 | .struct_type => { | ||
| 1023 | const struct_type = ip.loadStructType(ty.toIntern()); | ||
| 1024 | if (struct_type.layout == .@"packed") { | ||
| 1025 | switch (strat) { | ||
| 1026 | .sema => |sema| try sema.resolveTypeLayout(ty), | ||
| 1027 | .lazy => if (struct_type.backingIntType(ip).* == .none) return .{ | ||
| 1028 | .val = Value.fromInterned((try mod.intern(.{ .int = .{ | ||
| 1029 | .ty = .comptime_int_type, | ||
| 1030 | .storage = .{ .lazy_align = ty.toIntern() }, | ||
| 1031 | } }))), | ||
| 1032 | }, | ||
| 1033 | .eager => {}, | ||
| 1034 | } | ||
| 1035 | return .{ .scalar = Type.fromInterned(struct_type.backingIntType(ip).*).abiAlignment(mod) }; | ||
| 1036 | } | ||
| 1037 | |||
| 1038 | const flags = struct_type.flagsPtr(ip).*; | ||
| 1039 | if (flags.alignment != .none) return .{ .scalar = flags.alignment }; | ||
| 1040 | |||
| 1041 | return switch (strat) { | ||
| 1042 | .eager => unreachable, // struct alignment not resolved | ||
| 1043 | .sema => |sema| .{ | ||
| 1044 | .scalar = try sema.resolveStructAlignment(ty.toIntern(), struct_type), | ||
| 1045 | }, | ||
| 1046 | .lazy => .{ .val = Value.fromInterned((try mod.intern(.{ .int = .{ | ||
| 1047 | .ty = .comptime_int_type, | ||
| 1048 | .storage = .{ .lazy_align = ty.toIntern() }, | ||
| 1049 | } }))) }, | ||
| 1050 | }; | ||
| 1051 | }, | ||
| 1052 | .anon_struct_type => |tuple| { | ||
| 1053 | var big_align: Alignment = .@"1"; | ||
| 1054 | for (tuple.types.get(ip), tuple.values.get(ip)) |field_ty, val| { | ||
| 1055 | if (val != .none) continue; // comptime field | ||
| 1056 | switch (try Type.fromInterned(field_ty).abiAlignmentAdvanced(mod, strat)) { | ||
| 1057 | .scalar => |field_align| big_align = big_align.max(field_align), | ||
| 1058 | .val => switch (strat) { | ||
| 1059 | .eager => unreachable, // field type alignment not resolved | ||
| 1060 | .sema => unreachable, // passed to abiAlignmentAdvanced above | ||
| 1061 | .lazy => return .{ .val = Value.fromInterned((try mod.intern(.{ .int = .{ | ||
| 1062 | .ty = .comptime_int_type, | ||
| 1063 | .storage = .{ .lazy_align = ty.toIntern() }, | ||
| 1064 | } }))) }, | ||
| 1065 | }, | ||
| 1066 | } | ||
| 1067 | } | ||
| 1068 | return .{ .scalar = big_align }; | ||
| 1069 | }, | ||
| 1070 | .union_type => { | ||
| 1071 | const union_type = ip.loadUnionType(ty.toIntern()); | ||
| 1072 | const flags = union_type.flagsPtr(ip).*; | ||
| 1073 | if (flags.alignment != .none) return .{ .scalar = flags.alignment }; | ||
| 1074 | |||
| 1075 | if (!union_type.haveLayout(ip)) switch (strat) { | ||
| 1076 | .eager => unreachable, // union layout not resolved | ||
| 1077 | .sema => |sema| return .{ .scalar = try sema.resolveUnionAlignment(ty, union_type) }, | ||
| 1078 | .lazy => return .{ .val = Value.fromInterned((try mod.intern(.{ .int = .{ | ||
| 1079 | .ty = .comptime_int_type, | ||
| 1080 | .storage = .{ .lazy_align = ty.toIntern() }, | ||
| 1081 | } }))) }, | ||
| 1082 | }; | ||
| 1083 | |||
| 1084 | return .{ .scalar = union_type.flagsPtr(ip).alignment }; | ||
| 1085 | }, | ||
| 1086 | .opaque_type => return .{ .scalar = .@"1" }, | ||
| 1087 | .enum_type => return .{ | ||
| 1088 | .scalar = Type.fromInterned(ip.loadEnumType(ty.toIntern()).tag_ty).abiAlignment(mod), | ||
| 1089 | }, | ||
| 1090 | |||
| 1091 | // values, not types | ||
| 1092 | .undef, | ||
| 1093 | .simple_value, | ||
| 1094 | .variable, | ||
| 1095 | .extern_func, | ||
| 1096 | .func, | ||
| 1097 | .int, | ||
| 1098 | .err, | ||
| 1099 | .error_union, | ||
| 1100 | .enum_literal, | ||
| 1101 | .enum_tag, | ||
| 1102 | .empty_enum_value, | ||
| 1103 | .float, | ||
| 1104 | .ptr, | ||
| 1105 | .slice, | ||
| 1106 | .opt, | ||
| 1107 | .aggregate, | ||
| 1108 | .un, | ||
| 1109 | // memoization, not types | ||
| 1110 | .memoized_call, | ||
| 1111 | => unreachable, | ||
| 1112 | }, | ||
| 1113 | } | ||
| 1114 | } | ||
| 1115 | |||
| 1116 | fn abiAlignmentAdvancedErrorUnion( | ||
| 1117 | ty: Type, | ||
| 1118 | mod: *Module, | ||
| 1119 | strat: AbiAlignmentAdvancedStrat, | ||
| 1120 | payload_ty: Type, | ||
| 1121 | ) Module.CompileError!AbiAlignmentAdvanced { | ||
| 1122 | // This code needs to be kept in sync with the equivalent switch prong | ||
| 1123 | // in abiSizeAdvanced. | ||
| 1124 | const code_align = abiAlignment(Type.anyerror, mod); | ||
| 1125 | switch (strat) { | ||
| 1126 | .eager, .sema => { | ||
| 1127 | if (!(payload_ty.hasRuntimeBitsAdvanced(mod, false, strat) catch |err| switch (err) { | ||
| 1128 | error.NeedLazy => return .{ .val = Value.fromInterned((try mod.intern(.{ .int = .{ | ||
| 1129 | .ty = .comptime_int_type, | ||
| 1130 | .storage = .{ .lazy_align = ty.toIntern() }, | ||
| 1131 | } }))) }, | ||
| 1132 | else => |e| return e, | ||
| 1133 | })) { | ||
| 1134 | return .{ .scalar = code_align }; | ||
| 1135 | } | ||
| 1136 | return .{ .scalar = code_align.max( | ||
| 1137 | (try payload_ty.abiAlignmentAdvanced(mod, strat)).scalar, | ||
| 1138 | ) }; | ||
| 1139 | }, | ||
| 1140 | .lazy => { | ||
| 1141 | switch (try payload_ty.abiAlignmentAdvanced(mod, strat)) { | ||
| 1142 | .scalar => |payload_align| return .{ .scalar = code_align.max(payload_align) }, | ||
| 1143 | .val => {}, | ||
| 1144 | } | ||
| 1145 | return .{ .val = Value.fromInterned((try mod.intern(.{ .int = .{ | ||
| 1146 | .ty = .comptime_int_type, | ||
| 1147 | .storage = .{ .lazy_align = ty.toIntern() }, | ||
| 1148 | } }))) }; | ||
| 1149 | }, | ||
| 1150 | } | ||
| 1151 | } | ||
| 1152 | |||
| 1153 | fn abiAlignmentAdvancedOptional( | ||
| 1154 | ty: Type, | ||
| 1155 | mod: *Module, | ||
| 1156 | strat: AbiAlignmentAdvancedStrat, | ||
| 1157 | ) Module.CompileError!AbiAlignmentAdvanced { | ||
| 1158 | const target = mod.getTarget(); | ||
| 1159 | const child_type = ty.optionalChild(mod); | ||
| 1160 | |||
| 1161 | switch (child_type.zigTypeTag(mod)) { | ||
| 1162 | .Pointer => return .{ .scalar = ptrAbiAlignment(target) }, | ||
| 1163 | .ErrorSet => return abiAlignmentAdvanced(Type.anyerror, mod, strat), | ||
| 1164 | .NoReturn => return .{ .scalar = .@"1" }, | ||
| 1165 | else => {}, | ||
| 1166 | } | ||
| 1167 | |||
| 1168 | switch (strat) { | ||
| 1169 | .eager, .sema => { | ||
| 1170 | if (!(child_type.hasRuntimeBitsAdvanced(mod, false, strat) catch |err| switch (err) { | ||
| 1171 | error.NeedLazy => return .{ .val = Value.fromInterned((try mod.intern(.{ .int = .{ | ||
| 1172 | .ty = .comptime_int_type, | ||
| 1173 | .storage = .{ .lazy_align = ty.toIntern() }, | ||
| 1174 | } }))) }, | ||
| 1175 | else => |e| return e, | ||
| 1176 | })) { | ||
| 1177 | return .{ .scalar = .@"1" }; | ||
| 1178 | } | ||
| 1179 | return child_type.abiAlignmentAdvanced(mod, strat); | ||
| 1180 | }, | ||
| 1181 | .lazy => switch (try child_type.abiAlignmentAdvanced(mod, strat)) { | ||
| 1182 | .scalar => |x| return .{ .scalar = x.max(.@"1") }, | ||
| 1183 | .val => return .{ .val = Value.fromInterned((try mod.intern(.{ .int = .{ | ||
| 1184 | .ty = .comptime_int_type, | ||
| 1185 | .storage = .{ .lazy_align = ty.toIntern() }, | ||
| 1186 | } }))) }, | ||
| 1187 | }, | ||
| 1188 | } | ||
| 1189 | } | ||
| 1190 | |||
| 1191 | /// May capture a reference to `ty`. | ||
| 1192 | pub fn lazyAbiSize(ty: Type, mod: *Module) !Value { | ||
| 1193 | switch (try ty.abiSizeAdvanced(mod, .lazy)) { | ||
| 1194 | .val => |val| return val, | ||
| 1195 | .scalar => |x| return mod.intValue(Type.comptime_int, x), | ||
| 1196 | } | ||
| 1197 | } | ||
| 1198 | |||
| 1199 | /// Asserts the type has the ABI size already resolved. | ||
| 1200 | /// Types that return false for hasRuntimeBits() return 0. | ||
| 1201 | pub fn abiSize(ty: Type, mod: *Module) u64 { | ||
| 1202 | return (abiSizeAdvanced(ty, mod, .eager) catch unreachable).scalar; | ||
| 1203 | } | ||
| 1204 | |||
| 1205 | const AbiSizeAdvanced = union(enum) { | ||
| 1206 | scalar: u64, | ||
| 1207 | val: Value, | ||
| 1208 | }; | ||
| 1209 | |||
| 1210 | /// If you pass `eager` you will get back `scalar` and assert the type is resolved. | ||
| 1211 | /// In this case there will be no error, guaranteed. | ||
| 1212 | /// If you pass `lazy` you may get back `scalar` or `val`. | ||
| 1213 | /// If `val` is returned, a reference to `ty` has been captured. | ||
| 1214 | /// If you pass `sema` you will get back `scalar` and resolve the type if | ||
| 1215 | /// necessary, possibly returning a CompileError. | ||
| 1216 | pub fn abiSizeAdvanced( | ||
| 1217 | ty: Type, | ||
| 1218 | mod: *Module, | ||
| 1219 | strat: AbiAlignmentAdvancedStrat, | ||
| 1220 | ) Module.CompileError!AbiSizeAdvanced { | ||
| 1221 | const target = mod.getTarget(); | ||
| 1222 | const use_llvm = mod.comp.config.use_llvm; | ||
| 1223 | const ip = &mod.intern_pool; | ||
| 1224 | |||
| 1225 | switch (ty.toIntern()) { | ||
| 1226 | .empty_struct_type => return AbiSizeAdvanced{ .scalar = 0 }, | ||
| 1227 | |||
| 1228 | else => switch (ip.indexToKey(ty.toIntern())) { | ||
| 1229 | .int_type => |int_type| { | ||
| 1230 | if (int_type.bits == 0) return AbiSizeAdvanced{ .scalar = 0 }; | ||
| 1231 | return AbiSizeAdvanced{ .scalar = intAbiSize(int_type.bits, target, use_llvm) }; | ||
| 1232 | }, | ||
| 1233 | .ptr_type => |ptr_type| switch (ptr_type.flags.size) { | ||
| 1234 | .Slice => return .{ .scalar = @divExact(target.ptrBitWidth(), 8) * 2 }, | ||
| 1235 | else => return .{ .scalar = @divExact(target.ptrBitWidth(), 8) }, | ||
| 1236 | }, | ||
| 1237 | .anyframe_type => return AbiSizeAdvanced{ .scalar = @divExact(target.ptrBitWidth(), 8) }, | ||
| 1238 | |||
| 1239 | .array_type => |array_type| { | ||
| 1240 | const len = array_type.lenIncludingSentinel(); | ||
| 1241 | if (len == 0) return .{ .scalar = 0 }; | ||
| 1242 | switch (try Type.fromInterned(array_type.child).abiSizeAdvanced(mod, strat)) { | ||
| 1243 | .scalar => |elem_size| return .{ .scalar = len * elem_size }, | ||
| 1244 | .val => switch (strat) { | ||
| 1245 | .sema, .eager => unreachable, | ||
| 1246 | .lazy => return .{ .val = Value.fromInterned((try mod.intern(.{ .int = .{ | ||
| 1247 | .ty = .comptime_int_type, | ||
| 1248 | .storage = .{ .lazy_size = ty.toIntern() }, | ||
| 1249 | } }))) }, | ||
| 1250 | }, | ||
| 1251 | } | ||
| 1252 | }, | ||
| 1253 | .vector_type => |vector_type| { | ||
| 1254 | const opt_sema = switch (strat) { | ||
| 1255 | .sema => |sema| sema, | ||
| 1256 | .eager => null, | ||
| 1257 | .lazy => return .{ .val = Value.fromInterned((try mod.intern(.{ .int = .{ | ||
| 1258 | .ty = .comptime_int_type, | ||
| 1259 | .storage = .{ .lazy_size = ty.toIntern() }, | ||
| 1260 | } }))) }, | ||
| 1261 | }; | ||
| 1262 | const alignment = switch (try ty.abiAlignmentAdvanced(mod, strat)) { | ||
| 1263 | .scalar => |x| x, | ||
| 1264 | .val => return .{ .val = Value.fromInterned((try mod.intern(.{ .int = .{ | ||
| 1265 | .ty = .comptime_int_type, | ||
| 1266 | .storage = .{ .lazy_size = ty.toIntern() }, | ||
| 1267 | } }))) }, | ||
| 1268 | }; | ||
| 1269 | const total_bytes = switch (mod.comp.getZigBackend()) { | ||
| 1270 | else => total_bytes: { | ||
| 1271 | const elem_bits = try Type.fromInterned(vector_type.child).bitSizeAdvanced(mod, opt_sema); | ||
| 1272 | const total_bits = elem_bits * vector_type.len; | ||
| 1273 | break :total_bytes (total_bits + 7) / 8; | ||
| 1274 | }, | ||
| 1275 | .stage2_c => total_bytes: { | ||
| 1276 | const elem_bytes: u32 = @intCast((try Type.fromInterned(vector_type.child).abiSizeAdvanced(mod, strat)).scalar); | ||
| 1277 | break :total_bytes elem_bytes * vector_type.len; | ||
| 1278 | }, | ||
| 1279 | .stage2_x86_64 => total_bytes: { | ||
| 1280 | if (vector_type.child == .bool_type) break :total_bytes std.math.divCeil(u32, vector_type.len, 8) catch unreachable; | ||
| 1281 | const elem_bytes: u32 = @intCast((try Type.fromInterned(vector_type.child).abiSizeAdvanced(mod, strat)).scalar); | ||
| 1282 | break :total_bytes elem_bytes * vector_type.len; | ||
| 1283 | }, | ||
| 1284 | }; | ||
| 1285 | return AbiSizeAdvanced{ .scalar = alignment.forward(total_bytes) }; | ||
| 1286 | }, | ||
| 1287 | |||
| 1288 | .opt_type => return ty.abiSizeAdvancedOptional(mod, strat), | ||
| 1289 | |||
| 1290 | .error_set_type, .inferred_error_set_type => { | ||
| 1291 | const bits = mod.errorSetBits(); | ||
| 1292 | if (bits == 0) return AbiSizeAdvanced{ .scalar = 0 }; | ||
| 1293 | return AbiSizeAdvanced{ .scalar = intAbiSize(bits, target, use_llvm) }; | ||
| 1294 | }, | ||
| 1295 | |||
| 1296 | .error_union_type => |error_union_type| { | ||
| 1297 | const payload_ty = Type.fromInterned(error_union_type.payload_type); | ||
| 1298 | // This code needs to be kept in sync with the equivalent switch prong | ||
| 1299 | // in abiAlignmentAdvanced. | ||
| 1300 | const code_size = abiSize(Type.anyerror, mod); | ||
| 1301 | if (!(payload_ty.hasRuntimeBitsAdvanced(mod, false, strat) catch |err| switch (err) { | ||
| 1302 | error.NeedLazy => return .{ .val = Value.fromInterned((try mod.intern(.{ .int = .{ | ||
| 1303 | .ty = .comptime_int_type, | ||
| 1304 | .storage = .{ .lazy_size = ty.toIntern() }, | ||
| 1305 | } }))) }, | ||
| 1306 | else => |e| return e, | ||
| 1307 | })) { | ||
| 1308 | // Same as anyerror. | ||
| 1309 | return AbiSizeAdvanced{ .scalar = code_size }; | ||
| 1310 | } | ||
| 1311 | const code_align = abiAlignment(Type.anyerror, mod); | ||
| 1312 | const payload_align = abiAlignment(payload_ty, mod); | ||
| 1313 | const payload_size = switch (try payload_ty.abiSizeAdvanced(mod, strat)) { | ||
| 1314 | .scalar => |elem_size| elem_size, | ||
| 1315 | .val => switch (strat) { | ||
| 1316 | .sema => unreachable, | ||
| 1317 | .eager => unreachable, | ||
| 1318 | .lazy => return .{ .val = Value.fromInterned((try mod.intern(.{ .int = .{ | ||
| 1319 | .ty = .comptime_int_type, | ||
| 1320 | .storage = .{ .lazy_size = ty.toIntern() }, | ||
| 1321 | } }))) }, | ||
| 1322 | }, | ||
| 1323 | }; | ||
| 1324 | |||
| 1325 | var size: u64 = 0; | ||
| 1326 | if (code_align.compare(.gt, payload_align)) { | ||
| 1327 | size += code_size; | ||
| 1328 | size = payload_align.forward(size); | ||
| 1329 | size += payload_size; | ||
| 1330 | size = code_align.forward(size); | ||
| 1331 | } else { | ||
| 1332 | size += payload_size; | ||
| 1333 | size = code_align.forward(size); | ||
| 1334 | size += code_size; | ||
| 1335 | size = payload_align.forward(size); | ||
| 1336 | } | ||
| 1337 | return AbiSizeAdvanced{ .scalar = size }; | ||
| 1338 | }, | ||
| 1339 | .func_type => unreachable, // represents machine code; not a pointer | ||
| 1340 | .simple_type => |t| switch (t) { | ||
| 1341 | .bool, | ||
| 1342 | .atomic_order, | ||
| 1343 | .atomic_rmw_op, | ||
| 1344 | .calling_convention, | ||
| 1345 | .address_space, | ||
| 1346 | .float_mode, | ||
| 1347 | .reduce_op, | ||
| 1348 | .call_modifier, | ||
| 1349 | => return AbiSizeAdvanced{ .scalar = 1 }, | ||
| 1350 | |||
| 1351 | .f16 => return AbiSizeAdvanced{ .scalar = 2 }, | ||
| 1352 | .f32 => return AbiSizeAdvanced{ .scalar = 4 }, | ||
| 1353 | .f64 => return AbiSizeAdvanced{ .scalar = 8 }, | ||
| 1354 | .f128 => return AbiSizeAdvanced{ .scalar = 16 }, | ||
| 1355 | .f80 => switch (target.c_type_bit_size(.longdouble)) { | ||
| 1356 | 80 => return AbiSizeAdvanced{ .scalar = target.c_type_byte_size(.longdouble) }, | ||
| 1357 | else => { | ||
| 1358 | const u80_ty: Type = .{ .ip_index = .u80_type }; | ||
| 1359 | return AbiSizeAdvanced{ .scalar = abiSize(u80_ty, mod) }; | ||
| 1360 | }, | ||
| 1361 | }, | ||
| 1362 | |||
| 1363 | .usize, | ||
| 1364 | .isize, | ||
| 1365 | => return AbiSizeAdvanced{ .scalar = @divExact(target.ptrBitWidth(), 8) }, | ||
| 1366 | |||
| 1367 | .c_char => return AbiSizeAdvanced{ .scalar = target.c_type_byte_size(.char) }, | ||
| 1368 | .c_short => return AbiSizeAdvanced{ .scalar = target.c_type_byte_size(.short) }, | ||
| 1369 | .c_ushort => return AbiSizeAdvanced{ .scalar = target.c_type_byte_size(.ushort) }, | ||
| 1370 | .c_int => return AbiSizeAdvanced{ .scalar = target.c_type_byte_size(.int) }, | ||
| 1371 | .c_uint => return AbiSizeAdvanced{ .scalar = target.c_type_byte_size(.uint) }, | ||
| 1372 | .c_long => return AbiSizeAdvanced{ .scalar = target.c_type_byte_size(.long) }, | ||
| 1373 | .c_ulong => return AbiSizeAdvanced{ .scalar = target.c_type_byte_size(.ulong) }, | ||
| 1374 | .c_longlong => return AbiSizeAdvanced{ .scalar = target.c_type_byte_size(.longlong) }, | ||
| 1375 | .c_ulonglong => return AbiSizeAdvanced{ .scalar = target.c_type_byte_size(.ulonglong) }, | ||
| 1376 | .c_longdouble => return AbiSizeAdvanced{ .scalar = target.c_type_byte_size(.longdouble) }, | ||
| 1377 | |||
| 1378 | .anyopaque, | ||
| 1379 | .void, | ||
| 1380 | .type, | ||
| 1381 | .comptime_int, | ||
| 1382 | .comptime_float, | ||
| 1383 | .null, | ||
| 1384 | .undefined, | ||
| 1385 | .enum_literal, | ||
| 1386 | => return AbiSizeAdvanced{ .scalar = 0 }, | ||
| 1387 | |||
| 1388 | .anyerror, .adhoc_inferred_error_set => { | ||
| 1389 | const bits = mod.errorSetBits(); | ||
| 1390 | if (bits == 0) return AbiSizeAdvanced{ .scalar = 0 }; | ||
| 1391 | return AbiSizeAdvanced{ .scalar = intAbiSize(bits, target, use_llvm) }; | ||
| 1392 | }, | ||
| 1393 | |||
| 1394 | .prefetch_options => unreachable, // missing call to resolveTypeFields | ||
| 1395 | .export_options => unreachable, // missing call to resolveTypeFields | ||
| 1396 | .extern_options => unreachable, // missing call to resolveTypeFields | ||
| 1397 | |||
| 1398 | .type_info => unreachable, | ||
| 1399 | .noreturn => unreachable, | ||
| 1400 | .generic_poison => unreachable, | ||
| 1401 | }, | ||
| 1402 | .struct_type => { | ||
| 1403 | const struct_type = ip.loadStructType(ty.toIntern()); | ||
| 1404 | switch (strat) { | ||
| 1405 | .sema => |sema| try sema.resolveTypeLayout(ty), | ||
| 1406 | .lazy => switch (struct_type.layout) { | ||
| 1407 | .@"packed" => { | ||
| 1408 | if (struct_type.backingIntType(ip).* == .none) return .{ | ||
| 1409 | .val = Value.fromInterned((try mod.intern(.{ .int = .{ | ||
| 1410 | .ty = .comptime_int_type, | ||
| 1411 | .storage = .{ .lazy_size = ty.toIntern() }, | ||
| 1412 | } }))), | ||
| 1413 | }; | ||
| 1414 | }, | ||
| 1415 | .auto, .@"extern" => { | ||
| 1416 | if (!struct_type.haveLayout(ip)) return .{ | ||
| 1417 | .val = Value.fromInterned((try mod.intern(.{ .int = .{ | ||
| 1418 | .ty = .comptime_int_type, | ||
| 1419 | .storage = .{ .lazy_size = ty.toIntern() }, | ||
| 1420 | } }))), | ||
| 1421 | }; | ||
| 1422 | }, | ||
| 1423 | }, | ||
| 1424 | .eager => {}, | ||
| 1425 | } | ||
| 1426 | switch (struct_type.layout) { | ||
| 1427 | .@"packed" => return .{ | ||
| 1428 | .scalar = Type.fromInterned(struct_type.backingIntType(ip).*).abiSize(mod), | ||
| 1429 | }, | ||
| 1430 | .auto, .@"extern" => { | ||
| 1431 | assert(struct_type.haveLayout(ip)); | ||
| 1432 | return .{ .scalar = struct_type.size(ip).* }; | ||
| 1433 | }, | ||
| 1434 | } | ||
| 1435 | }, | ||
| 1436 | .anon_struct_type => |tuple| { | ||
| 1437 | switch (strat) { | ||
| 1438 | .sema => |sema| try sema.resolveTypeLayout(ty), | ||
| 1439 | .lazy, .eager => {}, | ||
| 1440 | } | ||
| 1441 | const field_count = tuple.types.len; | ||
| 1442 | if (field_count == 0) { | ||
| 1443 | return AbiSizeAdvanced{ .scalar = 0 }; | ||
| 1444 | } | ||
| 1445 | return AbiSizeAdvanced{ .scalar = ty.structFieldOffset(field_count, mod) }; | ||
| 1446 | }, | ||
| 1447 | |||
| 1448 | .union_type => { | ||
| 1449 | const union_type = ip.loadUnionType(ty.toIntern()); | ||
| 1450 | switch (strat) { | ||
| 1451 | .sema => |sema| try sema.resolveTypeLayout(ty), | ||
| 1452 | .lazy => if (!union_type.flagsPtr(ip).status.haveLayout()) return .{ | ||
| 1453 | .val = Value.fromInterned((try mod.intern(.{ .int = .{ | ||
| 1454 | .ty = .comptime_int_type, | ||
| 1455 | .storage = .{ .lazy_size = ty.toIntern() }, | ||
| 1456 | } }))), | ||
| 1457 | }, | ||
| 1458 | .eager => {}, | ||
| 1459 | } | ||
| 1460 | |||
| 1461 | assert(union_type.haveLayout(ip)); | ||
| 1462 | return .{ .scalar = union_type.size(ip).* }; | ||
| 1463 | }, | ||
| 1464 | .opaque_type => unreachable, // no size available | ||
| 1465 | .enum_type => return .{ .scalar = Type.fromInterned(ip.loadEnumType(ty.toIntern()).tag_ty).abiSize(mod) }, | ||
| 1466 | |||
| 1467 | // values, not types | ||
| 1468 | .undef, | ||
| 1469 | .simple_value, | ||
| 1470 | .variable, | ||
| 1471 | .extern_func, | ||
| 1472 | .func, | ||
| 1473 | .int, | ||
| 1474 | .err, | ||
| 1475 | .error_union, | ||
| 1476 | .enum_literal, | ||
| 1477 | .enum_tag, | ||
| 1478 | .empty_enum_value, | ||
| 1479 | .float, | ||
| 1480 | .ptr, | ||
| 1481 | .slice, | ||
| 1482 | .opt, | ||
| 1483 | .aggregate, | ||
| 1484 | .un, | ||
| 1485 | // memoization, not types | ||
| 1486 | .memoized_call, | ||
| 1487 | => unreachable, | ||
| 1488 | }, | ||
| 1489 | } | ||
| 1490 | } | ||
| 1491 | |||
| 1492 | fn abiSizeAdvancedOptional( | ||
| 1493 | ty: Type, | ||
| 1494 | mod: *Module, | ||
| 1495 | strat: AbiAlignmentAdvancedStrat, | ||
| 1496 | ) Module.CompileError!AbiSizeAdvanced { | ||
| 1497 | const child_ty = ty.optionalChild(mod); | ||
| 1498 | |||
| 1499 | if (child_ty.isNoReturn(mod)) { | ||
| 1500 | return AbiSizeAdvanced{ .scalar = 0 }; | ||
| 1501 | } | ||
| 1502 | |||
| 1503 | if (!(child_ty.hasRuntimeBitsAdvanced(mod, false, strat) catch |err| switch (err) { | ||
| 1504 | error.NeedLazy => return .{ .val = Value.fromInterned((try mod.intern(.{ .int = .{ | ||
| 1505 | .ty = .comptime_int_type, | ||
| 1506 | .storage = .{ .lazy_size = ty.toIntern() }, | ||
| 1507 | } }))) }, | ||
| 1508 | else => |e| return e, | ||
| 1509 | })) return AbiSizeAdvanced{ .scalar = 1 }; | ||
| 1510 | |||
| 1511 | if (ty.optionalReprIsPayload(mod)) { | ||
| 1512 | return abiSizeAdvanced(child_ty, mod, strat); | ||
| 1513 | } | ||
| 1514 | |||
| 1515 | const payload_size = switch (try child_ty.abiSizeAdvanced(mod, strat)) { | ||
| 1516 | .scalar => |elem_size| elem_size, | ||
| 1517 | .val => switch (strat) { | ||
| 1518 | .sema => unreachable, | ||
| 1519 | .eager => unreachable, | ||
| 1520 | .lazy => return .{ .val = Value.fromInterned((try mod.intern(.{ .int = .{ | ||
| 1521 | .ty = .comptime_int_type, | ||
| 1522 | .storage = .{ .lazy_size = ty.toIntern() }, | ||
| 1523 | } }))) }, | ||
| 1524 | }, | ||
| 1525 | }; | ||
| 1526 | |||
| 1527 | // Optional types are represented as a struct with the child type as the first | ||
| 1528 | // field and a boolean as the second. Since the child type's abi alignment is | ||
| 1529 | // guaranteed to be >= that of bool's (1 byte) the added size is exactly equal | ||
| 1530 | // to the child type's ABI alignment. | ||
| 1531 | return AbiSizeAdvanced{ | ||
| 1532 | .scalar = (child_ty.abiAlignment(mod).toByteUnits() orelse 0) + payload_size, | ||
| 1533 | }; | ||
| 1534 | } | ||
| 1535 | |||
| 1536 | pub fn ptrAbiAlignment(target: Target) Alignment { | ||
| 1537 | return Alignment.fromNonzeroByteUnits(@divExact(target.ptrBitWidth(), 8)); | ||
| 1538 | } | ||
| 1539 | |||
| 1540 | pub fn intAbiSize(bits: u16, target: Target, use_llvm: bool) u64 { | ||
| 1541 | return intAbiAlignment(bits, target, use_llvm).forward(@as(u16, @intCast((@as(u17, bits) + 7) / 8))); | ||
| 1542 | } | ||
| 1543 | |||
| 1544 | pub fn intAbiAlignment(bits: u16, target: Target, use_llvm: bool) Alignment { | ||
| 1545 | return switch (target.cpu.arch) { | ||
| 1546 | .x86 => switch (bits) { | ||
| 1547 | 0 => .none, | ||
| 1548 | 1...8 => .@"1", | ||
| 1549 | 9...16 => .@"2", | ||
| 1550 | 17...64 => .@"4", | ||
| 1551 | else => .@"16", | ||
| 1552 | }, | ||
| 1553 | .x86_64 => switch (bits) { | ||
| 1554 | 0 => .none, | ||
| 1555 | 1...8 => .@"1", | ||
| 1556 | 9...16 => .@"2", | ||
| 1557 | 17...32 => .@"4", | ||
| 1558 | 33...64 => .@"8", | ||
| 1559 | else => switch (target_util.zigBackend(target, use_llvm)) { | ||
| 1560 | .stage2_x86_64 => .@"8", | ||
| 1561 | else => .@"16", | ||
| 1562 | }, | ||
| 1563 | }, | ||
| 1564 | else => return Alignment.fromByteUnits(@min( | ||
| 1565 | std.math.ceilPowerOfTwoPromote(u16, @as(u16, @intCast((@as(u17, bits) + 7) / 8))), | ||
| 1566 | maxIntAlignment(target, use_llvm), | ||
| 1567 | )), | ||
| 1568 | }; | ||
| 1569 | } | ||
| 1570 | |||
| 1571 | pub fn maxIntAlignment(target: std.Target, use_llvm: bool) u16 { | ||
| 1572 | return switch (target.cpu.arch) { | ||
| 1573 | .avr => 1, | ||
| 1574 | .msp430 => 2, | ||
| 1575 | .xcore => 4, | ||
| 1576 | |||
| 1577 | .arm, | ||
| 1578 | .armeb, | ||
| 1579 | .thumb, | ||
| 1580 | .thumbeb, | ||
| 1581 | .hexagon, | ||
| 1582 | .mips, | ||
| 1583 | .mipsel, | ||
| 1584 | .powerpc, | ||
| 1585 | .powerpcle, | ||
| 1586 | .r600, | ||
| 1587 | .amdgcn, | ||
| 1588 | .riscv32, | ||
| 1589 | .sparc, | ||
| 1590 | .sparcel, | ||
| 1591 | .s390x, | ||
| 1592 | .lanai, | ||
| 1593 | .wasm32, | ||
| 1594 | .wasm64, | ||
| 1595 | => 8, | ||
| 1596 | |||
| 1597 | // For these, LLVMABIAlignmentOfType(i128) reports 8. Note that 16 | ||
| 1598 | // is a relevant number in three cases: | ||
| 1599 | // 1. Different machine code instruction when loading into SIMD register. | ||
| 1600 | // 2. The C ABI wants 16 for extern structs. | ||
| 1601 | // 3. 16-byte cmpxchg needs 16-byte alignment. | ||
| 1602 | // Same logic for powerpc64, mips64, sparc64. | ||
| 1603 | .powerpc64, | ||
| 1604 | .powerpc64le, | ||
| 1605 | .mips64, | ||
| 1606 | .mips64el, | ||
| 1607 | .sparc64, | ||
| 1608 | => switch (target.ofmt) { | ||
| 1609 | .c => 16, | ||
| 1610 | else => 8, | ||
| 1611 | }, | ||
| 1612 | |||
| 1613 | .x86_64 => switch (target_util.zigBackend(target, use_llvm)) { | ||
| 1614 | .stage2_x86_64 => 8, | ||
| 1615 | else => 16, | ||
| 1616 | }, | ||
| 1617 | |||
| 1618 | // Even LLVMABIAlignmentOfType(i128) agrees on these targets. | ||
| 1619 | .x86, | ||
| 1620 | .aarch64, | ||
| 1621 | .aarch64_be, | ||
| 1622 | .aarch64_32, | ||
| 1623 | .riscv64, | ||
| 1624 | .bpfel, | ||
| 1625 | .bpfeb, | ||
| 1626 | .nvptx, | ||
| 1627 | .nvptx64, | ||
| 1628 | => 16, | ||
| 1629 | |||
| 1630 | // Below this comment are unverified but based on the fact that C requires | ||
| 1631 | // int128_t to be 16 bytes aligned, it's a safe default. | ||
| 1632 | .spu_2, | ||
| 1633 | .csky, | ||
| 1634 | .arc, | ||
| 1635 | .m68k, | ||
| 1636 | .tce, | ||
| 1637 | .tcele, | ||
| 1638 | .le32, | ||
| 1639 | .amdil, | ||
| 1640 | .hsail, | ||
| 1641 | .spir, | ||
| 1642 | .kalimba, | ||
| 1643 | .renderscript32, | ||
| 1644 | .spirv, | ||
| 1645 | .spirv32, | ||
| 1646 | .shave, | ||
| 1647 | .le64, | ||
| 1648 | .amdil64, | ||
| 1649 | .hsail64, | ||
| 1650 | .spir64, | ||
| 1651 | .renderscript64, | ||
| 1652 | .ve, | ||
| 1653 | .spirv64, | ||
| 1654 | .dxil, | ||
| 1655 | .loongarch32, | ||
| 1656 | .loongarch64, | ||
| 1657 | .xtensa, | ||
| 1658 | => 16, | ||
| 1659 | }; | ||
| 1660 | } | ||
| 1661 | |||
| 1662 | pub fn bitSize(ty: Type, mod: *Module) u64 { | ||
| 1663 | return bitSizeAdvanced(ty, mod, null) catch unreachable; | ||
| 1664 | } | ||
| 1665 | |||
| 1666 | /// If you pass `opt_sema`, any recursive type resolutions will happen if | ||
| 1667 | /// necessary, possibly returning a CompileError. Passing `null` instead asserts | ||
| 1668 | /// the type is fully resolved, and there will be no error, guaranteed. | ||
| 1669 | pub fn bitSizeAdvanced( | ||
| 1670 | ty: Type, | ||
| 1671 | mod: *Module, | ||
| 1672 | opt_sema: ?*Sema, | ||
| 1673 | ) Module.CompileError!u64 { | ||
| 1674 | const target = mod.getTarget(); | ||
| 1675 | const ip = &mod.intern_pool; | ||
| 1676 | |||
| 1677 | const strat: AbiAlignmentAdvancedStrat = if (opt_sema) |sema| .{ .sema = sema } else .eager; | ||
| 1678 | |||
| 1679 | switch (ip.indexToKey(ty.toIntern())) { | ||
| 1680 | .int_type => |int_type| return int_type.bits, | ||
| 1681 | .ptr_type => |ptr_type| switch (ptr_type.flags.size) { | ||
| 1682 | .Slice => return target.ptrBitWidth() * 2, | ||
| 1683 | else => return target.ptrBitWidth(), | ||
| 1684 | }, | ||
| 1685 | .anyframe_type => return target.ptrBitWidth(), | ||
| 1686 | |||
| 1687 | .array_type => |array_type| { | ||
| 1688 | const len = array_type.lenIncludingSentinel(); | ||
| 1689 | if (len == 0) return 0; | ||
| 1690 | const elem_ty = Type.fromInterned(array_type.child); | ||
| 1691 | const elem_size = @max( | ||
| 1692 | (try elem_ty.abiAlignmentAdvanced(mod, strat)).scalar.toByteUnits() orelse 0, | ||
| 1693 | (try elem_ty.abiSizeAdvanced(mod, strat)).scalar, | ||
| 1694 | ); | ||
| 1695 | if (elem_size == 0) return 0; | ||
| 1696 | const elem_bit_size = try bitSizeAdvanced(elem_ty, mod, opt_sema); | ||
| 1697 | return (len - 1) * 8 * elem_size + elem_bit_size; | ||
| 1698 | }, | ||
| 1699 | .vector_type => |vector_type| { | ||
| 1700 | const child_ty = Type.fromInterned(vector_type.child); | ||
| 1701 | const elem_bit_size = try bitSizeAdvanced(child_ty, mod, opt_sema); | ||
| 1702 | return elem_bit_size * vector_type.len; | ||
| 1703 | }, | ||
| 1704 | .opt_type => { | ||
| 1705 | // Optionals and error unions are not packed so their bitsize | ||
| 1706 | // includes padding bits. | ||
| 1707 | return (try abiSizeAdvanced(ty, mod, strat)).scalar * 8; | ||
| 1708 | }, | ||
| 1709 | |||
| 1710 | .error_set_type, .inferred_error_set_type => return mod.errorSetBits(), | ||
| 1711 | |||
| 1712 | .error_union_type => { | ||
| 1713 | // Optionals and error unions are not packed so their bitsize | ||
| 1714 | // includes padding bits. | ||
| 1715 | return (try abiSizeAdvanced(ty, mod, strat)).scalar * 8; | ||
| 1716 | }, | ||
| 1717 | .func_type => unreachable, // represents machine code; not a pointer | ||
| 1718 | .simple_type => |t| switch (t) { | ||
| 1719 | .f16 => return 16, | ||
| 1720 | .f32 => return 32, | ||
| 1721 | .f64 => return 64, | ||
| 1722 | .f80 => return 80, | ||
| 1723 | .f128 => return 128, | ||
| 1724 | |||
| 1725 | .usize, | ||
| 1726 | .isize, | ||
| 1727 | => return target.ptrBitWidth(), | ||
| 1728 | |||
| 1729 | .c_char => return target.c_type_bit_size(.char), | ||
| 1730 | .c_short => return target.c_type_bit_size(.short), | ||
| 1731 | .c_ushort => return target.c_type_bit_size(.ushort), | ||
| 1732 | .c_int => return target.c_type_bit_size(.int), | ||
| 1733 | .c_uint => return target.c_type_bit_size(.uint), | ||
| 1734 | .c_long => return target.c_type_bit_size(.long), | ||
| 1735 | .c_ulong => return target.c_type_bit_size(.ulong), | ||
| 1736 | .c_longlong => return target.c_type_bit_size(.longlong), | ||
| 1737 | .c_ulonglong => return target.c_type_bit_size(.ulonglong), | ||
| 1738 | .c_longdouble => return target.c_type_bit_size(.longdouble), | ||
| 1739 | |||
| 1740 | .bool => return 1, | ||
| 1741 | .void => return 0, | ||
| 1742 | |||
| 1743 | .anyerror, | ||
| 1744 | .adhoc_inferred_error_set, | ||
| 1745 | => return mod.errorSetBits(), | ||
| 1746 | |||
| 1747 | .anyopaque => unreachable, | ||
| 1748 | .type => unreachable, | ||
| 1749 | .comptime_int => unreachable, | ||
| 1750 | .comptime_float => unreachable, | ||
| 1751 | .noreturn => unreachable, | ||
| 1752 | .null => unreachable, | ||
| 1753 | .undefined => unreachable, | ||
| 1754 | .enum_literal => unreachable, | ||
| 1755 | .generic_poison => unreachable, | ||
| 1756 | |||
| 1757 | .atomic_order => unreachable, | ||
| 1758 | .atomic_rmw_op => unreachable, | ||
| 1759 | .calling_convention => unreachable, | ||
| 1760 | .address_space => unreachable, | ||
| 1761 | .float_mode => unreachable, | ||
| 1762 | .reduce_op => unreachable, | ||
| 1763 | .call_modifier => unreachable, | ||
| 1764 | .prefetch_options => unreachable, | ||
| 1765 | .export_options => unreachable, | ||
| 1766 | .extern_options => unreachable, | ||
| 1767 | .type_info => unreachable, | ||
| 1768 | }, | ||
| 1769 | .struct_type => { | ||
| 1770 | const struct_type = ip.loadStructType(ty.toIntern()); | ||
| 1771 | const is_packed = struct_type.layout == .@"packed"; | ||
| 1772 | if (opt_sema) |sema| { | ||
| 1773 | try sema.resolveTypeFields(ty); | ||
| 1774 | if (is_packed) try sema.resolveTypeLayout(ty); | ||
| 1775 | } | ||
| 1776 | if (is_packed) { | ||
| 1777 | return try Type.fromInterned(struct_type.backingIntType(ip).*).bitSizeAdvanced(mod, opt_sema); | ||
| 1778 | } | ||
| 1779 | return (try ty.abiSizeAdvanced(mod, strat)).scalar * 8; | ||
| 1780 | }, | ||
| 1781 | |||
| 1782 | .anon_struct_type => { | ||
| 1783 | if (opt_sema) |sema| try sema.resolveTypeFields(ty); | ||
| 1784 | return (try ty.abiSizeAdvanced(mod, strat)).scalar * 8; | ||
| 1785 | }, | ||
| 1786 | |||
| 1787 | .union_type => { | ||
| 1788 | const union_type = ip.loadUnionType(ty.toIntern()); | ||
| 1789 | const is_packed = ty.containerLayout(mod) == .@"packed"; | ||
| 1790 | if (opt_sema) |sema| { | ||
| 1791 | try sema.resolveTypeFields(ty); | ||
| 1792 | if (is_packed) try sema.resolveTypeLayout(ty); | ||
| 1793 | } | ||
| 1794 | if (!is_packed) { | ||
| 1795 | return (try ty.abiSizeAdvanced(mod, strat)).scalar * 8; | ||
| 1796 | } | ||
| 1797 | assert(union_type.flagsPtr(ip).status.haveFieldTypes()); | ||
| 1798 | |||
| 1799 | var size: u64 = 0; | ||
| 1800 | for (0..union_type.field_types.len) |field_index| { | ||
| 1801 | const field_ty = union_type.field_types.get(ip)[field_index]; | ||
| 1802 | size = @max(size, try bitSizeAdvanced(Type.fromInterned(field_ty), mod, opt_sema)); | ||
| 1803 | } | ||
| 1804 | |||
| 1805 | return size; | ||
| 1806 | }, | ||
| 1807 | .opaque_type => unreachable, | ||
| 1808 | .enum_type => return bitSizeAdvanced(Type.fromInterned(ip.loadEnumType(ty.toIntern()).tag_ty), mod, opt_sema), | ||
| 1809 | |||
| 1810 | // values, not types | ||
| 1811 | .undef, | ||
| 1812 | .simple_value, | ||
| 1813 | .variable, | ||
| 1814 | .extern_func, | ||
| 1815 | .func, | ||
| 1816 | .int, | ||
| 1817 | .err, | ||
| 1818 | .error_union, | ||
| 1819 | .enum_literal, | ||
| 1820 | .enum_tag, | ||
| 1821 | .empty_enum_value, | ||
| 1822 | .float, | ||
| 1823 | .ptr, | ||
| 1824 | .slice, | ||
| 1825 | .opt, | ||
| 1826 | .aggregate, | ||
| 1827 | .un, | ||
| 1828 | // memoization, not types | ||
| 1829 | .memoized_call, | ||
| 1830 | => unreachable, | ||
| 1831 | } | ||
| 1832 | } | ||
| 1833 | |||
| 1834 | /// Returns true if the type's layout is already resolved and it is safe | ||
| 1835 | /// to use `abiSize`, `abiAlignment` and `bitSize` on it. | ||
| 1836 | pub fn layoutIsResolved(ty: Type, mod: *Module) bool { | ||
| 1837 | const ip = &mod.intern_pool; | ||
| 1838 | return switch (ip.indexToKey(ty.toIntern())) { | ||
| 1839 | .struct_type => ip.loadStructType(ty.toIntern()).haveLayout(ip), | ||
| 1840 | .union_type => ip.loadUnionType(ty.toIntern()).haveLayout(ip), | ||
| 1841 | .array_type => |array_type| { | ||
| 1842 | if (array_type.lenIncludingSentinel() == 0) return true; | ||
| 1843 | return Type.fromInterned(array_type.child).layoutIsResolved(mod); | ||
| 1844 | }, | ||
| 1845 | .opt_type => |child| Type.fromInterned(child).layoutIsResolved(mod), | ||
| 1846 | .error_union_type => |k| Type.fromInterned(k.payload_type).layoutIsResolved(mod), | ||
| 1847 | else => true, | ||
| 1848 | }; | ||
| 1849 | } | ||
| 1850 | |||
| 1851 | pub fn isSinglePointer(ty: Type, mod: *const Module) bool { | ||
| 1852 | return switch (mod.intern_pool.indexToKey(ty.toIntern())) { | ||
| 1853 | .ptr_type => |ptr_info| ptr_info.flags.size == .One, | ||
| 1854 | else => false, | ||
| 1855 | }; | ||
| 1856 | } | ||
| 1857 | |||
| 1858 | /// Asserts `ty` is a pointer. | ||
| 1859 | pub fn ptrSize(ty: Type, mod: *const Module) std.builtin.Type.Pointer.Size { | ||
| 1860 | return ptrSizeOrNull(ty, mod).?; | ||
| 1861 | } | ||
| 1862 | |||
| 1863 | /// Returns `null` if `ty` is not a pointer. | ||
| 1864 | pub fn ptrSizeOrNull(ty: Type, mod: *const Module) ?std.builtin.Type.Pointer.Size { | ||
| 1865 | return switch (mod.intern_pool.indexToKey(ty.toIntern())) { | ||
| 1866 | .ptr_type => |ptr_info| ptr_info.flags.size, | ||
| 1867 | else => null, | ||
| 1868 | }; | ||
| 1869 | } | ||
| 1870 | |||
| 1871 | pub fn isSlice(ty: Type, mod: *const Module) bool { | ||
| 1872 | return switch (mod.intern_pool.indexToKey(ty.toIntern())) { | ||
| 1873 | .ptr_type => |ptr_type| ptr_type.flags.size == .Slice, | ||
| 1874 | else => false, | ||
| 1875 | }; | ||
| 1876 | } | ||
| 1877 | |||
| 1878 | pub fn slicePtrFieldType(ty: Type, mod: *const Module) Type { | ||
| 1879 | return Type.fromInterned(mod.intern_pool.slicePtrType(ty.toIntern())); | ||
| 1880 | } | ||
| 1881 | |||
| 1882 | pub fn isConstPtr(ty: Type, mod: *const Module) bool { | ||
| 1883 | return switch (mod.intern_pool.indexToKey(ty.toIntern())) { | ||
| 1884 | .ptr_type => |ptr_type| ptr_type.flags.is_const, | ||
| 1885 | else => false, | ||
| 1886 | }; | ||
| 1887 | } | ||
| 1888 | |||
| 1889 | pub fn isVolatilePtr(ty: Type, mod: *const Module) bool { | ||
| 1890 | return isVolatilePtrIp(ty, &mod.intern_pool); | ||
| 1891 | } | ||
| 1892 | |||
| 1893 | pub fn isVolatilePtrIp(ty: Type, ip: *const InternPool) bool { | ||
| 1894 | return switch (ip.indexToKey(ty.toIntern())) { | ||
| 1895 | .ptr_type => |ptr_type| ptr_type.flags.is_volatile, | ||
| 1896 | else => false, | ||
| 1897 | }; | ||
| 1898 | } | ||
| 1899 | |||
| 1900 | pub fn isAllowzeroPtr(ty: Type, mod: *const Module) bool { | ||
| 1901 | return switch (mod.intern_pool.indexToKey(ty.toIntern())) { | ||
| 1902 | .ptr_type => |ptr_type| ptr_type.flags.is_allowzero, | ||
| 1903 | .opt_type => true, | ||
| 1904 | else => false, | ||
| 1905 | }; | ||
| 1906 | } | ||
| 1907 | |||
| 1908 | pub fn isCPtr(ty: Type, mod: *const Module) bool { | ||
| 1909 | return switch (mod.intern_pool.indexToKey(ty.toIntern())) { | ||
| 1910 | .ptr_type => |ptr_type| ptr_type.flags.size == .C, | ||
| 1911 | else => false, | ||
| 1912 | }; | ||
| 1913 | } | ||
| 1914 | |||
| 1915 | pub fn isPtrAtRuntime(ty: Type, mod: *const Module) bool { | ||
| 1916 | return switch (mod.intern_pool.indexToKey(ty.toIntern())) { | ||
| 1917 | .ptr_type => |ptr_type| switch (ptr_type.flags.size) { | ||
| 1918 | .Slice => false, | ||
| 1919 | .One, .Many, .C => true, | ||
| 1920 | }, | ||
| 1921 | .opt_type => |child| switch (mod.intern_pool.indexToKey(child)) { | ||
| 1922 | .ptr_type => |p| switch (p.flags.size) { | ||
| 1923 | .Slice, .C => false, | ||
| 1924 | .Many, .One => !p.flags.is_allowzero, | ||
| 1925 | }, | ||
| 1926 | else => false, | ||
| 1927 | }, | ||
| 1928 | else => false, | ||
| 1929 | }; | ||
| 1930 | } | ||
| 1931 | |||
| 1932 | /// For pointer-like optionals, returns true, otherwise returns the allowzero property | ||
| 1933 | /// of pointers. | ||
| 1934 | pub fn ptrAllowsZero(ty: Type, mod: *const Module) bool { | ||
| 1935 | if (ty.isPtrLikeOptional(mod)) { | ||
| 1936 | return true; | ||
| 1937 | } | ||
| 1938 | return ty.ptrInfo(mod).flags.is_allowzero; | ||
| 1939 | } | ||
| 1940 | |||
| 1941 | /// See also `isPtrLikeOptional`. | ||
| 1942 | pub fn optionalReprIsPayload(ty: Type, mod: *const Module) bool { | ||
| 1943 | return switch (mod.intern_pool.indexToKey(ty.toIntern())) { | ||
| 1944 | .opt_type => |child_type| child_type == .anyerror_type or switch (mod.intern_pool.indexToKey(child_type)) { | ||
| 1945 | .ptr_type => |ptr_type| ptr_type.flags.size != .C and !ptr_type.flags.is_allowzero, | ||
| 1946 | .error_set_type, .inferred_error_set_type => true, | ||
| 1947 | else => false, | ||
| 1948 | }, | ||
| 1949 | .ptr_type => |ptr_type| ptr_type.flags.size == .C, | ||
| 1950 | else => false, | ||
| 1951 | }; | ||
| 1952 | } | ||
| 1953 | |||
| 1954 | /// Returns true if the type is optional and would be lowered to a single pointer | ||
| 1955 | /// address value, using 0 for null. Note that this returns true for C pointers. | ||
| 1956 | /// This function must be kept in sync with `Sema.typePtrOrOptionalPtrTy`. | ||
| 1957 | pub fn isPtrLikeOptional(ty: Type, mod: *const Module) bool { | ||
| 1958 | return switch (mod.intern_pool.indexToKey(ty.toIntern())) { | ||
| 1959 | .ptr_type => |ptr_type| ptr_type.flags.size == .C, | ||
| 1960 | .opt_type => |child| switch (mod.intern_pool.indexToKey(child)) { | ||
| 1961 | .ptr_type => |ptr_type| switch (ptr_type.flags.size) { | ||
| 1962 | .Slice, .C => false, | ||
| 1963 | .Many, .One => !ptr_type.flags.is_allowzero, | ||
| 1964 | }, | ||
| 1965 | else => false, | ||
| 1966 | }, | ||
| 1967 | else => false, | ||
| 1968 | }; | ||
| 1969 | } | ||
| 1970 | |||
| 1971 | /// For *[N]T, returns [N]T. | ||
| 1972 | /// For *T, returns T. | ||
| 1973 | /// For [*]T, returns T. | ||
| 1974 | pub fn childType(ty: Type, mod: *const Module) Type { | ||
| 1975 | return childTypeIp(ty, &mod.intern_pool); | ||
| 1976 | } | ||
| 1977 | |||
| 1978 | pub fn childTypeIp(ty: Type, ip: *const InternPool) Type { | ||
| 1979 | return Type.fromInterned(ip.childType(ty.toIntern())); | ||
| 1980 | } | ||
| 1981 | |||
| 1982 | /// For *[N]T, returns T. | ||
| 1983 | /// For ?*T, returns T. | ||
| 1984 | /// For ?*[N]T, returns T. | ||
| 1985 | /// For ?[*]T, returns T. | ||
| 1986 | /// For *T, returns T. | ||
| 1987 | /// For [*]T, returns T. | ||
| 1988 | /// For [N]T, returns T. | ||
| 1989 | /// For []T, returns T. | ||
| 1990 | /// For anyframe->T, returns T. | ||
| 1991 | pub fn elemType2(ty: Type, mod: *const Module) Type { | ||
| 1992 | return switch (mod.intern_pool.indexToKey(ty.toIntern())) { | ||
| 1993 | .ptr_type => |ptr_type| switch (ptr_type.flags.size) { | ||
| 1994 | .One => Type.fromInterned(ptr_type.child).shallowElemType(mod), | ||
| 1995 | .Many, .C, .Slice => Type.fromInterned(ptr_type.child), | ||
| 1996 | }, | ||
| 1997 | .anyframe_type => |child| { | ||
| 1998 | assert(child != .none); | ||
| 1999 | return Type.fromInterned(child); | ||
| 2000 | }, | ||
| 2001 | .vector_type => |vector_type| Type.fromInterned(vector_type.child), | ||
| 2002 | .array_type => |array_type| Type.fromInterned(array_type.child), | ||
| 2003 | .opt_type => |child| Type.fromInterned(mod.intern_pool.childType(child)), | ||
| 2004 | else => unreachable, | ||
| 2005 | }; | ||
| 2006 | } | ||
| 2007 | |||
| 2008 | fn shallowElemType(child_ty: Type, mod: *const Module) Type { | ||
| 2009 | return switch (child_ty.zigTypeTag(mod)) { | ||
| 2010 | .Array, .Vector => child_ty.childType(mod), | ||
| 2011 | else => child_ty, | ||
| 2012 | }; | ||
| 2013 | } | ||
| 2014 | |||
| 2015 | /// For vectors, returns the element type. Otherwise returns self. | ||
| 2016 | pub fn scalarType(ty: Type, mod: *Module) Type { | ||
| 2017 | return switch (ty.zigTypeTag(mod)) { | ||
| 2018 | .Vector => ty.childType(mod), | ||
| 2019 | else => ty, | ||
| 2020 | }; | ||
| 2021 | } | ||
| 2022 | |||
| 2023 | /// Asserts that the type is an optional. | ||
| 2024 | /// Note that for C pointers this returns the type unmodified. | ||
| 2025 | pub fn optionalChild(ty: Type, mod: *const Module) Type { | ||
| 2026 | return switch (mod.intern_pool.indexToKey(ty.toIntern())) { | ||
| 2027 | .opt_type => |child| Type.fromInterned(child), | ||
| 2028 | .ptr_type => |ptr_type| b: { | ||
| 2029 | assert(ptr_type.flags.size == .C); | ||
| 2030 | break :b ty; | ||
| 2031 | }, | ||
| 2032 | else => unreachable, | ||
| 2033 | }; | ||
| 2034 | } | ||
| 2035 | |||
| 2036 | /// Returns the tag type of a union, if the type is a union and it has a tag type. | ||
| 2037 | /// Otherwise, returns `null`. | ||
| 2038 | pub fn unionTagType(ty: Type, mod: *Module) ?Type { | ||
| 2039 | const ip = &mod.intern_pool; | ||
| 2040 | switch (ip.indexToKey(ty.toIntern())) { | ||
| 2041 | .union_type => {}, | ||
| 2042 | else => return null, | ||
| 2043 | } | ||
| 2044 | const union_type = ip.loadUnionType(ty.toIntern()); | ||
| 2045 | switch (union_type.flagsPtr(ip).runtime_tag) { | ||
| 2046 | .tagged => { | ||
| 2047 | assert(union_type.flagsPtr(ip).status.haveFieldTypes()); | ||
| 2048 | return Type.fromInterned(union_type.enum_tag_ty); | ||
| 2049 | }, | ||
| 2050 | else => return null, | ||
| 2051 | } | ||
| 2052 | } | ||
| 2053 | |||
| 2054 | /// Same as `unionTagType` but includes safety tag. | ||
| 2055 | /// Codegen should use this version. | ||
| 2056 | pub fn unionTagTypeSafety(ty: Type, mod: *Module) ?Type { | ||
| 2057 | const ip = &mod.intern_pool; | ||
| 2058 | return switch (ip.indexToKey(ty.toIntern())) { | ||
| 2059 | .union_type => { | ||
| 2060 | const union_type = ip.loadUnionType(ty.toIntern()); | ||
| 2061 | if (!union_type.hasTag(ip)) return null; | ||
| 2062 | assert(union_type.haveFieldTypes(ip)); | ||
| 2063 | return Type.fromInterned(union_type.enum_tag_ty); | ||
| 2064 | }, | ||
| 2065 | else => null, | ||
| 2066 | }; | ||
| 2067 | } | ||
| 2068 | |||
| 2069 | /// Asserts the type is a union; returns the tag type, even if the tag will | ||
| 2070 | /// not be stored at runtime. | ||
| 2071 | pub fn unionTagTypeHypothetical(ty: Type, mod: *Module) Type { | ||
| 2072 | const union_obj = mod.typeToUnion(ty).?; | ||
| 2073 | return Type.fromInterned(union_obj.enum_tag_ty); | ||
| 2074 | } | ||
| 2075 | |||
| 2076 | pub fn unionFieldType(ty: Type, enum_tag: Value, mod: *Module) ?Type { | ||
| 2077 | const ip = &mod.intern_pool; | ||
| 2078 | const union_obj = mod.typeToUnion(ty).?; | ||
| 2079 | const union_fields = union_obj.field_types.get(ip); | ||
| 2080 | const index = mod.unionTagFieldIndex(union_obj, enum_tag) orelse return null; | ||
| 2081 | return Type.fromInterned(union_fields[index]); | ||
| 2082 | } | ||
| 2083 | |||
| 2084 | pub fn unionFieldTypeByIndex(ty: Type, index: usize, mod: *Module) Type { | ||
| 2085 | const ip = &mod.intern_pool; | ||
| 2086 | const union_obj = mod.typeToUnion(ty).?; | ||
| 2087 | return Type.fromInterned(union_obj.field_types.get(ip)[index]); | ||
| 2088 | } | ||
| 2089 | |||
| 2090 | pub fn unionTagFieldIndex(ty: Type, enum_tag: Value, mod: *Module) ?u32 { | ||
| 2091 | const union_obj = mod.typeToUnion(ty).?; | ||
| 2092 | return mod.unionTagFieldIndex(union_obj, enum_tag); | ||
| 2093 | } | ||
| 2094 | |||
| 2095 | pub fn unionHasAllZeroBitFieldTypes(ty: Type, mod: *Module) bool { | ||
| 2096 | const ip = &mod.intern_pool; | ||
| 2097 | const union_obj = mod.typeToUnion(ty).?; | ||
| 2098 | for (union_obj.field_types.get(ip)) |field_ty| { | ||
| 2099 | if (Type.fromInterned(field_ty).hasRuntimeBits(mod)) return false; | ||
| 2100 | } | ||
| 2101 | return true; | ||
| 2102 | } | ||
| 2103 | |||
| 2104 | /// Returns the type used for backing storage of this union during comptime operations. | ||
| 2105 | /// Asserts the type is either an extern or packed union. | ||
| 2106 | pub fn unionBackingType(ty: Type, mod: *Module) !Type { | ||
| 2107 | return switch (ty.containerLayout(mod)) { | ||
| 2108 | .@"extern" => try mod.arrayType(.{ .len = ty.abiSize(mod), .child = .u8_type }), | ||
| 2109 | .@"packed" => try mod.intType(.unsigned, @intCast(ty.bitSize(mod))), | ||
| 2110 | .auto => unreachable, | ||
| 2111 | }; | ||
| 2112 | } | ||
| 2113 | |||
| 2114 | pub fn unionGetLayout(ty: Type, mod: *Module) Module.UnionLayout { | ||
| 2115 | const ip = &mod.intern_pool; | ||
| 2116 | const union_obj = ip.loadUnionType(ty.toIntern()); | ||
| 2117 | return mod.getUnionLayout(union_obj); | ||
| 2118 | } | ||
| 2119 | |||
| 2120 | pub fn containerLayout(ty: Type, mod: *Module) std.builtin.Type.ContainerLayout { | ||
| 2121 | const ip = &mod.intern_pool; | ||
| 2122 | return switch (ip.indexToKey(ty.toIntern())) { | ||
| 2123 | .struct_type => ip.loadStructType(ty.toIntern()).layout, | ||
| 2124 | .anon_struct_type => .auto, | ||
| 2125 | .union_type => ip.loadUnionType(ty.toIntern()).flagsPtr(ip).layout, | ||
| 2126 | else => unreachable, | ||
| 2127 | }; | ||
| 2128 | } | ||
| 2129 | |||
| 2130 | /// Asserts that the type is an error union. | ||
| 2131 | pub fn errorUnionPayload(ty: Type, mod: *Module) Type { | ||
| 2132 | return Type.fromInterned(mod.intern_pool.indexToKey(ty.toIntern()).error_union_type.payload_type); | ||
| 2133 | } | ||
| 2134 | |||
| 2135 | /// Asserts that the type is an error union. | ||
| 2136 | pub fn errorUnionSet(ty: Type, mod: *Module) Type { | ||
| 2137 | return Type.fromInterned(mod.intern_pool.errorUnionSet(ty.toIntern())); | ||
| 2138 | } | ||
| 2139 | |||
| 2140 | /// Returns false for unresolved inferred error sets. | ||
| 2141 | pub fn errorSetIsEmpty(ty: Type, mod: *Module) bool { | ||
| 2142 | const ip = &mod.intern_pool; | ||
| 2143 | return switch (ty.toIntern()) { | ||
| 2144 | .anyerror_type, .adhoc_inferred_error_set_type => false, | ||
| 2145 | else => switch (ip.indexToKey(ty.toIntern())) { | ||
| 2146 | .error_set_type => |error_set_type| error_set_type.names.len == 0, | ||
| 2147 | .inferred_error_set_type => |i| switch (ip.funcIesResolved(i).*) { | ||
| 2148 | .none, .anyerror_type => false, | ||
| 2149 | else => |t| ip.indexToKey(t).error_set_type.names.len == 0, | ||
| 2150 | }, | ||
| 2151 | else => unreachable, | ||
| 2152 | }, | ||
| 2153 | }; | ||
| 2154 | } | ||
| 2155 | |||
| 2156 | /// Returns true if it is an error set that includes anyerror, false otherwise. | ||
| 2157 | /// Note that the result may be a false negative if the type did not get error set | ||
| 2158 | /// resolution prior to this call. | ||
| 2159 | pub fn isAnyError(ty: Type, mod: *Module) bool { | ||
| 2160 | const ip = &mod.intern_pool; | ||
| 2161 | return switch (ty.toIntern()) { | ||
| 2162 | .anyerror_type => true, | ||
| 2163 | .adhoc_inferred_error_set_type => false, | ||
| 2164 | else => switch (mod.intern_pool.indexToKey(ty.toIntern())) { | ||
| 2165 | .inferred_error_set_type => |i| ip.funcIesResolved(i).* == .anyerror_type, | ||
| 2166 | else => false, | ||
| 2167 | }, | ||
| 2168 | }; | ||
| 2169 | } | ||
| 2170 | |||
| 2171 | pub fn isError(ty: Type, mod: *const Module) bool { | ||
| 2172 | return switch (ty.zigTypeTag(mod)) { | ||
| 2173 | .ErrorUnion, .ErrorSet => true, | ||
| 2174 | else => false, | ||
| 2175 | }; | ||
| 2176 | } | ||
| 2177 | |||
| 2178 | /// Returns whether ty, which must be an error set, includes an error `name`. | ||
| 2179 | /// Might return a false negative if `ty` is an inferred error set and not fully | ||
| 2180 | /// resolved yet. | ||
| 2181 | pub fn errorSetHasFieldIp( | ||
| 2182 | ip: *const InternPool, | ||
| 2183 | ty: InternPool.Index, | ||
| 2184 | name: InternPool.NullTerminatedString, | ||
| 2185 | ) bool { | ||
| 2186 | return switch (ty) { | ||
| 2187 | .anyerror_type => true, | ||
| 2188 | else => switch (ip.indexToKey(ty)) { | ||
| 2189 | .error_set_type => |error_set_type| error_set_type.nameIndex(ip, name) != null, | ||
| 2190 | .inferred_error_set_type => |i| switch (ip.funcIesResolved(i).*) { | ||
| 2191 | .anyerror_type => true, | ||
| 2192 | .none => false, | ||
| 2193 | else => |t| ip.indexToKey(t).error_set_type.nameIndex(ip, name) != null, | ||
| 2194 | }, | ||
| 2195 | else => unreachable, | ||
| 2196 | }, | ||
| 2197 | }; | ||
| 2198 | } | ||
| 2199 | |||
| 2200 | /// Returns whether ty, which must be an error set, includes an error `name`. | ||
| 2201 | /// Might return a false negative if `ty` is an inferred error set and not fully | ||
| 2202 | /// resolved yet. | ||
| 2203 | pub fn errorSetHasField(ty: Type, name: []const u8, mod: *Module) bool { | ||
| 2204 | const ip = &mod.intern_pool; | ||
| 2205 | return switch (ty.toIntern()) { | ||
| 2206 | .anyerror_type => true, | ||
| 2207 | else => switch (ip.indexToKey(ty.toIntern())) { | ||
| 2208 | .error_set_type => |error_set_type| { | ||
| 2209 | // If the string is not interned, then the field certainly is not present. | ||
| 2210 | const field_name_interned = ip.getString(name).unwrap() orelse return false; | ||
| 2211 | return error_set_type.nameIndex(ip, field_name_interned) != null; | ||
| 2212 | }, | ||
| 2213 | .inferred_error_set_type => |i| switch (ip.funcIesResolved(i).*) { | ||
| 2214 | .anyerror_type => true, | ||
| 2215 | .none => false, | ||
| 2216 | else => |t| { | ||
| 2217 | // If the string is not interned, then the field certainly is not present. | ||
| 2218 | const field_name_interned = ip.getString(name).unwrap() orelse return false; | ||
| 2219 | return ip.indexToKey(t).error_set_type.nameIndex(ip, field_name_interned) != null; | ||
| 2220 | }, | ||
| 2221 | }, | ||
| 2222 | else => unreachable, | ||
| 2223 | }, | ||
| 2224 | }; | ||
| 2225 | } | ||
| 2226 | |||
| 2227 | /// Asserts the type is an array or vector or struct. | ||
| 2228 | pub fn arrayLen(ty: Type, mod: *const Module) u64 { | ||
| 2229 | return ty.arrayLenIp(&mod.intern_pool); | ||
| 2230 | } | ||
| 2231 | |||
| 2232 | pub fn arrayLenIp(ty: Type, ip: *const InternPool) u64 { | ||
| 2233 | return ip.aggregateTypeLen(ty.toIntern()); | ||
| 2234 | } | ||
| 2235 | |||
| 2236 | pub fn arrayLenIncludingSentinel(ty: Type, mod: *const Module) u64 { | ||
| 2237 | return mod.intern_pool.aggregateTypeLenIncludingSentinel(ty.toIntern()); | ||
| 2238 | } | ||
| 2239 | |||
| 2240 | pub fn vectorLen(ty: Type, mod: *const Module) u32 { | ||
| 2241 | return switch (mod.intern_pool.indexToKey(ty.toIntern())) { | ||
| 2242 | .vector_type => |vector_type| vector_type.len, | ||
| 2243 | .anon_struct_type => |tuple| @intCast(tuple.types.len), | ||
| 2244 | else => unreachable, | ||
| 2245 | }; | ||
| 2246 | } | ||
| 2247 | |||
| 2248 | /// Asserts the type is an array, pointer or vector. | ||
| 2249 | pub fn sentinel(ty: Type, mod: *const Module) ?Value { | ||
| 2250 | return switch (mod.intern_pool.indexToKey(ty.toIntern())) { | ||
| 2251 | .vector_type, | ||
| 2252 | .struct_type, | ||
| 2253 | .anon_struct_type, | ||
| 2254 | => null, | ||
| 2255 | |||
| 2256 | .array_type => |t| if (t.sentinel != .none) Value.fromInterned(t.sentinel) else null, | ||
| 2257 | .ptr_type => |t| if (t.sentinel != .none) Value.fromInterned(t.sentinel) else null, | ||
| 2258 | |||
| 2259 | else => unreachable, | ||
| 2260 | }; | ||
| 2261 | } | ||
| 2262 | |||
| 2263 | /// Returns true if and only if the type is a fixed-width integer. | ||
| 2264 | pub fn isInt(self: Type, mod: *const Module) bool { | ||
| 2265 | return self.toIntern() != .comptime_int_type and | ||
| 2266 | mod.intern_pool.isIntegerType(self.toIntern()); | ||
| 2267 | } | ||
| 2268 | |||
| 2269 | /// Returns true if and only if the type is a fixed-width, signed integer. | ||
| 2270 | pub fn isSignedInt(ty: Type, mod: *const Module) bool { | ||
| 2271 | return switch (ty.toIntern()) { | ||
| 2272 | .c_char_type => mod.getTarget().charSignedness() == .signed, | ||
| 2273 | .isize_type, .c_short_type, .c_int_type, .c_long_type, .c_longlong_type => true, | ||
| 2274 | else => switch (mod.intern_pool.indexToKey(ty.toIntern())) { | ||
| 2275 | .int_type => |int_type| int_type.signedness == .signed, | ||
| 2276 | else => false, | ||
| 2277 | }, | ||
| 2278 | }; | ||
| 2279 | } | ||
| 2280 | |||
| 2281 | /// Returns true if and only if the type is a fixed-width, unsigned integer. | ||
| 2282 | pub fn isUnsignedInt(ty: Type, mod: *const Module) bool { | ||
| 2283 | return switch (ty.toIntern()) { | ||
| 2284 | .c_char_type => mod.getTarget().charSignedness() == .unsigned, | ||
| 2285 | .usize_type, .c_ushort_type, .c_uint_type, .c_ulong_type, .c_ulonglong_type => true, | ||
| 2286 | else => switch (mod.intern_pool.indexToKey(ty.toIntern())) { | ||
| 2287 | .int_type => |int_type| int_type.signedness == .unsigned, | ||
| 2288 | else => false, | ||
| 2289 | }, | ||
| 2290 | }; | ||
| 2291 | } | ||
| 2292 | |||
| 2293 | /// Returns true for integers, enums, error sets, and packed structs. | ||
| 2294 | /// If this function returns true, then intInfo() can be called on the type. | ||
| 2295 | pub fn isAbiInt(ty: Type, mod: *Module) bool { | ||
| 2296 | return switch (ty.zigTypeTag(mod)) { | ||
| 2297 | .Int, .Enum, .ErrorSet => true, | ||
| 2298 | .Struct => ty.containerLayout(mod) == .@"packed", | ||
| 2299 | else => false, | ||
| 2300 | }; | ||
| 2301 | } | ||
| 2302 | |||
| 2303 | /// Asserts the type is an integer, enum, error set, or vector of one of them. | ||
| 2304 | pub fn intInfo(starting_ty: Type, mod: *Module) InternPool.Key.IntType { | ||
| 2305 | const ip = &mod.intern_pool; | ||
| 2306 | const target = mod.getTarget(); | ||
| 2307 | var ty = starting_ty; | ||
| 2308 | |||
| 2309 | while (true) switch (ty.toIntern()) { | ||
| 2310 | .anyerror_type, .adhoc_inferred_error_set_type => { | ||
| 2311 | return .{ .signedness = .unsigned, .bits = mod.errorSetBits() }; | ||
| 2312 | }, | ||
| 2313 | .usize_type => return .{ .signedness = .unsigned, .bits = target.ptrBitWidth() }, | ||
| 2314 | .isize_type => return .{ .signedness = .signed, .bits = target.ptrBitWidth() }, | ||
| 2315 | .c_char_type => return .{ .signedness = mod.getTarget().charSignedness(), .bits = target.c_type_bit_size(.char) }, | ||
| 2316 | .c_short_type => return .{ .signedness = .signed, .bits = target.c_type_bit_size(.short) }, | ||
| 2317 | .c_ushort_type => return .{ .signedness = .unsigned, .bits = target.c_type_bit_size(.ushort) }, | ||
| 2318 | .c_int_type => return .{ .signedness = .signed, .bits = target.c_type_bit_size(.int) }, | ||
| 2319 | .c_uint_type => return .{ .signedness = .unsigned, .bits = target.c_type_bit_size(.uint) }, | ||
| 2320 | .c_long_type => return .{ .signedness = .signed, .bits = target.c_type_bit_size(.long) }, | ||
| 2321 | .c_ulong_type => return .{ .signedness = .unsigned, .bits = target.c_type_bit_size(.ulong) }, | ||
| 2322 | .c_longlong_type => return .{ .signedness = .signed, .bits = target.c_type_bit_size(.longlong) }, | ||
| 2323 | .c_ulonglong_type => return .{ .signedness = .unsigned, .bits = target.c_type_bit_size(.ulonglong) }, | ||
| 2324 | else => switch (ip.indexToKey(ty.toIntern())) { | ||
| 2325 | .int_type => |int_type| return int_type, | ||
| 2326 | .struct_type => ty = Type.fromInterned(ip.loadStructType(ty.toIntern()).backingIntType(ip).*), | ||
| 2327 | .enum_type => ty = Type.fromInterned(ip.loadEnumType(ty.toIntern()).tag_ty), | ||
| 2328 | .vector_type => |vector_type| ty = Type.fromInterned(vector_type.child), | ||
| 2329 | |||
| 2330 | .error_set_type, .inferred_error_set_type => { | ||
| 2331 | return .{ .signedness = .unsigned, .bits = mod.errorSetBits() }; | ||
| 2332 | }, | ||
| 2333 | |||
| 2334 | .anon_struct_type => unreachable, | ||
| 2335 | |||
| 2336 | .ptr_type => unreachable, | ||
| 2337 | .anyframe_type => unreachable, | ||
| 2338 | .array_type => unreachable, | ||
| 2339 | |||
| 2340 | .opt_type => unreachable, | ||
| 2341 | .error_union_type => unreachable, | ||
| 2342 | .func_type => unreachable, | ||
| 2343 | .simple_type => unreachable, // handled via Index enum tag above | ||
| 2344 | |||
| 2345 | .union_type => unreachable, | ||
| 2346 | .opaque_type => unreachable, | ||
| 2347 | |||
| 2348 | // values, not types | ||
| 2349 | .undef, | ||
| 2350 | .simple_value, | ||
| 2351 | .variable, | ||
| 2352 | .extern_func, | ||
| 2353 | .func, | ||
| 2354 | .int, | ||
| 2355 | .err, | ||
| 2356 | .error_union, | ||
| 2357 | .enum_literal, | ||
| 2358 | .enum_tag, | ||
| 2359 | .empty_enum_value, | ||
| 2360 | .float, | ||
| 2361 | .ptr, | ||
| 2362 | .slice, | ||
| 2363 | .opt, | ||
| 2364 | .aggregate, | ||
| 2365 | .un, | ||
| 2366 | // memoization, not types | ||
| 2367 | .memoized_call, | ||
| 2368 | => unreachable, | ||
| 2369 | }, | ||
| 2370 | }; | ||
| 2371 | } | ||
| 2372 | |||
| 2373 | pub fn isNamedInt(ty: Type) bool { | ||
| 2374 | return switch (ty.toIntern()) { | ||
| 2375 | .usize_type, | ||
| 2376 | .isize_type, | ||
| 2377 | .c_char_type, | ||
| 2378 | .c_short_type, | ||
| 2379 | .c_ushort_type, | ||
| 2380 | .c_int_type, | ||
| 2381 | .c_uint_type, | ||
| 2382 | .c_long_type, | ||
| 2383 | .c_ulong_type, | ||
| 2384 | .c_longlong_type, | ||
| 2385 | .c_ulonglong_type, | ||
| 2386 | => true, | ||
| 2387 | |||
| 2388 | else => false, | ||
| 2389 | }; | ||
| 2390 | } | ||
| 2391 | |||
| 2392 | /// Returns `false` for `comptime_float`. | ||
| 2393 | pub fn isRuntimeFloat(ty: Type) bool { | ||
| 2394 | return switch (ty.toIntern()) { | ||
| 2395 | .f16_type, | ||
| 2396 | .f32_type, | ||
| 2397 | .f64_type, | ||
| 2398 | .f80_type, | ||
| 2399 | .f128_type, | ||
| 2400 | .c_longdouble_type, | ||
| 2401 | => true, | ||
| 2402 | |||
| 2403 | else => false, | ||
| 2404 | }; | ||
| 2405 | } | ||
| 2406 | |||
| 2407 | /// Returns `true` for `comptime_float`. | ||
| 2408 | pub fn isAnyFloat(ty: Type) bool { | ||
| 2409 | return switch (ty.toIntern()) { | ||
| 2410 | .f16_type, | ||
| 2411 | .f32_type, | ||
| 2412 | .f64_type, | ||
| 2413 | .f80_type, | ||
| 2414 | .f128_type, | ||
| 2415 | .c_longdouble_type, | ||
| 2416 | .comptime_float_type, | ||
| 2417 | => true, | ||
| 2418 | |||
| 2419 | else => false, | ||
| 2420 | }; | ||
| 2421 | } | ||
| 2422 | |||
| 2423 | /// Asserts the type is a fixed-size float or comptime_float. | ||
| 2424 | /// Returns 128 for comptime_float types. | ||
| 2425 | pub fn floatBits(ty: Type, target: Target) u16 { | ||
| 2426 | return switch (ty.toIntern()) { | ||
| 2427 | .f16_type => 16, | ||
| 2428 | .f32_type => 32, | ||
| 2429 | .f64_type => 64, | ||
| 2430 | .f80_type => 80, | ||
| 2431 | .f128_type, .comptime_float_type => 128, | ||
| 2432 | .c_longdouble_type => target.c_type_bit_size(.longdouble), | ||
| 2433 | |||
| 2434 | else => unreachable, | ||
| 2435 | }; | ||
| 2436 | } | ||
| 2437 | |||
| 2438 | /// Asserts the type is a function or a function pointer. | ||
| 2439 | pub fn fnReturnType(ty: Type, mod: *Module) Type { | ||
| 2440 | return Type.fromInterned(mod.intern_pool.funcTypeReturnType(ty.toIntern())); | ||
| 2441 | } | ||
| 2442 | |||
| 2443 | /// Asserts the type is a function. | ||
| 2444 | pub fn fnCallingConvention(ty: Type, mod: *Module) std.builtin.CallingConvention { | ||
| 2445 | return mod.intern_pool.indexToKey(ty.toIntern()).func_type.cc; | ||
| 2446 | } | ||
| 2447 | |||
| 2448 | pub fn isValidParamType(self: Type, mod: *const Module) bool { | ||
| 2449 | return switch (self.zigTypeTagOrPoison(mod) catch return true) { | ||
| 2450 | .Opaque, .NoReturn => false, | ||
| 2451 | else => true, | ||
| 2452 | }; | ||
| 2453 | } | ||
| 2454 | |||
| 2455 | pub fn isValidReturnType(self: Type, mod: *const Module) bool { | ||
| 2456 | return switch (self.zigTypeTagOrPoison(mod) catch return true) { | ||
| 2457 | .Opaque => false, | ||
| 2458 | else => true, | ||
| 2459 | }; | ||
| 2460 | } | ||
| 2461 | |||
| 2462 | /// Asserts the type is a function. | ||
| 2463 | pub fn fnIsVarArgs(ty: Type, mod: *Module) bool { | ||
| 2464 | return mod.intern_pool.indexToKey(ty.toIntern()).func_type.is_var_args; | ||
| 2465 | } | ||
| 2466 | |||
| 2467 | pub fn isNumeric(ty: Type, mod: *const Module) bool { | ||
| 2468 | return switch (ty.toIntern()) { | ||
| 2469 | .f16_type, | ||
| 2470 | .f32_type, | ||
| 2471 | .f64_type, | ||
| 2472 | .f80_type, | ||
| 2473 | .f128_type, | ||
| 2474 | .c_longdouble_type, | ||
| 2475 | .comptime_int_type, | ||
| 2476 | .comptime_float_type, | ||
| 2477 | .usize_type, | ||
| 2478 | .isize_type, | ||
| 2479 | .c_char_type, | ||
| 2480 | .c_short_type, | ||
| 2481 | .c_ushort_type, | ||
| 2482 | .c_int_type, | ||
| 2483 | .c_uint_type, | ||
| 2484 | .c_long_type, | ||
| 2485 | .c_ulong_type, | ||
| 2486 | .c_longlong_type, | ||
| 2487 | .c_ulonglong_type, | ||
| 2488 | => true, | ||
| 2489 | |||
| 2490 | else => switch (mod.intern_pool.indexToKey(ty.toIntern())) { | ||
| 2491 | .int_type => true, | ||
| 2492 | else => false, | ||
| 2493 | }, | ||
| 2494 | }; | ||
| 2495 | } | ||
| 2496 | |||
| 2497 | /// During semantic analysis, instead call `Sema.typeHasOnePossibleValue` which | ||
| 2498 | /// resolves field types rather than asserting they are already resolved. | ||
| 2499 | pub fn onePossibleValue(starting_type: Type, mod: *Module) !?Value { | ||
| 2500 | var ty = starting_type; | ||
| 2501 | const ip = &mod.intern_pool; | ||
| 2502 | while (true) switch (ty.toIntern()) { | ||
| 2503 | .empty_struct_type => return Value.empty_struct, | ||
| 2504 | |||
| 2505 | else => switch (ip.indexToKey(ty.toIntern())) { | ||
| 2506 | .int_type => |int_type| { | ||
| 2507 | if (int_type.bits == 0) { | ||
| 2508 | return try mod.intValue(ty, 0); | ||
| 2509 | } else { | ||
| 2510 | return null; | ||
| 2511 | } | ||
| 2512 | }, | ||
| 2513 | |||
| 2514 | .ptr_type, | ||
| 2515 | .error_union_type, | ||
| 2516 | .func_type, | ||
| 2517 | .anyframe_type, | ||
| 2518 | .error_set_type, | ||
| 2519 | .inferred_error_set_type, | ||
| 2520 | => return null, | ||
| 2521 | |||
| 2522 | inline .array_type, .vector_type => |seq_type, seq_tag| { | ||
| 2523 | const has_sentinel = seq_tag == .array_type and seq_type.sentinel != .none; | ||
| 2524 | if (seq_type.len + @intFromBool(has_sentinel) == 0) return Value.fromInterned((try mod.intern(.{ .aggregate = .{ | ||
| 2525 | .ty = ty.toIntern(), | ||
| 2526 | .storage = .{ .elems = &.{} }, | ||
| 2527 | } }))); | ||
| 2528 | if (try Type.fromInterned(seq_type.child).onePossibleValue(mod)) |opv| { | ||
| 2529 | return Value.fromInterned((try mod.intern(.{ .aggregate = .{ | ||
| 2530 | .ty = ty.toIntern(), | ||
| 2531 | .storage = .{ .repeated_elem = opv.toIntern() }, | ||
| 2532 | } }))); | ||
| 2533 | } | ||
| 2534 | return null; | ||
| 2535 | }, | ||
| 2536 | .opt_type => |child| { | ||
| 2537 | if (child == .noreturn_type) { | ||
| 2538 | return try mod.nullValue(ty); | ||
| 2539 | } else { | ||
| 2540 | return null; | ||
| 2541 | } | ||
| 2542 | }, | ||
| 2543 | |||
| 2544 | .simple_type => |t| switch (t) { | ||
| 2545 | .f16, | ||
| 2546 | .f32, | ||
| 2547 | .f64, | ||
| 2548 | .f80, | ||
| 2549 | .f128, | ||
| 2550 | .usize, | ||
| 2551 | .isize, | ||
| 2552 | .c_char, | ||
| 2553 | .c_short, | ||
| 2554 | .c_ushort, | ||
| 2555 | .c_int, | ||
| 2556 | .c_uint, | ||
| 2557 | .c_long, | ||
| 2558 | .c_ulong, | ||
| 2559 | .c_longlong, | ||
| 2560 | .c_ulonglong, | ||
| 2561 | .c_longdouble, | ||
| 2562 | .anyopaque, | ||
| 2563 | .bool, | ||
| 2564 | .type, | ||
| 2565 | .anyerror, | ||
| 2566 | .comptime_int, | ||
| 2567 | .comptime_float, | ||
| 2568 | .enum_literal, | ||
| 2569 | .atomic_order, | ||
| 2570 | .atomic_rmw_op, | ||
| 2571 | .calling_convention, | ||
| 2572 | .address_space, | ||
| 2573 | .float_mode, | ||
| 2574 | .reduce_op, | ||
| 2575 | .call_modifier, | ||
| 2576 | .prefetch_options, | ||
| 2577 | .export_options, | ||
| 2578 | .extern_options, | ||
| 2579 | .type_info, | ||
| 2580 | .adhoc_inferred_error_set, | ||
| 2581 | => return null, | ||
| 2582 | |||
| 2583 | .void => return Value.void, | ||
| 2584 | .noreturn => return Value.@"unreachable", | ||
| 2585 | .null => return Value.null, | ||
| 2586 | .undefined => return Value.undef, | ||
| 2587 | |||
| 2588 | .generic_poison => unreachable, | ||
| 2589 | }, | ||
| 2590 | .struct_type => { | ||
| 2591 | const struct_type = ip.loadStructType(ty.toIntern()); | ||
| 2592 | assert(struct_type.haveFieldTypes(ip)); | ||
| 2593 | if (struct_type.knownNonOpv(ip)) | ||
| 2594 | return null; | ||
| 2595 | const field_vals = try mod.gpa.alloc(InternPool.Index, struct_type.field_types.len); | ||
| 2596 | defer mod.gpa.free(field_vals); | ||
| 2597 | for (field_vals, 0..) |*field_val, i_usize| { | ||
| 2598 | const i: u32 = @intCast(i_usize); | ||
| 2599 | if (struct_type.fieldIsComptime(ip, i)) { | ||
| 2600 | assert(struct_type.haveFieldInits(ip)); | ||
| 2601 | field_val.* = struct_type.field_inits.get(ip)[i]; | ||
| 2602 | continue; | ||
| 2603 | } | ||
| 2604 | const field_ty = Type.fromInterned(struct_type.field_types.get(ip)[i]); | ||
| 2605 | if (try field_ty.onePossibleValue(mod)) |field_opv| { | ||
| 2606 | field_val.* = field_opv.toIntern(); | ||
| 2607 | } else return null; | ||
| 2608 | } | ||
| 2609 | |||
| 2610 | // In this case the struct has no runtime-known fields and | ||
| 2611 | // therefore has one possible value. | ||
| 2612 | return Value.fromInterned((try mod.intern(.{ .aggregate = .{ | ||
| 2613 | .ty = ty.toIntern(), | ||
| 2614 | .storage = .{ .elems = field_vals }, | ||
| 2615 | } }))); | ||
| 2616 | }, | ||
| 2617 | |||
| 2618 | .anon_struct_type => |tuple| { | ||
| 2619 | for (tuple.values.get(ip)) |val| { | ||
| 2620 | if (val == .none) return null; | ||
| 2621 | } | ||
| 2622 | // In this case the struct has all comptime-known fields and | ||
| 2623 | // therefore has one possible value. | ||
| 2624 | // TODO: write something like getCoercedInts to avoid needing to dupe | ||
| 2625 | const duped_values = try mod.gpa.dupe(InternPool.Index, tuple.values.get(ip)); | ||
| 2626 | defer mod.gpa.free(duped_values); | ||
| 2627 | return Value.fromInterned((try mod.intern(.{ .aggregate = .{ | ||
| 2628 | .ty = ty.toIntern(), | ||
| 2629 | .storage = .{ .elems = duped_values }, | ||
| 2630 | } }))); | ||
| 2631 | }, | ||
| 2632 | |||
| 2633 | .union_type => { | ||
| 2634 | const union_obj = ip.loadUnionType(ty.toIntern()); | ||
| 2635 | const tag_val = (try Type.fromInterned(union_obj.enum_tag_ty).onePossibleValue(mod)) orelse | ||
| 2636 | return null; | ||
| 2637 | if (union_obj.field_types.len == 0) { | ||
| 2638 | const only = try mod.intern(.{ .empty_enum_value = ty.toIntern() }); | ||
| 2639 | return Value.fromInterned(only); | ||
| 2640 | } | ||
| 2641 | const only_field_ty = union_obj.field_types.get(ip)[0]; | ||
| 2642 | const val_val = (try Type.fromInterned(only_field_ty).onePossibleValue(mod)) orelse | ||
| 2643 | return null; | ||
| 2644 | const only = try mod.intern(.{ .un = .{ | ||
| 2645 | .ty = ty.toIntern(), | ||
| 2646 | .tag = tag_val.toIntern(), | ||
| 2647 | .val = val_val.toIntern(), | ||
| 2648 | } }); | ||
| 2649 | return Value.fromInterned(only); | ||
| 2650 | }, | ||
| 2651 | .opaque_type => return null, | ||
| 2652 | .enum_type => { | ||
| 2653 | const enum_type = ip.loadEnumType(ty.toIntern()); | ||
| 2654 | switch (enum_type.tag_mode) { | ||
| 2655 | .nonexhaustive => { | ||
| 2656 | if (enum_type.tag_ty == .comptime_int_type) return null; | ||
| 2657 | |||
| 2658 | if (try Type.fromInterned(enum_type.tag_ty).onePossibleValue(mod)) |int_opv| { | ||
| 2659 | const only = try mod.intern(.{ .enum_tag = .{ | ||
| 2660 | .ty = ty.toIntern(), | ||
| 2661 | .int = int_opv.toIntern(), | ||
| 2662 | } }); | ||
| 2663 | return Value.fromInterned(only); | ||
| 2664 | } | ||
| 2665 | |||
| 2666 | return null; | ||
| 2667 | }, | ||
| 2668 | .auto, .explicit => { | ||
| 2669 | if (Type.fromInterned(enum_type.tag_ty).hasRuntimeBits(mod)) return null; | ||
| 2670 | |||
| 2671 | switch (enum_type.names.len) { | ||
| 2672 | 0 => { | ||
| 2673 | const only = try mod.intern(.{ .empty_enum_value = ty.toIntern() }); | ||
| 2674 | return Value.fromInterned(only); | ||
| 2675 | }, | ||
| 2676 | 1 => { | ||
| 2677 | if (enum_type.values.len == 0) { | ||
| 2678 | const only = try mod.intern(.{ .enum_tag = .{ | ||
| 2679 | .ty = ty.toIntern(), | ||
| 2680 | .int = try mod.intern(.{ .int = .{ | ||
| 2681 | .ty = enum_type.tag_ty, | ||
| 2682 | .storage = .{ .u64 = 0 }, | ||
| 2683 | } }), | ||
| 2684 | } }); | ||
| 2685 | return Value.fromInterned(only); | ||
| 2686 | } else { | ||
| 2687 | return Value.fromInterned(enum_type.values.get(ip)[0]); | ||
| 2688 | } | ||
| 2689 | }, | ||
| 2690 | else => return null, | ||
| 2691 | } | ||
| 2692 | }, | ||
| 2693 | } | ||
| 2694 | }, | ||
| 2695 | |||
| 2696 | // values, not types | ||
| 2697 | .undef, | ||
| 2698 | .simple_value, | ||
| 2699 | .variable, | ||
| 2700 | .extern_func, | ||
| 2701 | .func, | ||
| 2702 | .int, | ||
| 2703 | .err, | ||
| 2704 | .error_union, | ||
| 2705 | .enum_literal, | ||
| 2706 | .enum_tag, | ||
| 2707 | .empty_enum_value, | ||
| 2708 | .float, | ||
| 2709 | .ptr, | ||
| 2710 | .slice, | ||
| 2711 | .opt, | ||
| 2712 | .aggregate, | ||
| 2713 | .un, | ||
| 2714 | // memoization, not types | ||
| 2715 | .memoized_call, | ||
| 2716 | => unreachable, | ||
| 2717 | }, | ||
| 2718 | }; | ||
| 2719 | } | ||
| 2720 | |||
| 2721 | /// During semantic analysis, instead call `Sema.typeRequiresComptime` which | ||
| 2722 | /// resolves field types rather than asserting they are already resolved. | ||
| 2723 | pub fn comptimeOnly(ty: Type, mod: *Module) bool { | ||
| 2724 | return ty.comptimeOnlyAdvanced(mod, null) catch unreachable; | ||
| 2725 | } | ||
| 2726 | |||
| 2727 | /// `generic_poison` will return false. | ||
| 2728 | /// May return false negatives when structs and unions are having their field types resolved. | ||
| 2729 | /// If `opt_sema` is not provided, asserts that the type is sufficiently resolved. | ||
| 2730 | pub fn comptimeOnlyAdvanced(ty: Type, mod: *Module, opt_sema: ?*Sema) Module.CompileError!bool { | ||
| 2731 | const ip = &mod.intern_pool; | ||
| 2732 | return switch (ty.toIntern()) { | ||
| 2733 | .empty_struct_type => false, | ||
| 2734 | |||
| 2735 | else => switch (ip.indexToKey(ty.toIntern())) { | ||
| 2736 | .int_type => false, | ||
| 2737 | .ptr_type => |ptr_type| { | ||
| 2738 | const child_ty = Type.fromInterned(ptr_type.child); | ||
| 2739 | switch (child_ty.zigTypeTag(mod)) { | ||
| 2740 | .Fn => return !try child_ty.fnHasRuntimeBitsAdvanced(mod, opt_sema), | ||
| 2741 | .Opaque => return false, | ||
| 2742 | else => return child_ty.comptimeOnlyAdvanced(mod, opt_sema), | ||
| 2743 | } | ||
| 2744 | }, | ||
| 2745 | .anyframe_type => |child| { | ||
| 2746 | if (child == .none) return false; | ||
| 2747 | return Type.fromInterned(child).comptimeOnlyAdvanced(mod, opt_sema); | ||
| 2748 | }, | ||
| 2749 | .array_type => |array_type| return Type.fromInterned(array_type.child).comptimeOnlyAdvanced(mod, opt_sema), | ||
| 2750 | .vector_type => |vector_type| return Type.fromInterned(vector_type.child).comptimeOnlyAdvanced(mod, opt_sema), | ||
| 2751 | .opt_type => |child| return Type.fromInterned(child).comptimeOnlyAdvanced(mod, opt_sema), | ||
| 2752 | .error_union_type => |error_union_type| return Type.fromInterned(error_union_type.payload_type).comptimeOnlyAdvanced(mod, opt_sema), | ||
| 2753 | |||
| 2754 | .error_set_type, | ||
| 2755 | .inferred_error_set_type, | ||
| 2756 | => false, | ||
| 2757 | |||
| 2758 | // These are function bodies, not function pointers. | ||
| 2759 | .func_type => true, | ||
| 2760 | |||
| 2761 | .simple_type => |t| switch (t) { | ||
| 2762 | .f16, | ||
| 2763 | .f32, | ||
| 2764 | .f64, | ||
| 2765 | .f80, | ||
| 2766 | .f128, | ||
| 2767 | .usize, | ||
| 2768 | .isize, | ||
| 2769 | .c_char, | ||
| 2770 | .c_short, | ||
| 2771 | .c_ushort, | ||
| 2772 | .c_int, | ||
| 2773 | .c_uint, | ||
| 2774 | .c_long, | ||
| 2775 | .c_ulong, | ||
| 2776 | .c_longlong, | ||
| 2777 | .c_ulonglong, | ||
| 2778 | .c_longdouble, | ||
| 2779 | .anyopaque, | ||
| 2780 | .bool, | ||
| 2781 | .void, | ||
| 2782 | .anyerror, | ||
| 2783 | .adhoc_inferred_error_set, | ||
| 2784 | .noreturn, | ||
| 2785 | .generic_poison, | ||
| 2786 | .atomic_order, | ||
| 2787 | .atomic_rmw_op, | ||
| 2788 | .calling_convention, | ||
| 2789 | .address_space, | ||
| 2790 | .float_mode, | ||
| 2791 | .reduce_op, | ||
| 2792 | .call_modifier, | ||
| 2793 | .prefetch_options, | ||
| 2794 | .export_options, | ||
| 2795 | .extern_options, | ||
| 2796 | => false, | ||
| 2797 | |||
| 2798 | .type, | ||
| 2799 | .comptime_int, | ||
| 2800 | .comptime_float, | ||
| 2801 | .null, | ||
| 2802 | .undefined, | ||
| 2803 | .enum_literal, | ||
| 2804 | .type_info, | ||
| 2805 | => true, | ||
| 2806 | }, | ||
| 2807 | .struct_type => { | ||
| 2808 | const struct_type = ip.loadStructType(ty.toIntern()); | ||
| 2809 | // packed structs cannot be comptime-only because they have a well-defined | ||
| 2810 | // memory layout and every field has a well-defined bit pattern. | ||
| 2811 | if (struct_type.layout == .@"packed") | ||
| 2812 | return false; | ||
| 2813 | |||
| 2814 | // A struct with no fields is not comptime-only. | ||
| 2815 | return switch (struct_type.flagsPtr(ip).requires_comptime) { | ||
| 2816 | .no, .wip => false, | ||
| 2817 | .yes => true, | ||
| 2818 | .unknown => { | ||
| 2819 | // The type is not resolved; assert that we have a Sema. | ||
| 2820 | const sema = opt_sema.?; | ||
| 2821 | |||
| 2822 | if (struct_type.flagsPtr(ip).field_types_wip) | ||
| 2823 | return false; | ||
| 2824 | |||
| 2825 | struct_type.flagsPtr(ip).requires_comptime = .wip; | ||
| 2826 | errdefer struct_type.flagsPtr(ip).requires_comptime = .unknown; | ||
| 2827 | |||
| 2828 | try sema.resolveTypeFieldsStruct(ty.toIntern(), struct_type); | ||
| 2829 | |||
| 2830 | for (0..struct_type.field_types.len) |i_usize| { | ||
| 2831 | const i: u32 = @intCast(i_usize); | ||
| 2832 | if (struct_type.fieldIsComptime(ip, i)) continue; | ||
| 2833 | const field_ty = struct_type.field_types.get(ip)[i]; | ||
| 2834 | if (try Type.fromInterned(field_ty).comptimeOnlyAdvanced(mod, opt_sema)) { | ||
| 2835 | // Note that this does not cause the layout to | ||
| 2836 | // be considered resolved. Comptime-only types | ||
| 2837 | // still maintain a layout of their | ||
| 2838 | // runtime-known fields. | ||
| 2839 | struct_type.flagsPtr(ip).requires_comptime = .yes; | ||
| 2840 | return true; | ||
| 2841 | } | ||
| 2842 | } | ||
| 2843 | |||
| 2844 | struct_type.flagsPtr(ip).requires_comptime = .no; | ||
| 2845 | return false; | ||
| 2846 | }, | ||
| 2847 | }; | ||
| 2848 | }, | ||
| 2849 | |||
| 2850 | .anon_struct_type => |tuple| { | ||
| 2851 | for (tuple.types.get(ip), tuple.values.get(ip)) |field_ty, val| { | ||
| 2852 | const have_comptime_val = val != .none; | ||
| 2853 | if (!have_comptime_val and try Type.fromInterned(field_ty).comptimeOnlyAdvanced(mod, opt_sema)) return true; | ||
| 2854 | } | ||
| 2855 | return false; | ||
| 2856 | }, | ||
| 2857 | |||
| 2858 | .union_type => { | ||
| 2859 | const union_type = ip.loadUnionType(ty.toIntern()); | ||
| 2860 | switch (union_type.flagsPtr(ip).requires_comptime) { | ||
| 2861 | .no, .wip => return false, | ||
| 2862 | .yes => return true, | ||
| 2863 | .unknown => { | ||
| 2864 | // The type is not resolved; assert that we have a Sema. | ||
| 2865 | const sema = opt_sema.?; | ||
| 2866 | |||
| 2867 | if (union_type.flagsPtr(ip).status == .field_types_wip) | ||
| 2868 | return false; | ||
| 2869 | |||
| 2870 | union_type.flagsPtr(ip).requires_comptime = .wip; | ||
| 2871 | errdefer union_type.flagsPtr(ip).requires_comptime = .unknown; | ||
| 2872 | |||
| 2873 | try sema.resolveTypeFieldsUnion(ty, union_type); | ||
| 2874 | |||
| 2875 | for (0..union_type.field_types.len) |field_idx| { | ||
| 2876 | const field_ty = union_type.field_types.get(ip)[field_idx]; | ||
| 2877 | if (try Type.fromInterned(field_ty).comptimeOnlyAdvanced(mod, opt_sema)) { | ||
| 2878 | union_type.flagsPtr(ip).requires_comptime = .yes; | ||
| 2879 | return true; | ||
| 2880 | } | ||
| 2881 | } | ||
| 2882 | |||
| 2883 | union_type.flagsPtr(ip).requires_comptime = .no; | ||
| 2884 | return false; | ||
| 2885 | }, | ||
| 2886 | } | ||
| 2887 | }, | ||
| 2888 | |||
| 2889 | .opaque_type => false, | ||
| 2890 | |||
| 2891 | .enum_type => return Type.fromInterned(ip.loadEnumType(ty.toIntern()).tag_ty).comptimeOnlyAdvanced(mod, opt_sema), | ||
| 2892 | |||
| 2893 | // values, not types | ||
| 2894 | .undef, | ||
| 2895 | .simple_value, | ||
| 2896 | .variable, | ||
| 2897 | .extern_func, | ||
| 2898 | .func, | ||
| 2899 | .int, | ||
| 2900 | .err, | ||
| 2901 | .error_union, | ||
| 2902 | .enum_literal, | ||
| 2903 | .enum_tag, | ||
| 2904 | .empty_enum_value, | ||
| 2905 | .float, | ||
| 2906 | .ptr, | ||
| 2907 | .slice, | ||
| 2908 | .opt, | ||
| 2909 | .aggregate, | ||
| 2910 | .un, | ||
| 2911 | // memoization, not types | ||
| 2912 | .memoized_call, | ||
| 2913 | => unreachable, | ||
| 2914 | }, | ||
| 2915 | }; | ||
| 2916 | } | ||
| 2917 | |||
| 2918 | pub fn isVector(ty: Type, mod: *const Module) bool { | ||
| 2919 | return ty.zigTypeTag(mod) == .Vector; | ||
| 2920 | } | ||
| 2921 | |||
| 2922 | /// Returns 0 if not a vector, otherwise returns @bitSizeOf(Element) * vector_len. | ||
| 2923 | pub fn totalVectorBits(ty: Type, zcu: *Zcu) u64 { | ||
| 2924 | if (!ty.isVector(zcu)) return 0; | ||
| 2925 | const v = zcu.intern_pool.indexToKey(ty.toIntern()).vector_type; | ||
| 2926 | return v.len * Type.fromInterned(v.child).bitSize(zcu); | ||
| 2927 | } | ||
| 2928 | |||
| 2929 | pub fn isArrayOrVector(ty: Type, mod: *const Module) bool { | ||
| 2930 | return switch (ty.zigTypeTag(mod)) { | ||
| 2931 | .Array, .Vector => true, | ||
| 2932 | else => false, | ||
| 2933 | }; | ||
| 2934 | } | ||
| 2935 | |||
| 2936 | pub fn isIndexable(ty: Type, mod: *Module) bool { | ||
| 2937 | return switch (ty.zigTypeTag(mod)) { | ||
| 2938 | .Array, .Vector => true, | ||
| 2939 | .Pointer => switch (ty.ptrSize(mod)) { | ||
| 2940 | .Slice, .Many, .C => true, | ||
| 2941 | .One => switch (ty.childType(mod).zigTypeTag(mod)) { | ||
| 2942 | .Array, .Vector => true, | ||
| 2943 | .Struct => ty.childType(mod).isTuple(mod), | ||
| 2944 | else => false, | ||
| 2945 | }, | ||
| 2946 | }, | ||
| 2947 | .Struct => ty.isTuple(mod), | ||
| 2948 | else => false, | ||
| 2949 | }; | ||
| 2950 | } | ||
| 2951 | |||
| 2952 | pub fn indexableHasLen(ty: Type, mod: *Module) bool { | ||
| 2953 | return switch (ty.zigTypeTag(mod)) { | ||
| 2954 | .Array, .Vector => true, | ||
| 2955 | .Pointer => switch (ty.ptrSize(mod)) { | ||
| 2956 | .Many, .C => false, | ||
| 2957 | .Slice => true, | ||
| 2958 | .One => switch (ty.childType(mod).zigTypeTag(mod)) { | ||
| 2959 | .Array, .Vector => true, | ||
| 2960 | .Struct => ty.childType(mod).isTuple(mod), | ||
| 2961 | else => false, | ||
| 2962 | }, | ||
| 2963 | }, | ||
| 2964 | .Struct => ty.isTuple(mod), | ||
| 2965 | else => false, | ||
| 2966 | }; | ||
| 2967 | } | ||
| 2968 | |||
| 2969 | /// Asserts that the type can have a namespace. | ||
| 2970 | pub fn getNamespaceIndex(ty: Type, zcu: *Zcu) InternPool.OptionalNamespaceIndex { | ||
| 2971 | return ty.getNamespace(zcu).?; | ||
| 2972 | } | ||
| 2973 | |||
| 2974 | /// Returns null if the type has no namespace. | ||
| 2975 | pub fn getNamespace(ty: Type, zcu: *Zcu) ?InternPool.OptionalNamespaceIndex { | ||
| 2976 | const ip = &zcu.intern_pool; | ||
| 2977 | return switch (ip.indexToKey(ty.toIntern())) { | ||
| 2978 | .opaque_type => ip.loadOpaqueType(ty.toIntern()).namespace, | ||
| 2979 | .struct_type => ip.loadStructType(ty.toIntern()).namespace, | ||
| 2980 | .union_type => ip.loadUnionType(ty.toIntern()).namespace, | ||
| 2981 | .enum_type => ip.loadEnumType(ty.toIntern()).namespace, | ||
| 2982 | |||
| 2983 | .anon_struct_type => .none, | ||
| 2984 | .simple_type => |s| switch (s) { | ||
| 2985 | .anyopaque, | ||
| 2986 | .atomic_order, | ||
| 2987 | .atomic_rmw_op, | ||
| 2988 | .calling_convention, | ||
| 2989 | .address_space, | ||
| 2990 | .float_mode, | ||
| 2991 | .reduce_op, | ||
| 2992 | .call_modifier, | ||
| 2993 | .prefetch_options, | ||
| 2994 | .export_options, | ||
| 2995 | .extern_options, | ||
| 2996 | .type_info, | ||
| 2997 | => .none, | ||
| 2998 | else => null, | ||
| 2999 | }, | ||
| 3000 | |||
| 3001 | else => null, | ||
| 3002 | }; | ||
| 3003 | } | ||
| 3004 | |||
| 3005 | // Works for vectors and vectors of integers. | ||
| 3006 | pub fn minInt(ty: Type, mod: *Module, dest_ty: Type) !Value { | ||
| 3007 | const scalar = try minIntScalar(ty.scalarType(mod), mod, dest_ty.scalarType(mod)); | ||
| 3008 | return if (ty.zigTypeTag(mod) == .Vector) Value.fromInterned((try mod.intern(.{ .aggregate = .{ | ||
| 3009 | .ty = dest_ty.toIntern(), | ||
| 3010 | .storage = .{ .repeated_elem = scalar.toIntern() }, | ||
| 3011 | } }))) else scalar; | ||
| 3012 | } | ||
| 3013 | |||
| 3014 | /// Asserts that the type is an integer. | ||
| 3015 | pub fn minIntScalar(ty: Type, mod: *Module, dest_ty: Type) !Value { | ||
| 3016 | const info = ty.intInfo(mod); | ||
| 3017 | if (info.signedness == .unsigned) return mod.intValue(dest_ty, 0); | ||
| 3018 | if (info.bits == 0) return mod.intValue(dest_ty, -1); | ||
| 3019 | |||
| 3020 | if (std.math.cast(u6, info.bits - 1)) |shift| { | ||
| 3021 | const n = @as(i64, std.math.minInt(i64)) >> (63 - shift); | ||
| 3022 | return mod.intValue(dest_ty, n); | ||
| 3023 | } | ||
| 3024 | |||
| 3025 | var res = try std.math.big.int.Managed.init(mod.gpa); | ||
| 3026 | defer res.deinit(); | ||
| 3027 | |||
| 3028 | try res.setTwosCompIntLimit(.min, info.signedness, info.bits); | ||
| 3029 | |||
| 3030 | return mod.intValue_big(dest_ty, res.toConst()); | ||
| 3031 | } | ||
| 3032 | |||
| 3033 | // Works for vectors and vectors of integers. | ||
| 3034 | /// The returned Value will have type dest_ty. | ||
| 3035 | pub fn maxInt(ty: Type, mod: *Module, dest_ty: Type) !Value { | ||
| 3036 | const scalar = try maxIntScalar(ty.scalarType(mod), mod, dest_ty.scalarType(mod)); | ||
| 3037 | return if (ty.zigTypeTag(mod) == .Vector) Value.fromInterned((try mod.intern(.{ .aggregate = .{ | ||
| 3038 | .ty = dest_ty.toIntern(), | ||
| 3039 | .storage = .{ .repeated_elem = scalar.toIntern() }, | ||
| 3040 | } }))) else scalar; | ||
| 3041 | } | ||
| 3042 | |||
| 3043 | /// The returned Value will have type dest_ty. | ||
| 3044 | pub fn maxIntScalar(ty: Type, mod: *Module, dest_ty: Type) !Value { | ||
| 3045 | const info = ty.intInfo(mod); | ||
| 3046 | |||
| 3047 | switch (info.bits) { | ||
| 3048 | 0 => return switch (info.signedness) { | ||
| 3049 | .signed => try mod.intValue(dest_ty, -1), | ||
| 3050 | .unsigned => try mod.intValue(dest_ty, 0), | ||
| 3051 | }, | ||
| 3052 | 1 => return switch (info.signedness) { | ||
| 3053 | .signed => try mod.intValue(dest_ty, 0), | ||
| 3054 | .unsigned => try mod.intValue(dest_ty, 1), | ||
| 3055 | }, | ||
| 3056 | else => {}, | ||
| 3057 | } | ||
| 3058 | |||
| 3059 | if (std.math.cast(u6, info.bits - 1)) |shift| switch (info.signedness) { | ||
| 3060 | .signed => { | ||
| 3061 | const n = @as(i64, std.math.maxInt(i64)) >> (63 - shift); | ||
| 3062 | return mod.intValue(dest_ty, n); | ||
| 3063 | }, | ||
| 3064 | .unsigned => { | ||
| 3065 | const n = @as(u64, std.math.maxInt(u64)) >> (63 - shift); | ||
| 3066 | return mod.intValue(dest_ty, n); | ||
| 3067 | }, | ||
| 3068 | }; | ||
| 3069 | |||
| 3070 | var res = try std.math.big.int.Managed.init(mod.gpa); | ||
| 3071 | defer res.deinit(); | ||
| 3072 | |||
| 3073 | try res.setTwosCompIntLimit(.max, info.signedness, info.bits); | ||
| 3074 | |||
| 3075 | return mod.intValue_big(dest_ty, res.toConst()); | ||
| 3076 | } | ||
| 3077 | |||
| 3078 | /// Asserts the type is an enum or a union. | ||
| 3079 | pub fn intTagType(ty: Type, mod: *Module) Type { | ||
| 3080 | const ip = &mod.intern_pool; | ||
| 3081 | return switch (ip.indexToKey(ty.toIntern())) { | ||
| 3082 | .union_type => Type.fromInterned(ip.loadUnionType(ty.toIntern()).enum_tag_ty).intTagType(mod), | ||
| 3083 | .enum_type => Type.fromInterned(ip.loadEnumType(ty.toIntern()).tag_ty), | ||
| 3084 | else => unreachable, | ||
| 3085 | }; | ||
| 3086 | } | ||
| 3087 | |||
| 3088 | pub fn isNonexhaustiveEnum(ty: Type, mod: *Module) bool { | ||
| 3089 | const ip = &mod.intern_pool; | ||
| 3090 | return switch (ip.indexToKey(ty.toIntern())) { | ||
| 3091 | .enum_type => switch (ip.loadEnumType(ty.toIntern()).tag_mode) { | ||
| 3092 | .nonexhaustive => true, | ||
| 3093 | .auto, .explicit => false, | ||
| 3094 | }, | ||
| 3095 | else => false, | ||
| 3096 | }; | ||
| 3097 | } | ||
| 3098 | |||
| 3099 | // Asserts that `ty` is an error set and not `anyerror`. | ||
| 3100 | // Asserts that `ty` is resolved if it is an inferred error set. | ||
| 3101 | pub fn errorSetNames(ty: Type, mod: *Module) InternPool.NullTerminatedString.Slice { | ||
| 3102 | const ip = &mod.intern_pool; | ||
| 3103 | return switch (ip.indexToKey(ty.toIntern())) { | ||
| 3104 | .error_set_type => |x| x.names, | ||
| 3105 | .inferred_error_set_type => |i| switch (ip.funcIesResolved(i).*) { | ||
| 3106 | .none => unreachable, // unresolved inferred error set | ||
| 3107 | .anyerror_type => unreachable, | ||
| 3108 | else => |t| ip.indexToKey(t).error_set_type.names, | ||
| 3109 | }, | ||
| 3110 | else => unreachable, | ||
| 3111 | }; | ||
| 3112 | } | ||
| 3113 | |||
| 3114 | pub fn enumFields(ty: Type, mod: *Module) InternPool.NullTerminatedString.Slice { | ||
| 3115 | return mod.intern_pool.loadEnumType(ty.toIntern()).names; | ||
| 3116 | } | ||
| 3117 | |||
| 3118 | pub fn enumFieldCount(ty: Type, mod: *Module) usize { | ||
| 3119 | return mod.intern_pool.loadEnumType(ty.toIntern()).names.len; | ||
| 3120 | } | ||
| 3121 | |||
| 3122 | pub fn enumFieldName(ty: Type, field_index: usize, mod: *Module) InternPool.NullTerminatedString { | ||
| 3123 | const ip = &mod.intern_pool; | ||
| 3124 | return ip.loadEnumType(ty.toIntern()).names.get(ip)[field_index]; | ||
| 3125 | } | ||
| 3126 | |||
| 3127 | pub fn enumFieldIndex(ty: Type, field_name: InternPool.NullTerminatedString, mod: *Module) ?u32 { | ||
| 3128 | const ip = &mod.intern_pool; | ||
| 3129 | const enum_type = ip.loadEnumType(ty.toIntern()); | ||
| 3130 | return enum_type.nameIndex(ip, field_name); | ||
| 3131 | } | ||
| 3132 | |||
| 3133 | /// Asserts `ty` is an enum. `enum_tag` can either be `enum_field_index` or | ||
| 3134 | /// an integer which represents the enum value. Returns the field index in | ||
| 3135 | /// declaration order, or `null` if `enum_tag` does not match any field. | ||
| 3136 | pub fn enumTagFieldIndex(ty: Type, enum_tag: Value, mod: *Module) ?u32 { | ||
| 3137 | const ip = &mod.intern_pool; | ||
| 3138 | const enum_type = ip.loadEnumType(ty.toIntern()); | ||
| 3139 | const int_tag = switch (ip.indexToKey(enum_tag.toIntern())) { | ||
| 3140 | .int => enum_tag.toIntern(), | ||
| 3141 | .enum_tag => |info| info.int, | ||
| 3142 | else => unreachable, | ||
| 3143 | }; | ||
| 3144 | assert(ip.typeOf(int_tag) == enum_type.tag_ty); | ||
| 3145 | return enum_type.tagValueIndex(ip, int_tag); | ||
| 3146 | } | ||
| 3147 | |||
| 3148 | /// Returns none in the case of a tuple which uses the integer index as the field name. | ||
| 3149 | pub fn structFieldName(ty: Type, index: usize, mod: *Module) InternPool.OptionalNullTerminatedString { | ||
| 3150 | const ip = &mod.intern_pool; | ||
| 3151 | return switch (ip.indexToKey(ty.toIntern())) { | ||
| 3152 | .struct_type => ip.loadStructType(ty.toIntern()).fieldName(ip, index), | ||
| 3153 | .anon_struct_type => |anon_struct| anon_struct.fieldName(ip, index), | ||
| 3154 | else => unreachable, | ||
| 3155 | }; | ||
| 3156 | } | ||
| 3157 | |||
| 3158 | pub fn structFieldCount(ty: Type, mod: *Module) u32 { | ||
| 3159 | const ip = &mod.intern_pool; | ||
| 3160 | return switch (ip.indexToKey(ty.toIntern())) { | ||
| 3161 | .struct_type => ip.loadStructType(ty.toIntern()).field_types.len, | ||
| 3162 | .anon_struct_type => |anon_struct| anon_struct.types.len, | ||
| 3163 | else => unreachable, | ||
| 3164 | }; | ||
| 3165 | } | ||
| 3166 | |||
| 3167 | /// Supports structs and unions. | ||
| 3168 | pub fn structFieldType(ty: Type, index: usize, mod: *Module) Type { | ||
| 3169 | const ip = &mod.intern_pool; | ||
| 3170 | return switch (ip.indexToKey(ty.toIntern())) { | ||
| 3171 | .struct_type => Type.fromInterned(ip.loadStructType(ty.toIntern()).field_types.get(ip)[index]), | ||
| 3172 | .union_type => { | ||
| 3173 | const union_obj = ip.loadUnionType(ty.toIntern()); | ||
| 3174 | return Type.fromInterned(union_obj.field_types.get(ip)[index]); | ||
| 3175 | }, | ||
| 3176 | .anon_struct_type => |anon_struct| Type.fromInterned(anon_struct.types.get(ip)[index]), | ||
| 3177 | else => unreachable, | ||
| 3178 | }; | ||
| 3179 | } | ||
| 3180 | |||
| 3181 | pub fn structFieldAlign(ty: Type, index: usize, zcu: *Zcu) Alignment { | ||
| 3182 | return ty.structFieldAlignAdvanced(index, zcu, null) catch unreachable; | ||
| 3183 | } | ||
| 3184 | |||
| 3185 | pub fn structFieldAlignAdvanced(ty: Type, index: usize, zcu: *Zcu, opt_sema: ?*Sema) !Alignment { | ||
| 3186 | const ip = &zcu.intern_pool; | ||
| 3187 | switch (ip.indexToKey(ty.toIntern())) { | ||
| 3188 | .struct_type => { | ||
| 3189 | const struct_type = ip.loadStructType(ty.toIntern()); | ||
| 3190 | assert(struct_type.layout != .@"packed"); | ||
| 3191 | const explicit_align = struct_type.fieldAlign(ip, index); | ||
| 3192 | const field_ty = Type.fromInterned(struct_type.field_types.get(ip)[index]); | ||
| 3193 | if (opt_sema) |sema| { | ||
| 3194 | return sema.structFieldAlignment(explicit_align, field_ty, struct_type.layout); | ||
| 3195 | } else { | ||
| 3196 | return zcu.structFieldAlignment(explicit_align, field_ty, struct_type.layout); | ||
| 3197 | } | ||
| 3198 | }, | ||
| 3199 | .anon_struct_type => |anon_struct| { | ||
| 3200 | return (try Type.fromInterned(anon_struct.types.get(ip)[index]).abiAlignmentAdvanced(zcu, if (opt_sema) |sema| .{ .sema = sema } else .eager)).scalar; | ||
| 3201 | }, | ||
| 3202 | .union_type => { | ||
| 3203 | const union_obj = ip.loadUnionType(ty.toIntern()); | ||
| 3204 | if (opt_sema) |sema| { | ||
| 3205 | return sema.unionFieldAlignment(union_obj, @intCast(index)); | ||
| 3206 | } else { | ||
| 3207 | return zcu.unionFieldNormalAlignment(union_obj, @intCast(index)); | ||
| 3208 | } | ||
| 3209 | }, | ||
| 3210 | else => unreachable, | ||
| 3211 | } | ||
| 3212 | } | ||
| 3213 | |||
| 3214 | pub fn structFieldDefaultValue(ty: Type, index: usize, mod: *Module) Value { | ||
| 3215 | const ip = &mod.intern_pool; | ||
| 3216 | switch (ip.indexToKey(ty.toIntern())) { | ||
| 3217 | .struct_type => { | ||
| 3218 | const struct_type = ip.loadStructType(ty.toIntern()); | ||
| 3219 | const val = struct_type.fieldInit(ip, index); | ||
| 3220 | // TODO: avoid using `unreachable` to indicate this. | ||
| 3221 | if (val == .none) return Value.@"unreachable"; | ||
| 3222 | return Value.fromInterned(val); | ||
| 3223 | }, | ||
| 3224 | .anon_struct_type => |anon_struct| { | ||
| 3225 | const val = anon_struct.values.get(ip)[index]; | ||
| 3226 | // TODO: avoid using `unreachable` to indicate this. | ||
| 3227 | if (val == .none) return Value.@"unreachable"; | ||
| 3228 | return Value.fromInterned(val); | ||
| 3229 | }, | ||
| 3230 | else => unreachable, | ||
| 3231 | } | ||
| 3232 | } | ||
| 3233 | |||
| 3234 | pub fn structFieldValueComptime(ty: Type, mod: *Module, index: usize) !?Value { | ||
| 3235 | const ip = &mod.intern_pool; | ||
| 3236 | switch (ip.indexToKey(ty.toIntern())) { | ||
| 3237 | .struct_type => { | ||
| 3238 | const struct_type = ip.loadStructType(ty.toIntern()); | ||
| 3239 | if (struct_type.fieldIsComptime(ip, index)) { | ||
| 3240 | assert(struct_type.haveFieldInits(ip)); | ||
| 3241 | return Value.fromInterned(struct_type.field_inits.get(ip)[index]); | ||
| 3242 | } else { | ||
| 3243 | return Type.fromInterned(struct_type.field_types.get(ip)[index]).onePossibleValue(mod); | ||
| 3244 | } | ||
| 3245 | }, | ||
| 3246 | .anon_struct_type => |tuple| { | ||
| 3247 | const val = tuple.values.get(ip)[index]; | ||
| 3248 | if (val == .none) { | ||
| 3249 | return Type.fromInterned(tuple.types.get(ip)[index]).onePossibleValue(mod); | ||
| 3250 | } else { | ||
| 3251 | return Value.fromInterned(val); | ||
| 3252 | } | ||
| 3253 | }, | ||
| 3254 | else => unreachable, | ||
| 3255 | } | ||
| 3256 | } | ||
| 3257 | |||
| 3258 | pub fn structFieldIsComptime(ty: Type, index: usize, mod: *Module) bool { | ||
| 3259 | const ip = &mod.intern_pool; | ||
| 3260 | return switch (ip.indexToKey(ty.toIntern())) { | ||
| 3261 | .struct_type => ip.loadStructType(ty.toIntern()).fieldIsComptime(ip, index), | ||
| 3262 | .anon_struct_type => |anon_struct| anon_struct.values.get(ip)[index] != .none, | ||
| 3263 | else => unreachable, | ||
| 3264 | }; | ||
| 3265 | } | ||
| 3266 | |||
| 3267 | pub const FieldOffset = struct { | ||
| 3268 | field: usize, | ||
| 3269 | offset: u64, | ||
| 3270 | }; | ||
| 3271 | |||
| 3272 | /// Supports structs and unions. | ||
| 3273 | pub fn structFieldOffset(ty: Type, index: usize, mod: *Module) u64 { | ||
| 3274 | const ip = &mod.intern_pool; | ||
| 3275 | switch (ip.indexToKey(ty.toIntern())) { | ||
| 3276 | .struct_type => { | ||
| 3277 | const struct_type = ip.loadStructType(ty.toIntern()); | ||
| 3278 | assert(struct_type.haveLayout(ip)); | ||
| 3279 | assert(struct_type.layout != .@"packed"); | ||
| 3280 | return struct_type.offsets.get(ip)[index]; | ||
| 3281 | }, | ||
| 3282 | |||
| 3283 | .anon_struct_type => |tuple| { | ||
| 3284 | var offset: u64 = 0; | ||
| 3285 | var big_align: Alignment = .none; | ||
| 3286 | |||
| 3287 | for (tuple.types.get(ip), tuple.values.get(ip), 0..) |field_ty, field_val, i| { | ||
| 3288 | if (field_val != .none or !Type.fromInterned(field_ty).hasRuntimeBits(mod)) { | ||
| 3289 | // comptime field | ||
| 3290 | if (i == index) return offset; | ||
| 3291 | continue; | ||
| 3292 | } | ||
| 3293 | |||
| 3294 | const field_align = Type.fromInterned(field_ty).abiAlignment(mod); | ||
| 3295 | big_align = big_align.max(field_align); | ||
| 3296 | offset = field_align.forward(offset); | ||
| 3297 | if (i == index) return offset; | ||
| 3298 | offset += Type.fromInterned(field_ty).abiSize(mod); | ||
| 3299 | } | ||
| 3300 | offset = big_align.max(.@"1").forward(offset); | ||
| 3301 | return offset; | ||
| 3302 | }, | ||
| 3303 | |||
| 3304 | .union_type => { | ||
| 3305 | const union_type = ip.loadUnionType(ty.toIntern()); | ||
| 3306 | if (!union_type.hasTag(ip)) | ||
| 3307 | return 0; | ||
| 3308 | const layout = mod.getUnionLayout(union_type); | ||
| 3309 | if (layout.tag_align.compare(.gte, layout.payload_align)) { | ||
| 3310 | // {Tag, Payload} | ||
| 3311 | return layout.payload_align.forward(layout.tag_size); | ||
| 3312 | } else { | ||
| 3313 | // {Payload, Tag} | ||
| 3314 | return 0; | ||
| 3315 | } | ||
| 3316 | }, | ||
| 3317 | |||
| 3318 | else => unreachable, | ||
| 3319 | } | ||
| 3320 | } | ||
| 3321 | |||
| 3322 | pub fn getOwnerDecl(ty: Type, mod: *Module) InternPool.DeclIndex { | ||
| 3323 | return ty.getOwnerDeclOrNull(mod) orelse unreachable; | ||
| 3324 | } | ||
| 3325 | |||
| 3326 | pub fn getOwnerDeclOrNull(ty: Type, mod: *Module) ?InternPool.DeclIndex { | ||
| 3327 | const ip = &mod.intern_pool; | ||
| 3328 | return switch (ip.indexToKey(ty.toIntern())) { | ||
| 3329 | .struct_type => ip.loadStructType(ty.toIntern()).decl.unwrap(), | ||
| 3330 | .union_type => ip.loadUnionType(ty.toIntern()).decl, | ||
| 3331 | .opaque_type => ip.loadOpaqueType(ty.toIntern()).decl, | ||
| 3332 | .enum_type => ip.loadEnumType(ty.toIntern()).decl, | ||
| 3333 | else => null, | ||
| 3334 | }; | ||
| 3335 | } | ||
| 3336 | |||
| 3337 | pub fn srcLocOrNull(ty: Type, zcu: *Zcu) ?Module.LazySrcLoc { | ||
| 3338 | const ip = &zcu.intern_pool; | ||
| 3339 | return .{ | ||
| 3340 | .base_node_inst = switch (ip.indexToKey(ty.toIntern())) { | ||
| 3341 | .struct_type, .union_type, .opaque_type, .enum_type => |info| switch (info) { | ||
| 3342 | .declared => |d| d.zir_index, | ||
| 3343 | .reified => |r| r.zir_index, | ||
| 3344 | .generated_tag => |gt| ip.loadUnionType(gt.union_type).zir_index, | ||
| 3345 | .empty_struct => return null, | ||
| 3346 | }, | ||
| 3347 | else => return null, | ||
| 3348 | }, | ||
| 3349 | .offset = Module.LazySrcLoc.Offset.nodeOffset(0), | ||
| 3350 | }; | ||
| 3351 | } | ||
| 3352 | |||
| 3353 | pub fn srcLoc(ty: Type, zcu: *Zcu) Module.LazySrcLoc { | ||
| 3354 | return ty.srcLocOrNull(zcu).?; | ||
| 3355 | } | ||
| 3356 | |||
| 3357 | pub fn isGenericPoison(ty: Type) bool { | ||
| 3358 | return ty.toIntern() == .generic_poison_type; | ||
| 3359 | } | ||
| 3360 | |||
| 3361 | pub fn isTuple(ty: Type, mod: *Module) bool { | ||
| 3362 | const ip = &mod.intern_pool; | ||
| 3363 | return switch (ip.indexToKey(ty.toIntern())) { | ||
| 3364 | .struct_type => { | ||
| 3365 | const struct_type = ip.loadStructType(ty.toIntern()); | ||
| 3366 | if (struct_type.layout == .@"packed") return false; | ||
| 3367 | if (struct_type.decl == .none) return false; | ||
| 3368 | return struct_type.flagsPtr(ip).is_tuple; | ||
| 3369 | }, | ||
| 3370 | .anon_struct_type => |anon_struct| anon_struct.names.len == 0, | ||
| 3371 | else => false, | ||
| 3372 | }; | ||
| 3373 | } | ||
| 3374 | |||
| 3375 | pub fn isAnonStruct(ty: Type, mod: *Module) bool { | ||
| 3376 | if (ty.toIntern() == .empty_struct_type) return true; | ||
| 3377 | return switch (mod.intern_pool.indexToKey(ty.toIntern())) { | ||
| 3378 | .anon_struct_type => |anon_struct_type| anon_struct_type.names.len > 0, | ||
| 3379 | else => false, | ||
| 3380 | }; | ||
| 3381 | } | ||
| 3382 | |||
| 3383 | pub fn isTupleOrAnonStruct(ty: Type, mod: *Module) bool { | ||
| 3384 | const ip = &mod.intern_pool; | ||
| 3385 | return switch (ip.indexToKey(ty.toIntern())) { | ||
| 3386 | .struct_type => { | ||
| 3387 | const struct_type = ip.loadStructType(ty.toIntern()); | ||
| 3388 | if (struct_type.layout == .@"packed") return false; | ||
| 3389 | if (struct_type.decl == .none) return false; | ||
| 3390 | return struct_type.flagsPtr(ip).is_tuple; | ||
| 3391 | }, | ||
| 3392 | .anon_struct_type => true, | ||
| 3393 | else => false, | ||
| 3394 | }; | ||
| 3395 | } | ||
| 3396 | |||
| 3397 | pub fn isSimpleTuple(ty: Type, mod: *Module) bool { | ||
| 3398 | return switch (mod.intern_pool.indexToKey(ty.toIntern())) { | ||
| 3399 | .anon_struct_type => |anon_struct_type| anon_struct_type.names.len == 0, | ||
| 3400 | else => false, | ||
| 3401 | }; | ||
| 3402 | } | ||
| 3403 | |||
| 3404 | pub fn isSimpleTupleOrAnonStruct(ty: Type, mod: *Module) bool { | ||
| 3405 | return switch (mod.intern_pool.indexToKey(ty.toIntern())) { | ||
| 3406 | .anon_struct_type => true, | ||
| 3407 | else => false, | ||
| 3408 | }; | ||
| 3409 | } | ||
| 3410 | |||
| 3411 | /// Traverses optional child types and error union payloads until the type | ||
| 3412 | /// is not a pointer. For `E!?u32`, returns `u32`; for `*u8`, returns `*u8`. | ||
| 3413 | pub fn optEuBaseType(ty: Type, mod: *Module) Type { | ||
| 3414 | var cur = ty; | ||
| 3415 | while (true) switch (cur.zigTypeTag(mod)) { | ||
| 3416 | .Optional => cur = cur.optionalChild(mod), | ||
| 3417 | .ErrorUnion => cur = cur.errorUnionPayload(mod), | ||
| 3418 | else => return cur, | ||
| 3419 | }; | ||
| 3420 | } | ||
| 3421 | |||
| 3422 | pub fn toUnsigned(ty: Type, mod: *Module) !Type { | ||
| 3423 | return switch (ty.zigTypeTag(mod)) { | ||
| 3424 | .Int => mod.intType(.unsigned, ty.intInfo(mod).bits), | ||
| 3425 | .Vector => try mod.vectorType(.{ | ||
| 3426 | .len = ty.vectorLen(mod), | ||
| 3427 | .child = (try ty.childType(mod).toUnsigned(mod)).toIntern(), | ||
| 3428 | }), | ||
| 3429 | else => unreachable, | ||
| 3430 | }; | ||
| 3431 | } | ||
| 3432 | |||
| 3433 | pub fn typeDeclInst(ty: Type, zcu: *const Zcu) ?InternPool.TrackedInst.Index { | ||
| 3434 | const ip = &zcu.intern_pool; | ||
| 3435 | return switch (ip.indexToKey(ty.toIntern())) { | ||
| 3436 | .struct_type => ip.loadStructType(ty.toIntern()).zir_index.unwrap(), | ||
| 3437 | .union_type => ip.loadUnionType(ty.toIntern()).zir_index, | ||
| 3438 | .enum_type => ip.loadEnumType(ty.toIntern()).zir_index.unwrap(), | ||
| 3439 | .opaque_type => ip.loadOpaqueType(ty.toIntern()).zir_index, | ||
| 3440 | else => null, | ||
| 3441 | }; | ||
| 3442 | } | ||
| 3443 | |||
| 3444 | pub fn typeDeclSrcLine(ty: Type, zcu: *const Zcu) ?u32 { | ||
| 3445 | const ip = &zcu.intern_pool; | ||
| 3446 | const tracked = switch (ip.indexToKey(ty.toIntern())) { | ||
| 3447 | .struct_type, .union_type, .opaque_type, .enum_type => |info| switch (info) { | ||
| 3448 | .declared => |d| d.zir_index, | ||
| 3449 | .reified => |r| r.zir_index, | ||
| 3450 | .generated_tag => |gt| ip.loadUnionType(gt.union_type).zir_index, | ||
| 3451 | .empty_struct => return null, | ||
| 3452 | }, | ||
| 3453 | else => return null, | ||
| 3454 | }; | ||
| 3455 | const info = tracked.resolveFull(&zcu.intern_pool); | ||
| 3456 | const file = zcu.import_table.values()[zcu.path_digest_map.getIndex(info.path_digest).?]; | ||
| 3457 | assert(file.zir_loaded); | ||
| 3458 | const zir = file.zir; | ||
| 3459 | const inst = zir.instructions.get(@intFromEnum(info.inst)); | ||
| 3460 | assert(inst.tag == .extended); | ||
| 3461 | return switch (inst.data.extended.opcode) { | ||
| 3462 | .struct_decl => zir.extraData(Zir.Inst.StructDecl, inst.data.extended.operand).data.src_line, | ||
| 3463 | .union_decl => zir.extraData(Zir.Inst.UnionDecl, inst.data.extended.operand).data.src_line, | ||
| 3464 | .enum_decl => zir.extraData(Zir.Inst.EnumDecl, inst.data.extended.operand).data.src_line, | ||
| 3465 | .opaque_decl => zir.extraData(Zir.Inst.OpaqueDecl, inst.data.extended.operand).data.src_line, | ||
| 3466 | .reify => zir.extraData(Zir.Inst.Reify, inst.data.extended.operand).data.src_line, | ||
| 3467 | else => unreachable, | ||
| 3468 | }; | ||
| 3469 | } | ||
| 3470 | |||
| 3471 | /// Given a namespace type, returns its list of caotured values. | ||
| 3472 | pub fn getCaptures(ty: Type, zcu: *const Zcu) InternPool.CaptureValue.Slice { | ||
| 3473 | const ip = &zcu.intern_pool; | ||
| 3474 | return switch (ip.indexToKey(ty.toIntern())) { | ||
| 3475 | .struct_type => ip.loadStructType(ty.toIntern()).captures, | ||
| 3476 | .union_type => ip.loadUnionType(ty.toIntern()).captures, | ||
| 3477 | .enum_type => ip.loadEnumType(ty.toIntern()).captures, | ||
| 3478 | .opaque_type => ip.loadOpaqueType(ty.toIntern()).captures, | ||
| 3479 | else => unreachable, | ||
| 3480 | }; | ||
| 3481 | } | ||
| 3482 | |||
| 3483 | pub fn arrayBase(ty: Type, zcu: *const Zcu) struct { Type, u64 } { | ||
| 3484 | var cur_ty: Type = ty; | ||
| 3485 | var cur_len: u64 = 1; | ||
| 3486 | while (cur_ty.zigTypeTag(zcu) == .Array) { | ||
| 3487 | cur_len *= cur_ty.arrayLenIncludingSentinel(zcu); | ||
| 3488 | cur_ty = cur_ty.childType(zcu); | ||
| 3489 | } | ||
| 3490 | return .{ cur_ty, cur_len }; | ||
| 3491 | } | ||
| 3492 | |||
| 3493 | pub fn packedStructFieldPtrInfo(struct_ty: Type, parent_ptr_ty: Type, field_idx: u32, zcu: *Zcu) union(enum) { | ||
| 3494 | /// The result is a bit-pointer with the same value and a new packed offset. | ||
| 3495 | bit_ptr: InternPool.Key.PtrType.PackedOffset, | ||
| 3496 | /// The result is a standard pointer. | ||
| 3497 | byte_ptr: struct { | ||
| 3498 | /// The byte offset of the field pointer from the parent pointer value. | ||
| 3499 | offset: u64, | ||
| 3500 | /// The alignment of the field pointer type. | ||
| 3501 | alignment: InternPool.Alignment, | ||
| 3502 | }, | ||
| 3503 | } { | ||
| 3504 | comptime assert(Type.packed_struct_layout_version == 2); | ||
| 3505 | |||
| 3506 | const parent_ptr_info = parent_ptr_ty.ptrInfo(zcu); | ||
| 3507 | const field_ty = struct_ty.structFieldType(field_idx, zcu); | ||
| 3508 | |||
| 3509 | var bit_offset: u16 = 0; | ||
| 3510 | var running_bits: u16 = 0; | ||
| 3511 | for (0..struct_ty.structFieldCount(zcu)) |i| { | ||
| 3512 | const f_ty = struct_ty.structFieldType(i, zcu); | ||
| 3513 | if (i == field_idx) { | ||
| 3514 | bit_offset = running_bits; | ||
| 3515 | } | ||
| 3516 | running_bits += @intCast(f_ty.bitSize(zcu)); | ||
| 3517 | } | ||
| 3518 | |||
| 3519 | const res_host_size: u16, const res_bit_offset: u16 = if (parent_ptr_info.packed_offset.host_size != 0) | ||
| 3520 | .{ parent_ptr_info.packed_offset.host_size, parent_ptr_info.packed_offset.bit_offset + bit_offset } | ||
| 3521 | else | ||
| 3522 | .{ (running_bits + 7) / 8, bit_offset }; | ||
| 3523 | |||
| 3524 | // If the field happens to be byte-aligned, simplify the pointer type. | ||
| 3525 | // We can only do this if the pointee's bit size matches its ABI byte size, | ||
| 3526 | // so that loads and stores do not interfere with surrounding packed bits. | ||
| 3527 | // | ||
| 3528 | // TODO: we do not attempt this with big-endian targets yet because of nested | ||
| 3529 | // structs and floats. I need to double-check the desired behavior for big endian | ||
| 3530 | // targets before adding the necessary complications to this code. This will not | ||
| 3531 | // cause miscompilations; it only means the field pointer uses bit masking when it | ||
| 3532 | // might not be strictly necessary. | ||
| 3533 | if (res_bit_offset % 8 == 0 and field_ty.bitSize(zcu) == field_ty.abiSize(zcu) * 8 and zcu.getTarget().cpu.arch.endian() == .little) { | ||
| 3534 | const byte_offset = res_bit_offset / 8; | ||
| 3535 | const new_align = Alignment.fromLog2Units(@ctz(byte_offset | parent_ptr_ty.ptrAlignment(zcu).toByteUnits().?)); | ||
| 3536 | return .{ .byte_ptr = .{ | ||
| 3537 | .offset = byte_offset, | ||
| 3538 | .alignment = new_align, | ||
| 3539 | } }; | ||
| 3540 | } | ||
| 3541 | |||
| 3542 | return .{ .bit_ptr = .{ | ||
| 3543 | .host_size = res_host_size, | ||
| 3544 | .bit_offset = res_bit_offset, | ||
| 3545 | } }; | ||
| 3546 | } | ||
| 3547 | |||
| 3548 | pub const @"u1": Type = .{ .ip_index = .u1_type }; | ||
| 3549 | pub const @"u8": Type = .{ .ip_index = .u8_type }; | ||
| 3550 | pub const @"u16": Type = .{ .ip_index = .u16_type }; | ||
| 3551 | pub const @"u29": Type = .{ .ip_index = .u29_type }; | ||
| 3552 | pub const @"u32": Type = .{ .ip_index = .u32_type }; | ||
| 3553 | pub const @"u64": Type = .{ .ip_index = .u64_type }; | ||
| 3554 | pub const @"u128": Type = .{ .ip_index = .u128_type }; | ||
| 3555 | |||
| 3556 | pub const @"i8": Type = .{ .ip_index = .i8_type }; | ||
| 3557 | pub const @"i16": Type = .{ .ip_index = .i16_type }; | ||
| 3558 | pub const @"i32": Type = .{ .ip_index = .i32_type }; | ||
| 3559 | pub const @"i64": Type = .{ .ip_index = .i64_type }; | ||
| 3560 | pub const @"i128": Type = .{ .ip_index = .i128_type }; | ||
| 3561 | |||
| 3562 | pub const @"f16": Type = .{ .ip_index = .f16_type }; | ||
| 3563 | pub const @"f32": Type = .{ .ip_index = .f32_type }; | ||
| 3564 | pub const @"f64": Type = .{ .ip_index = .f64_type }; | ||
| 3565 | pub const @"f80": Type = .{ .ip_index = .f80_type }; | ||
| 3566 | pub const @"f128": Type = .{ .ip_index = .f128_type }; | ||
| 3567 | |||
| 3568 | pub const @"bool": Type = .{ .ip_index = .bool_type }; | ||
| 3569 | pub const @"usize": Type = .{ .ip_index = .usize_type }; | ||
| 3570 | pub const @"isize": Type = .{ .ip_index = .isize_type }; | ||
| 3571 | pub const @"comptime_int": Type = .{ .ip_index = .comptime_int_type }; | ||
| 3572 | pub const @"comptime_float": Type = .{ .ip_index = .comptime_float_type }; | ||
| 3573 | pub const @"void": Type = .{ .ip_index = .void_type }; | ||
| 3574 | pub const @"type": Type = .{ .ip_index = .type_type }; | ||
| 3575 | pub const @"anyerror": Type = .{ .ip_index = .anyerror_type }; | ||
| 3576 | pub const @"anyopaque": Type = .{ .ip_index = .anyopaque_type }; | ||
| 3577 | pub const @"anyframe": Type = .{ .ip_index = .anyframe_type }; | ||
| 3578 | pub const @"null": Type = .{ .ip_index = .null_type }; | ||
| 3579 | pub const @"undefined": Type = .{ .ip_index = .undefined_type }; | ||
| 3580 | pub const @"noreturn": Type = .{ .ip_index = .noreturn_type }; | ||
| 3581 | |||
| 3582 | pub const @"c_char": Type = .{ .ip_index = .c_char_type }; | ||
| 3583 | pub const @"c_short": Type = .{ .ip_index = .c_short_type }; | ||
| 3584 | pub const @"c_ushort": Type = .{ .ip_index = .c_ushort_type }; | ||
| 3585 | pub const @"c_int": Type = .{ .ip_index = .c_int_type }; | ||
| 3586 | pub const @"c_uint": Type = .{ .ip_index = .c_uint_type }; | ||
| 3587 | pub const @"c_long": Type = .{ .ip_index = .c_long_type }; | ||
| 3588 | pub const @"c_ulong": Type = .{ .ip_index = .c_ulong_type }; | ||
| 3589 | pub const @"c_longlong": Type = .{ .ip_index = .c_longlong_type }; | ||
| 3590 | pub const @"c_ulonglong": Type = .{ .ip_index = .c_ulonglong_type }; | ||
| 3591 | pub const @"c_longdouble": Type = .{ .ip_index = .c_longdouble_type }; | ||
| 3592 | |||
| 3593 | pub const slice_const_u8: Type = .{ .ip_index = .slice_const_u8_type }; | ||
| 3594 | pub const manyptr_u8: Type = .{ .ip_index = .manyptr_u8_type }; | ||
| 3595 | pub const single_const_pointer_to_comptime_int: Type = .{ | ||
| 3596 | .ip_index = .single_const_pointer_to_comptime_int_type, | ||
| 3597 | }; | ||
| 3598 | pub const slice_const_u8_sentinel_0: Type = .{ .ip_index = .slice_const_u8_sentinel_0_type }; | ||
| 3599 | pub const empty_struct_literal: Type = .{ .ip_index = .empty_struct_type }; | ||
| 3600 | |||
| 3601 | pub const generic_poison: Type = .{ .ip_index = .generic_poison_type }; | ||
| 3602 | |||
| 3603 | pub fn smallestUnsignedBits(max: u64) u16 { | ||
| 3604 | if (max == 0) return 0; | ||
| 3605 | const base = std.math.log2(max); | ||
| 3606 | const upper = (@as(u64, 1) << @as(u6, @intCast(base))) - 1; | ||
| 3607 | return @as(u16, @intCast(base + @intFromBool(upper < max))); | ||
| 3608 | } | ||
| 3609 | |||
| 3610 | /// This is only used for comptime asserts. Bump this number when you make a change | ||
| 3611 | /// to packed struct layout to find out all the places in the codebase you need to edit! | ||
| 3612 | pub const packed_struct_layout_version = 2; | ||
| 3613 | }; | ||
| 3614 | |||
| 3615 | fn cTypeAlign(target: Target, c_type: Target.CType) Alignment { | ||
| 3616 | return Alignment.fromByteUnits(target.c_type_alignment(c_type)); | ||
| 3617 | } | ||