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