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
6const std = @import("std");
7const builtin = @import("builtin");
8const Allocator = std.mem.Allocator;
9const Value = @import("Value.zig");
10const assert = std.debug.assert;
11const Target = std.Target;
12const Zcu = @import("Zcu.zig");
13const log = std.log.scoped(.Type);
14const target_util = @import("target.zig");
15const InternPool = @import("InternPool.zig");
16const Alignment = InternPool.Alignment;
17const Zir = std.zig.Zir;
18const Type = @This();
19
20ip_index: InternPool.Index,
21
22pub fn zigTypeTag(ty: Type, zcu: *const Zcu) std.lang.TypeId {
23 return zcu.intern_pool.zigTypeTag(ty.toIntern());
24}
25
26/// Every type is a member of exactly one "class" which determines:
27/// * whether values of the type can exist at all
28/// * whether values of the type can be runtime-knwon
29/// * whether the type is considered comptime-only
30/// * whether the type has runtime bits (nonzero ABI size)
31pub const Class = enum(u3) {
32 /// Values of this type cannot exist because the type semantically has no values. Attempting to
33 /// create a value of this type (such as by coercing `undefined`) always emits a compile error.
34 ///
35 /// Not comptime-only. No runtime bits, i.e. ABI size is 0.
36 ///
37 /// Exhaustive list of no-possible-value ("NPV") types:
38 /// * `noreturn`
39 /// * `anyopaque`, and any `opaque` type
40 /// * `[n]T` where `n` is non-zero and `T` is NPV
41 /// * Any tuple where at least one non-`comptime` field has an NPV type
42 /// * Any enum whose backing type is `noreturn`
43 /// * Any struct where at least one non-`comptime` field has an NPV type
44 /// * Any union where every field has an NPV type (including unions with no fields)
45 /// * If the union would typically have a runtime tag, even if that tag would have runtime
46 /// bits, the union type is still NPV; the runtime tag is effectively omitted.
47 no_possible_value,
48
49 /// Values of this type are always comptime-known because there is only one value inhabiting the
50 /// type. This matches the colloquial understanding of a "zero-bit type".
51 ///
52 /// Not comptime-only (although always comptime-known). No runtime bits, i.e. ABI size is 0.
53 ///
54 /// Exhaustive list of one-possible-value ("OPV") types:
55 /// * `void`
56 /// * `u0`, `i0`
57 /// * `[0]T` for any `T`
58 /// * `[n]T` where `T` is OPV
59 /// * `[n:s]T` where `T` is OPV
60 /// * `@Vector(0, T)` for any `T`
61 /// * `@Vector(n, T)` where `T` is OPV
62 /// * Any tuple where every non-`comptime` field has an OPV type (including tuples with no fields)
63 /// * Any enum whose backing type is OPV
64 /// * Any struct where every non-`comptime` field has an OPV type (including structs with no fields)
65 /// * Any union with no runtime tag where all fields have OPV
66 /// * Any union where one field has an OPV type, and either:
67 /// * All other fields have NPV types (in this case, if there would be a runtime tag, it is omitted)
68 /// * All other fields have NPV or OPV types, and the union has no runtime tag
69 one_possible_value,
70
71 /// The type holds state (so it is neither NPV nor OPV), but contains no comptime-only state, so
72 /// values may be runtime-known.
73 ///
74 /// Not comptime-only. Has runtime bits, i.e. ABI size is non-zero.
75 ///
76 /// Most types which are typically used in Zig inhabit this class. For instance, all pointer
77 /// types, all integer types other than `u0` and `i0`, and most user-defined aggregates fall
78 /// into this category.
79 runtime,
80
81 /// The type holds state (so it is neither NPV nor OPV). Some, but not all, of the contained
82 /// state is comptime-only.
83 ///
84 /// Comptime-only. Has runtime bits, i.e. ABI size is non-zero.
85 ///
86 /// Partially-comptime types arise from aggregates (`struct`s, `union`s, or tuples) which have
87 /// some fields with fully-comptime types (such as `comptime_int`) and some fields with runtime
88 /// types (such as `u8`). Because the user may acquire pointers to these fields, pointers to the
89 /// embedded runtime state must be valid, so backends are required to lower the runtime state
90 /// within the type.
91 ///
92 /// Note that logically-runtime state which cannot be directly referenced by the user (such as
93 /// the enum tag of a tagged union type, or the "populated" bit of an optional type) does not
94 /// cause a type to be partially-comptime.
95 partially_comptime,
96
97 /// The type contains exclusively comptime-only state.
98 ///
99 /// Comptime-only. No runtime bits, i.e. ABI size is 0.
100 ///
101 /// Fully-comptime types arise from a handful of primitive fully-comptime types:
102 /// * `type`
103 /// * `comptime_int`
104 /// * `comptime_float`
105 /// * `@EnumLiteral()`
106 /// * `@TypeOf(null)`
107 /// * `@TypeOf(undefined)`
108 ///
109 /// Then, aggregates containing fully-comptime types may themselves be either fully-comptime or
110 /// partially-comptime; see the doc comment on `.partially_comptime` for details.
111 fully_comptime,
112
113 pub fn hasRuntimeBits(class: Class) bool {
114 return switch (class) {
115 .no_possible_value, .one_possible_value, .fully_comptime => false,
116 .runtime, .partially_comptime => true,
117 };
118 }
119
120 pub fn comptimeOnly(class: Class) bool {
121 return switch (class) {
122 .no_possible_value, .one_possible_value, .runtime => false,
123 .partially_comptime, .fully_comptime => true,
124 };
125 }
126};
127
128/// Returns the `Class` for the type `ty`. Asserts that the layout of `ty` is resolved.
129pub fn classify(start_ty: Type, zcu: *const Zcu) Class {
130 const ip = &zcu.intern_pool;
131
132 // We avoid recursion in most cases to make us more optimizer-friendly because this can be a
133 // very hot code path. The only case where recursion is necessary is tuples, so that case is
134 // outlined into a separate function; see `classifyTuple`.
135
136 var extra_states: enum { none, one, many } = .none;
137
138 var cur_ty = start_ty;
139 const base: Class = while (true) break switch (ip.indexToKey(cur_ty.toIntern())) {
140 .simple_type => |t| switch (t) {
141 .f16,
142 .f32,
143 .f64,
144 .f80,
145 .f128,
146 .usize,
147 .isize,
148 .c_char,
149 .c_short,
150 .c_ushort,
151 .c_int,
152 .c_uint,
153 .c_long,
154 .c_ulong,
155 .c_longlong,
156 .c_ulonglong,
157 .c_longdouble,
158 .bool,
159 .anyerror,
160 .adhoc_inferred_error_set,
161 => .runtime,
162
163 .anyopaque => .no_possible_value,
164
165 .type,
166 .comptime_int,
167 .comptime_float,
168 .enum_literal,
169 .null,
170 .undefined,
171 => .fully_comptime,
172
173 .void => .one_possible_value,
174 .noreturn => .no_possible_value,
175
176 .generic_poison => unreachable,
177 },
178
179 .error_set_type,
180 .inferred_error_set_type,
181 .ptr_type,
182 .anyframe_type,
183 => .runtime,
184
185 .func_type => .fully_comptime,
186
187 .spirv_type => if (cur_ty.isSpirvRuntimeArray(zcu)) .runtime else .no_possible_value,
188 .opaque_type => .no_possible_value,
189
190 .error_union_type => |eu| {
191 extra_states = .many;
192 cur_ty = .fromInterned(eu.payload_type);
193 continue;
194 },
195
196 .int_type => |int| switch (int.bits) {
197 0 => .one_possible_value,
198 else => .runtime,
199 },
200 .array_type => |arr| {
201 if (arr.len == 0 and arr.sentinel == .none) break .one_possible_value;
202 cur_ty = .fromInterned(arr.child);
203 continue;
204 },
205 .vector_type => |vec| {
206 if (vec.len == 0) break .one_possible_value;
207 cur_ty = .fromInterned(vec.child);
208 continue;
209 },
210 .opt_type => |child_ty_ip| {
211 extra_states = switch (extra_states) {
212 .none => .one,
213 .one, .many => .many,
214 };
215 cur_ty = .fromInterned(child_ty_ip);
216 continue;
217 },
218 .tuple_type => |tuple| {
219 @branchHint(.unlikely);
220 break classifyTuple(tuple.types.get(ip), tuple.values.get(ip), zcu);
221 },
222 .struct_type => {
223 const struct_obj = ip.loadStructType(cur_ty.toIntern());
224 switch (struct_obj.layout) {
225 .auto, .@"extern" => {
226 assert(struct_obj.want_layout);
227 zcu.assertUpToDate(.wrap(.{ .type_layout = cur_ty.toIntern() }));
228 break struct_obj.class;
229 },
230 .@"packed" => {
231 cur_ty = .fromInterned(struct_obj.packed_backing_int_type);
232 continue;
233 },
234 }
235 },
236 .union_type => {
237 const union_obj = ip.loadUnionType(cur_ty.toIntern());
238 switch (union_obj.layout) {
239 .auto, .@"extern" => {
240 assert(union_obj.want_layout);
241 zcu.assertUpToDate(.wrap(.{ .type_layout = cur_ty.toIntern() }));
242 break union_obj.class;
243 },
244 .@"packed" => {
245 cur_ty = .fromInterned(union_obj.packed_backing_int_type);
246 continue;
247 },
248 }
249 },
250 .enum_type => {
251 const enum_obj = ip.loadEnumType(cur_ty.toIntern());
252 assert(enum_obj.want_layout);
253 zcu.assertUpToDate(.wrap(.{ .type_layout = cur_ty.toIntern() }));
254 cur_ty = .fromInterned(enum_obj.int_tag_type);
255 continue;
256 },
257
258 // values, not types
259 .undef,
260 .simple_value,
261 .@"extern",
262 .func,
263 .int,
264 .err,
265 .error_union,
266 .enum_literal,
267 .enum_tag,
268 .float,
269 .ptr,
270 .slice,
271 .opt,
272 .aggregate,
273 .un,
274 .bitpack,
275 // memoization, not types
276 .memoized_call,
277 => unreachable,
278 };
279
280 return switch (base) {
281 .runtime => .runtime, // extra states are irrelevant, we already have many!
282 .partially_comptime => .partially_comptime, // likewise
283 .fully_comptime => {
284 // We do not need to change to `.partially_comptime` here because the extra states do
285 // not necessarily require runtime bits. This is because Zig does not provide a way to
286 // take the address of the "is null" bit of an optional or the error set "inside" of an
287 // error union.
288 return .fully_comptime;
289 },
290
291 .no_possible_value => switch (extra_states) {
292 .none => .no_possible_value,
293 .one => .one_possible_value,
294 .many => .runtime,
295 },
296
297 .one_possible_value => switch (extra_states) {
298 .none => .one_possible_value,
299 .one, .many => .runtime,
300 },
301 };
302}
303/// This is a separate function to `classify` to avoid recursion in the main `classify` function,
304/// which can encourage the optimizer to e.g. inline `classify` where it would be beneficial.
305fn classifyTuple(types: []const InternPool.Index, values: []const InternPool.Index, zcu: *const Zcu) Class {
306 var has_runtime_state = false;
307 var has_comptime_state = false;
308 for (types, values) |field_ty, field_comptime_val| {
309 if (field_comptime_val != .none) continue;
310 switch (Type.fromInterned(field_ty).classify(zcu)) {
311 .no_possible_value => return .no_possible_value,
312 .one_possible_value => {},
313 .runtime => has_runtime_state = true,
314 .fully_comptime => has_comptime_state = true,
315 .partially_comptime => {
316 has_runtime_state = true;
317 has_comptime_state = true;
318 },
319 }
320 }
321 if (has_comptime_state) {
322 return if (has_runtime_state) .partially_comptime else .fully_comptime;
323 } else {
324 return if (has_runtime_state) .runtime else .one_possible_value;
325 }
326}
327
328/// Asserts the type is resolved.
329pub fn isSelfComparable(ty: Type, zcu: *const Zcu, is_equality_cmp: bool) bool {
330 return switch (ty.zigTypeTag(zcu)) {
331 .int,
332 .float,
333 .comptime_float,
334 .comptime_int,
335 => true,
336
337 .vector => ty.childType(zcu).isSelfComparable(zcu, is_equality_cmp),
338
339 .bool,
340 .type,
341 .void,
342 .error_set,
343 .@"fn",
344 .@"opaque",
345 .spirv,
346 .@"anyframe",
347 .@"enum",
348 .enum_literal,
349 => is_equality_cmp,
350
351 .noreturn,
352 .array,
353 .undefined,
354 .null,
355 .error_union,
356 .frame,
357 => false,
358
359 .@"struct", .@"union" => is_equality_cmp and ty.containerLayout(zcu) == .@"packed",
360 .pointer => !ty.isSlice(zcu) and (is_equality_cmp or ty.isCPtr(zcu)),
361 .optional => {
362 if (!is_equality_cmp) return false;
363 return ty.optionalChild(zcu).isSelfComparable(zcu, is_equality_cmp);
364 },
365 };
366}
367
368/// If it is a function pointer, returns the function type. Otherwise returns null.
369pub fn castPtrToFn(ty: Type, zcu: *const Zcu) ?Type {
370 if (ty.zigTypeTag(zcu) != .pointer) return null;
371 const elem_ty = ty.childType(zcu);
372 if (elem_ty.zigTypeTag(zcu) != .@"fn") return null;
373 return elem_ty;
374}
375
376/// Asserts the type is a pointer.
377pub fn ptrIsMutable(ty: Type, zcu: *const Zcu) bool {
378 return !zcu.intern_pool.indexToKey(ty.toIntern()).ptr_type.flags.is_const;
379}
380
381pub const ArrayInfo = struct {
382 elem_type: Type,
383 sentinel: ?Value = null,
384 len: u64,
385};
386
387pub fn arrayInfo(self: Type, zcu: *const Zcu) ArrayInfo {
388 return .{
389 .len = self.arrayLen(zcu),
390 .sentinel = self.sentinel(zcu),
391 .elem_type = self.childType(zcu),
392 };
393}
394
395pub fn ptrInfo(ty: Type, zcu: *const Zcu) InternPool.Key.PtrType {
396 return switch (zcu.intern_pool.indexToKey(ty.toIntern())) {
397 .ptr_type => |p| p,
398 .opt_type => |child| switch (zcu.intern_pool.indexToKey(child)) {
399 .ptr_type => |p| p,
400 else => unreachable,
401 },
402 else => unreachable,
403 };
404}
405
406pub fn eql(a: Type, b: Type) bool {
407 // The InternPool data structure hashes based on Key to make interned objects
408 // unique. An Index can be treated simply as u32 value for the
409 // purpose of Type/Value hashing and equality.
410 return a.toIntern() == b.toIntern();
411}
412
413pub const format = @compileError("do not format types directly; use either ty.fmtDebug() or ty.fmt()");
414
415pub const Formatter = std.fmt.Alt(Format, Format.default);
416
417pub fn fmt(ty: Type, pt: Zcu.PerThread) Formatter {
418 return .{ .data = .{
419 .ty = ty,
420 .pt = pt,
421 } };
422}
423
424const Format = struct {
425 ty: Type,
426 pt: Zcu.PerThread,
427
428 fn default(f: Format, writer: *std.Io.Writer) std.Io.Writer.Error!void {
429 return print(f.ty, writer, f.pt, null);
430 }
431};
432
433pub fn fmtDebug(ty: Type) std.fmt.Alt(Type, dump) {
434 return .{ .data = ty };
435}
436
437/// This is a debug function. In order to print types in a meaningful way
438/// we also need access to the module.
439pub fn dump(start_type: Type, writer: *std.Io.Writer) std.Io.Writer.Error!void {
440 return writer.print("{any}", .{start_type.ip_index});
441}
442
443/// Prints a name suitable for `@typeName`.
444/// TODO: take an `opt_sema` to pass to `fmtValue` when printing sentinels.
445pub fn print(ty: Type, writer: *std.Io.Writer, pt: Zcu.PerThread, ctx: ?*Comparison) std.Io.Writer.Error!void {
446 if (ctx) |c| {
447 const should_dedupe = shouldDedupeType(ty, c, pt) catch |err| switch (err) {
448 error.OutOfMemory => return error.WriteFailed,
449 };
450 switch (should_dedupe) {
451 .dont_dedupe => {},
452 .dedupe => |placeholder| return placeholder.format(writer),
453 }
454 }
455
456 const zcu = pt.zcu;
457 const ip = &zcu.intern_pool;
458 switch (ip.indexToKey(ty.toIntern())) {
459 .undef => return writer.writeAll("@as(type, undefined)"),
460 .int_type => |int_type| {
461 const sign_char: u8 = switch (int_type.signedness) {
462 .signed => 'i',
463 .unsigned => 'u',
464 };
465 return writer.print("{c}{d}", .{ sign_char, int_type.bits });
466 },
467 .ptr_type => {
468 const info = ty.ptrInfo(zcu);
469
470 if (info.sentinel != .none) switch (info.flags.size) {
471 .one, .c => unreachable,
472 .many => try writer.print("[*:{f}]", .{Value.fromInterned(info.sentinel).fmtValue(pt)}),
473 .slice => try writer.print("[:{f}]", .{Value.fromInterned(info.sentinel).fmtValue(pt)}),
474 } else switch (info.flags.size) {
475 .one => try writer.writeAll("*"),
476 .many => try writer.writeAll("[*]"),
477 .c => try writer.writeAll("[*c]"),
478 .slice => try writer.writeAll("[]"),
479 }
480 if (info.flags.is_allowzero and info.flags.size != .c) try writer.writeAll("allowzero ");
481 if (info.flags.alignment != .none or
482 info.packed_offset.host_size != 0 or
483 info.flags.vector_index != .none)
484 {
485 const alignment = if (info.flags.alignment != .none)
486 info.flags.alignment
487 else
488 Type.fromInterned(info.child).abiAlignment(pt.zcu);
489 try writer.print("align({d}", .{alignment.toByteUnits() orelse 0});
490
491 if (info.packed_offset.bit_offset != 0 or info.packed_offset.host_size != 0) {
492 try writer.print(":{d}:{d}", .{
493 info.packed_offset.bit_offset, info.packed_offset.host_size,
494 });
495 }
496 if (info.flags.vector_index != .none) {
497 try writer.print(":{d}", .{@backingInt(info.flags.vector_index)});
498 }
499 try writer.writeAll(") ");
500 }
501 if (info.flags.address_space != .generic) {
502 try writer.print("addrspace(.{s}) ", .{@tagName(info.flags.address_space)});
503 }
504 if (info.flags.is_const) try writer.writeAll("const ");
505 if (info.flags.is_volatile) try writer.writeAll("volatile ");
506
507 try print(Type.fromInterned(info.child), writer, pt, ctx);
508 return;
509 },
510 .array_type => |array_type| {
511 if (array_type.sentinel == .none) {
512 try writer.print("[{d}]", .{array_type.len});
513 try print(Type.fromInterned(array_type.child), writer, pt, ctx);
514 } else {
515 try writer.print("[{d}:{f}]", .{
516 array_type.len,
517 Value.fromInterned(array_type.sentinel).fmtValue(pt),
518 });
519 try print(Type.fromInterned(array_type.child), writer, pt, ctx);
520 }
521 return;
522 },
523 .vector_type => |vector_type| {
524 try writer.print("@Vector({d}, ", .{vector_type.len});
525 try print(Type.fromInterned(vector_type.child), writer, pt, ctx);
526 try writer.writeAll(")");
527 return;
528 },
529 .opt_type => |child| {
530 try writer.writeByte('?');
531 return print(Type.fromInterned(child), writer, pt, ctx);
532 },
533 .error_union_type => |error_union_type| {
534 try print(Type.fromInterned(error_union_type.error_set_type), writer, pt, ctx);
535 try writer.writeByte('!');
536 if (error_union_type.payload_type == .generic_poison_type) {
537 try writer.writeAll("anytype");
538 } else {
539 try print(Type.fromInterned(error_union_type.payload_type), writer, pt, ctx);
540 }
541 return;
542 },
543 .inferred_error_set_type => |func_index| {
544 const func_nav = ip.getNav(zcu.funcInfo(func_index).owner_nav);
545 try writer.print("@typeInfo(@typeInfo(@TypeOf({f})).@\"fn\".return_type.?).error_union.error_set", .{
546 func_nav.fqn.fmt(ip),
547 });
548 },
549 .error_set_type => |error_set_type| {
550 const NullTerminatedString = InternPool.NullTerminatedString;
551 const sorted_names = zcu.gpa.dupe(NullTerminatedString, error_set_type.names.get(ip)) catch {
552 zcu.comp.setAllocFailure();
553 return writer.writeAll("error{...}");
554 };
555 defer zcu.gpa.free(sorted_names);
556
557 std.mem.sortUnstable(NullTerminatedString, sorted_names, ip, struct {
558 fn lessThan(ip_: *InternPool, lhs: NullTerminatedString, rhs: NullTerminatedString) bool {
559 const lhs_slice = lhs.toSlice(ip_);
560 const rhs_slice = rhs.toSlice(ip_);
561 return std.mem.lessThan(u8, lhs_slice, rhs_slice);
562 }
563 }.lessThan);
564
565 try writer.writeAll("error{");
566 for (sorted_names, 0..) |name, i| {
567 if (i != 0) try writer.writeByte(',');
568 try writer.print("{f}", .{name.fmt(ip)});
569 }
570 try writer.writeAll("}");
571 },
572 .simple_type => |s| switch (s) {
573 .f16,
574 .f32,
575 .f64,
576 .f80,
577 .f128,
578 .usize,
579 .isize,
580 .c_char,
581 .c_short,
582 .c_ushort,
583 .c_int,
584 .c_uint,
585 .c_long,
586 .c_ulong,
587 .c_longlong,
588 .c_ulonglong,
589 .c_longdouble,
590 .anyopaque,
591 .bool,
592 .void,
593 .type,
594 .anyerror,
595 .comptime_int,
596 .comptime_float,
597 .noreturn,
598 .adhoc_inferred_error_set,
599 => return writer.writeAll(@tagName(s)),
600
601 .null,
602 .undefined,
603 => try writer.print("@TypeOf({s})", .{@tagName(s)}),
604
605 .enum_literal => try writer.writeAll("@EnumLiteral()"),
606
607 .generic_poison => unreachable,
608 },
609 .struct_type => {
610 const fqn = ip.loadStructType(ty.toIntern()).fqn;
611 try writer.print("{f}", .{fqn.fmt(ip)});
612 },
613 .tuple_type => |tuple| {
614 if (tuple.types.len == 0) {
615 return writer.writeAll("@TypeOf(.{})");
616 }
617 try writer.writeAll("struct {");
618 for (tuple.types.get(ip), tuple.values.get(ip), 0..) |field_ty, val, i| {
619 try writer.writeAll(if (i == 0) " " else ", ");
620 if (val != .none) try writer.writeAll("comptime ");
621 try print(Type.fromInterned(field_ty), writer, pt, ctx);
622 if (val != .none) try writer.print(" = {f}", .{Value.fromInterned(val).fmtValue(pt)});
623 }
624 try writer.writeAll(" }");
625 },
626
627 .union_type => {
628 const fqn = ip.loadUnionType(ty.toIntern()).fqn;
629 try writer.print("{f}", .{fqn.fmt(ip)});
630 },
631 .opaque_type => {
632 const fqn = ip.loadOpaqueType(ty.toIntern()).fqn;
633 try writer.print("{f}", .{fqn.fmt(ip)});
634 },
635 .enum_type => {
636 const fqn = ip.loadEnumType(ty.toIntern()).fqn;
637 try writer.print("{f}", .{fqn.fmt(ip)});
638 },
639 .spirv_type => {
640 const info = ip.loadSpirvType(ty.toIntern());
641 switch (info.flags.tag) {
642 .sampler => try writer.writeAll("@SpirvType(.sampler)"),
643 .image => try writer.writeAll("@SpirvType(.image)"),
644 .sampled_image => {
645 try writer.writeAll("@SpirvType(.sampled_image, ");
646 try print(Type.fromInterned(info.ty), writer, pt, ctx);
647 try writer.writeAll(")");
648 },
649 .runtime_array => {
650 try writer.writeAll("@SpirvType(.runtime_array, ");
651 try print(Type.fromInterned(info.ty), writer, pt, ctx);
652 try writer.writeAll(")");
653 },
654 }
655 },
656 .func_type => |fn_info| {
657 if (fn_info.is_noinline) {
658 try writer.writeAll("noinline ");
659 }
660 try writer.writeAll("fn (");
661 const param_types = fn_info.param_types.get(&zcu.intern_pool);
662 for (param_types, 0..) |param_ty, i| {
663 if (i != 0) try writer.writeAll(", ");
664 if (std.math.cast(u5, i)) |index| {
665 if (fn_info.paramIsComptime(index)) {
666 try writer.writeAll("comptime ");
667 }
668 if (fn_info.paramIsNoalias(index)) {
669 try writer.writeAll("noalias ");
670 }
671 }
672 if (param_ty == .generic_poison_type) {
673 try writer.writeAll("anytype");
674 } else {
675 try print(Type.fromInterned(param_ty), writer, pt, ctx);
676 }
677 }
678 if (fn_info.is_var_args) {
679 if (param_types.len != 0) {
680 try writer.writeAll(", ");
681 }
682 try writer.writeAll("...");
683 }
684 try writer.writeAll(") ");
685 if (fn_info.cc != .auto) print_cc: {
686 if (zcu.getTarget().cCallingConvention()) |ccc| {
687 if (fn_info.cc.eql(ccc)) {
688 try writer.writeAll("callconv(.c) ");
689 break :print_cc;
690 }
691 }
692 switch (fn_info.cc) {
693 .auto, .async, .naked, .@"inline" => try writer.print("callconv(.{f}) ", .{
694 std.zig.fmtId(@tagName(fn_info.cc)),
695 }),
696 else => try writer.print("callconv({any}) ", .{fn_info.cc}),
697 }
698 }
699 if (fn_info.return_type == .generic_poison_type) {
700 try writer.writeAll("anytype");
701 } else {
702 try print(Type.fromInterned(fn_info.return_type), writer, pt, ctx);
703 }
704 },
705 .anyframe_type => |child| {
706 if (child == .none) return writer.writeAll("anyframe");
707 try writer.writeAll("anyframe->");
708 return print(Type.fromInterned(child), writer, pt, ctx);
709 },
710
711 // values, not types
712 .simple_value,
713 .@"extern",
714 .func,
715 .int,
716 .err,
717 .error_union,
718 .enum_literal,
719 .enum_tag,
720 .float,
721 .ptr,
722 .slice,
723 .opt,
724 .aggregate,
725 .un,
726 .bitpack,
727 // memoization, not types
728 .memoized_call,
729 => unreachable,
730 }
731}
732
733pub fn fromInterned(i: InternPool.Index) Type {
734 assert(i != .none);
735 return .{ .ip_index = i };
736}
737
738pub fn toIntern(ty: Type) InternPool.Index {
739 assert(ty.ip_index != .none);
740 return ty.ip_index;
741}
742
743pub fn isSpirvRuntimeArray(ty: Type, zcu: *const Zcu) bool {
744 const ip = &zcu.intern_pool;
745 return switch (ip.indexToKey(ty.toIntern())) {
746 .spirv_type => ip.loadSpirvType(ty.toIntern()).flags.tag == .runtime_array,
747 else => false,
748 };
749}
750
751pub fn toValue(self: Type) Value {
752 return .fromInterned(self.toIntern());
753}
754
755/// Returns `true` if and only if the type takes up space in memory at runtime. This is also exactly
756/// whether or not the backend/linker needs to be sent values of this type to emit to the binary.
757///
758/// Types without runtime bits have an ABI size of 0; all other types have a non-zero ABI size. All
759/// types, regardless of whether they have runtime bits, have a non-zero ABI alignment.
760///
761/// Comptime-only types may still have runtime bits. For instance, `struct { a: u32, b: type }` is a
762/// comptime-only type, but it nonetheless has runtime bits and a runtime memory layout (where the
763/// field `b: type` is omitted). This is because a user may take a pointer to the field `a`, which
764/// must then be valid to use at runtime.
765///
766/// This function is a trivial wrapper around `classify`:
767///
768/// * Types with one possible value, such as `void`, or no possible value, such as `noreturn`, do
769/// not have runtime bits and have an ABI size of 0 because they simply contain no state.
770///
771/// * Types which are fully comptime, such as `type` and `comptime_int`, do not have runtime bits
772/// because they contain only comptime state. (This compiler implementation also currently makes
773/// types like `struct { x: comptime_int }` fully comptime, but that could change in the future if
774/// we start inserting hidden safety fields into them.)
775///
776/// * All other types contain some runtime state, so have runtime bits and a non-zero ABI size.
777pub fn hasRuntimeBits(ty: Type, zcu: *const Zcu) bool {
778 return ty.classify(zcu).hasRuntimeBits();
779}
780
781/// Returns `true` iff the memory layout of `ty` is defined by the Zig language specification.
782///
783/// Does not require `ty` to be resolved.
784pub fn hasWellDefinedLayout(ty: Type, zcu: *const Zcu) bool {
785 const ip = &zcu.intern_pool;
786 return switch (ip.indexToKey(ty.toIntern())) {
787 .int_type,
788 => true,
789
790 .vector_type,
791 .error_union_type,
792 .error_set_type,
793 .inferred_error_set_type,
794 .tuple_type,
795 .spirv_type,
796 .opaque_type,
797 .anyframe_type,
798 // These are function bodies, not function pointers.
799 .func_type,
800 => false,
801
802 .array_type => |array_type| Type.fromInterned(array_type.child).hasWellDefinedLayout(zcu),
803 .opt_type => ty.isPtrLikeOptional(zcu),
804 .ptr_type => |ptr_type| ptr_type.flags.size != .slice,
805
806 .simple_type => |t| switch (t) {
807 .f16,
808 .f32,
809 .f64,
810 .f80,
811 .f128,
812 .usize,
813 .isize,
814 .c_char,
815 .c_short,
816 .c_ushort,
817 .c_int,
818 .c_uint,
819 .c_long,
820 .c_ulong,
821 .c_longlong,
822 .c_ulonglong,
823 .c_longdouble,
824 .bool,
825 .void,
826 => true,
827
828 .anyerror,
829 .adhoc_inferred_error_set,
830 .anyopaque,
831 .type,
832 .comptime_int,
833 .comptime_float,
834 .noreturn,
835 .null,
836 .undefined,
837 .enum_literal,
838 .generic_poison,
839 => false,
840 },
841 .struct_type => switch (ip.loadStructType(ty.toIntern()).layout) {
842 .auto => false,
843 .@"extern", .@"packed" => true,
844 },
845 .union_type => switch (ip.loadUnionType(ty.toIntern()).layout) {
846 .auto => false,
847 .@"extern", .@"packed" => true,
848 },
849 .enum_type => switch (ip.loadEnumType(ty.toIntern()).int_tag_mode) {
850 .explicit => true,
851 .auto => false,
852 },
853
854 // values, not types
855 .undef,
856 .simple_value,
857 .@"extern",
858 .func,
859 .int,
860 .err,
861 .error_union,
862 .enum_literal,
863 .enum_tag,
864 .float,
865 .ptr,
866 .slice,
867 .opt,
868 .aggregate,
869 .un,
870 .bitpack,
871 // memoization, not types
872 .memoized_call,
873 => unreachable,
874 };
875}
876
877/// Determines whether a function type has runtime bits, i.e. whether a
878/// function with this type can exist at runtime.
879/// Asserts that `ty` is a function type.
880pub fn fnHasRuntimeBits(fn_ty: Type, zcu: *const Zcu) bool {
881 assertHasLayout(fn_ty, zcu);
882 const fn_info = zcu.typeToFunc(fn_ty).?;
883 if (fn_info.comptime_bits != 0) return false;
884 for (fn_info.param_types.get(&zcu.intern_pool)) |param_ty| {
885 if (param_ty == .generic_poison_type) return false;
886 switch (Type.fromInterned(param_ty).classify(zcu)) {
887 .fully_comptime,
888 .partially_comptime,
889 .no_possible_value,
890 => return false,
891
892 .one_possible_value,
893 .runtime,
894 => {},
895 }
896 }
897 const ret_ty: Type = .fromInterned(fn_info.return_type);
898 if (ret_ty.toIntern() == .generic_poison_type) {
899 return false;
900 }
901 if (ret_ty.zigTypeTag(zcu) == .error_union and
902 ret_ty.errorUnionPayload(zcu).toIntern() == .generic_poison_type)
903 {
904 return false;
905 }
906 switch (ret_ty.classify(zcu)) {
907 .fully_comptime,
908 .partially_comptime,
909 => return false,
910
911 .no_possible_value,
912 .one_possible_value,
913 .runtime,
914 => {},
915 }
916 if (fn_info.cc == .@"inline") return false;
917 return true;
918}
919
920/// Like `hasRuntimeBits`, but also returns `true` for runtime functions.
921pub fn isRuntimeFnOrHasRuntimeBits(ty: Type, zcu: *const Zcu) bool {
922 switch (ty.zigTypeTag(zcu)) {
923 .@"fn" => return ty.fnHasRuntimeBits(zcu),
924 else => return ty.hasRuntimeBits(zcu),
925 }
926}
927
928/// Returns whether `ty` is NPV, meaning it is "like `noreturn`" in a sense. See doc comments on
929/// `Class` for more details.
930///
931/// Exactly equivalent to `ty.classify(zcu) == .no_possible_value`.
932pub fn isNoReturn(ty: Type, zcu: *const Zcu) bool {
933 return ty.classify(zcu) == .no_possible_value;
934}
935
936/// Never returns `none`. Asserts that all necessary type resolution is already done.
937pub fn ptrAlignment(ptr_ty: Type, zcu: *Zcu) Alignment {
938 const ip = &zcu.intern_pool;
939 const ptr_key: InternPool.Key.PtrType = switch (ip.indexToKey(ptr_ty.toIntern())) {
940 .ptr_type => |key| key,
941 .opt_type => |child| ip.indexToKey(child).ptr_type,
942 else => unreachable,
943 };
944 if (ptr_key.flags.alignment != .none) return ptr_key.flags.alignment;
945 return Type.fromInterned(ptr_key.child).abiAlignment(zcu);
946}
947
948pub fn ptrAddressSpace(ty: Type, zcu: *const Zcu) std.lang.AddressSpace {
949 return switch (zcu.intern_pool.indexToKey(ty.toIntern())) {
950 .ptr_type => |ptr_type| ptr_type.flags.address_space,
951 .opt_type => |child| zcu.intern_pool.indexToKey(child).ptr_type.flags.address_space,
952 else => unreachable,
953 };
954}
955
956/// Never returns `.none`. Asserts that the layout of `ty` is resolved.
957///
958/// Unlike ABI size, a type's ABI alignment is not affected by its `Class`. In other words, any
959/// alignment is possible regardless of the result of `ty.classify(zcu)`.
960pub fn abiAlignment(ty: Type, zcu: *const Zcu) Alignment {
961 const ip = &zcu.intern_pool;
962 const target = zcu.getTarget();
963 assertHasLayout(ty, zcu);
964 return switch (ip.indexToKey(ty.toIntern())) {
965 .int_type => |int_type| {
966 if (int_type.bits == 0) return .@"1";
967 return .fromByteUnits(std.zig.target.intAlignment(target, int_type.bits));
968 },
969 .ptr_type, .anyframe_type => ptrAbiAlignment(target),
970 .array_type => |array_type| Type.fromInterned(array_type.child).abiAlignment(zcu),
971 .vector_type => |vector_type| {
972 if (vector_type.len == 0) return .@"1";
973 switch (zcu.comp.getZigBackend()) {
974 else => {
975 const elem_ty: Type = .fromInterned(vector_type.child);
976 switch (if (elem_ty.isRuntimeFloat())
977 std.zig.target.compilerRtFloatAbi(target, elem_ty.floatBits(target))
978 else
979 .hard) {
980 .hard => {},
981 .soft => return elem_ty.abiAlignment(zcu),
982 }
983 const elem_bits: u32 = @intCast(elem_ty.bitSize(zcu));
984 if (elem_bits == 0) return .@"1";
985 const bytes = ((elem_bits * vector_type.len) + 7) / 8;
986 const arch = target.cpu.arch;
987 return .fromByteUnits(std.math.ceilPowerOfTwoAssert(
988 u32,
989 if (arch.isArm() or arch.isAARCH64() or arch == .s390x)
990 @min(bytes, target.stackAlignment())
991 else
992 bytes,
993 ));
994 },
995 .stage2_c, .stage2_wasm => return Type.fromInterned(vector_type.child).abiAlignment(zcu),
996 .stage2_x86_64 => {
997 if (vector_type.child == .bool_type) {
998 if (vector_type.len > 256 and target.cpu.has(.x86, .avx512f)) return .@"64";
999 if (vector_type.len > 128 and target.cpu.has(.x86, .avx)) return .@"32";
1000 if (vector_type.len > 64) return .@"16";
1001 const bytes = @divCeil(vector_type.len, 8);
1002 return .fromByteUnits(std.math.ceilPowerOfTwoAssert(u32, bytes));
1003 }
1004 const elem_bytes: u32 = @intCast(Type.fromInterned(vector_type.child).abiSize(zcu));
1005 if (elem_bytes == 0) return .@"1";
1006 const bytes = elem_bytes * vector_type.len;
1007 if (bytes > 32 and target.cpu.has(.x86, .avx512f)) return .@"64";
1008 if (bytes > 16 and target.cpu.has(.x86, .avx)) return .@"32";
1009 return .@"16";
1010 },
1011 }
1012 },
1013
1014 .opt_type => |child| Type.fromInterned(child).abiAlignment(zcu),
1015 .error_union_type => |eu| Alignment.maxStrict(
1016 Type.fromInterned(eu.payload_type).abiAlignment(zcu),
1017 errorAbiAlignment(zcu),
1018 ),
1019
1020 .error_set_type, .inferred_error_set_type => errorAbiAlignment(zcu),
1021
1022 .func_type => target_util.minFunctionAlignment(target),
1023
1024 .simple_type => |t| switch (t) {
1025 .bool,
1026 .void,
1027 .noreturn,
1028 .anyopaque,
1029 .type,
1030 .comptime_int,
1031 .comptime_float,
1032 .null,
1033 .undefined,
1034 .enum_literal,
1035 => .@"1",
1036
1037 .anyerror, .adhoc_inferred_error_set => errorAbiAlignment(zcu),
1038 .usize, .isize => .fromByteUnits(std.zig.target.intAlignment(target, target.ptrBitWidth())),
1039
1040 .c_char => cTypeAlign(target, .char),
1041 .c_short => cTypeAlign(target, .short),
1042 .c_ushort => cTypeAlign(target, .ushort),
1043 .c_int => cTypeAlign(target, .int),
1044 .c_uint => cTypeAlign(target, .uint),
1045 .c_long => cTypeAlign(target, .long),
1046 .c_ulong => cTypeAlign(target, .ulong),
1047 .c_longlong => cTypeAlign(target, .longlong),
1048 .c_ulonglong => cTypeAlign(target, .ulonglong),
1049 .c_longdouble => cTypeAlign(target, .longdouble),
1050
1051 .f16 => .fromByteUnits(std.zig.target.intAlignment(target, 16)), // repr: u16
1052 .f32 => if (target.cTypeBitSize(.float) == 32)
1053 cTypeAlign(target, .float) // abi: c_float,
1054 else
1055 .fromByteUnits(std.zig.target.intAlignment(target, 32)), // repr: u32,
1056 .f64 => if (target.cTypeBitSize(.double) == 64)
1057 cTypeAlign(target, .double) // abi: c_double,
1058 else
1059 .fromByteUnits(std.zig.target.intAlignment(target, 64)), // repr: u64,
1060 .f80 => if (target.cTypeBitSize(.longdouble) == 80)
1061 cTypeAlign(target, .longdouble) // abi: c_longdouble,
1062 else
1063 .fromByteUnits(switch (std.zig.target.compilerRtFloatAbi(target, 80)) {
1064 .hard => std.zig.target.intAlignment(target, 80), // repr: u80,
1065 .soft => @max(
1066 std.zig.target.intAlignment(target, 64), // mantissa: u64,
1067 std.zig.target.intAlignment(target, 16), // exponent: u16,
1068 ),
1069 }),
1070 .f128 => if (target.cTypeBitSize(.longdouble) == 128)
1071 cTypeAlign(target, .longdouble) // abi: c_longdouble,
1072 else switch (std.zig.target.compilerRtFloatAbi(target, 128)) {
1073 .hard => if (target.cpu.arch.isX86())
1074 .@"16" // abi: c___float128,
1075 else
1076 .fromByteUnits(std.zig.target.intAlignment(target, 128)), // repr: u128,
1077 .soft => .fromByteUnits(std.zig.target.intAlignment(target, 64)), // lo: u64, hi: u64,
1078 },
1079
1080 .generic_poison => unreachable,
1081 },
1082 .tuple_type => |tuple| {
1083 var big_align: Alignment = .@"1";
1084 for (tuple.types.get(ip), tuple.values.get(ip)) |field_ty, val| {
1085 if (val != .none) continue; // comptime field
1086 const field_align = Type.fromInterned(field_ty).abiAlignment(zcu);
1087 big_align = big_align.maxStrict(field_align);
1088 }
1089 return big_align;
1090 },
1091 .struct_type => {
1092 const struct_obj = ip.loadStructType(ty.toIntern());
1093 switch (struct_obj.layout) {
1094 .@"packed" => return Type.fromInterned(struct_obj.packed_backing_int_type).abiAlignment(zcu),
1095 .auto, .@"extern" => {
1096 assert(struct_obj.alignment != .none);
1097 return struct_obj.alignment;
1098 },
1099 }
1100 },
1101 .union_type => {
1102 const union_obj = ip.loadUnionType(ty.toIntern());
1103 switch (union_obj.layout) {
1104 .@"packed" => return Type.fromInterned(union_obj.packed_backing_int_type).abiAlignment(zcu),
1105 .auto, .@"extern" => {
1106 assert(union_obj.alignment != .none);
1107 return union_obj.alignment;
1108 },
1109 }
1110 },
1111 .enum_type => Type.fromInterned(ip.loadEnumType(ty.toIntern()).int_tag_type).abiAlignment(zcu),
1112 .spirv_type => if (ty.isSpirvRuntimeArray(zcu)) ty.childType(zcu).abiAlignment(zcu) else .@"1",
1113 .opaque_type => .@"1",
1114
1115 // values, not types
1116 .undef,
1117 .simple_value,
1118 .@"extern",
1119 .func,
1120 .int,
1121 .err,
1122 .error_union,
1123 .enum_literal,
1124 .enum_tag,
1125 .float,
1126 .ptr,
1127 .slice,
1128 .opt,
1129 .aggregate,
1130 .un,
1131 .bitpack,
1132 // memoization, not types
1133 .memoized_call,
1134 => unreachable,
1135 };
1136}
1137
1138/// Asserts that `ty` is not an opaque type, and that the layout of `ty` is resolved.
1139///
1140/// If the type is NPV, OPV, or fully-comptime (see `Class`), the return value of this function is
1141/// guaranteed to be zero. Otherwise (if the type is runtime or partially-comptime) the return value
1142/// is guaranteed to be non-zero.
1143pub fn abiSize(ty: Type, zcu: *const Zcu) u64 {
1144 const ip = &zcu.intern_pool;
1145 const target = zcu.getTarget();
1146 assertHasLayout(ty, zcu);
1147 return switch (ip.indexToKey(ty.toIntern())) {
1148 .int_type => |int_type| std.zig.target.intByteSize(target, int_type.bits),
1149 .ptr_type => |ptr_type| switch (ptr_type.flags.size) {
1150 .slice => ptrAbiSize(target) * 2,
1151 .one, .many, .c => ptrAbiSize(target),
1152 },
1153 .anyframe_type => ptrAbiSize(target),
1154 .array_type => |arr| arr.lenIncludingSentinel() * Type.fromInterned(arr.child).abiSize(zcu),
1155 .vector_type => |vec| {
1156 const elem_ty: Type = .fromInterned(vec.child);
1157 const bytes = switch (zcu.comp.getZigBackend()) {
1158 else => switch (if (elem_ty.isRuntimeFloat())
1159 std.zig.target.compilerRtFloatAbi(target, elem_ty.floatBits(target))
1160 else
1161 .hard) {
1162 .hard => @divCeil(vec.len * elem_ty.bitSize(zcu), 8),
1163 .soft => vec.len * elem_ty.abiSize(zcu),
1164 },
1165 .stage2_c, .stage2_wasm => vec.len * elem_ty.abiSize(zcu),
1166 .stage2_x86_64 => switch (elem_ty.toIntern()) {
1167 .bool_type => @divCeil(vec.len, 8),
1168 else => vec.len * elem_ty.abiSize(zcu),
1169 },
1170 };
1171 return ty.abiAlignment(zcu).forward(bytes);
1172 },
1173 .opt_type => |child_ty_ip| {
1174 const child_ty: Type = .fromInterned(child_ty_ip);
1175 switch (child_ty.classify(zcu)) {
1176 .no_possible_value => return 0, // we are OPV
1177 .fully_comptime => return 0, // we are also fully_comptime (same justification as error unions, see below)
1178 .one_possible_value, .partially_comptime, .runtime => {
1179 if (ty.optionalReprIsPayload(zcu)) return child_ty.abiSize(zcu);
1180 // Optional types are represented as a struct with the child type as the first
1181 // field and a boolean as the second. Since the child type's abi alignment is
1182 // guaranteed to be >= that of bool's (1 byte) the added size is exactly equal
1183 // to the child type's ABI alignment.
1184 return child_ty.abiSize(zcu) + child_ty.abiAlignment(zcu).toByteUnits().?;
1185 },
1186 }
1187 },
1188 .error_set_type, .inferred_error_set_type => errorAbiSize(zcu),
1189 .error_union_type => |error_union| {
1190 const payload_ty: Type = .fromInterned(error_union.payload_type);
1191 switch (payload_ty.classify(zcu)) {
1192 // Zig has no way to take the address of the error set "in" an error union (giving
1193 // implementations more freedom in terms of data layout), so if the payload type is
1194 // fully comptime, we don't need to dedicate runtime bits to the error set.
1195 .fully_comptime => return 0,
1196 else => {},
1197 }
1198 // The layout will either be (code, payload, padding) or (payload, code, padding)
1199 // depending on which has larger alignment. So the overall size is just the code
1200 // and payload sizes added and padded to the larger alignment.
1201 const big_align: Alignment = .maxStrict(errorAbiAlignment(zcu), payload_ty.abiAlignment(zcu));
1202 return big_align.forward(errorAbiSize(zcu) + payload_ty.abiSize(zcu));
1203 },
1204 .func_type => 0,
1205 .simple_type => |t| switch (t) {
1206 .void,
1207 .noreturn,
1208 .type,
1209 .comptime_int,
1210 .comptime_float,
1211 .null,
1212 .undefined,
1213 .enum_literal,
1214 => 0,
1215
1216 .bool => 1,
1217 .anyerror, .adhoc_inferred_error_set => errorAbiSize(zcu),
1218 .usize, .isize => ptrAbiSize(target),
1219
1220 .c_char => target.cTypeByteSize(.char).?,
1221 .c_short => target.cTypeByteSize(.short).?,
1222 .c_ushort => target.cTypeByteSize(.ushort).?,
1223 .c_int => target.cTypeByteSize(.int).?,
1224 .c_uint => target.cTypeByteSize(.uint).?,
1225 .c_long => target.cTypeByteSize(.long).?,
1226 .c_ulong => target.cTypeByteSize(.ulong).?,
1227 .c_longlong => target.cTypeByteSize(.longlong).?,
1228 .c_ulonglong => target.cTypeByteSize(.ulonglong).?,
1229 .c_longdouble => target.cTypeByteSize(.longdouble).?,
1230
1231 .f16 => std.zig.target.intByteSize(target, 16), // repr: u16
1232 .f32 => if (target.cTypeBitSize(.float) == 32)
1233 target.cTypeByteSize(.float).? // abi: c_float,
1234 else
1235 std.zig.target.intByteSize(target, 32), // repr: u32,
1236 .f64 => if (target.cTypeBitSize(.double) == 64)
1237 target.cTypeByteSize(.double).? // abi: c_double,
1238 else
1239 std.zig.target.intByteSize(target, 64), // repr: u64,
1240 .f80 => if (target.cTypeBitSize(.longdouble) == 80)
1241 target.cTypeByteSize(.longdouble).? // abi: c_longdouble,
1242 else switch (std.zig.target.compilerRtFloatAbi(target, 80)) {
1243 .hard => std.zig.target.intByteSize(target, 80), // repr: u80,
1244 .soft => ty.abiAlignment(zcu).forward(
1245 std.zig.target.intByteSize(target, 64) + // mantissa: u64,
1246 std.zig.target.intByteSize(target, 16), // exponent: u16
1247 ),
1248 },
1249 .f128 => if (target.cTypeBitSize(.longdouble) == 128)
1250 target.cTypeByteSize(.longdouble).? // abi: c_longdouble,
1251 else switch (std.zig.target.compilerRtFloatAbi(target, 128)) {
1252 .hard => if (target.cpu.arch.isX86())
1253 16 // abi: c___float128,
1254 else
1255 std.zig.target.intByteSize(target, 128), // repr: u128,
1256 .soft => std.zig.target.intByteSize(target, 64) * 2, // lo: u64, hi: u64,
1257 },
1258
1259 .anyopaque => unreachable,
1260 .generic_poison => unreachable,
1261 },
1262 .tuple_type => |tuple| switch (ty.classify(zcu)) {
1263 // `structFieldOffset` is bogus on NPV tuples, because there may be some fields with
1264 // non-zero size.
1265 .no_possible_value => 0,
1266 else => ty.structFieldOffset(tuple.types.len, zcu),
1267 },
1268 .struct_type => {
1269 const struct_obj = ip.loadStructType(ty.toIntern());
1270 switch (struct_obj.layout) {
1271 .@"packed" => return Type.fromInterned(struct_obj.packed_backing_int_type).abiSize(zcu),
1272 .auto, .@"extern" => return struct_obj.size,
1273 }
1274 },
1275 .union_type => {
1276 const union_obj = ip.loadUnionType(ty.toIntern());
1277 switch (union_obj.layout) {
1278 .@"packed" => return Type.fromInterned(union_obj.packed_backing_int_type).abiSize(zcu),
1279 .auto, .@"extern" => return union_obj.size,
1280 }
1281 },
1282 .enum_type => Type.fromInterned(ip.loadEnumType(ty.toIntern()).int_tag_type).abiSize(zcu),
1283 .spirv_type => unreachable,
1284 .opaque_type => unreachable,
1285
1286 // values, not types
1287 .undef,
1288 .simple_value,
1289 .@"extern",
1290 .func,
1291 .int,
1292 .err,
1293 .error_union,
1294 .enum_literal,
1295 .enum_tag,
1296 .float,
1297 .ptr,
1298 .slice,
1299 .opt,
1300 .aggregate,
1301 .un,
1302 .bitpack,
1303 // memoization, not types
1304 .memoized_call,
1305 => unreachable,
1306 };
1307}
1308
1309pub fn ptrAbiAlignment(target: *const Target) Alignment {
1310 // The eZ80 has 24-bit pointers, which aren't exact powers of two, tripping
1311 // the assert. The alignment of eZ80 pointers is 1, so we bypass the check.
1312 if (target.cpu.arch == .ez80) return .@"1";
1313 return .fromNonzeroByteUnits(@divExact(target.ptrBitWidth(), 8));
1314}
1315pub fn ptrAbiSize(target: *const Target) u64 {
1316 return @divExact(target.ptrBitWidth(), 8);
1317}
1318pub fn errorAbiAlignment(zcu: *const Zcu) Alignment {
1319 return .fromNonzeroByteUnits(std.zig.target.intAlignment(zcu.getTarget(), zcu.errorSetBits()));
1320}
1321pub fn errorAbiSize(zcu: *const Zcu) u64 {
1322 return std.zig.target.intByteSize(zcu.getTarget(), zcu.errorSetBits());
1323}
1324
1325/// Asserts that `ty` is not an opaque or comptime-only type.
1326pub fn bitSize(ty: Type, zcu: *const Zcu) u64 {
1327 return switch (ty.zigTypeTag(zcu)) {
1328 .void => 0,
1329 .bool => 1,
1330 .float => ty.floatBits(zcu.getTarget()),
1331 .pointer, .optional => {
1332 assert(ty.isPtrAtRuntime(zcu));
1333 return zcu.getTarget().ptrBitWidth();
1334 },
1335 .array, .vector => ty.arrayLenIncludingSentinel(zcu) * ty.childType(zcu).bitSize(zcu),
1336 else => ty.intInfo(zcu).bits,
1337 };
1338}
1339
1340pub fn isSinglePointer(ty: Type, zcu: *const Zcu) bool {
1341 return switch (zcu.intern_pool.indexToKey(ty.toIntern())) {
1342 .ptr_type => |ptr_info| ptr_info.flags.size == .one,
1343 else => false,
1344 };
1345}
1346
1347/// Asserts `ty` is a pointer.
1348pub fn ptrSize(ty: Type, zcu: *const Zcu) std.lang.Type.Pointer.Size {
1349 return ty.ptrSizeOrNull(zcu).?;
1350}
1351
1352/// Returns `null` if `ty` is not a pointer.
1353pub fn ptrSizeOrNull(ty: Type, zcu: *const Zcu) ?std.lang.Type.Pointer.Size {
1354 return switch (zcu.intern_pool.indexToKey(ty.toIntern())) {
1355 .ptr_type => |ptr_info| ptr_info.flags.size,
1356 else => null,
1357 };
1358}
1359
1360pub fn isSlice(ty: Type, zcu: *const Zcu) bool {
1361 return switch (zcu.intern_pool.indexToKey(ty.toIntern())) {
1362 .ptr_type => |ptr_type| ptr_type.flags.size == .slice,
1363 else => false,
1364 };
1365}
1366
1367pub fn isSliceAtRuntime(ty: Type, zcu: *const Zcu) bool {
1368 return switch (zcu.intern_pool.indexToKey(ty.toIntern())) {
1369 .ptr_type => |ptr_type| ptr_type.flags.size == .slice,
1370 .opt_type => |child| switch (zcu.intern_pool.indexToKey(child)) {
1371 .ptr_type => |ptr_type| !ptr_type.flags.is_allowzero and ptr_type.flags.size == .slice,
1372 else => false,
1373 },
1374 else => false,
1375 };
1376}
1377
1378pub fn slicePtrFieldType(ty: Type, zcu: *const Zcu) Type {
1379 return .fromInterned(zcu.intern_pool.slicePtrType(ty.toIntern()));
1380}
1381
1382pub fn isConstPtr(ty: Type, zcu: *const Zcu) bool {
1383 return switch (zcu.intern_pool.indexToKey(ty.toIntern())) {
1384 .ptr_type => |ptr_type| ptr_type.flags.is_const,
1385 else => false,
1386 };
1387}
1388
1389pub fn isVolatilePtr(ty: Type, zcu: *const Zcu) bool {
1390 return isVolatilePtrIp(ty, &zcu.intern_pool);
1391}
1392
1393pub fn isVolatilePtrIp(ty: Type, ip: *const InternPool) bool {
1394 return switch (ip.indexToKey(ty.toIntern())) {
1395 .ptr_type => |ptr_type| ptr_type.flags.is_volatile,
1396 else => false,
1397 };
1398}
1399
1400pub fn isAllowzeroPtr(ty: Type, zcu: *const Zcu) bool {
1401 return switch (zcu.intern_pool.indexToKey(ty.toIntern())) {
1402 .ptr_type => |ptr_type| ptr_type.flags.is_allowzero,
1403 .opt_type => true,
1404 else => false,
1405 };
1406}
1407
1408pub fn isCPtr(ty: Type, zcu: *const Zcu) bool {
1409 return switch (zcu.intern_pool.indexToKey(ty.toIntern())) {
1410 .ptr_type => |ptr_type| ptr_type.flags.size == .c,
1411 else => false,
1412 };
1413}
1414
1415pub fn isPtrAtRuntime(ty: Type, zcu: *const Zcu) bool {
1416 return switch (zcu.intern_pool.indexToKey(ty.toIntern())) {
1417 .ptr_type => |ptr_type| switch (ptr_type.flags.size) {
1418 .slice => false,
1419 .one, .many, .c => true,
1420 },
1421 .opt_type => |child| switch (zcu.intern_pool.indexToKey(child)) {
1422 .ptr_type => |p| switch (p.flags.size) {
1423 .slice, .c => false,
1424 .many, .one => !p.flags.is_allowzero,
1425 },
1426 else => false,
1427 },
1428 else => false,
1429 };
1430}
1431
1432/// For pointer-like optionals, returns true, otherwise returns the allowzero property
1433/// of pointers.
1434pub fn ptrAllowsZero(ty: Type, zcu: *const Zcu) bool {
1435 return ty.isPtrLikeOptional(zcu) or ty.ptrInfo(zcu).flags.is_allowzero;
1436}
1437
1438/// See also `isPtrLikeOptional`.
1439pub fn optionalReprIsPayload(ty: Type, zcu: *const Zcu) bool {
1440 return switch (zcu.intern_pool.indexToKey(ty.toIntern())) {
1441 .opt_type => |child_type| child_type == .anyerror_type or switch (zcu.intern_pool.indexToKey(child_type)) {
1442 .ptr_type => |ptr_type| ptr_type.flags.size != .c and !ptr_type.flags.is_allowzero,
1443 .error_set_type, .inferred_error_set_type => true,
1444 else => false,
1445 },
1446 .ptr_type => |ptr_type| ptr_type.flags.size == .c,
1447 else => false,
1448 };
1449}
1450
1451/// Returns true if the type is optional and would be lowered to a single pointer
1452/// address value, using 0 for null. Note that this returns true for C pointers.
1453pub fn isPtrLikeOptional(ty: Type, zcu: *const Zcu) bool {
1454 return switch (zcu.intern_pool.indexToKey(ty.toIntern())) {
1455 .ptr_type => |ptr_type| ptr_type.flags.size == .c,
1456 .opt_type => |child| switch (zcu.intern_pool.indexToKey(child)) {
1457 .ptr_type => |ptr_type| switch (ptr_type.flags.size) {
1458 .slice, .c => false,
1459 .many, .one => !ptr_type.flags.is_allowzero,
1460 },
1461 else => false,
1462 },
1463 else => false,
1464 };
1465}
1466
1467/// For `*[N]T`, returns `[N]T`.
1468/// For `*T`, returns `T`.
1469/// For `[*]T`, returns `T`.
1470/// For `@Vector(N, T)`, returns `T`.
1471/// For `[N]T`, returns `T`.
1472/// For `?T`, returns `T`.
1473pub fn childType(ty: Type, zcu: *const Zcu) Type {
1474 return childTypeIp(ty, &zcu.intern_pool);
1475}
1476
1477pub fn childTypeIp(ty: Type, ip: *const InternPool) Type {
1478 return Type.fromInterned(ip.childType(ty.toIntern()));
1479}
1480
1481/// Similar to `childType`, but for pointer-like (or slice-like) optionals, gets the child type
1482/// of the *pointer* type. Asserts that `ty` is either a pointer or a pointer-like optional.
1483///
1484/// Essentially, unwraps any one of the following into `T`:
1485/// ```
1486/// *T ?*T *allowzero T
1487/// [*]T ?[*]T [*]allowzero T
1488/// []T ?[]T []allowzero T
1489/// [*c]T
1490/// ```
1491/// This is primarily useful in Sema to implement operations which can act on optional pointers.
1492pub fn nullablePtrElem(ty: Type, zcu: *const Zcu) Type {
1493 switch (ty.zigTypeTag(zcu)) {
1494 .pointer => return ty.childType(zcu),
1495 .optional => {
1496 const ptr_ty = ty.childType(zcu);
1497 const ptr_info = zcu.intern_pool.indexToKey(ptr_ty.toIntern()).ptr_type;
1498 assert(ptr_info.flags.size != .c);
1499 assert(!ptr_info.flags.is_allowzero);
1500 return .fromInterned(ptr_info.child);
1501 },
1502 else => unreachable,
1503 }
1504}
1505
1506/// Asserts that `ty` is an indexable type, and returns its element type. Tuples (and pointers to
1507/// tuples) are not supported because they do not have a single element type.
1508///
1509/// Returns `T` for each of the following types:
1510/// * `[n]T`
1511/// * `@Vector(n, T)`
1512/// * `*[n]T`
1513/// * `*@Vector(n, T)`
1514/// * `[]T`
1515/// * `[*]T`
1516/// * `[*c]T`
1517/// * `@SpirvType(.{ .runtime_array = T })`
1518/// * `*@SpirvType(.{ .runtime_array = T })`
1519pub fn indexableElem(ty: Type, zcu: *const Zcu) Type {
1520 const ip = &zcu.intern_pool;
1521 return switch (ip.indexToKey(ty.toIntern())) {
1522 inline .array_type, .vector_type => |arr| .fromInterned(arr.child),
1523 .spirv_type => ty.childType(zcu),
1524 .ptr_type => |ptr_type| switch (ptr_type.flags.size) {
1525 .many, .slice, .c => .fromInterned(ptr_type.child),
1526 .one => switch (ip.indexToKey(ptr_type.child)) {
1527 inline .array_type, .vector_type => |arr| .fromInterned(arr.child),
1528 .spirv_type => Type.fromInterned(ptr_type.child).childType(zcu),
1529 else => unreachable,
1530 },
1531 },
1532 else => unreachable,
1533 };
1534}
1535
1536/// For vectors, returns the element type. Otherwise returns self.
1537pub fn scalarType(ty: Type, zcu: *const Zcu) Type {
1538 return switch (ty.zigTypeTag(zcu)) {
1539 .vector => ty.childType(zcu),
1540 else => ty,
1541 };
1542}
1543
1544/// Asserts that the type is an optional, or a C pointer.
1545/// For C pointers this returns the type unmodified.
1546pub fn optionalChild(ty: Type, zcu: *const Zcu) Type {
1547 switch (zcu.intern_pool.indexToKey(ty.toIntern())) {
1548 .opt_type => |child| return .fromInterned(child),
1549 .ptr_type => |ptr_type| {
1550 assert(ptr_type.flags.size == .c);
1551 return ty;
1552 },
1553 else => unreachable,
1554 }
1555}
1556
1557/// If `ty` is a tagged union, returns its tag type. Otherwise, returns `null`.
1558pub fn unionTagType(ty: Type, zcu: *const Zcu) ?Type {
1559 assertHasLayout(ty, zcu);
1560 const ip = &zcu.intern_pool;
1561 switch (ip.indexToKey(ty.toIntern())) {
1562 .union_type => {},
1563 else => return null,
1564 }
1565 const union_obj = ip.loadUnionType(ty.toIntern());
1566 return switch (union_obj.tag_usage) {
1567 .tagged => .fromInterned(union_obj.enum_tag_type),
1568 .none, .safety => null,
1569 };
1570}
1571
1572/// If the given union type contains a tag (including a safety tag) in its runtime layout, returns
1573/// its enum tag type. Otherwise, returns null. Asserts that `ty` is a union type.
1574///
1575/// In general, codegen logic should call this function instead of `unionTagType`.
1576pub fn unionTagTypeRuntime(ty: Type, zcu: *const Zcu) ?Type {
1577 assertHasLayout(ty, zcu);
1578 const union_type = zcu.intern_pool.loadUnionType(ty.toIntern());
1579 if (!union_type.has_runtime_tag) return null;
1580 return .fromInterned(union_type.enum_tag_type);
1581}
1582
1583/// Asserts that `ty` is a union type, and returns its tag type, even if the tag will not be stored at runtime.
1584pub fn unionTagTypeHypothetical(ty: Type, zcu: *const Zcu) Type {
1585 assertHasLayout(ty, zcu);
1586 const union_obj = zcu.intern_pool.loadUnionType(ty.toIntern());
1587 return .fromInterned(union_obj.enum_tag_type);
1588}
1589
1590pub fn unionFieldType(ty: Type, enum_tag: Value, zcu: *const Zcu) ?Type {
1591 assertHasLayout(ty, zcu);
1592 const ip = &zcu.intern_pool;
1593 const union_obj = zcu.typeToUnion(ty).?;
1594 const union_fields = union_obj.field_types.get(ip);
1595 const index = zcu.unionTagFieldIndex(union_obj, enum_tag) orelse return null;
1596 return Type.fromInterned(union_fields[index]);
1597}
1598
1599pub fn unionFieldTypeByIndex(ty: Type, index: usize, zcu: *const Zcu) Type {
1600 assertHasLayout(ty, zcu);
1601 const ip = &zcu.intern_pool;
1602 const union_obj = zcu.typeToUnion(ty).?;
1603 return Type.fromInterned(union_obj.field_types.get(ip)[index]);
1604}
1605
1606pub fn unionTagFieldIndex(ty: Type, enum_tag: Value, zcu: *const Zcu) ?u32 {
1607 assertHasLayout(ty, zcu);
1608 const union_obj = zcu.typeToUnion(ty).?;
1609 return zcu.unionTagFieldIndex(union_obj, enum_tag);
1610}
1611
1612pub fn unionHasAllZeroBitFieldTypes(ty: Type, zcu: *const Zcu) bool {
1613 assertHasLayout(ty, zcu);
1614 const ip = &zcu.intern_pool;
1615 const union_obj = zcu.typeToUnion(ty).?;
1616 for (union_obj.field_types.get(ip)) |field_ty| {
1617 if (Type.fromInterned(field_ty).hasRuntimeBits(zcu)) return false;
1618 }
1619 return true;
1620}
1621
1622/// Returns the type used for backing storage of this union during comptime operations.
1623/// Asserts the type is an extern union.
1624pub fn externUnionBackingType(ty: Type, pt: Zcu.PerThread) !Type {
1625 const zcu = pt.zcu;
1626 assertHasLayout(ty, zcu);
1627 const loaded_union = zcu.intern_pool.loadUnionType(ty.toIntern());
1628 switch (loaded_union.layout) {
1629 .@"extern" => return pt.arrayType(.{ .len = ty.abiSize(zcu), .child = .u8_type }),
1630 .@"packed" => unreachable,
1631 .auto => unreachable,
1632 }
1633}
1634
1635/// Asserts that `ty` is a non-packed union type.
1636pub fn unionGetLayout(ty: Type, zcu: *const Zcu) Zcu.UnionLayout {
1637 assertHasLayout(ty, zcu);
1638 const union_obj = zcu.intern_pool.loadUnionType(ty.toIntern());
1639 return Type.getUnionLayout(union_obj, zcu);
1640}
1641
1642pub fn containerLayout(ty: Type, zcu: *const Zcu) std.lang.Type.ContainerLayout {
1643 const ip = &zcu.intern_pool;
1644 return switch (ip.indexToKey(ty.toIntern())) {
1645 .tuple_type => .auto,
1646 .struct_type => ip.loadStructType(ty.toIntern()).layout,
1647 .union_type => ip.loadUnionType(ty.toIntern()).layout,
1648 else => unreachable,
1649 };
1650}
1651
1652/// Asserts that the type is either an enum or a bitpack.
1653pub fn backingIntType(ty: Type, zcu: *const Zcu) Type {
1654 const ip = &zcu.intern_pool;
1655 return switch (ip.indexToKey(ty.toIntern())) {
1656 .enum_type => .fromInterned(ip.loadEnumType(ty.toIntern()).int_tag_type),
1657 .struct_type => .fromInterned(ip.loadStructType(ty.toIntern()).packed_backing_int_type),
1658 .union_type => .fromInterned(ip.loadUnionType(ty.toIntern()).packed_backing_int_type),
1659 else => unreachable,
1660 };
1661}
1662
1663/// For unions, returns the *backing int* mode, not the *enum tag* mode.
1664pub fn backingIntMode(ty: Type, zcu: *const Zcu) InternPool.BackingTypeMode {
1665 const ip = &zcu.intern_pool;
1666 return switch (ip.indexToKey(ty.toIntern())) {
1667 .enum_type => ip.loadEnumType(ty.toIntern()).int_tag_mode,
1668 .struct_type => ip.loadStructType(ty.toIntern()).packed_backing_mode,
1669 .union_type => ip.loadUnionType(ty.toIntern()).packed_backing_mode,
1670 else => unreachable,
1671 };
1672}
1673
1674/// Asserts that the type is an error union.
1675pub fn errorUnionPayload(ty: Type, zcu: *const Zcu) Type {
1676 return Type.fromInterned(zcu.intern_pool.indexToKey(ty.toIntern()).error_union_type.payload_type);
1677}
1678
1679/// Asserts that the type is an error union.
1680pub fn errorUnionSet(ty: Type, zcu: *const Zcu) Type {
1681 return Type.fromInterned(zcu.intern_pool.errorUnionSet(ty.toIntern()));
1682}
1683
1684/// Returns false for unresolved inferred error sets.
1685///
1686/// TODO: this function will behave incorrectly under incremental compilation, because in that case
1687/// it may see an outdated resolved error set. This function must be either deleted, or its contract
1688/// changed to require the caller to resolve the error set beforehand. If you must introduce new
1689/// call sites, please make sure the error set in question is definitely resolved first!
1690pub fn errorSetIsEmpty(ty: Type, zcu: *const Zcu) bool {
1691 const ip = &zcu.intern_pool;
1692 return switch (ty.toIntern()) {
1693 .anyerror_type, .adhoc_inferred_error_set_type => false,
1694 else => switch (ip.indexToKey(ty.toIntern())) {
1695 .error_set_type => |error_set_type| error_set_type.names.len == 0,
1696 .inferred_error_set_type => |i| switch (ip.funcIesResolvedUnordered(i)) {
1697 .none, .anyerror_type => false,
1698 else => |t| ip.indexToKey(t).error_set_type.names.len == 0,
1699 },
1700 else => unreachable,
1701 },
1702 };
1703}
1704
1705/// Returns true if it is an error set that includes anyerror, false otherwise.
1706/// Note that the result may be a false negative if the type did not get error set
1707/// resolution prior to this call.
1708///
1709/// TODO: this function will behave incorrectly under incremental compilation, because in that case
1710/// it may see an outdated resolved error set. This function must be either deleted, or its contract
1711/// changed to require the caller to resolve the error set beforehand. If you must introduce new
1712/// call sites, please make sure the error set in question is definitely resolved first!
1713pub fn isAnyError(ty: Type, zcu: *const Zcu) bool {
1714 const ip = &zcu.intern_pool;
1715 return switch (ty.toIntern()) {
1716 .anyerror_type => true,
1717 .adhoc_inferred_error_set_type => false,
1718 else => switch (zcu.intern_pool.indexToKey(ty.toIntern())) {
1719 .inferred_error_set_type => |i| ip.funcIesResolvedUnordered(i) == .anyerror_type,
1720 else => false,
1721 },
1722 };
1723}
1724
1725pub fn isError(ty: Type, zcu: *const Zcu) bool {
1726 return switch (ty.zigTypeTag(zcu)) {
1727 .error_union, .error_set => true,
1728 else => false,
1729 };
1730}
1731
1732/// Returns whether ty, which must be an error set, includes an error `name`.
1733/// Might return a false negative if `ty` is an inferred error set and not fully
1734/// resolved yet.
1735///
1736/// TODO: this function will behave incorrectly under incremental compilation, because in that case
1737/// it may see an outdated resolved error set. This function must be either deleted, or its contract
1738/// changed to require the caller to resolve the error set beforehand. If you must introduce new
1739/// call sites, please make sure the error set in question is definitely resolved first!
1740pub fn errorSetHasField(
1741 ty: Type,
1742 name: InternPool.NullTerminatedString,
1743 zcu: *const Zcu,
1744) bool {
1745 const ip = &zcu.intern_pool;
1746 return switch (ty.toIntern()) {
1747 .anyerror_type => true,
1748 else => switch (ip.indexToKey(ty.toIntern())) {
1749 .error_set_type => |error_set_type| error_set_type.nameIndex(ip, name) != null,
1750 .inferred_error_set_type => |i| switch (ip.funcIesResolvedUnordered(i)) {
1751 .anyerror_type => true,
1752 .none => false,
1753 else => |t| ip.indexToKey(t).error_set_type.nameIndex(ip, name) != null,
1754 },
1755 else => unreachable,
1756 },
1757 };
1758}
1759
1760/// Asserts the type is an array or vector or struct.
1761pub fn arrayLen(ty: Type, zcu: *const Zcu) u64 {
1762 return ty.arrayLenIp(&zcu.intern_pool);
1763}
1764
1765pub fn arrayLenIp(ty: Type, ip: *const InternPool) u64 {
1766 return ip.aggregateTypeLen(ty.toIntern());
1767}
1768
1769pub fn arrayLenIncludingSentinel(ty: Type, zcu: *const Zcu) u64 {
1770 return zcu.intern_pool.aggregateTypeLenIncludingSentinel(ty.toIntern());
1771}
1772
1773pub fn vectorLen(ty: Type, zcu: *const Zcu) u32 {
1774 return switch (zcu.intern_pool.indexToKey(ty.toIntern())) {
1775 .vector_type => |vector_type| vector_type.len,
1776 .tuple_type => |tuple| @intCast(tuple.types.len),
1777 else => unreachable,
1778 };
1779}
1780
1781/// Asserts the type is an array, pointer or vector.
1782pub fn sentinel(ty: Type, zcu: *const Zcu) ?Value {
1783 return switch (zcu.intern_pool.indexToKey(ty.toIntern())) {
1784 .vector_type,
1785 .struct_type,
1786 .tuple_type,
1787 => null,
1788
1789 .array_type => |t| if (t.sentinel != .none) Value.fromInterned(t.sentinel) else null,
1790 .ptr_type => |t| if (t.sentinel != .none) Value.fromInterned(t.sentinel) else null,
1791
1792 else => unreachable,
1793 };
1794}
1795
1796/// Returns true if and only if the type is a fixed-width integer.
1797pub fn isInt(self: Type, zcu: *const Zcu) bool {
1798 return self.toIntern() != .comptime_int_type and
1799 zcu.intern_pool.isIntegerType(self.toIntern());
1800}
1801
1802/// Returns true if and only if the type is a fixed-width, signed integer.
1803pub fn isSignedInt(ty: Type, zcu: *const Zcu) bool {
1804 return switch (ty.toIntern()) {
1805 .c_char_type => zcu.getTarget().cCharSignedness().? == .signed,
1806 .isize_type, .c_short_type, .c_int_type, .c_long_type, .c_longlong_type => true,
1807 else => switch (zcu.intern_pool.indexToKey(ty.toIntern())) {
1808 .int_type => |int_type| int_type.signedness == .signed,
1809 else => false,
1810 },
1811 };
1812}
1813
1814/// Returns true if and only if the type is a fixed-width, unsigned integer.
1815pub fn isUnsignedInt(ty: Type, zcu: *const Zcu) bool {
1816 return switch (ty.toIntern()) {
1817 .c_char_type => zcu.getTarget().cCharSignedness().? == .unsigned,
1818 .usize_type, .c_ushort_type, .c_uint_type, .c_ulong_type, .c_ulonglong_type => true,
1819 else => switch (zcu.intern_pool.indexToKey(ty.toIntern())) {
1820 .int_type => |int_type| int_type.signedness == .unsigned,
1821 else => false,
1822 },
1823 };
1824}
1825
1826/// Returns true for integers, enums, error sets, and packed structs/unions.
1827/// If this function returns true, then intInfo() can be called on the type.
1828pub fn isAbiInt(ty: Type, zcu: *const Zcu) bool {
1829 return switch (ty.zigTypeTag(zcu)) {
1830 .int, .@"enum", .error_set => true,
1831 .@"struct", .@"union" => ty.containerLayout(zcu) == .@"packed",
1832 else => false,
1833 };
1834}
1835
1836/// Asserts the type is an integer, enum, error set, or vector of one of them.
1837pub fn intInfo(starting_ty: Type, zcu: *const Zcu) InternPool.Key.IntType {
1838 const ip = &zcu.intern_pool;
1839 const target = zcu.getTarget();
1840 var ty = starting_ty;
1841
1842 while (true) switch (ty.toIntern()) {
1843 .anyerror_type, .adhoc_inferred_error_set_type => {
1844 return .{ .signedness = .unsigned, .bits = zcu.errorSetBits() };
1845 },
1846 .usize_type => return .{ .signedness = .unsigned, .bits = target.ptrBitWidth() },
1847 .isize_type => return .{ .signedness = .signed, .bits = target.ptrBitWidth() },
1848 .c_char_type => return .{ .signedness = target.cCharSignedness().?, .bits = target.cTypeBitSize(.char).? },
1849 .c_short_type => return .{ .signedness = .signed, .bits = target.cTypeBitSize(.short).? },
1850 .c_ushort_type => return .{ .signedness = .unsigned, .bits = target.cTypeBitSize(.ushort).? },
1851 .c_int_type => return .{ .signedness = .signed, .bits = target.cTypeBitSize(.int).? },
1852 .c_uint_type => return .{ .signedness = .unsigned, .bits = target.cTypeBitSize(.uint).? },
1853 .c_long_type => return .{ .signedness = .signed, .bits = target.cTypeBitSize(.long).? },
1854 .c_ulong_type => return .{ .signedness = .unsigned, .bits = target.cTypeBitSize(.ulong).? },
1855 .c_longlong_type => return .{ .signedness = .signed, .bits = target.cTypeBitSize(.longlong).? },
1856 .c_ulonglong_type => return .{ .signedness = .unsigned, .bits = target.cTypeBitSize(.ulonglong).? },
1857 else => switch (ip.indexToKey(ty.toIntern())) {
1858 .int_type => |int_type| return int_type,
1859 .struct_type => {
1860 const struct_obj = ip.loadStructType(ty.toIntern());
1861 assert(struct_obj.layout == .@"packed");
1862 ty = .fromInterned(struct_obj.packed_backing_int_type);
1863 },
1864 .union_type => {
1865 const union_obj = ip.loadUnionType(ty.toIntern());
1866 assert(union_obj.layout == .@"packed");
1867 ty = .fromInterned(union_obj.packed_backing_int_type);
1868 },
1869 .enum_type => ty = .fromInterned(ip.loadEnumType(ty.toIntern()).int_tag_type),
1870 .vector_type => |vector_type| ty = Type.fromInterned(vector_type.child),
1871
1872 .error_set_type, .inferred_error_set_type => {
1873 return .{ .signedness = .unsigned, .bits = zcu.errorSetBits() };
1874 },
1875
1876 .tuple_type => unreachable,
1877
1878 .ptr_type => unreachable,
1879 .anyframe_type => unreachable,
1880 .array_type => unreachable,
1881
1882 .opt_type => unreachable,
1883 .error_union_type => unreachable,
1884 .func_type => unreachable,
1885 .simple_type => unreachable, // handled via Index enum tag above
1886
1887 .spirv_type => unreachable,
1888 .opaque_type => unreachable,
1889
1890 // values, not types
1891 .undef,
1892 .simple_value,
1893 .@"extern",
1894 .func,
1895 .int,
1896 .err,
1897 .error_union,
1898 .enum_literal,
1899 .enum_tag,
1900 .float,
1901 .ptr,
1902 .slice,
1903 .opt,
1904 .aggregate,
1905 .un,
1906 .bitpack,
1907 // memoization, not types
1908 .memoized_call,
1909 => unreachable,
1910 },
1911 };
1912}
1913
1914/// Returns `false` for `comptime_float`.
1915pub fn isRuntimeFloat(ty: Type) bool {
1916 return switch (ty.toIntern()) {
1917 .f16_type,
1918 .f32_type,
1919 .f64_type,
1920 .f80_type,
1921 .f128_type,
1922 .c_longdouble_type,
1923 => true,
1924
1925 else => false,
1926 };
1927}
1928
1929/// Returns `true` for `comptime_float`.
1930pub fn isAnyFloat(ty: Type) bool {
1931 return switch (ty.toIntern()) {
1932 .f16_type,
1933 .f32_type,
1934 .f64_type,
1935 .f80_type,
1936 .f128_type,
1937 .c_longdouble_type,
1938 .comptime_float_type,
1939 => true,
1940
1941 else => false,
1942 };
1943}
1944
1945/// Asserts the type is a fixed-size float or comptime_float.
1946/// Returns 128 for comptime_float types.
1947pub fn floatBits(ty: Type, target: *const Target) u16 {
1948 return switch (ty.toIntern()) {
1949 .f16_type => 16,
1950 .f32_type => 32,
1951 .f64_type => 64,
1952 .f80_type => 80,
1953 .f128_type, .comptime_float_type => 128,
1954 .c_longdouble_type => target.cTypeBitSize(.longdouble).?,
1955
1956 else => unreachable,
1957 };
1958}
1959
1960/// Asserts the type is a fixed-size float or comptime_float.
1961pub fn floatSignificandBits(ty: Type, target: *const Target) u16 {
1962 return switch (ty.floatBits(target)) {
1963 16 => 11,
1964 32 => 24,
1965 64 => 53,
1966 80 => 64,
1967 128 => 113,
1968 else => unreachable,
1969 };
1970}
1971
1972/// Asserts the type is a function or a function pointer.
1973pub fn fnReturnType(ty: Type, zcu: *const Zcu) Type {
1974 return Type.fromInterned(zcu.intern_pool.funcTypeReturnType(ty.toIntern()));
1975}
1976
1977/// Asserts the type is a function.
1978pub fn fnCallingConvention(ty: Type, zcu: *const Zcu) std.lang.CallingConvention {
1979 return zcu.intern_pool.indexToKey(ty.toIntern()).func_type.cc;
1980}
1981
1982pub fn isValidParamType(self: Type, zcu: *const Zcu) bool {
1983 if (self.toIntern() == .generic_poison_type) return true;
1984 return switch (self.zigTypeTag(zcu)) {
1985 .@"opaque", .noreturn => false,
1986 else => true,
1987 };
1988}
1989
1990pub fn isValidReturnType(self: Type, zcu: *const Zcu) bool {
1991 if (self.toIntern() == .generic_poison_type) return true;
1992 return switch (self.zigTypeTag(zcu)) {
1993 .@"opaque" => false,
1994 else => true,
1995 };
1996}
1997
1998/// Asserts the type is a function.
1999pub fn fnIsVarArgs(ty: Type, zcu: *const Zcu) bool {
2000 return zcu.intern_pool.indexToKey(ty.toIntern()).func_type.is_var_args;
2001}
2002
2003pub fn fnPtrMaskOrNull(ty: Type, zcu: *const Zcu) ?u64 {
2004 return switch (ty.zigTypeTag(zcu)) {
2005 .@"fn" => target_util.functionPointerMask(zcu.getTarget()),
2006 else => null,
2007 };
2008}
2009
2010pub fn isNumeric(ty: Type, zcu: *const Zcu) bool {
2011 return switch (ty.toIntern()) {
2012 .f16_type,
2013 .f32_type,
2014 .f64_type,
2015 .f80_type,
2016 .f128_type,
2017 .c_longdouble_type,
2018 .comptime_int_type,
2019 .comptime_float_type,
2020 .usize_type,
2021 .isize_type,
2022 .c_char_type,
2023 .c_short_type,
2024 .c_ushort_type,
2025 .c_int_type,
2026 .c_uint_type,
2027 .c_long_type,
2028 .c_ulong_type,
2029 .c_longlong_type,
2030 .c_ulonglong_type,
2031 => true,
2032
2033 else => switch (zcu.intern_pool.indexToKey(ty.toIntern())) {
2034 .int_type => true,
2035 else => false,
2036 },
2037 };
2038}
2039
2040/// If the type's classification is `Class.one_possible_value` (see `classify`), returns the only
2041/// possible value for the type. Otherwise, returns `null`.
2042pub fn onePossibleValue(ty: Type, pt: Zcu.PerThread) !?Value {
2043 const zcu = pt.zcu;
2044 const comp = zcu.comp;
2045 const gpa = comp.gpa;
2046 const ip = &zcu.intern_pool;
2047 assertHasLayout(ty, zcu);
2048 return switch (ip.indexToKey(ty.toIntern())) {
2049 .ptr_type,
2050 .error_union_type,
2051 .func_type,
2052 .anyframe_type,
2053 .error_set_type,
2054 .inferred_error_set_type,
2055 .opaque_type,
2056 .spirv_type,
2057 => null,
2058
2059 .simple_type => |t| switch (t) {
2060 .f16,
2061 .f32,
2062 .f64,
2063 .f80,
2064 .f128,
2065 .usize,
2066 .isize,
2067 .c_char,
2068 .c_short,
2069 .c_ushort,
2070 .c_int,
2071 .c_uint,
2072 .c_long,
2073 .c_ulong,
2074 .c_longlong,
2075 .c_ulonglong,
2076 .c_longdouble,
2077 .anyopaque,
2078 .bool,
2079 .type,
2080 .anyerror,
2081 .comptime_int,
2082 .comptime_float,
2083 .enum_literal,
2084 .adhoc_inferred_error_set,
2085 .null,
2086 .undefined,
2087 .noreturn,
2088 => null,
2089
2090 .void => .void,
2091
2092 .generic_poison => unreachable,
2093 },
2094
2095 .int_type => |int_type| switch (int_type.bits) {
2096 0 => try pt.intValue(ty, 0),
2097 else => null,
2098 },
2099
2100 inline .array_type, .vector_type => |seq_type, seq_tag| {
2101 const has_sentinel = seq_tag == .array_type and seq_type.sentinel != .none;
2102 if (seq_type.len + @intFromBool(has_sentinel) == 0) {
2103 return try pt.aggregateValue(ty, &.{});
2104 }
2105 if (try Type.fromInterned(seq_type.child).onePossibleValue(pt)) |opv| {
2106 return try pt.aggregateSplatValue(ty, opv);
2107 }
2108 return null;
2109 },
2110 .opt_type => |child| switch (Type.fromInterned(child).classify(zcu)) {
2111 .no_possible_value => try pt.nullValue(ty),
2112 else => null,
2113 },
2114 .tuple_type => |tuple| {
2115 // Check *whether* the OPV exists first, because constructing it is a little more expensive.
2116 if (ty.classify(zcu) != .one_possible_value) return null;
2117 const field_vals = try zcu.gpa.dupe(InternPool.Index, tuple.values.get(ip));
2118 defer zcu.gpa.free(field_vals);
2119 for (field_vals, tuple.types.get(ip)) |*field_val, field_ty_ip| {
2120 if (field_val.* != .none) continue; // comptime field value
2121 const field_ty: Type = .fromInterned(field_ty_ip);
2122 field_val.* = (try field_ty.onePossibleValue(pt)).?.toIntern();
2123 }
2124 return try pt.aggregateValue(ty, field_vals);
2125 },
2126 .struct_type => {
2127 const struct_obj = ip.loadStructType(ty.toIntern());
2128 switch (struct_obj.layout) {
2129 .auto, .@"extern" => {},
2130 .@"packed" => {
2131 const backing_ty: Type = .fromInterned(struct_obj.packed_backing_int_type);
2132 const backing_val = try backing_ty.onePossibleValue(pt) orelse return null;
2133 return try pt.bitpackValue(ty, backing_val);
2134 },
2135 }
2136 // Type resolution already figured out whether there is an OPV, but if there is, it's
2137 // our job to compute it.
2138 if (struct_obj.class != .one_possible_value) return null;
2139 const field_vals = try gpa.alloc(InternPool.Index, struct_obj.field_types.len);
2140 defer gpa.free(field_vals);
2141 for (field_vals, 0..) |*field_val, i_usize| {
2142 const i: u32 = @intCast(i_usize);
2143 if (struct_obj.field_is_comptime_bits.get(ip, i)) {
2144 field_val.* = struct_obj.field_defaults.get(ip)[i];
2145 assert(field_val.* != .none);
2146 continue;
2147 }
2148 const field_ty: Type = .fromInterned(struct_obj.field_types.get(ip)[i]);
2149 field_val.* = (try field_ty.onePossibleValue(pt)).?.toIntern();
2150 }
2151 return try pt.aggregateValue(ty, field_vals);
2152 },
2153 .union_type => {
2154 const union_obj = ip.loadUnionType(ty.toIntern());
2155 if (union_obj.layout == .@"packed") {
2156 const backing_ty: Type = .fromInterned(union_obj.packed_backing_int_type);
2157 const backing_val = try backing_ty.onePossibleValue(pt) orelse return null;
2158 return try pt.bitpackValue(ty, backing_val);
2159 }
2160 // Type resolution already figured out whether there is an OPV, but if there is, it's
2161 // our job to compute it.
2162 if (union_obj.class != .one_possible_value) return null;
2163 // The OPV comes from exactly one field whose type is OPV, while all others are NPV.
2164 for (union_obj.field_types.get(ip), 0..) |field_ty_ip, field_index| {
2165 const field_ty: Type = .fromInterned(field_ty_ip);
2166 switch (field_ty.classify(zcu)) {
2167 .no_possible_value => continue,
2168 .one_possible_value => {},
2169 else => unreachable,
2170 }
2171 // This field is the one!
2172 const enum_tag_ty: Type = .fromInterned(union_obj.enum_tag_type);
2173 const tag_val = try pt.enumValueFieldIndex(enum_tag_ty, @intCast(field_index));
2174 const payload_val = (try field_ty.onePossibleValue(pt)).?;
2175 return try pt.unionValue(ty, tag_val, payload_val);
2176 } else unreachable;
2177 },
2178 .enum_type => if (try ty.backingIntType(zcu).onePossibleValue(pt)) |int_tag_opv| {
2179 return try pt.enumValue(ty, int_tag_opv);
2180 } else null,
2181
2182 // values, not types
2183 .undef,
2184 .simple_value,
2185 .@"extern",
2186 .func,
2187 .int,
2188 .err,
2189 .error_union,
2190 .enum_literal,
2191 .enum_tag,
2192 .float,
2193 .ptr,
2194 .slice,
2195 .opt,
2196 .aggregate,
2197 .un,
2198 .bitpack,
2199 // memoization, not types
2200 .memoized_call,
2201 => unreachable,
2202 };
2203}
2204
2205/// Asserts that `ty` has its layout resolved. `generic_poison` will return `false`.
2206pub fn comptimeOnly(ty: Type, zcu: *const Zcu) bool {
2207 if (ty.toIntern() == .generic_poison_type) return false;
2208 if (ty.zigTypeTag(zcu) == .error_union and ty.errorUnionPayload(zcu).toIntern() == .generic_poison_type) return false;
2209 return ty.classify(zcu).comptimeOnly();
2210}
2211
2212pub fn isVector(ty: Type, zcu: *const Zcu) bool {
2213 return ty.zigTypeTag(zcu) == .vector;
2214}
2215
2216pub fn isArrayOrVector(ty: Type, zcu: *const Zcu) bool {
2217 return switch (ty.zigTypeTag(zcu)) {
2218 .array, .vector => true,
2219 else => false,
2220 };
2221}
2222
2223pub fn isIndexable(ty: Type, zcu: *const Zcu) bool {
2224 return switch (ty.zigTypeTag(zcu)) {
2225 .array, .vector => true,
2226 .pointer => switch (ty.ptrSize(zcu)) {
2227 .slice, .many, .c => true,
2228 .one => switch (ty.childType(zcu).zigTypeTag(zcu)) {
2229 .array, .vector => true,
2230 .@"struct" => ty.childType(zcu).isTuple(zcu),
2231 .spirv => ty.childType(zcu).isSpirvRuntimeArray(zcu),
2232 else => false,
2233 },
2234 },
2235 .@"struct" => ty.isTuple(zcu),
2236 .spirv => ty.isSpirvRuntimeArray(zcu),
2237 else => false,
2238 };
2239}
2240
2241pub fn indexableHasLen(ty: Type, zcu: *const Zcu) bool {
2242 return switch (ty.zigTypeTag(zcu)) {
2243 .array, .vector => true,
2244 .pointer => switch (ty.ptrSize(zcu)) {
2245 .many, .c => false,
2246 .slice => true,
2247 .one => switch (ty.childType(zcu).zigTypeTag(zcu)) {
2248 .array, .vector => true,
2249 .@"struct" => ty.childType(zcu).isTuple(zcu),
2250 else => false,
2251 },
2252 },
2253 .@"struct" => ty.isTuple(zcu),
2254 else => false,
2255 };
2256}
2257
2258/// Asserts that the type can have a namespace.
2259pub fn getNamespaceIndex(ty: Type, zcu: *Zcu) InternPool.NamespaceIndex {
2260 return ty.getNamespace(zcu).unwrap().?;
2261}
2262
2263/// Returns null if the type has no namespace.
2264pub fn getNamespace(ty: Type, zcu: *Zcu) InternPool.OptionalNamespaceIndex {
2265 const ip = &zcu.intern_pool;
2266 return switch (ip.indexToKey(ty.toIntern())) {
2267 .opaque_type => ip.loadOpaqueType(ty.toIntern()).namespace.toOptional(),
2268 .struct_type => ip.loadStructType(ty.toIntern()).namespace.toOptional(),
2269 .union_type => ip.loadUnionType(ty.toIntern()).namespace.toOptional(),
2270 .enum_type => ip.loadEnumType(ty.toIntern()).namespace.toOptional(),
2271 else => .none,
2272 };
2273}
2274
2275// TODO: new dwarf structure will also need the enclosing code block for types created in imperative scopes
2276pub fn getParentNamespace(ty: Type, zcu: *Zcu) InternPool.OptionalNamespaceIndex {
2277 return zcu.namespacePtr(ty.getNamespace(zcu).unwrap() orelse return .none).parent;
2278}
2279
2280// Works for vectors and vectors of integers.
2281pub fn minInt(ty: Type, pt: Zcu.PerThread, dest_ty: Type) !Value {
2282 const zcu = pt.zcu;
2283 const scalar = try minIntScalar(ty.scalarType(zcu), pt, dest_ty.scalarType(zcu));
2284 return if (ty.zigTypeTag(zcu) == .vector) pt.aggregateSplatValue(dest_ty, scalar) else scalar;
2285}
2286
2287/// Asserts that the type is an integer.
2288pub fn minIntScalar(ty: Type, pt: Zcu.PerThread, dest_ty: Type) !Value {
2289 const zcu = pt.zcu;
2290 const info = ty.intInfo(zcu);
2291 if (info.signedness == .unsigned) return pt.intValue(dest_ty, 0);
2292
2293 if (std.math.cast(u6, info.bits - 1)) |shift| {
2294 const n = @as(i64, std.math.minInt(i64)) >> (63 - shift);
2295 return pt.intValue(dest_ty, n);
2296 }
2297
2298 var res = try std.math.big.int.Managed.init(zcu.gpa);
2299 defer res.deinit();
2300
2301 try res.setTwosCompIntLimit(.min, info.signedness, info.bits);
2302
2303 return pt.intValue_big(dest_ty, res.toConst());
2304}
2305
2306// Works for vectors and vectors of integers.
2307/// The returned Value will have type dest_ty.
2308pub fn maxInt(ty: Type, pt: Zcu.PerThread, dest_ty: Type) !Value {
2309 const zcu = pt.zcu;
2310 const scalar = try maxIntScalar(ty.scalarType(zcu), pt, dest_ty.scalarType(zcu));
2311 return if (ty.zigTypeTag(zcu) == .vector) pt.aggregateSplatValue(dest_ty, scalar) else scalar;
2312}
2313
2314/// The returned Value will have type dest_ty.
2315pub fn maxIntScalar(ty: Type, pt: Zcu.PerThread, dest_ty: Type) !Value {
2316 const info = ty.intInfo(pt.zcu);
2317
2318 switch (info.bits) {
2319 0 => return pt.intValue(dest_ty, 0),
2320 1 => return switch (info.signedness) {
2321 .signed => try pt.intValue(dest_ty, 0),
2322 .unsigned => try pt.intValue(dest_ty, 1),
2323 },
2324 else => {},
2325 }
2326
2327 if (std.math.cast(u6, info.bits - 1)) |shift| switch (info.signedness) {
2328 .signed => {
2329 const n = @as(i64, std.math.maxInt(i64)) >> (63 - shift);
2330 return pt.intValue(dest_ty, n);
2331 },
2332 .unsigned => {
2333 const n = @as(u64, std.math.maxInt(u64)) >> (63 - shift);
2334 return pt.intValue(dest_ty, n);
2335 },
2336 };
2337
2338 var res = try std.math.big.int.Managed.init(pt.zcu.gpa);
2339 defer res.deinit();
2340
2341 try res.setTwosCompIntLimit(.max, info.signedness, info.bits);
2342
2343 return pt.intValue_big(dest_ty, res.toConst());
2344}
2345
2346pub fn isNonexhaustiveEnum(ty: Type, zcu: *const Zcu) bool {
2347 const ip = &zcu.intern_pool;
2348 return switch (ip.indexToKey(ty.toIntern())) {
2349 .enum_type => ip.loadEnumType(ty.toIntern()).nonexhaustive,
2350 else => false,
2351 };
2352}
2353
2354// Asserts that `ty` is an error set and not `anyerror`.
2355// Asserts that `ty` is resolved if it is an inferred error set.
2356pub fn errorSetNames(ty: Type, zcu: *const Zcu) InternPool.NullTerminatedString.Slice {
2357 const ip = &zcu.intern_pool;
2358 return switch (ip.indexToKey(ty.toIntern())) {
2359 .error_set_type => |x| x.names,
2360 .inferred_error_set_type => |i| switch (ip.funcIesResolvedUnordered(i)) {
2361 .none => unreachable, // unresolved inferred error set
2362 .anyerror_type => unreachable,
2363 else => |t| ip.indexToKey(t).error_set_type.names,
2364 },
2365 else => unreachable,
2366 };
2367}
2368
2369pub fn enumFields(ty: Type, zcu: *const Zcu) InternPool.NullTerminatedString.Slice {
2370 assertHasLayout(ty, zcu);
2371 return zcu.intern_pool.loadEnumType(ty.toIntern()).field_names;
2372}
2373
2374pub fn enumFieldCount(ty: Type, zcu: *const Zcu) usize {
2375 assertHasLayout(ty, zcu);
2376 return zcu.intern_pool.loadEnumType(ty.toIntern()).field_names.len;
2377}
2378
2379pub fn enumFieldName(ty: Type, field_index: usize, zcu: *const Zcu) InternPool.NullTerminatedString {
2380 assertHasLayout(ty, zcu);
2381 const ip = &zcu.intern_pool;
2382 return ip.loadEnumType(ty.toIntern()).field_names.get(ip)[field_index];
2383}
2384
2385pub fn enumFieldIndex(ty: Type, field_name: InternPool.NullTerminatedString, zcu: *const Zcu) ?u32 {
2386 assertHasLayout(ty, zcu);
2387 const ip = &zcu.intern_pool;
2388 const enum_type = ip.loadEnumType(ty.toIntern());
2389 return enum_type.nameIndex(ip, field_name);
2390}
2391
2392/// Asserts `ty` is an enum. `enum_tag` can either be the actual enum tag value
2393/// or an integer which represents the enum value. Returns the field index in
2394/// declaration order, or `null` if `enum_tag` does not match any field.
2395pub fn enumTagFieldIndex(ty: Type, enum_tag: Value, zcu: *const Zcu) ?u32 {
2396 assertHasLayout(ty, zcu);
2397 const ip = &zcu.intern_pool;
2398 const enum_type = ip.loadEnumType(ty.toIntern());
2399 const int_tag = switch (ip.indexToKey(enum_tag.toIntern())) {
2400 .int => enum_tag.toIntern(),
2401 .enum_tag => |info| info.int,
2402 else => unreachable,
2403 };
2404 assert(ip.typeOf(int_tag) == enum_type.int_tag_type);
2405 return enum_type.tagValueIndex(ip, int_tag);
2406}
2407
2408/// Returns none in the case of a tuple which uses the integer index as the field name.
2409pub fn structFieldName(ty: Type, index: usize, zcu: *const Zcu) InternPool.OptionalNullTerminatedString {
2410 const ip = &zcu.intern_pool;
2411 switch (ip.indexToKey(ty.toIntern())) {
2412 .struct_type => {
2413 assertHasLayout(ty, zcu);
2414 return ip.loadStructType(ty.toIntern()).field_names.get(ip)[index].toOptional();
2415 },
2416 .tuple_type => return .none,
2417 else => unreachable,
2418 }
2419}
2420
2421pub fn structFieldCount(ty: Type, zcu: *const Zcu) u32 {
2422 const ip = &zcu.intern_pool;
2423 switch (ip.indexToKey(ty.toIntern())) {
2424 .struct_type => {
2425 assertHasLayout(ty, zcu);
2426 return ip.loadStructType(ty.toIntern()).field_types.len;
2427 },
2428 .tuple_type => |tuple| return tuple.types.len,
2429 else => unreachable,
2430 }
2431}
2432
2433/// Returns the field type. Supports tuples, structs, and unions.
2434pub fn fieldType(ty: Type, index: usize, zcu: *const Zcu) Type {
2435 const ip = &zcu.intern_pool;
2436 const types = switch (ip.indexToKey(ty.toIntern())) {
2437 .struct_type => types: {
2438 assertHasLayout(ty, zcu);
2439 break :types ip.loadStructType(ty.toIntern()).field_types;
2440 },
2441 .union_type => types: {
2442 assertHasLayout(ty, zcu);
2443 break :types ip.loadUnionType(ty.toIntern()).field_types;
2444 },
2445 .tuple_type => |tuple| tuple.types,
2446 else => unreachable,
2447 };
2448 return .fromInterned(types.get(ip)[index]);
2449}
2450
2451/// If an alignment was explicitly specified for the given field of the struct or union type `ty`,
2452/// returns that. Otherwise, returns `.none`. This function also supports tuples, for which it
2453/// always returns `.none`.
2454///
2455/// Asserts that the layout of `ty` is resolved, unless `ty` is a tuple.
2456pub fn explicitFieldAlignment(ty: Type, index: usize, zcu: *const Zcu) Alignment {
2457 const ip = &zcu.intern_pool;
2458 return switch (ip.indexToKey(ty.toIntern())) {
2459 .tuple_type => .none,
2460 .struct_type => {
2461 assertHasLayout(ty, zcu);
2462 const struct_obj = ip.loadStructType(ty.toIntern());
2463 assert(struct_obj.layout != .@"packed");
2464 if (struct_obj.field_aligns.len == 0) return .none;
2465 return struct_obj.field_aligns.get(ip)[index];
2466 },
2467 .union_type => {
2468 assertHasLayout(ty, zcu);
2469 const union_obj = ip.loadUnionType(ty.toIntern());
2470 assert(union_obj.layout != .@"packed");
2471 if (union_obj.field_aligns.len == 0) return .none;
2472 return union_obj.field_aligns.get(ip)[index];
2473 },
2474 else => unreachable,
2475 };
2476}
2477
2478pub fn structFieldDefaultValue(ty: Type, index: usize, zcu: *const Zcu) ?Value {
2479 const ip = &zcu.intern_pool;
2480 switch (ip.indexToKey(ty.toIntern())) {
2481 .struct_type => {
2482 const field_defaults = ip.loadStructType(ty.toIntern()).field_defaults.get(ip);
2483 if (field_defaults.len == 0) return null;
2484 if (field_defaults[index] == .none) return null;
2485 return .fromInterned(field_defaults[index]);
2486 },
2487 .tuple_type => |tuple| {
2488 const val = tuple.values.get(ip)[index];
2489 if (val == .none) return null;
2490 return .fromInterned(val);
2491 },
2492 else => unreachable,
2493 }
2494}
2495
2496pub fn structFieldValueComptime(ty: Type, pt: Zcu.PerThread, index: usize) !?Value {
2497 const zcu = pt.zcu;
2498 const ip = &zcu.intern_pool;
2499 switch (ip.indexToKey(ty.toIntern())) {
2500 .struct_type => {
2501 const struct_type = ip.loadStructType(ty.toIntern());
2502 if (struct_type.field_is_comptime_bits.get(ip, index)) {
2503 return .fromInterned(struct_type.field_defaults.get(ip)[index]);
2504 } else {
2505 return Type.fromInterned(struct_type.field_types.get(ip)[index]).onePossibleValue(pt);
2506 }
2507 },
2508 .tuple_type => |tuple| {
2509 const val = tuple.values.get(ip)[index];
2510 if (val == .none) {
2511 return Type.fromInterned(tuple.types.get(ip)[index]).onePossibleValue(pt);
2512 } else {
2513 return .fromInterned(val);
2514 }
2515 },
2516 else => unreachable,
2517 }
2518}
2519
2520pub fn structFieldIsComptime(ty: Type, index: usize, zcu: *const Zcu) bool {
2521 const ip = &zcu.intern_pool;
2522 switch (ip.indexToKey(ty.toIntern())) {
2523 .struct_type => {
2524 assertHasLayout(ty, zcu);
2525 return ip.loadStructType(ty.toIntern()).field_is_comptime_bits.get(ip, index);
2526 },
2527 .tuple_type => |tuple| return tuple.values.get(ip)[index] != .none,
2528 else => unreachable,
2529 }
2530}
2531
2532pub const FieldOffset = struct {
2533 field: usize,
2534 offset: u64,
2535};
2536
2537/// Supports structs, tuples, and unions.
2538pub fn structFieldOffset(ty: Type, index: usize, zcu: *const Zcu) u64 {
2539 assertHasLayout(ty, zcu);
2540 const ip = &zcu.intern_pool;
2541 switch (ip.indexToKey(ty.toIntern())) {
2542 .struct_type => {
2543 const struct_type = ip.loadStructType(ty.toIntern());
2544 assert(struct_type.layout != .@"packed");
2545 return struct_type.field_offsets.get(ip)[index];
2546 },
2547
2548 .tuple_type => |tuple| {
2549 var offset: u64 = 0;
2550 var big_align: Alignment = .none;
2551
2552 for (tuple.types.get(ip), tuple.values.get(ip), 0..) |field_ty, field_val, i| {
2553 if (field_val != .none or !Type.fromInterned(field_ty).hasRuntimeBits(zcu)) {
2554 // comptime field
2555 if (i == index) return 0;
2556 continue;
2557 }
2558
2559 const field_align = Type.fromInterned(field_ty).abiAlignment(zcu);
2560 big_align = big_align.max(field_align);
2561 offset = field_align.forward(offset);
2562 if (i == index) return offset;
2563 offset += Type.fromInterned(field_ty).abiSize(zcu);
2564 }
2565 offset = big_align.max(.@"1").forward(offset);
2566 return offset;
2567 },
2568
2569 .union_type => {
2570 const union_type = ip.loadUnionType(ty.toIntern());
2571 if (!union_type.has_runtime_tag) return 0;
2572 const layout = Type.getUnionLayout(union_type, zcu);
2573 if (layout.tag_align.compare(.gte, layout.payload_align)) {
2574 // {Tag, Payload}
2575 return layout.payload_align.forward(layout.tag_size);
2576 } else {
2577 // {Payload, Tag}
2578 return 0;
2579 }
2580 },
2581
2582 else => unreachable,
2583 }
2584}
2585
2586pub fn srcLocOrNull(ty: Type, zcu: *Zcu) ?Zcu.LazySrcLoc {
2587 const ip = &zcu.intern_pool;
2588 return .{
2589 .base_node_inst = switch (ip.indexToKey(ty.toIntern())) {
2590 .struct_type, .union_type, .opaque_type, .enum_type => |info| switch (info) {
2591 .declared => |d| d.zir_index,
2592 .reified => |r| r.zir_index,
2593 .generated_union_tag => |union_ty| ip.loadUnionType(union_ty).zir_index,
2594 },
2595 else => return null,
2596 },
2597 .offset = Zcu.LazySrcLoc.Offset.nodeOffset(.zero),
2598 };
2599}
2600
2601pub fn srcLoc(ty: Type, zcu: *Zcu) Zcu.LazySrcLoc {
2602 return ty.srcLocOrNull(zcu).?;
2603}
2604
2605pub fn isGenericPoison(ty: Type) bool {
2606 return ty.toIntern() == .generic_poison_type;
2607}
2608
2609pub fn isTuple(ty: Type, zcu: *const Zcu) bool {
2610 const ip = &zcu.intern_pool;
2611 return switch (ip.indexToKey(ty.toIntern())) {
2612 .tuple_type => true,
2613 else => false,
2614 };
2615}
2616
2617/// Traverses optional child types and error union payloads until the type is neither of those.
2618/// For `E!?u32`, returns `u32`; for `*u8`, returns `*u8`.
2619pub fn optEuBaseType(ty: Type, zcu: *const Zcu) Type {
2620 var cur = ty;
2621 while (true) switch (cur.zigTypeTag(zcu)) {
2622 .optional => cur = cur.optionalChild(zcu),
2623 .error_union => cur = cur.errorUnionPayload(zcu),
2624 else => return cur,
2625 };
2626}
2627
2628pub fn toUnsigned(ty: Type, pt: Zcu.PerThread) !Type {
2629 const zcu = pt.zcu;
2630 return switch (ty.toIntern()) {
2631 // zig fmt: off
2632 .usize_type, .isize_type => .usize,
2633 .c_ushort_type, .c_short_type => .c_ushort,
2634 .c_uint_type, .c_int_type => .c_uint,
2635 .c_ulong_type, .c_long_type => .c_ulong,
2636 .c_ulonglong_type, .c_longlong_type => .c_ulonglong,
2637 // zig fmt: on
2638 else => switch (ty.zigTypeTag(zcu)) {
2639 .int => pt.intType(.unsigned, ty.intInfo(zcu).bits),
2640 .vector => try pt.vectorType(.{
2641 .len = ty.vectorLen(zcu),
2642 .child = (try ty.childType(zcu).toUnsigned(pt)).toIntern(),
2643 }),
2644 else => unreachable,
2645 },
2646 };
2647}
2648
2649pub fn typeDeclInst(ty: Type, zcu: *const Zcu) ?InternPool.TrackedInst.Index {
2650 const ip = &zcu.intern_pool;
2651 return switch (ip.indexToKey(ty.toIntern())) {
2652 .struct_type => ip.loadStructType(ty.toIntern()).zir_index,
2653 .union_type => ip.loadUnionType(ty.toIntern()).zir_index,
2654 .enum_type => ip.loadEnumType(ty.toIntern()).zir_index.unwrap(),
2655 .opaque_type => ip.loadOpaqueType(ty.toIntern()).zir_index,
2656 else => null,
2657 };
2658}
2659
2660pub fn typeDeclInstAllowGeneratedTag(ty: Type, zcu: *const Zcu) ?InternPool.TrackedInst.Index {
2661 const ip = &zcu.intern_pool;
2662 return switch (ip.indexToKey(ty.toIntern())) {
2663 .struct_type => ip.loadStructType(ty.toIntern()).zir_index,
2664 .union_type => ip.loadUnionType(ty.toIntern()).zir_index,
2665 .enum_type => |e| switch (e) {
2666 .declared, .reified => ip.loadEnumType(ty.toIntern()).zir_index.unwrap().?,
2667 .generated_union_tag => |union_ty| ip.loadUnionType(union_ty).zir_index,
2668 },
2669 .opaque_type => ip.loadOpaqueType(ty.toIntern()).zir_index,
2670 else => null,
2671 };
2672}
2673
2674pub fn typeDeclSrcLine(ty: Type, zcu: *Zcu) ?u32 {
2675 // Note that changes to ZIR instruction tracking only need to update this code
2676 // if a newly-tracked instruction can be a type's owner `zir_index`.
2677 comptime assert(Zir.inst_tracking_version == 0);
2678
2679 const ip = &zcu.intern_pool;
2680 const tracked = switch (ip.indexToKey(ty.toIntern())) {
2681 .struct_type, .union_type, .opaque_type, .enum_type => |info| switch (info) {
2682 .declared => |d| d.zir_index,
2683 .reified => |r| r.zir_index,
2684 .generated_union_tag => |union_ty| ip.loadUnionType(union_ty).zir_index,
2685 },
2686 else => return null,
2687 };
2688 const info = tracked.resolveFull(&zcu.intern_pool) orelse return null;
2689 const file = zcu.fileByIndex(info.file);
2690 const zir = switch (file.getMode()) {
2691 .zig => file.zir.?,
2692 .zon => return 0,
2693 };
2694 const inst = zir.instructions.get(@backingInt(info.inst));
2695 return switch (inst.tag) {
2696 .struct_init, .struct_init_ref => zir.extraData(Zir.Inst.StructInit, inst.data.pl_node.payload_index).data.src_line,
2697 .struct_init_anon => zir.extraData(Zir.Inst.StructInitAnon, inst.data.pl_node.payload_index).data.src_line,
2698 .extended => switch (inst.data.extended.opcode) {
2699 .struct_decl => zir.getStructDecl(info.inst).src_line,
2700 .union_decl => zir.getUnionDecl(info.inst).src_line,
2701 .enum_decl => zir.getEnumDecl(info.inst).src_line,
2702 .opaque_decl => zir.getOpaqueDecl(info.inst).src_line,
2703 .reify_enum => zir.extraData(Zir.Inst.ReifyEnum, inst.data.extended.operand).data.src_line,
2704 .reify_struct => zir.extraData(Zir.Inst.ReifyStruct, inst.data.extended.operand).data.src_line,
2705 .reify_union => zir.extraData(Zir.Inst.ReifyUnion, inst.data.extended.operand).data.src_line,
2706 else => unreachable,
2707 },
2708 else => unreachable,
2709 };
2710}
2711
2712/// Given a namespace type, returns its list of captured values.
2713pub fn getCaptures(ty: Type, zcu: *const Zcu) InternPool.CaptureValue.Slice {
2714 const ip = &zcu.intern_pool;
2715 return switch (ip.indexToKey(ty.toIntern())) {
2716 .struct_type => ip.loadStructType(ty.toIntern()).captures,
2717 .union_type => ip.loadUnionType(ty.toIntern()).captures,
2718 .enum_type => ip.loadEnumType(ty.toIntern()).captures,
2719 .opaque_type => ip.loadOpaqueType(ty.toIntern()).captures,
2720 else => unreachable,
2721 };
2722}
2723
2724pub fn arrayBase(ty: Type, zcu: *const Zcu) struct { Type, u64 } {
2725 var cur_ty: Type = ty;
2726 var cur_len: u64 = 1;
2727 while (cur_ty.zigTypeTag(zcu) == .array) {
2728 cur_len *= cur_ty.arrayLenIncludingSentinel(zcu);
2729 cur_ty = cur_ty.childType(zcu);
2730 }
2731 return .{ cur_ty, cur_len };
2732}
2733
2734/// Asserts that `loaded_union.layout` is not `.@"packed"`.
2735pub fn getUnionLayout(loaded_union: InternPool.LoadedUnionType, zcu: *const Zcu) Zcu.UnionLayout {
2736 assert(loaded_union.layout != .@"packed");
2737
2738 const ip = &zcu.intern_pool;
2739 var most_aligned_field: u32 = 0;
2740 var most_aligned_field_align: InternPool.Alignment = .@"1";
2741 var most_aligned_field_size: u64 = 0;
2742 var biggest_field: u32 = 0;
2743 var payload_size: u64 = 0;
2744 var payload_align: InternPool.Alignment = .@"1";
2745 for (loaded_union.field_types.get(ip), 0..) |field_ty_ip_index, field_index| {
2746 const field_ty: Type = .fromInterned(field_ty_ip_index);
2747 if (field_ty.isNoReturn(zcu)) continue;
2748
2749 const field_align: InternPool.Alignment = a: {
2750 const explicit_aligns = loaded_union.field_aligns.get(ip);
2751 if (explicit_aligns.len > 0) {
2752 const a = explicit_aligns[field_index];
2753 if (a != .none) break :a a;
2754 }
2755 break :a field_ty.abiAlignment(zcu);
2756 };
2757 if (field_ty.hasRuntimeBits(zcu)) {
2758 const field_size = field_ty.abiSize(zcu);
2759 if (field_size > payload_size) {
2760 payload_size = field_size;
2761 biggest_field = @intCast(field_index);
2762 }
2763 if (field_size > 0 and field_align.compare(.gte, most_aligned_field_align)) {
2764 most_aligned_field = @intCast(field_index);
2765 most_aligned_field_align = field_align;
2766 most_aligned_field_size = field_size;
2767 }
2768 }
2769 payload_align = payload_align.max(field_align);
2770 }
2771 if (!loaded_union.has_runtime_tag or
2772 !Type.fromInterned(loaded_union.enum_tag_type).hasRuntimeBits(zcu))
2773 {
2774 return .{
2775 .abi_size = payload_align.forward(payload_size),
2776 .abi_align = payload_align,
2777 .most_aligned_field = most_aligned_field,
2778 .most_aligned_field_size = most_aligned_field_size,
2779 .biggest_field = biggest_field,
2780 .payload_size = payload_size,
2781 .payload_align = payload_align,
2782 .tag_align = .none,
2783 .tag_size = 0,
2784 .padding = 0,
2785 };
2786 }
2787
2788 const tag_size = Type.fromInterned(loaded_union.enum_tag_type).abiSize(zcu);
2789 const tag_align = Type.fromInterned(loaded_union.enum_tag_type).abiAlignment(zcu).max(.@"1");
2790 return .{
2791 .abi_size = loaded_union.size,
2792 .abi_align = tag_align.max(payload_align),
2793 .most_aligned_field = most_aligned_field,
2794 .most_aligned_field_size = most_aligned_field_size,
2795 .biggest_field = biggest_field,
2796 .payload_size = payload_size,
2797 .payload_align = payload_align,
2798 .tag_align = tag_align,
2799 .tag_size = tag_size,
2800 .padding = loaded_union.padding,
2801 };
2802}
2803
2804/// Asserts that `ptr_ty` is either a many-item pointer, a slice, a C pointer, or a single pointer
2805/// to array (in other words, a pointer which is indexed by pointer arithmetic), and returns the
2806/// type of the element pointer at the given index.
2807///
2808/// Asserts that the layout of the pointer element type is resolved.
2809///
2810/// If `index` is `null`, the index is an arbitrary runtime-known value.
2811pub fn elemPtrType(ptr_ty: Type, index: ?u64, pt: Zcu.PerThread) Allocator.Error!Type {
2812 const zcu = pt.zcu;
2813 const ip = &zcu.intern_pool;
2814 const ptr_info = ip.indexToKey(ptr_ty.toIntern()).ptr_type;
2815 const elem_ty: Type = switch (ptr_info.flags.size) {
2816 .slice, .many, .c => .fromInterned(ptr_info.child),
2817 .one => switch (ip.indexToKey(ptr_info.child)) {
2818 .array_type => |array_type| .fromInterned(array_type.child),
2819 .spirv_type => Type.fromInterned(ptr_info.child).childType(zcu),
2820 else => unreachable,
2821 },
2822 };
2823 elem_ty.assertHasLayout(zcu);
2824 const elem_align: Alignment = switch (elem_ty.classify(zcu)) {
2825 .no_possible_value,
2826 .one_possible_value,
2827 => ptr_info.flags.alignment,
2828
2829 .partially_comptime,
2830 .fully_comptime,
2831 => switch (ptr_info.flags.alignment) {
2832 .none => .none,
2833 else => |array_align| .minStrict(array_align, elem_ty.abiAlignment(zcu)),
2834 },
2835
2836 .runtime => switch (ptr_info.flags.alignment) {
2837 .none => .none,
2838 else => |array_align| elem_align: {
2839 // If the index is runtime-known, use 1 as it gives the minimum possible alignment.
2840 const effective_index = index orelse 1;
2841 if (effective_index == 0) break :elem_align array_align;
2842 const byte_offset = effective_index * elem_ty.abiSize(zcu);
2843 break :elem_align .minStrict(array_align, .fromLog2Units(@ctz(byte_offset)));
2844 },
2845 },
2846 };
2847 return pt.ptrType(.{
2848 .child = elem_ty.toIntern(),
2849 .flags = .{
2850 .size = .one,
2851 .is_const = ptr_info.flags.is_const,
2852 .is_volatile = ptr_info.flags.is_volatile,
2853 .is_allowzero = ptr_info.flags.is_allowzero and (index == null or index == 0),
2854 .address_space = ptr_info.flags.address_space,
2855 .alignment = elem_align,
2856 },
2857 });
2858}
2859
2860/// Asserts that `ptr_ty` is a pointer (single-item or C) to a struct, union, tuple, or slice, and
2861/// returns the type of a pointer to the field at `field_index`.
2862///
2863/// Asserts that the layout of the pointer child type is resolved.
2864///
2865/// For slices, `Value.slice_ptr_index` and `Value.slice_len_index` are used for the field index.
2866pub fn fieldPtrType(ptr_ty: Type, field_index: u32, pt: Zcu.PerThread) Allocator.Error!Type {
2867 const zcu = pt.zcu;
2868 const ip = &zcu.intern_pool;
2869 const ptr_info = ip.indexToKey(ptr_ty.toIntern()).ptr_type;
2870 assert(ptr_info.flags.size == .one or ptr_info.flags.size == .c);
2871 const aggregate_ty: Type = .fromInterned(ptr_info.child);
2872 aggregate_ty.assertHasLayout(zcu);
2873 // We only exit this `switch` for default-layout aggregates, where the field pointer alignment
2874 // is a simple minimum of the aggregate pointer alignment and the field alignment.
2875 // `field_align` is `.none` if there is no explicit alignment annotation.
2876 const field_ty: Type, const field_align: Alignment = switch (aggregate_ty.zigTypeTag(zcu)) {
2877 .@"struct" => switch (aggregate_ty.containerLayout(zcu)) {
2878 .auto => field: {
2879 if (aggregate_ty.isTuple(zcu)) {
2880 break :field .{ aggregate_ty.fieldType(field_index, zcu), .none };
2881 }
2882 const struct_obj = ip.loadStructType(aggregate_ty.toIntern());
2883 break :field .{
2884 .fromInterned(struct_obj.field_types.get(ip)[field_index]),
2885 struct_obj.field_aligns.getOrNone(ip, field_index),
2886 };
2887 },
2888 .@"extern" => {
2889 // Field alignment is determined based on the actual field offset. For instance, in
2890 // `extern struct { x: u32, y: u16 }`, the `y` field is 4-byte aligned.
2891 const field_ty = aggregate_ty.fieldType(field_index, zcu);
2892 const field_offset = aggregate_ty.structFieldOffset(field_index, zcu);
2893 const parent_align = switch (ptr_info.flags.alignment) {
2894 .none => aggregate_ty.abiAlignment(zcu),
2895 else => |a| a,
2896 };
2897 const actual_field_align = switch (field_offset) {
2898 0 => parent_align,
2899 else => parent_align.minStrict(.fromLog2Units(@ctz(field_offset))),
2900 };
2901 const field_ptr_align: Alignment = a: {
2902 if (ptr_info.flags.alignment == .none and
2903 aggregate_ty.explicitFieldAlignment(field_index, zcu) == .none and
2904 actual_field_align == field_ty.abiAlignment(zcu))
2905 {
2906 // There's no user-specified 'align' in sight, and the alignment from the
2907 // field offset matches the field type's natural alignment, so just use a
2908 // default-aligned pointer.
2909 break :a .none;
2910 }
2911 break :a actual_field_align;
2912 };
2913 var field_ptr_info = ptr_info;
2914 field_ptr_info.child = field_ty.toIntern();
2915 field_ptr_info.flags.alignment = field_ptr_align;
2916 return pt.ptrType(field_ptr_info);
2917 },
2918 .@"packed" => {
2919 var field_ptr_info = ptr_info;
2920 if (field_ptr_info.flags.alignment == .none) {
2921 field_ptr_info.flags.alignment = aggregate_ty.abiAlignment(zcu);
2922 }
2923 field_ptr_info.packed_offset = packed_offset: {
2924 comptime assert(Type.packed_struct_layout_version == 2);
2925 const bit_offset = zcu.structPackedFieldBitOffset(
2926 ip.loadStructType(aggregate_ty.toIntern()),
2927 field_index,
2928 );
2929 break :packed_offset if (ptr_info.packed_offset.host_size != 0) .{
2930 .host_size = ptr_info.packed_offset.host_size,
2931 .bit_offset = ptr_info.packed_offset.bit_offset + bit_offset,
2932 } else .{
2933 .host_size = switch (zcu.comp.getZigBackend()) {
2934 else => @intCast((aggregate_ty.bitSize(zcu) + 7) / 8),
2935 .stage2_x86_64, .stage2_c => @intCast(aggregate_ty.abiSize(zcu)),
2936 },
2937 .bit_offset = ptr_info.packed_offset.bit_offset + bit_offset,
2938 };
2939 };
2940 field_ptr_info.child = aggregate_ty.fieldType(field_index, zcu).toIntern();
2941 return pt.ptrType(field_ptr_info);
2942 },
2943 },
2944 .@"union" => switch (aggregate_ty.containerLayout(zcu)) {
2945 .auto => field: {
2946 const union_obj = ip.loadUnionType(aggregate_ty.toIntern());
2947 break :field .{
2948 .fromInterned(union_obj.field_types.get(ip)[field_index]),
2949 union_obj.field_aligns.getOrNone(ip, field_index),
2950 };
2951 },
2952 .@"extern" => {
2953 // The alignment always matches that of the union pointer. If the union pointer is
2954 // default aligned (`.none`), we may need to explicitly align the result pointer.
2955 const field_ty = aggregate_ty.fieldType(field_index, zcu);
2956 var field_ptr_info = ptr_info;
2957 field_ptr_info.child = field_ty.toIntern();
2958 if (field_ptr_info.flags.alignment == .none and
2959 Alignment.compareStrict(field_ty.abiAlignment(zcu), .neq, aggregate_ty.abiAlignment(zcu)))
2960 {
2961 field_ptr_info.flags.alignment = aggregate_ty.abiAlignment(zcu);
2962 }
2963 return pt.ptrType(field_ptr_info);
2964 },
2965 .@"packed" => {
2966 const field_ty = aggregate_ty.fieldType(field_index, zcu);
2967 var field_ptr_info = ptr_info;
2968 if (field_ptr_info.flags.alignment == .none) {
2969 const resolved_align = aggregate_ty.abiAlignment(zcu);
2970 if (field_ty.abiAlignment(zcu) != resolved_align) {
2971 field_ptr_info.flags.alignment = resolved_align;
2972 }
2973 }
2974 field_ptr_info.child = aggregate_ty.fieldType(field_index, zcu).toIntern();
2975 return pt.ptrType(field_ptr_info);
2976 },
2977 },
2978 .pointer => field: {
2979 assert(aggregate_ty.isSlice(zcu));
2980 break :field switch (field_index) {
2981 Value.slice_ptr_index => .{ aggregate_ty.slicePtrFieldType(zcu), .none },
2982 Value.slice_len_index => .{ .usize, .none },
2983 else => unreachable,
2984 };
2985 },
2986 else => unreachable,
2987 };
2988 const field_ptr_align: Alignment = a: {
2989 if (aggregate_ty.zigTypeTag(zcu) == .@"struct" and aggregate_ty.structFieldIsComptime(field_index, zcu)) {
2990 // For `comptime` fields, just use exactly what was specified, or ABI alignment if nothing was specified.
2991 break :a field_align;
2992 }
2993 const actual_field_align = switch (field_align) {
2994 .none => switch (ip.indexToKey(aggregate_ty.toIntern())) {
2995 .struct_type, .tuple_type, .union_type => field_ty.abiAlignment(zcu),
2996 .ptr_type => Type.usize.abiAlignment(zcu),
2997 else => unreachable,
2998 },
2999 else => |a| a,
3000 };
3001 const actual_aggregate_align = switch (ptr_info.flags.alignment) {
3002 .none => aggregate_ty.abiAlignment(zcu),
3003 else => |a| a,
3004 };
3005 if (actual_aggregate_align.compareStrict(.lt, actual_field_align)) {
3006 // Underaligned aggregate; use that alignment.
3007 assert(ptr_info.flags.alignment != .none);
3008 break :a actual_aggregate_align;
3009 }
3010 if (field_align == .none and actual_field_align == field_ty.abiAlignment(zcu)) {
3011 // No explicit annotation on the field (nor an unusual default), and the aggregate
3012 // alignment is irrelevant to us, so return an un-annotated pointer.
3013 break :a .none;
3014 }
3015 break :a actual_field_align;
3016 };
3017 var field_ptr_info = ptr_info;
3018 field_ptr_info.flags.alignment = field_ptr_align;
3019 field_ptr_info.child = field_ty.toIntern();
3020 return pt.ptrType(field_ptr_info);
3021}
3022
3023pub fn containerTypeName(ty: Type, ip: *const InternPool) struct {
3024 name: InternPool.NullTerminatedString,
3025 fqn: InternPool.NullTerminatedString,
3026} {
3027 switch (ip.indexToKey(ty.toIntern())) {
3028 .struct_type => {
3029 const loaded_struct = ip.loadStructType(ty.toIntern());
3030 return .{ .name = loaded_struct.name, .fqn = loaded_struct.fqn };
3031 },
3032 .union_type => {
3033 const loaded_union = ip.loadUnionType(ty.toIntern());
3034 return .{ .name = loaded_union.name, .fqn = loaded_union.fqn };
3035 },
3036 .enum_type => {
3037 const loaded_enum = ip.loadEnumType(ty.toIntern());
3038 return .{ .name = loaded_enum.name, .fqn = loaded_enum.fqn };
3039 },
3040 .opaque_type => {
3041 const loaded_opaque = ip.loadOpaqueType(ty.toIntern());
3042 return .{ .name = loaded_opaque.name, .fqn = loaded_opaque.fqn };
3043 },
3044 else => unreachable,
3045 }
3046}
3047
3048pub fn destructurable(ty: Type, zcu: *const Zcu) bool {
3049 return switch (ty.zigTypeTag(zcu)) {
3050 .array, .vector => true,
3051 .@"struct" => ty.isTuple(zcu),
3052 else => false,
3053 };
3054}
3055
3056pub const UnpackableReason = union(enum) {
3057 comptime_only,
3058 pointer,
3059 enum_inferred_int_tag: Type,
3060 non_packed_struct: Type,
3061 non_packed_union: Type,
3062 slice,
3063 other,
3064};
3065
3066/// Returns `null` iff `ty` is allowed in packed types.
3067pub fn unpackable(ty: Type, zcu: *const Zcu) ?UnpackableReason {
3068 return switch (ty.zigTypeTag(zcu)) {
3069 .void,
3070 .bool,
3071 .float,
3072 .int,
3073 => null,
3074
3075 .type,
3076 .comptime_float,
3077 .comptime_int,
3078 .enum_literal,
3079 .undefined,
3080 .null,
3081 => .comptime_only,
3082
3083 .noreturn,
3084 .@"opaque",
3085 .spirv,
3086 .error_union,
3087 .error_set,
3088 .frame,
3089 .@"anyframe",
3090 .@"fn",
3091 .array,
3092 .vector,
3093 => .other,
3094
3095 .optional => if (ty.isPtrLikeOptional(zcu))
3096 .pointer
3097 else
3098 .other,
3099
3100 .pointer => switch (ty.ptrSize(zcu)) {
3101 .slice => .slice,
3102 .one, .many, .c => .pointer,
3103 },
3104
3105 .@"enum" => switch (ty.backingIntMode(zcu)) {
3106 .explicit => switch (ty.backingIntType(zcu).toIntern()) {
3107 else => null,
3108 .noreturn_type => .other,
3109 },
3110 .auto => .{ .enum_inferred_int_tag = ty },
3111 },
3112
3113 .@"struct" => switch (ty.containerLayout(zcu)) {
3114 .@"packed" => null,
3115 .auto, .@"extern" => .{ .non_packed_struct = ty },
3116 },
3117 .@"union" => switch (ty.containerLayout(zcu)) {
3118 .@"packed" => null,
3119 .auto, .@"extern" => .{ .non_packed_union = ty },
3120 },
3121 };
3122}
3123
3124pub const ExternPosition = enum {
3125 ret_ty,
3126 param_ty,
3127 union_field,
3128 struct_field,
3129 element,
3130 other,
3131};
3132
3133/// Returns true if `ty` is allowed in extern types.
3134/// Asserts that `ty` is fully resolved.
3135/// Keep in sync with `Sema.explainWhyTypeIsNotExtern`.
3136pub fn validateExtern(ty: Type, position: ExternPosition, zcu: *const Zcu) bool {
3137 ty.assertHasLayout(zcu);
3138 return switch (ty.zigTypeTag(zcu)) {
3139 .type,
3140 .comptime_float,
3141 .comptime_int,
3142 .enum_literal,
3143 .undefined,
3144 .null,
3145 .error_union,
3146 .error_set,
3147 .frame,
3148 => false,
3149
3150 .vector => {
3151 if (zcu.getTarget().cpu.arch.isSpirV()) return true;
3152 return position == .param_ty or position == .ret_ty;
3153 },
3154
3155 .void => switch (position) {
3156 .ret_ty,
3157 .union_field,
3158 .struct_field,
3159 .element,
3160 => true,
3161 .param_ty,
3162 .other,
3163 => false,
3164 },
3165
3166 .noreturn => position == .ret_ty,
3167
3168 .@"opaque",
3169 .bool,
3170 .@"anyframe",
3171 => true,
3172
3173 .spirv => switch (position) {
3174 .struct_field, .union_field => true,
3175 .ret_ty, .param_ty, .element => !ty.isSpirvRuntimeArray(zcu),
3176 .other => !ty.isSpirvRuntimeArray(zcu) or zcu.getTarget().cpu.has(.spirv, .runtime_descriptor_array),
3177 },
3178
3179 .pointer => {
3180 if (ty.isSlice(zcu)) return false;
3181 const child_ty = ty.childType(zcu);
3182 if (child_ty.zigTypeTag(zcu) == .@"fn") {
3183 return ty.isConstPtr(zcu) and validateExternCallconv(child_ty.fnCallingConvention(zcu));
3184 }
3185 return true;
3186 },
3187 .int => switch (ty.intInfo(zcu).bits) {
3188 0, 8, 16, 32, 64, 128 => true,
3189 24, 48 => zcu.getTarget().cpu.arch == .ez80,
3190 else => false,
3191 },
3192 .float => switch (ty.floatBits(zcu.getTarget())) {
3193 else => true,
3194 80 => |bits| std.zig.target.compilerRtFloatAbi(zcu.getTarget(), bits) == .hard,
3195 },
3196 .@"fn" => {
3197 if (position != .other) return false;
3198 return validateExternCallconv(ty.fnCallingConvention(zcu));
3199 },
3200 .@"enum" => {
3201 const enum_obj = zcu.intern_pool.loadEnumType(ty.toIntern());
3202 return switch (enum_obj.int_tag_mode) {
3203 .auto => false,
3204 .explicit => Type.fromInterned(enum_obj.int_tag_type).validateExtern(position, zcu),
3205 };
3206 },
3207 .@"struct" => {
3208 if (ty.isTuple(zcu)) return false;
3209 const struct_obj = zcu.intern_pool.loadStructType(ty.toIntern());
3210 return switch (struct_obj.layout) {
3211 .auto => false,
3212 .@"extern" => true,
3213 .@"packed" => switch (struct_obj.packed_backing_mode) {
3214 .auto => false,
3215 .explicit => Type.fromInterned(struct_obj.packed_backing_int_type).validateExtern(position, zcu),
3216 },
3217 };
3218 },
3219 .@"union" => {
3220 const union_obj = zcu.intern_pool.loadUnionType(ty.toIntern());
3221 return switch (union_obj.layout) {
3222 .auto => false,
3223 .@"extern" => true,
3224 .@"packed" => switch (union_obj.packed_backing_mode) {
3225 .auto => false,
3226 .explicit => Type.fromInterned(union_obj.packed_backing_int_type).validateExtern(position, zcu),
3227 },
3228 };
3229 },
3230 .array => switch (position) {
3231 .ret_ty,
3232 .param_ty,
3233 => false,
3234
3235 .union_field,
3236 .struct_field,
3237 .element,
3238 .other,
3239 => ty.childType(zcu).validateExtern(.element, zcu),
3240 },
3241 .optional => ty.isPtrLikeOptional(zcu),
3242 };
3243}
3244fn validateExternCallconv(cc: std.lang.CallingConvention) bool {
3245 return switch (cc) {
3246 // For now we want to authorize PTX kernel to use zig objects, even if we end up exposing the ABI.
3247 // The goal is to experiment with more integrated CPU/GPU code.
3248 .nvptx_kernel => true,
3249 else => !target_util.fnCallConvAllowsZigTypes(cc),
3250 };
3251}
3252
3253/// Returns whether `ty` is considered by Zig to have a bit-level representation, meaning it is
3254/// allowed as the operand to `@bitSizeOf`. This is a superset of packable types.
3255pub fn hasBitRepresentation(ty: Type, zcu: *const Zcu) bool {
3256 return switch (ty.zigTypeTag(zcu)) {
3257 .@"fn",
3258 .noreturn,
3259 .undefined,
3260 .null,
3261 .@"opaque",
3262 .spirv,
3263 .type,
3264 .enum_literal,
3265 .comptime_float,
3266 .comptime_int,
3267 .error_set,
3268 .error_union,
3269 .frame,
3270 .@"anyframe",
3271 => false,
3272
3273 .void,
3274 .bool,
3275 .int,
3276 .float,
3277 => true,
3278
3279 .@"enum" => {
3280 const enum_obj = zcu.intern_pool.loadEnumType(ty.toIntern());
3281 return enum_obj.int_tag_mode == .explicit and
3282 enum_obj.int_tag_type != .noreturn_type;
3283 },
3284 .pointer, .optional => ty.isPtrAtRuntime(zcu),
3285 .@"struct", .@"union" => ty.containerLayout(zcu) == .@"packed",
3286
3287 .array, .vector => ty.childType(zcu).hasBitRepresentation(zcu),
3288 };
3289}
3290
3291/// Asserts that `ty` has resolved layout.
3292pub fn assertHasLayout(ty: Type, zcu: *const Zcu) void {
3293 if (!std.debug.runtime_safety) {
3294 // This early exit isn't necessary (`Zcu.assertUpToDate` checks `std.debug.runtime_safety`
3295 // itself), but LLVM has been observed to fail at optimizing away this safety check, which
3296 // has a major performance impact on ReleaseFast compiler builds.
3297 return;
3298 }
3299 switch (zcu.intern_pool.indexToKey(ty.toIntern())) {
3300 .int_type,
3301 .ptr_type,
3302 .anyframe_type,
3303 .simple_type,
3304 .opaque_type,
3305 .error_set_type,
3306 .spirv_type,
3307 .inferred_error_set_type,
3308 => {},
3309 .func_type => |func_type| {
3310 for (func_type.param_types.get(&zcu.intern_pool)) |param_ty| {
3311 assertHasLayout(.fromInterned(param_ty), zcu);
3312 }
3313 assertHasLayout(.fromInterned(func_type.return_type), zcu);
3314 },
3315 .array_type => |arr| assertHasLayout(.fromInterned(arr.child), zcu),
3316 .vector_type => |vec| assertHasLayout(.fromInterned(vec.child), zcu),
3317 .opt_type => |child| assertHasLayout(.fromInterned(child), zcu),
3318 .error_union_type => |eu| assertHasLayout(.fromInterned(eu.payload_type), zcu),
3319 .tuple_type => |tuple| for (tuple.types.get(&zcu.intern_pool)) |field_ty| {
3320 assertHasLayout(.fromInterned(field_ty), zcu);
3321 },
3322 .struct_type => {
3323 assert(zcu.intern_pool.loadStructType(ty.toIntern()).want_layout);
3324 zcu.assertUpToDate(.wrap(.{ .type_layout = ty.toIntern() }));
3325 },
3326 .union_type => {
3327 assert(zcu.intern_pool.loadUnionType(ty.toIntern()).want_layout);
3328 zcu.assertUpToDate(.wrap(.{ .type_layout = ty.toIntern() }));
3329 },
3330 .enum_type => {
3331 assert(zcu.intern_pool.loadEnumType(ty.toIntern()).want_layout);
3332 zcu.assertUpToDate(.wrap(.{ .type_layout = ty.toIntern() }));
3333 },
3334
3335 // values, not types
3336 .simple_value,
3337 .@"extern",
3338 .func,
3339 .int,
3340 .err,
3341 .error_union,
3342 .enum_literal,
3343 .enum_tag,
3344 .float,
3345 .ptr,
3346 .slice,
3347 .opt,
3348 .aggregate,
3349 .un,
3350 .bitpack,
3351 .undef,
3352 // memoization, not types
3353 .memoized_call,
3354 => unreachable,
3355 }
3356}
3357
3358/// Recursively walks the type and marks for each subtype how many times it has been seen
3359fn collectSubtypes(ty: Type, pt: Zcu.PerThread, visited: *std.array_hash_map.Auto(Type, u16)) error{OutOfMemory}!void {
3360 const zcu = pt.zcu;
3361 const ip = &zcu.intern_pool;
3362
3363 const gop = try visited.getOrPut(zcu.gpa, ty);
3364 if (gop.found_existing) {
3365 gop.value_ptr.* += 1;
3366 } else {
3367 gop.value_ptr.* = 1;
3368 }
3369
3370 switch (ip.indexToKey(ty.toIntern())) {
3371 .ptr_type => try collectSubtypes(Type.fromInterned(ty.ptrInfo(zcu).child), pt, visited),
3372 .array_type => |array_type| try collectSubtypes(Type.fromInterned(array_type.child), pt, visited),
3373 .vector_type => |vector_type| try collectSubtypes(Type.fromInterned(vector_type.child), pt, visited),
3374 .opt_type => |child| try collectSubtypes(Type.fromInterned(child), pt, visited),
3375 .error_union_type => |error_union_type| {
3376 try collectSubtypes(Type.fromInterned(error_union_type.error_set_type), pt, visited);
3377 if (error_union_type.payload_type != .generic_poison_type) {
3378 try collectSubtypes(Type.fromInterned(error_union_type.payload_type), pt, visited);
3379 }
3380 },
3381 .tuple_type => |tuple| {
3382 for (tuple.types.get(ip)) |field_ty| {
3383 try collectSubtypes(Type.fromInterned(field_ty), pt, visited);
3384 }
3385 },
3386 .func_type => |fn_info| {
3387 const param_types = fn_info.param_types.get(&zcu.intern_pool);
3388 for (param_types) |param_ty| {
3389 if (param_ty != .generic_poison_type) {
3390 try collectSubtypes(Type.fromInterned(param_ty), pt, visited);
3391 }
3392 }
3393
3394 if (fn_info.return_type != .generic_poison_type) {
3395 try collectSubtypes(Type.fromInterned(fn_info.return_type), pt, visited);
3396 }
3397 },
3398 .anyframe_type => |child| try collectSubtypes(Type.fromInterned(child), pt, visited),
3399
3400 // leaf types
3401 .undef,
3402 .inferred_error_set_type,
3403 .error_set_type,
3404 .struct_type,
3405 .union_type,
3406 .opaque_type,
3407 .enum_type,
3408 .spirv_type,
3409 .simple_type,
3410 .int_type,
3411 => {},
3412
3413 // values, not types
3414 .simple_value,
3415 .@"extern",
3416 .func,
3417 .int,
3418 .err,
3419 .error_union,
3420 .enum_literal,
3421 .enum_tag,
3422 .float,
3423 .ptr,
3424 .slice,
3425 .opt,
3426 .aggregate,
3427 .un,
3428 .bitpack,
3429 // memoization, not types
3430 .memoized_call,
3431 => unreachable,
3432 }
3433}
3434
3435fn shouldDedupeType(ty: Type, ctx: *Comparison, pt: Zcu.PerThread) error{OutOfMemory}!Comparison.DedupeEntry {
3436 if (ctx.type_occurrences.get(ty)) |occ| {
3437 if (ctx.type_dedupe_cache.get(ty)) |cached| {
3438 return cached;
3439 }
3440
3441 var discarding: std.Io.Writer.Discarding = .init(&.{});
3442
3443 print(ty, &discarding.writer, pt, null) catch
3444 unreachable; // we are writing into a discarding writer, it should never fail
3445
3446 const type_len: i32 = @intCast(discarding.count);
3447
3448 const placeholder_len: i32 = 1;
3449 const min_saved_bytes: i32 = 20;
3450
3451 const saved_bytes = (type_len - placeholder_len) * (occ - 1);
3452 const max_placeholders = 7; // T to Z
3453 const should_dedupe = saved_bytes >= min_saved_bytes and ctx.placeholder_index < max_placeholders;
3454
3455 const entry: Comparison.DedupeEntry = if (should_dedupe) b: {
3456 ctx.placeholder_index += 1;
3457 break :b .{ .dedupe = .{ .index = ctx.placeholder_index - 1 } };
3458 } else .dont_dedupe;
3459
3460 try ctx.type_dedupe_cache.put(pt.zcu.gpa, ty, entry);
3461
3462 return entry;
3463 } else {
3464 return .{ .dont_dedupe = {} };
3465 }
3466}
3467
3468/// The comparison recursively walks all types given and notes how many times
3469/// each subtype occurs. It then while recursively printing decides for each
3470/// subtype whether to print the type inline or create a placeholder based on
3471/// the subtype length and number of occurences. Placeholders are then found by
3472/// iterating `type_dedupe_cache` which caches the inline/placeholder decisions.
3473pub const Comparison = struct {
3474 type_occurrences: std.array_hash_map.Auto(Type, u16),
3475 type_dedupe_cache: std.array_hash_map.Auto(Type, DedupeEntry),
3476 placeholder_index: u8,
3477
3478 pub const Placeholder = struct {
3479 index: u8,
3480
3481 pub fn format(p: Placeholder, writer: *std.Io.Writer) error{WriteFailed}!void {
3482 return writer.print("{c}", .{p.index + 'T'});
3483 }
3484 };
3485
3486 pub const DedupeEntry = union(enum) {
3487 dont_dedupe: void,
3488 dedupe: Placeholder,
3489 };
3490
3491 pub fn init(types: []const Type, pt: Zcu.PerThread) error{OutOfMemory}!Comparison {
3492 var cmp: Comparison = .{
3493 .type_occurrences = .empty,
3494 .type_dedupe_cache = .empty,
3495 .placeholder_index = 0,
3496 };
3497
3498 errdefer cmp.deinit(pt);
3499
3500 for (types) |ty| {
3501 try collectSubtypes(ty, pt, &cmp.type_occurrences);
3502 }
3503
3504 return cmp;
3505 }
3506
3507 pub fn deinit(cmp: *Comparison, pt: Zcu.PerThread) void {
3508 const gpa = pt.zcu.gpa;
3509 cmp.type_occurrences.deinit(gpa);
3510 cmp.type_dedupe_cache.deinit(gpa);
3511 }
3512
3513 pub fn fmtType(ctx: *Comparison, ty: Type, pt: Zcu.PerThread) Comparison.Formatter {
3514 return .{ .ty = ty, .ctx = ctx, .pt = pt };
3515 }
3516 pub const Formatter = struct {
3517 ty: Type,
3518 ctx: *Comparison,
3519 pt: Zcu.PerThread,
3520
3521 pub fn format(self: Comparison.Formatter, writer: anytype) error{WriteFailed}!void {
3522 print(self.ty, writer, self.pt, self.ctx) catch return error.WriteFailed;
3523 }
3524 };
3525};
3526
3527pub const @"u0": Type = .{ .ip_index = .u0_type };
3528pub const @"u1": Type = .{ .ip_index = .u1_type };
3529pub const @"u8": Type = .{ .ip_index = .u8_type };
3530pub const @"u16": Type = .{ .ip_index = .u16_type };
3531pub const @"u29": Type = .{ .ip_index = .u29_type };
3532pub const @"u32": Type = .{ .ip_index = .u32_type };
3533pub const @"u64": Type = .{ .ip_index = .u64_type };
3534pub const @"u80": Type = .{ .ip_index = .u80_type };
3535pub const @"u128": Type = .{ .ip_index = .u128_type };
3536pub const @"u256": Type = .{ .ip_index = .u256_type };
3537
3538pub const @"i8": Type = .{ .ip_index = .i8_type };
3539pub const @"i16": Type = .{ .ip_index = .i16_type };
3540pub const @"i32": Type = .{ .ip_index = .i32_type };
3541pub const @"i64": Type = .{ .ip_index = .i64_type };
3542pub const @"i128": Type = .{ .ip_index = .i128_type };
3543
3544pub const @"f16": Type = .{ .ip_index = .f16_type };
3545pub const @"f32": Type = .{ .ip_index = .f32_type };
3546pub const @"f64": Type = .{ .ip_index = .f64_type };
3547pub const @"f80": Type = .{ .ip_index = .f80_type };
3548pub const @"f128": Type = .{ .ip_index = .f128_type };
3549
3550pub const @"bool": Type = .{ .ip_index = .bool_type };
3551pub const @"usize": Type = .{ .ip_index = .usize_type };
3552pub const @"isize": Type = .{ .ip_index = .isize_type };
3553pub const @"comptime_int": Type = .{ .ip_index = .comptime_int_type };
3554pub const @"comptime_float": Type = .{ .ip_index = .comptime_float_type };
3555pub const @"void": Type = .{ .ip_index = .void_type };
3556pub const @"type": Type = .{ .ip_index = .type_type };
3557pub const @"anyerror": Type = .{ .ip_index = .anyerror_type };
3558pub const @"anyopaque": Type = .{ .ip_index = .anyopaque_type };
3559pub const @"anyframe": Type = .{ .ip_index = .anyframe_type };
3560pub const @"null": Type = .{ .ip_index = .null_type };
3561pub const @"undefined": Type = .{ .ip_index = .undefined_type };
3562pub const @"noreturn": Type = .{ .ip_index = .noreturn_type };
3563pub const enum_literal: Type = .{ .ip_index = .enum_literal_type };
3564
3565pub const @"c_char": Type = .{ .ip_index = .c_char_type };
3566pub const @"c_short": Type = .{ .ip_index = .c_short_type };
3567pub const @"c_ushort": Type = .{ .ip_index = .c_ushort_type };
3568pub const @"c_int": Type = .{ .ip_index = .c_int_type };
3569pub const @"c_uint": Type = .{ .ip_index = .c_uint_type };
3570pub const @"c_long": Type = .{ .ip_index = .c_long_type };
3571pub const @"c_ulong": Type = .{ .ip_index = .c_ulong_type };
3572pub const @"c_longlong": Type = .{ .ip_index = .c_longlong_type };
3573pub const @"c_ulonglong": Type = .{ .ip_index = .c_ulonglong_type };
3574pub const @"c_longdouble": Type = .{ .ip_index = .c_longdouble_type };
3575
3576pub const ptr_usize: Type = .{ .ip_index = .ptr_usize_type };
3577pub const ptr_const_comptime_int: Type = .{ .ip_index = .ptr_const_comptime_int_type };
3578pub const manyptr_u8: Type = .{ .ip_index = .manyptr_u8_type };
3579pub const manyptr_const_u8: Type = .{ .ip_index = .manyptr_const_u8_type };
3580pub const manyptr_const_u8_sentinel_0: Type = .{ .ip_index = .manyptr_const_u8_sentinel_0_type };
3581pub const slice_const_u8: Type = .{ .ip_index = .slice_const_u8_type };
3582pub const slice_const_u8_sentinel_0: Type = .{ .ip_index = .slice_const_u8_sentinel_0_type };
3583pub const slice_const_slice_const_u8: Type = .{ .ip_index = .slice_const_slice_const_u8_type };
3584pub const slice_const_type: Type = .{ .ip_index = .slice_const_type_type };
3585pub const optional_type: Type = .{ .ip_index = .optional_type_type };
3586pub const optional_noreturn: Type = .{ .ip_index = .optional_noreturn_type };
3587
3588pub const vector_8_i8: Type = .{ .ip_index = .vector_8_i8_type };
3589pub const vector_16_i8: Type = .{ .ip_index = .vector_16_i8_type };
3590pub const vector_32_i8: Type = .{ .ip_index = .vector_32_i8_type };
3591pub const vector_64_i8: Type = .{ .ip_index = .vector_64_i8_type };
3592pub const vector_1_u8: Type = .{ .ip_index = .vector_1_u8_type };
3593pub const vector_2_u8: Type = .{ .ip_index = .vector_2_u8_type };
3594pub const vector_4_u8: Type = .{ .ip_index = .vector_4_u8_type };
3595pub const vector_8_u8: Type = .{ .ip_index = .vector_8_u8_type };
3596pub const vector_16_u8: Type = .{ .ip_index = .vector_16_u8_type };
3597pub const vector_32_u8: Type = .{ .ip_index = .vector_32_u8_type };
3598pub const vector_64_u8: Type = .{ .ip_index = .vector_64_u8_type };
3599pub const vector_2_i16: Type = .{ .ip_index = .vector_2_i16_type };
3600pub const vector_4_i16: Type = .{ .ip_index = .vector_4_i16_type };
3601pub const vector_8_i16: Type = .{ .ip_index = .vector_8_i16_type };
3602pub const vector_16_i16: Type = .{ .ip_index = .vector_16_i16_type };
3603pub const vector_32_i16: Type = .{ .ip_index = .vector_32_i16_type };
3604pub const vector_4_u16: Type = .{ .ip_index = .vector_4_u16_type };
3605pub const vector_8_u16: Type = .{ .ip_index = .vector_8_u16_type };
3606pub const vector_16_u16: Type = .{ .ip_index = .vector_16_u16_type };
3607pub const vector_32_u16: Type = .{ .ip_index = .vector_32_u16_type };
3608pub const vector_2_i32: Type = .{ .ip_index = .vector_2_i32_type };
3609pub const vector_4_i32: Type = .{ .ip_index = .vector_4_i32_type };
3610pub const vector_8_i32: Type = .{ .ip_index = .vector_8_i32_type };
3611pub const vector_16_i32: Type = .{ .ip_index = .vector_16_i32_type };
3612pub const vector_4_u32: Type = .{ .ip_index = .vector_4_u32_type };
3613pub const vector_8_u32: Type = .{ .ip_index = .vector_8_u32_type };
3614pub const vector_16_u32: Type = .{ .ip_index = .vector_16_u32_type };
3615pub const vector_2_i64: Type = .{ .ip_index = .vector_2_i64_type };
3616pub const vector_4_i64: Type = .{ .ip_index = .vector_4_i64_type };
3617pub const vector_8_i64: Type = .{ .ip_index = .vector_8_i64_type };
3618pub const vector_2_u64: Type = .{ .ip_index = .vector_2_u64_type };
3619pub const vector_4_u64: Type = .{ .ip_index = .vector_4_u64_type };
3620pub const vector_8_u64: Type = .{ .ip_index = .vector_8_u64_type };
3621pub const vector_1_u128: Type = .{ .ip_index = .vector_1_u128_type };
3622pub const vector_2_u128: Type = .{ .ip_index = .vector_2_u128_type };
3623pub const vector_1_u256: Type = .{ .ip_index = .vector_1_u256_type };
3624pub const vector_4_f16: Type = .{ .ip_index = .vector_4_f16_type };
3625pub const vector_8_f16: Type = .{ .ip_index = .vector_8_f16_type };
3626pub const vector_16_f16: Type = .{ .ip_index = .vector_16_f16_type };
3627pub const vector_32_f16: Type = .{ .ip_index = .vector_32_f16_type };
3628pub const vector_2_f32: Type = .{ .ip_index = .vector_2_f32_type };
3629pub const vector_4_f32: Type = .{ .ip_index = .vector_4_f32_type };
3630pub const vector_8_f32: Type = .{ .ip_index = .vector_8_f32_type };
3631pub const vector_16_f32: Type = .{ .ip_index = .vector_16_f32_type };
3632pub const vector_2_f64: Type = .{ .ip_index = .vector_2_f64_type };
3633pub const vector_4_f64: Type = .{ .ip_index = .vector_4_f64_type };
3634pub const vector_8_f64: Type = .{ .ip_index = .vector_8_f64_type };
3635
3636pub const empty_tuple: Type = .{ .ip_index = .empty_tuple_type };
3637
3638pub const generic_poison: Type = .{ .ip_index = .generic_poison_type };
3639
3640pub fn smallestUnsignedBits(max: u64) u16 {
3641 return switch (max) {
3642 0 => 0,
3643 else => @as(u16, 1) + std.math.log2_int(u64, max),
3644 };
3645}
3646
3647/// This is only used for comptime asserts. Bump this number when you make a change
3648/// to packed struct layout to find out all the places in the codebase you need to edit!
3649pub const packed_struct_layout_version = 2;
3650
3651fn cTypeAlign(target: *const Target, c_type: Target.CType) Alignment {
3652 return .fromByteUnits(target.cTypeAlignment(c_type).?);
3653}