master
   1index: CType.Index,
   2
   3pub const @"void": CType = .{ .index = .void };
   4pub const @"bool": CType = .{ .index = .bool };
   5pub const @"i8": CType = .{ .index = .int8_t };
   6pub const @"u8": CType = .{ .index = .uint8_t };
   7pub const @"i16": CType = .{ .index = .int16_t };
   8pub const @"u16": CType = .{ .index = .uint16_t };
   9pub const @"i32": CType = .{ .index = .int32_t };
  10pub const @"u32": CType = .{ .index = .uint32_t };
  11pub const @"i64": CType = .{ .index = .int64_t };
  12pub const @"u64": CType = .{ .index = .uint64_t };
  13pub const @"i128": CType = .{ .index = .zig_i128 };
  14pub const @"u128": CType = .{ .index = .zig_u128 };
  15pub const @"isize": CType = .{ .index = .intptr_t };
  16pub const @"usize": CType = .{ .index = .uintptr_t };
  17pub const @"f16": CType = .{ .index = .zig_f16 };
  18pub const @"f32": CType = .{ .index = .zig_f32 };
  19pub const @"f64": CType = .{ .index = .zig_f64 };
  20pub const @"f80": CType = .{ .index = .zig_f80 };
  21pub const @"f128": CType = .{ .index = .zig_f128 };
  22
  23pub fn fromPoolIndex(pool_index: usize) CType {
  24    return .{ .index = @enumFromInt(CType.Index.first_pool_index + pool_index) };
  25}
  26
  27pub fn toPoolIndex(ctype: CType) ?u32 {
  28    const pool_index, const is_null =
  29        @subWithOverflow(@intFromEnum(ctype.index), CType.Index.first_pool_index);
  30    return switch (is_null) {
  31        0 => pool_index,
  32        1 => null,
  33    };
  34}
  35
  36pub fn eql(lhs: CType, rhs: CType) bool {
  37    return lhs.index == rhs.index;
  38}
  39
  40pub fn isBool(ctype: CType) bool {
  41    return switch (ctype.index) {
  42        ._Bool, .bool => true,
  43        else => false,
  44    };
  45}
  46
  47pub fn isInteger(ctype: CType) bool {
  48    return switch (ctype.index) {
  49        .char,
  50        .@"signed char",
  51        .short,
  52        .int,
  53        .long,
  54        .@"long long",
  55        .@"unsigned char",
  56        .@"unsigned short",
  57        .@"unsigned int",
  58        .@"unsigned long",
  59        .@"unsigned long long",
  60        .size_t,
  61        .ptrdiff_t,
  62        .uint8_t,
  63        .int8_t,
  64        .uint16_t,
  65        .int16_t,
  66        .uint32_t,
  67        .int32_t,
  68        .uint64_t,
  69        .int64_t,
  70        .uintptr_t,
  71        .intptr_t,
  72        .zig_u128,
  73        .zig_i128,
  74        => true,
  75        else => false,
  76    };
  77}
  78
  79pub fn signedness(ctype: CType, mod: *Module) std.builtin.Signedness {
  80    return switch (ctype.index) {
  81        .char => mod.resolved_target.result.cCharSignedness(),
  82        .@"signed char",
  83        .short,
  84        .int,
  85        .long,
  86        .@"long long",
  87        .ptrdiff_t,
  88        .int8_t,
  89        .int16_t,
  90        .int32_t,
  91        .int64_t,
  92        .intptr_t,
  93        .zig_i128,
  94        => .signed,
  95        .@"unsigned char",
  96        .@"unsigned short",
  97        .@"unsigned int",
  98        .@"unsigned long",
  99        .@"unsigned long long",
 100        .size_t,
 101        .uint8_t,
 102        .uint16_t,
 103        .uint32_t,
 104        .uint64_t,
 105        .uintptr_t,
 106        .zig_u128,
 107        => .unsigned,
 108        else => unreachable,
 109    };
 110}
 111
 112pub fn isFloat(ctype: CType) bool {
 113    return switch (ctype.index) {
 114        .float,
 115        .double,
 116        .@"long double",
 117        .zig_f16,
 118        .zig_f32,
 119        .zig_f64,
 120        .zig_f80,
 121        .zig_f128,
 122        .zig_c_longdouble,
 123        => true,
 124        else => false,
 125    };
 126}
 127
 128pub fn toSigned(ctype: CType) CType {
 129    return switch (ctype.index) {
 130        .char, .@"signed char", .@"unsigned char" => .{ .index = .@"signed char" },
 131        .short, .@"unsigned short" => .{ .index = .short },
 132        .int, .@"unsigned int" => .{ .index = .int },
 133        .long, .@"unsigned long" => .{ .index = .long },
 134        .@"long long", .@"unsigned long long" => .{ .index = .@"long long" },
 135        .size_t, .ptrdiff_t => .{ .index = .ptrdiff_t },
 136        .uint8_t, .int8_t => .{ .index = .int8_t },
 137        .uint16_t, .int16_t => .{ .index = .int16_t },
 138        .uint32_t, .int32_t => .{ .index = .int32_t },
 139        .uint64_t, .int64_t => .{ .index = .int64_t },
 140        .uintptr_t, .intptr_t => .{ .index = .intptr_t },
 141        .zig_u128, .zig_i128 => .{ .index = .zig_i128 },
 142        .float,
 143        .double,
 144        .@"long double",
 145        .zig_f16,
 146        .zig_f32,
 147        .zig_f80,
 148        .zig_f128,
 149        .zig_c_longdouble,
 150        => ctype,
 151        else => unreachable,
 152    };
 153}
 154
 155pub fn toUnsigned(ctype: CType) CType {
 156    return switch (ctype.index) {
 157        .char, .@"signed char", .@"unsigned char" => .{ .index = .@"unsigned char" },
 158        .short, .@"unsigned short" => .{ .index = .@"unsigned short" },
 159        .int, .@"unsigned int" => .{ .index = .@"unsigned int" },
 160        .long, .@"unsigned long" => .{ .index = .@"unsigned long" },
 161        .@"long long", .@"unsigned long long" => .{ .index = .@"unsigned long long" },
 162        .size_t, .ptrdiff_t => .{ .index = .size_t },
 163        .uint8_t, .int8_t => .{ .index = .uint8_t },
 164        .uint16_t, .int16_t => .{ .index = .uint16_t },
 165        .uint32_t, .int32_t => .{ .index = .uint32_t },
 166        .uint64_t, .int64_t => .{ .index = .uint64_t },
 167        .uintptr_t, .intptr_t => .{ .index = .uintptr_t },
 168        .zig_u128, .zig_i128 => .{ .index = .zig_u128 },
 169        else => unreachable,
 170    };
 171}
 172
 173pub fn toSignedness(ctype: CType, s: std.builtin.Signedness) CType {
 174    return switch (s) {
 175        .unsigned => ctype.toUnsigned(),
 176        .signed => ctype.toSigned(),
 177    };
 178}
 179
 180pub fn isAnyChar(ctype: CType) bool {
 181    return switch (ctype.index) {
 182        else => false,
 183        .char, .@"signed char", .@"unsigned char", .uint8_t, .int8_t => true,
 184    };
 185}
 186
 187pub fn isString(ctype: CType, pool: *const Pool) bool {
 188    return info: switch (ctype.info(pool)) {
 189        .basic, .fwd_decl, .aggregate, .function => false,
 190        .pointer => |pointer_info| pointer_info.elem_ctype.isAnyChar(),
 191        .aligned => |aligned_info| continue :info aligned_info.ctype.info(pool),
 192        .array, .vector => |sequence_info| sequence_info.elem_type.isAnyChar(),
 193    };
 194}
 195
 196pub fn isNonString(ctype: CType, pool: *const Pool) bool {
 197    var allow_pointer = true;
 198    return info: switch (ctype.info(pool)) {
 199        .basic, .fwd_decl, .aggregate, .function => false,
 200        .pointer => |pointer_info| allow_pointer and pointer_info.nonstring,
 201        .aligned => |aligned_info| continue :info aligned_info.ctype.info(pool),
 202        .array, .vector => |sequence_info| sequence_info.nonstring or {
 203            allow_pointer = false;
 204            continue :info sequence_info.elem_ctype.info(pool);
 205        },
 206    };
 207}
 208
 209pub fn getStandardDefineAbbrev(ctype: CType) ?[]const u8 {
 210    return switch (ctype.index) {
 211        .char => "CHAR",
 212        .@"signed char" => "SCHAR",
 213        .short => "SHRT",
 214        .int => "INT",
 215        .long => "LONG",
 216        .@"long long" => "LLONG",
 217        .@"unsigned char" => "UCHAR",
 218        .@"unsigned short" => "USHRT",
 219        .@"unsigned int" => "UINT",
 220        .@"unsigned long" => "ULONG",
 221        .@"unsigned long long" => "ULLONG",
 222        .float => "FLT",
 223        .double => "DBL",
 224        .@"long double" => "LDBL",
 225        .size_t => "SIZE",
 226        .ptrdiff_t => "PTRDIFF",
 227        .uint8_t => "UINT8",
 228        .int8_t => "INT8",
 229        .uint16_t => "UINT16",
 230        .int16_t => "INT16",
 231        .uint32_t => "UINT32",
 232        .int32_t => "INT32",
 233        .uint64_t => "UINT64",
 234        .int64_t => "INT64",
 235        .uintptr_t => "UINTPTR",
 236        .intptr_t => "INTPTR",
 237        else => null,
 238    };
 239}
 240
 241pub fn renderLiteralPrefix(ctype: CType, w: *Writer, kind: Kind, pool: *const Pool) Writer.Error!void {
 242    switch (ctype.info(pool)) {
 243        .basic => |basic_info| switch (basic_info) {
 244            .void => unreachable,
 245            ._Bool,
 246            .char,
 247            .@"signed char",
 248            .short,
 249            .@"unsigned short",
 250            .bool,
 251            .size_t,
 252            .ptrdiff_t,
 253            .uintptr_t,
 254            .intptr_t,
 255            => switch (kind) {
 256                else => try w.print("({s})", .{@tagName(basic_info)}),
 257                .global => {},
 258            },
 259            .int,
 260            .long,
 261            .@"long long",
 262            .@"unsigned char",
 263            .@"unsigned int",
 264            .@"unsigned long",
 265            .@"unsigned long long",
 266            .float,
 267            .double,
 268            .@"long double",
 269            => {},
 270            .uint8_t,
 271            .int8_t,
 272            .uint16_t,
 273            .int16_t,
 274            .uint32_t,
 275            .int32_t,
 276            .uint64_t,
 277            .int64_t,
 278            => try w.print("{s}_C(", .{ctype.getStandardDefineAbbrev().?}),
 279            .zig_u128,
 280            .zig_i128,
 281            .zig_f16,
 282            .zig_f32,
 283            .zig_f64,
 284            .zig_f80,
 285            .zig_f128,
 286            .zig_c_longdouble,
 287            => try w.print("zig_{s}_{s}(", .{
 288                switch (kind) {
 289                    else => "make",
 290                    .global => "init",
 291                },
 292                @tagName(basic_info)["zig_".len..],
 293            }),
 294            .va_list => unreachable,
 295            _ => unreachable,
 296        },
 297        .array, .vector => try w.writeByte('{'),
 298        else => unreachable,
 299    }
 300}
 301
 302pub fn renderLiteralSuffix(ctype: CType, w: *Writer, pool: *const Pool) Writer.Error!void {
 303    switch (ctype.info(pool)) {
 304        .basic => |basic_info| switch (basic_info) {
 305            .void => unreachable,
 306            ._Bool => {},
 307            .char,
 308            .@"signed char",
 309            .short,
 310            .int,
 311            => {},
 312            .long => try w.writeByte('l'),
 313            .@"long long" => try w.writeAll("ll"),
 314            .@"unsigned char",
 315            .@"unsigned short",
 316            .@"unsigned int",
 317            => try w.writeByte('u'),
 318            .@"unsigned long",
 319            .size_t,
 320            .uintptr_t,
 321            => try w.writeAll("ul"),
 322            .@"unsigned long long" => try w.writeAll("ull"),
 323            .float => try w.writeByte('f'),
 324            .double => {},
 325            .@"long double" => try w.writeByte('l'),
 326            .bool,
 327            .ptrdiff_t,
 328            .intptr_t,
 329            => {},
 330            .uint8_t,
 331            .int8_t,
 332            .uint16_t,
 333            .int16_t,
 334            .uint32_t,
 335            .int32_t,
 336            .uint64_t,
 337            .int64_t,
 338            .zig_u128,
 339            .zig_i128,
 340            .zig_f16,
 341            .zig_f32,
 342            .zig_f64,
 343            .zig_f80,
 344            .zig_f128,
 345            .zig_c_longdouble,
 346            => try w.writeByte(')'),
 347            .va_list => unreachable,
 348            _ => unreachable,
 349        },
 350        .array, .vector => try w.writeByte('}'),
 351        else => unreachable,
 352    }
 353}
 354
 355pub fn floatActiveBits(ctype: CType, mod: *Module) u16 {
 356    const target = &mod.resolved_target.result;
 357    return switch (ctype.index) {
 358        .float => target.cTypeBitSize(.float),
 359        .double => target.cTypeBitSize(.double),
 360        .@"long double", .zig_c_longdouble => target.cTypeBitSize(.longdouble),
 361        .zig_f16 => 16,
 362        .zig_f32 => 32,
 363        .zig_f64 => 64,
 364        .zig_f80 => 80,
 365        .zig_f128 => 128,
 366        else => unreachable,
 367    };
 368}
 369
 370pub fn byteSize(ctype: CType, pool: *const Pool, mod: *Module) u64 {
 371    const target = &mod.resolved_target.result;
 372    return switch (ctype.info(pool)) {
 373        .basic => |basic_info| switch (basic_info) {
 374            .void => 0,
 375            .char, .@"signed char", ._Bool, .@"unsigned char", .bool, .uint8_t, .int8_t => 1,
 376            .short => target.cTypeByteSize(.short),
 377            .int => target.cTypeByteSize(.int),
 378            .long => target.cTypeByteSize(.long),
 379            .@"long long" => target.cTypeByteSize(.longlong),
 380            .@"unsigned short" => target.cTypeByteSize(.ushort),
 381            .@"unsigned int" => target.cTypeByteSize(.uint),
 382            .@"unsigned long" => target.cTypeByteSize(.ulong),
 383            .@"unsigned long long" => target.cTypeByteSize(.ulonglong),
 384            .float => target.cTypeByteSize(.float),
 385            .double => target.cTypeByteSize(.double),
 386            .@"long double" => target.cTypeByteSize(.longdouble),
 387            .size_t,
 388            .ptrdiff_t,
 389            .uintptr_t,
 390            .intptr_t,
 391            => @divExact(target.ptrBitWidth(), 8),
 392            .uint16_t, .int16_t, .zig_f16 => 2,
 393            .uint32_t, .int32_t, .zig_f32 => 4,
 394            .uint64_t, .int64_t, .zig_f64 => 8,
 395            .zig_u128, .zig_i128, .zig_f128 => 16,
 396            .zig_f80 => if (target.cTypeBitSize(.longdouble) == 80)
 397                target.cTypeByteSize(.longdouble)
 398            else
 399                16,
 400            .zig_c_longdouble => target.cTypeByteSize(.longdouble),
 401            .va_list => unreachable,
 402            _ => unreachable,
 403        },
 404        .pointer => @divExact(target.ptrBitWidth(), 8),
 405        .array, .vector => |sequence_info| sequence_info.elem_ctype.byteSize(pool, mod) * sequence_info.len,
 406        else => unreachable,
 407    };
 408}
 409
 410pub fn info(ctype: CType, pool: *const Pool) Info {
 411    const pool_index = ctype.toPoolIndex() orelse return .{ .basic = ctype.index };
 412    const item = pool.items.get(pool_index);
 413    switch (item.tag) {
 414        .basic => unreachable,
 415        .pointer => return .{ .pointer = .{
 416            .elem_ctype = .{ .index = @enumFromInt(item.data) },
 417        } },
 418        .pointer_const => return .{ .pointer = .{
 419            .elem_ctype = .{ .index = @enumFromInt(item.data) },
 420            .@"const" = true,
 421        } },
 422        .pointer_volatile => return .{ .pointer = .{
 423            .elem_ctype = .{ .index = @enumFromInt(item.data) },
 424            .@"volatile" = true,
 425        } },
 426        .pointer_const_volatile => return .{ .pointer = .{
 427            .elem_ctype = .{ .index = @enumFromInt(item.data) },
 428            .@"const" = true,
 429            .@"volatile" = true,
 430        } },
 431        .aligned => {
 432            const extra = pool.getExtra(Pool.Aligned, item.data);
 433            return .{ .aligned = .{
 434                .ctype = .{ .index = extra.ctype },
 435                .alignas = extra.flags.alignas,
 436            } };
 437        },
 438        .array_small => {
 439            const extra = pool.getExtra(Pool.SequenceSmall, item.data);
 440            return .{ .array = .{
 441                .elem_ctype = .{ .index = extra.elem_ctype },
 442                .len = extra.len,
 443            } };
 444        },
 445        .array_large => {
 446            const extra = pool.getExtra(Pool.SequenceLarge, item.data);
 447            return .{ .array = .{
 448                .elem_ctype = .{ .index = extra.elem_ctype },
 449                .len = extra.len(),
 450            } };
 451        },
 452        .vector => {
 453            const extra = pool.getExtra(Pool.SequenceSmall, item.data);
 454            return .{ .vector = .{
 455                .elem_ctype = .{ .index = extra.elem_ctype },
 456                .len = extra.len,
 457            } };
 458        },
 459        .nonstring => {
 460            var child_info = info(.{ .index = @enumFromInt(item.data) }, pool);
 461            switch (child_info) {
 462                else => unreachable,
 463                .pointer => |*pointer_info| pointer_info.nonstring = true,
 464                .array, .vector => |*sequence_info| sequence_info.nonstring = true,
 465            }
 466            return child_info;
 467        },
 468        .fwd_decl_struct_anon => {
 469            const extra_trail = pool.getExtraTrail(Pool.FwdDeclAnon, item.data);
 470            return .{ .fwd_decl = .{
 471                .tag = .@"struct",
 472                .name = .{ .anon = .{
 473                    .extra_index = extra_trail.trail.extra_index,
 474                    .len = extra_trail.extra.fields_len,
 475                } },
 476            } };
 477        },
 478        .fwd_decl_union_anon => {
 479            const extra_trail = pool.getExtraTrail(Pool.FwdDeclAnon, item.data);
 480            return .{ .fwd_decl = .{
 481                .tag = .@"union",
 482                .name = .{ .anon = .{
 483                    .extra_index = extra_trail.trail.extra_index,
 484                    .len = extra_trail.extra.fields_len,
 485                } },
 486            } };
 487        },
 488        .fwd_decl_struct => return .{ .fwd_decl = .{
 489            .tag = .@"struct",
 490            .name = .{ .index = @enumFromInt(item.data) },
 491        } },
 492        .fwd_decl_union => return .{ .fwd_decl = .{
 493            .tag = .@"union",
 494            .name = .{ .index = @enumFromInt(item.data) },
 495        } },
 496        .aggregate_struct_anon => {
 497            const extra_trail = pool.getExtraTrail(Pool.AggregateAnon, item.data);
 498            return .{ .aggregate = .{
 499                .tag = .@"struct",
 500                .name = .{ .anon = .{
 501                    .index = extra_trail.extra.index,
 502                    .id = extra_trail.extra.id,
 503                } },
 504                .fields = .{
 505                    .extra_index = extra_trail.trail.extra_index,
 506                    .len = extra_trail.extra.fields_len,
 507                },
 508            } };
 509        },
 510        .aggregate_union_anon => {
 511            const extra_trail = pool.getExtraTrail(Pool.AggregateAnon, item.data);
 512            return .{ .aggregate = .{
 513                .tag = .@"union",
 514                .name = .{ .anon = .{
 515                    .index = extra_trail.extra.index,
 516                    .id = extra_trail.extra.id,
 517                } },
 518                .fields = .{
 519                    .extra_index = extra_trail.trail.extra_index,
 520                    .len = extra_trail.extra.fields_len,
 521                },
 522            } };
 523        },
 524        .aggregate_struct_packed_anon => {
 525            const extra_trail = pool.getExtraTrail(Pool.AggregateAnon, item.data);
 526            return .{ .aggregate = .{
 527                .tag = .@"struct",
 528                .@"packed" = true,
 529                .name = .{ .anon = .{
 530                    .index = extra_trail.extra.index,
 531                    .id = extra_trail.extra.id,
 532                } },
 533                .fields = .{
 534                    .extra_index = extra_trail.trail.extra_index,
 535                    .len = extra_trail.extra.fields_len,
 536                },
 537            } };
 538        },
 539        .aggregate_union_packed_anon => {
 540            const extra_trail = pool.getExtraTrail(Pool.AggregateAnon, item.data);
 541            return .{ .aggregate = .{
 542                .tag = .@"union",
 543                .@"packed" = true,
 544                .name = .{ .anon = .{
 545                    .index = extra_trail.extra.index,
 546                    .id = extra_trail.extra.id,
 547                } },
 548                .fields = .{
 549                    .extra_index = extra_trail.trail.extra_index,
 550                    .len = extra_trail.extra.fields_len,
 551                },
 552            } };
 553        },
 554        .aggregate_struct => {
 555            const extra_trail = pool.getExtraTrail(Pool.Aggregate, item.data);
 556            return .{ .aggregate = .{
 557                .tag = .@"struct",
 558                .name = .{ .fwd_decl = .{ .index = extra_trail.extra.fwd_decl } },
 559                .fields = .{
 560                    .extra_index = extra_trail.trail.extra_index,
 561                    .len = extra_trail.extra.fields_len,
 562                },
 563            } };
 564        },
 565        .aggregate_union => {
 566            const extra_trail = pool.getExtraTrail(Pool.Aggregate, item.data);
 567            return .{ .aggregate = .{
 568                .tag = .@"union",
 569                .name = .{ .fwd_decl = .{ .index = extra_trail.extra.fwd_decl } },
 570                .fields = .{
 571                    .extra_index = extra_trail.trail.extra_index,
 572                    .len = extra_trail.extra.fields_len,
 573                },
 574            } };
 575        },
 576        .aggregate_struct_packed => {
 577            const extra_trail = pool.getExtraTrail(Pool.Aggregate, item.data);
 578            return .{ .aggregate = .{
 579                .tag = .@"struct",
 580                .@"packed" = true,
 581                .name = .{ .fwd_decl = .{ .index = extra_trail.extra.fwd_decl } },
 582                .fields = .{
 583                    .extra_index = extra_trail.trail.extra_index,
 584                    .len = extra_trail.extra.fields_len,
 585                },
 586            } };
 587        },
 588        .aggregate_union_packed => {
 589            const extra_trail = pool.getExtraTrail(Pool.Aggregate, item.data);
 590            return .{ .aggregate = .{
 591                .tag = .@"union",
 592                .@"packed" = true,
 593                .name = .{ .fwd_decl = .{ .index = extra_trail.extra.fwd_decl } },
 594                .fields = .{
 595                    .extra_index = extra_trail.trail.extra_index,
 596                    .len = extra_trail.extra.fields_len,
 597                },
 598            } };
 599        },
 600        .function => {
 601            const extra_trail = pool.getExtraTrail(Pool.Function, item.data);
 602            return .{ .function = .{
 603                .return_ctype = .{ .index = extra_trail.extra.return_ctype },
 604                .param_ctypes = .{
 605                    .extra_index = extra_trail.trail.extra_index,
 606                    .len = extra_trail.extra.param_ctypes_len,
 607                },
 608                .varargs = false,
 609            } };
 610        },
 611        .function_varargs => {
 612            const extra_trail = pool.getExtraTrail(Pool.Function, item.data);
 613            return .{ .function = .{
 614                .return_ctype = .{ .index = extra_trail.extra.return_ctype },
 615                .param_ctypes = .{
 616                    .extra_index = extra_trail.trail.extra_index,
 617                    .len = extra_trail.extra.param_ctypes_len,
 618                },
 619                .varargs = true,
 620            } };
 621        },
 622    }
 623}
 624
 625pub fn hash(ctype: CType, pool: *const Pool) Pool.Map.Hash {
 626    return if (ctype.toPoolIndex()) |pool_index|
 627        pool.map.entries.items(.hash)[pool_index]
 628    else
 629        CType.Index.basic_hashes[@intFromEnum(ctype.index)];
 630}
 631
 632fn toForward(ctype: CType, pool: *Pool, allocator: std.mem.Allocator) !CType {
 633    return switch (ctype.info(pool)) {
 634        .basic, .pointer, .fwd_decl => ctype,
 635        .aligned => |aligned_info| pool.getAligned(allocator, .{
 636            .ctype = try aligned_info.ctype.toForward(pool, allocator),
 637            .alignas = aligned_info.alignas,
 638        }),
 639        .array => |array_info| pool.getArray(allocator, .{
 640            .elem_ctype = try array_info.elem_ctype.toForward(pool, allocator),
 641            .len = array_info.len,
 642            .nonstring = array_info.nonstring,
 643        }),
 644        .vector => |vector_info| pool.getVector(allocator, .{
 645            .elem_ctype = try vector_info.elem_ctype.toForward(pool, allocator),
 646            .len = vector_info.len,
 647            .nonstring = vector_info.nonstring,
 648        }),
 649        .aggregate => |aggregate_info| switch (aggregate_info.name) {
 650            .anon => ctype,
 651            .fwd_decl => |fwd_decl| fwd_decl,
 652        },
 653        .function => unreachable,
 654    };
 655}
 656
 657const Index = enum(u32) {
 658    void,
 659
 660    // C basic types
 661    char,
 662
 663    @"signed char",
 664    short,
 665    int,
 666    long,
 667    @"long long",
 668
 669    _Bool,
 670    @"unsigned char",
 671    @"unsigned short",
 672    @"unsigned int",
 673    @"unsigned long",
 674    @"unsigned long long",
 675
 676    float,
 677    double,
 678    @"long double",
 679
 680    // C header types
 681    //  - stdbool.h
 682    bool,
 683    //  - stddef.h
 684    size_t,
 685    ptrdiff_t,
 686    //  - stdint.h
 687    uint8_t,
 688    int8_t,
 689    uint16_t,
 690    int16_t,
 691    uint32_t,
 692    int32_t,
 693    uint64_t,
 694    int64_t,
 695    uintptr_t,
 696    intptr_t,
 697    //  - stdarg.h
 698    va_list,
 699
 700    // zig.h types
 701    zig_u128,
 702    zig_i128,
 703    zig_f16,
 704    zig_f32,
 705    zig_f64,
 706    zig_f80,
 707    zig_f128,
 708    zig_c_longdouble,
 709
 710    _,
 711
 712    const first_pool_index: u32 = @typeInfo(CType.Index).@"enum".fields.len;
 713    const basic_hashes = init: {
 714        @setEvalBranchQuota(1_600);
 715        var basic_hashes_init: [first_pool_index]Pool.Map.Hash = undefined;
 716        for (&basic_hashes_init, 0..) |*basic_hash, index| {
 717            const ctype_index: CType.Index = @enumFromInt(index);
 718            var hasher = Pool.Hasher.init;
 719            hasher.update(@intFromEnum(ctype_index));
 720            basic_hash.* = hasher.final(.basic);
 721        }
 722        break :init basic_hashes_init;
 723    };
 724};
 725
 726const Slice = struct {
 727    extra_index: Pool.ExtraIndex,
 728    len: u32,
 729
 730    pub fn at(slice: CType.Slice, index: usize, pool: *const Pool) CType {
 731        var extra: Pool.ExtraTrail = .{ .extra_index = slice.extra_index };
 732        return .{ .index = extra.next(slice.len, CType.Index, pool)[index] };
 733    }
 734};
 735
 736pub const Kind = enum {
 737    forward,
 738    forward_parameter,
 739    complete,
 740    global,
 741    parameter,
 742
 743    pub fn isForward(kind: Kind) bool {
 744        return switch (kind) {
 745            .forward, .forward_parameter => true,
 746            .complete, .global, .parameter => false,
 747        };
 748    }
 749
 750    pub fn isParameter(kind: Kind) bool {
 751        return switch (kind) {
 752            .forward_parameter, .parameter => true,
 753            .forward, .complete, .global => false,
 754        };
 755    }
 756
 757    pub fn asParameter(kind: Kind) Kind {
 758        return switch (kind) {
 759            .forward, .forward_parameter => .forward_parameter,
 760            .complete, .parameter, .global => .parameter,
 761        };
 762    }
 763
 764    pub fn noParameter(kind: Kind) Kind {
 765        return switch (kind) {
 766            .forward, .forward_parameter => .forward,
 767            .complete, .parameter => .complete,
 768            .global => .global,
 769        };
 770    }
 771
 772    pub fn asComplete(kind: Kind) Kind {
 773        return switch (kind) {
 774            .forward, .complete => .complete,
 775            .forward_parameter, .parameter => .parameter,
 776            .global => .global,
 777        };
 778    }
 779};
 780
 781pub const Info = union(enum) {
 782    basic: CType.Index,
 783    pointer: Pointer,
 784    aligned: Aligned,
 785    array: Sequence,
 786    vector: Sequence,
 787    fwd_decl: FwdDecl,
 788    aggregate: Aggregate,
 789    function: Function,
 790
 791    const Tag = @typeInfo(Info).@"union".tag_type.?;
 792
 793    pub const Pointer = struct {
 794        elem_ctype: CType,
 795        @"const": bool = false,
 796        @"volatile": bool = false,
 797        nonstring: bool = false,
 798
 799        fn tag(pointer_info: Pointer) Pool.Tag {
 800            return @enumFromInt(@intFromEnum(Pool.Tag.pointer) +
 801                @as(u2, @bitCast(packed struct(u2) {
 802                    @"const": bool,
 803                    @"volatile": bool,
 804                }{
 805                    .@"const" = pointer_info.@"const",
 806                    .@"volatile" = pointer_info.@"volatile",
 807                })));
 808        }
 809    };
 810
 811    pub const Aligned = struct {
 812        ctype: CType,
 813        alignas: AlignAs,
 814    };
 815
 816    pub const Sequence = struct {
 817        elem_ctype: CType,
 818        len: u64,
 819        nonstring: bool = false,
 820    };
 821
 822    pub const AggregateTag = enum { @"enum", @"struct", @"union" };
 823
 824    pub const Field = struct {
 825        name: Pool.String,
 826        ctype: CType,
 827        alignas: AlignAs,
 828
 829        pub const Slice = struct {
 830            extra_index: Pool.ExtraIndex,
 831            len: u32,
 832
 833            pub fn at(slice: Field.Slice, index: usize, pool: *const Pool) Field {
 834                assert(index < slice.len);
 835                const extra = pool.getExtra(Pool.Field, @intCast(slice.extra_index +
 836                    index * @typeInfo(Pool.Field).@"struct".fields.len));
 837                return .{
 838                    .name = .{ .index = extra.name },
 839                    .ctype = .{ .index = extra.ctype },
 840                    .alignas = extra.flags.alignas,
 841                };
 842            }
 843
 844            fn eqlAdapted(
 845                lhs_slice: Field.Slice,
 846                lhs_pool: *const Pool,
 847                rhs_slice: Field.Slice,
 848                rhs_pool: *const Pool,
 849                pool_adapter: anytype,
 850            ) bool {
 851                if (lhs_slice.len != rhs_slice.len) return false;
 852                for (0..lhs_slice.len) |index| {
 853                    if (!lhs_slice.at(index, lhs_pool).eqlAdapted(
 854                        lhs_pool,
 855                        rhs_slice.at(index, rhs_pool),
 856                        rhs_pool,
 857                        pool_adapter,
 858                    )) return false;
 859                }
 860                return true;
 861            }
 862        };
 863
 864        fn eqlAdapted(
 865            lhs_field: Field,
 866            lhs_pool: *const Pool,
 867            rhs_field: Field,
 868            rhs_pool: *const Pool,
 869            pool_adapter: anytype,
 870        ) bool {
 871            if (!std.meta.eql(lhs_field.alignas, rhs_field.alignas)) return false;
 872            if (!pool_adapter.eql(lhs_field.ctype, rhs_field.ctype)) return false;
 873            return if (lhs_field.name.toPoolSlice(lhs_pool)) |lhs_name|
 874                if (rhs_field.name.toPoolSlice(rhs_pool)) |rhs_name|
 875                    std.mem.eql(u8, lhs_name, rhs_name)
 876                else
 877                    false
 878            else
 879                lhs_field.name.index == rhs_field.name.index;
 880        }
 881    };
 882
 883    pub const FwdDecl = struct {
 884        tag: AggregateTag,
 885        name: union(enum) {
 886            anon: Field.Slice,
 887            index: InternPool.Index,
 888        },
 889    };
 890
 891    pub const Aggregate = struct {
 892        tag: AggregateTag,
 893        @"packed": bool = false,
 894        name: union(enum) {
 895            anon: struct {
 896                index: InternPool.Index,
 897                id: u32,
 898            },
 899            fwd_decl: CType,
 900        },
 901        fields: Field.Slice,
 902    };
 903
 904    pub const Function = struct {
 905        return_ctype: CType,
 906        param_ctypes: CType.Slice,
 907        varargs: bool = false,
 908    };
 909
 910    pub fn eqlAdapted(
 911        lhs_info: Info,
 912        lhs_pool: *const Pool,
 913        rhs_ctype: CType,
 914        rhs_pool: *const Pool,
 915        pool_adapter: anytype,
 916    ) bool {
 917        const rhs_info = rhs_ctype.info(rhs_pool);
 918        if (@as(Info.Tag, lhs_info) != @as(Info.Tag, rhs_info)) return false;
 919        return switch (lhs_info) {
 920            .basic => |lhs_basic_info| lhs_basic_info == rhs_info.basic,
 921            .pointer => |lhs_pointer_info| lhs_pointer_info.@"const" == rhs_info.pointer.@"const" and
 922                lhs_pointer_info.@"volatile" == rhs_info.pointer.@"volatile" and
 923                lhs_pointer_info.nonstring == rhs_info.pointer.nonstring and
 924                pool_adapter.eql(lhs_pointer_info.elem_ctype, rhs_info.pointer.elem_ctype),
 925            .aligned => |lhs_aligned_info| std.meta.eql(lhs_aligned_info.alignas, rhs_info.aligned.alignas) and
 926                pool_adapter.eql(lhs_aligned_info.ctype, rhs_info.aligned.ctype),
 927            .array => |lhs_array_info| lhs_array_info.len == rhs_info.array.len and
 928                lhs_array_info.nonstring == rhs_info.array.nonstring and
 929                pool_adapter.eql(lhs_array_info.elem_ctype, rhs_info.array.elem_ctype),
 930            .vector => |lhs_vector_info| lhs_vector_info.len == rhs_info.vector.len and
 931                lhs_vector_info.nonstring == rhs_info.vector.nonstring and
 932                pool_adapter.eql(lhs_vector_info.elem_ctype, rhs_info.vector.elem_ctype),
 933            .fwd_decl => |lhs_fwd_decl_info| lhs_fwd_decl_info.tag == rhs_info.fwd_decl.tag and
 934                switch (lhs_fwd_decl_info.name) {
 935                    .anon => |lhs_anon| rhs_info.fwd_decl.name == .anon and lhs_anon.eqlAdapted(
 936                        lhs_pool,
 937                        rhs_info.fwd_decl.name.anon,
 938                        rhs_pool,
 939                        pool_adapter,
 940                    ),
 941                    .index => |lhs_index| rhs_info.fwd_decl.name == .index and
 942                        lhs_index == rhs_info.fwd_decl.name.index,
 943                },
 944            .aggregate => |lhs_aggregate_info| lhs_aggregate_info.tag == rhs_info.aggregate.tag and
 945                lhs_aggregate_info.@"packed" == rhs_info.aggregate.@"packed" and
 946                switch (lhs_aggregate_info.name) {
 947                    .anon => |lhs_anon| rhs_info.aggregate.name == .anon and
 948                        lhs_anon.index == rhs_info.aggregate.name.anon.index and
 949                        lhs_anon.id == rhs_info.aggregate.name.anon.id,
 950                    .fwd_decl => |lhs_fwd_decl| rhs_info.aggregate.name == .fwd_decl and
 951                        pool_adapter.eql(lhs_fwd_decl, rhs_info.aggregate.name.fwd_decl),
 952                } and lhs_aggregate_info.fields.eqlAdapted(
 953                lhs_pool,
 954                rhs_info.aggregate.fields,
 955                rhs_pool,
 956                pool_adapter,
 957            ),
 958            .function => |lhs_function_info| lhs_function_info.param_ctypes.len ==
 959                rhs_info.function.param_ctypes.len and
 960                pool_adapter.eql(lhs_function_info.return_ctype, rhs_info.function.return_ctype) and
 961                for (0..lhs_function_info.param_ctypes.len) |param_index| {
 962                    if (!pool_adapter.eql(
 963                        lhs_function_info.param_ctypes.at(param_index, lhs_pool),
 964                        rhs_info.function.param_ctypes.at(param_index, rhs_pool),
 965                    )) break false;
 966                } else true,
 967        };
 968    }
 969};
 970
 971pub const Pool = struct {
 972    map: Map,
 973    items: std.MultiArrayList(Item),
 974    extra: std.ArrayList(u32),
 975
 976    string_map: Map,
 977    string_indices: std.ArrayList(u32),
 978    string_bytes: std.ArrayList(u8),
 979
 980    const Map = std.AutoArrayHashMapUnmanaged(void, void);
 981
 982    pub const String = struct {
 983        index: String.Index,
 984
 985        const FormatData = struct { string: String, pool: *const Pool };
 986        fn format(data: FormatData, writer: *Writer) Writer.Error!void {
 987            if (data.string.toSlice(data.pool)) |slice|
 988                try writer.writeAll(slice)
 989            else
 990                try writer.print("f{d}", .{@intFromEnum(data.string.index)});
 991        }
 992        pub fn fmt(str: String, pool: *const Pool) std.fmt.Alt(FormatData, format) {
 993            return .{ .data = .{ .string = str, .pool = pool } };
 994        }
 995
 996        fn fromUnnamed(index: u31) String {
 997            return .{ .index = @enumFromInt(index) };
 998        }
 999
1000        fn isNamed(str: String) bool {
1001            return @intFromEnum(str.index) >= String.Index.first_named_index;
1002        }
1003
1004        pub fn toSlice(str: String, pool: *const Pool) ?[]const u8 {
1005            return str.toPoolSlice(pool) orelse if (str.isNamed()) @tagName(str.index) else null;
1006        }
1007
1008        fn toPoolSlice(str: String, pool: *const Pool) ?[]const u8 {
1009            if (str.toPoolIndex()) |pool_index| {
1010                const start = pool.string_indices.items[pool_index + 0];
1011                const end = pool.string_indices.items[pool_index + 1];
1012                return pool.string_bytes.items[start..end];
1013            } else return null;
1014        }
1015
1016        fn fromPoolIndex(pool_index: usize) String {
1017            return .{ .index = @enumFromInt(String.Index.first_pool_index + pool_index) };
1018        }
1019
1020        fn toPoolIndex(str: String) ?u32 {
1021            const pool_index, const is_null =
1022                @subWithOverflow(@intFromEnum(str.index), String.Index.first_pool_index);
1023            return switch (is_null) {
1024                0 => pool_index,
1025                1 => null,
1026            };
1027        }
1028
1029        const Index = enum(u32) {
1030            array = first_named_index,
1031            @"error",
1032            is_null,
1033            len,
1034            payload,
1035            ptr,
1036            tag,
1037            _,
1038
1039            const first_named_index: u32 = 1 << 31;
1040            const first_pool_index: u32 = first_named_index + @typeInfo(String.Index).@"enum".fields.len;
1041        };
1042
1043        const Adapter = struct {
1044            pool: *const Pool,
1045            pub fn hash(_: @This(), slice: []const u8) Map.Hash {
1046                return @truncate(Hasher.Impl.hash(1, slice));
1047            }
1048            pub fn eql(string_adapter: @This(), lhs_slice: []const u8, _: void, rhs_index: usize) bool {
1049                const rhs_string = String.fromPoolIndex(rhs_index);
1050                const rhs_slice = rhs_string.toPoolSlice(string_adapter.pool).?;
1051                return std.mem.eql(u8, lhs_slice, rhs_slice);
1052            }
1053        };
1054    };
1055
1056    pub const empty: Pool = .{
1057        .map = .{},
1058        .items = .{},
1059        .extra = .{},
1060
1061        .string_map = .{},
1062        .string_indices = .{},
1063        .string_bytes = .{},
1064    };
1065
1066    pub fn init(pool: *Pool, allocator: std.mem.Allocator) !void {
1067        if (pool.string_indices.items.len == 0)
1068            try pool.string_indices.append(allocator, 0);
1069    }
1070
1071    pub fn deinit(pool: *Pool, allocator: std.mem.Allocator) void {
1072        pool.map.deinit(allocator);
1073        pool.items.deinit(allocator);
1074        pool.extra.deinit(allocator);
1075
1076        pool.string_map.deinit(allocator);
1077        pool.string_indices.deinit(allocator);
1078        pool.string_bytes.deinit(allocator);
1079
1080        pool.* = undefined;
1081    }
1082
1083    pub fn move(pool: *Pool) Pool {
1084        defer pool.* = empty;
1085        return pool.*;
1086    }
1087
1088    pub fn clearRetainingCapacity(pool: *Pool) void {
1089        pool.map.clearRetainingCapacity();
1090        pool.items.shrinkRetainingCapacity(0);
1091        pool.extra.clearRetainingCapacity();
1092
1093        pool.string_map.clearRetainingCapacity();
1094        pool.string_indices.shrinkRetainingCapacity(1);
1095        pool.string_bytes.clearRetainingCapacity();
1096    }
1097
1098    pub fn freeUnusedCapacity(pool: *Pool, allocator: std.mem.Allocator) void {
1099        pool.map.shrinkAndFree(allocator, pool.map.count());
1100        pool.items.shrinkAndFree(allocator, pool.items.len);
1101        pool.extra.shrinkAndFree(allocator, pool.extra.items.len);
1102
1103        pool.string_map.shrinkAndFree(allocator, pool.string_map.count());
1104        pool.string_indices.shrinkAndFree(allocator, pool.string_indices.items.len);
1105        pool.string_bytes.shrinkAndFree(allocator, pool.string_bytes.items.len);
1106    }
1107
1108    pub fn getPointer(pool: *Pool, allocator: std.mem.Allocator, pointer_info: Info.Pointer) !CType {
1109        var hasher = Hasher.init;
1110        hasher.update(pointer_info.elem_ctype.hash(pool));
1111        return pool.getNonString(allocator, try pool.tagData(
1112            allocator,
1113            hasher,
1114            pointer_info.tag(),
1115            @intFromEnum(pointer_info.elem_ctype.index),
1116        ), pointer_info.nonstring);
1117    }
1118
1119    pub fn getAligned(pool: *Pool, allocator: std.mem.Allocator, aligned_info: Info.Aligned) !CType {
1120        return pool.tagExtra(allocator, .aligned, Aligned, .{
1121            .ctype = aligned_info.ctype.index,
1122            .flags = .{ .alignas = aligned_info.alignas },
1123        });
1124    }
1125
1126    pub fn getArray(pool: *Pool, allocator: std.mem.Allocator, array_info: Info.Sequence) !CType {
1127        return pool.getNonString(allocator, if (std.math.cast(u32, array_info.len)) |small_len|
1128            try pool.tagExtra(allocator, .array_small, SequenceSmall, .{
1129                .elem_ctype = array_info.elem_ctype.index,
1130                .len = small_len,
1131            })
1132        else
1133            try pool.tagExtra(allocator, .array_large, SequenceLarge, .{
1134                .elem_ctype = array_info.elem_ctype.index,
1135                .len_lo = @truncate(array_info.len >> 0),
1136                .len_hi = @truncate(array_info.len >> 32),
1137            }), array_info.nonstring);
1138    }
1139
1140    pub fn getVector(pool: *Pool, allocator: std.mem.Allocator, vector_info: Info.Sequence) !CType {
1141        return pool.getNonString(allocator, try pool.tagExtra(allocator, .vector, SequenceSmall, .{
1142            .elem_ctype = vector_info.elem_ctype.index,
1143            .len = @intCast(vector_info.len),
1144        }), vector_info.nonstring);
1145    }
1146
1147    pub fn getNonString(
1148        pool: *Pool,
1149        allocator: std.mem.Allocator,
1150        child_ctype: CType,
1151        nonstring: bool,
1152    ) !CType {
1153        if (!nonstring) return child_ctype;
1154        var hasher = Hasher.init;
1155        hasher.update(child_ctype.hash(pool));
1156        return pool.tagData(allocator, hasher, .nonstring, @intFromEnum(child_ctype.index));
1157    }
1158
1159    pub fn getFwdDecl(
1160        pool: *Pool,
1161        allocator: std.mem.Allocator,
1162        fwd_decl_info: struct {
1163            tag: Info.AggregateTag,
1164            name: union(enum) {
1165                anon: []const Info.Field,
1166                index: InternPool.Index,
1167            },
1168        },
1169    ) !CType {
1170        var hasher = Hasher.init;
1171        switch (fwd_decl_info.name) {
1172            .anon => |fields| {
1173                const ExpectedContents = [32]CType;
1174                var stack align(@max(
1175                    @alignOf(std.heap.StackFallbackAllocator(0)),
1176                    @alignOf(ExpectedContents),
1177                )) = std.heap.stackFallback(@sizeOf(ExpectedContents), allocator);
1178                const stack_allocator = stack.get();
1179                const field_ctypes = try stack_allocator.alloc(CType, fields.len);
1180                defer stack_allocator.free(field_ctypes);
1181                for (field_ctypes, fields) |*field_ctype, field|
1182                    field_ctype.* = try field.ctype.toForward(pool, allocator);
1183                const extra: FwdDeclAnon = .{ .fields_len = @intCast(fields.len) };
1184                const extra_index = try pool.addExtra(
1185                    allocator,
1186                    FwdDeclAnon,
1187                    extra,
1188                    fields.len * @typeInfo(Field).@"struct".fields.len,
1189                );
1190                for (fields, field_ctypes) |field, field_ctype| pool.addHashedExtraAssumeCapacity(
1191                    &hasher,
1192                    Field,
1193                    .{
1194                        .name = field.name.index,
1195                        .ctype = field_ctype.index,
1196                        .flags = .{ .alignas = field.alignas },
1197                    },
1198                );
1199                hasher.updateExtra(FwdDeclAnon, extra, pool);
1200                return pool.tagTrailingExtra(allocator, hasher, switch (fwd_decl_info.tag) {
1201                    .@"struct" => .fwd_decl_struct_anon,
1202                    .@"union" => .fwd_decl_union_anon,
1203                    .@"enum" => unreachable,
1204                }, extra_index);
1205            },
1206            .index => |index| {
1207                hasher.update(index);
1208                return pool.tagData(allocator, hasher, switch (fwd_decl_info.tag) {
1209                    .@"struct" => .fwd_decl_struct,
1210                    .@"union" => .fwd_decl_union,
1211                    .@"enum" => unreachable,
1212                }, @intFromEnum(index));
1213            },
1214        }
1215    }
1216
1217    pub fn getAggregate(
1218        pool: *Pool,
1219        allocator: std.mem.Allocator,
1220        aggregate_info: struct {
1221            tag: Info.AggregateTag,
1222            @"packed": bool = false,
1223            name: union(enum) {
1224                anon: struct {
1225                    index: InternPool.Index,
1226                    id: u32,
1227                },
1228                fwd_decl: CType,
1229            },
1230            fields: []const Info.Field,
1231        },
1232    ) !CType {
1233        var hasher = Hasher.init;
1234        switch (aggregate_info.name) {
1235            .anon => |anon| {
1236                const extra: AggregateAnon = .{
1237                    .index = anon.index,
1238                    .id = anon.id,
1239                    .fields_len = @intCast(aggregate_info.fields.len),
1240                };
1241                const extra_index = try pool.addExtra(
1242                    allocator,
1243                    AggregateAnon,
1244                    extra,
1245                    aggregate_info.fields.len * @typeInfo(Field).@"struct".fields.len,
1246                );
1247                for (aggregate_info.fields) |field| pool.addHashedExtraAssumeCapacity(&hasher, Field, .{
1248                    .name = field.name.index,
1249                    .ctype = field.ctype.index,
1250                    .flags = .{ .alignas = field.alignas },
1251                });
1252                hasher.updateExtra(AggregateAnon, extra, pool);
1253                return pool.tagTrailingExtra(allocator, hasher, switch (aggregate_info.tag) {
1254                    .@"struct" => switch (aggregate_info.@"packed") {
1255                        false => .aggregate_struct_anon,
1256                        true => .aggregate_struct_packed_anon,
1257                    },
1258                    .@"union" => switch (aggregate_info.@"packed") {
1259                        false => .aggregate_union_anon,
1260                        true => .aggregate_union_packed_anon,
1261                    },
1262                    .@"enum" => unreachable,
1263                }, extra_index);
1264            },
1265            .fwd_decl => |fwd_decl| {
1266                const extra: Aggregate = .{
1267                    .fwd_decl = fwd_decl.index,
1268                    .fields_len = @intCast(aggregate_info.fields.len),
1269                };
1270                const extra_index = try pool.addExtra(
1271                    allocator,
1272                    Aggregate,
1273                    extra,
1274                    aggregate_info.fields.len * @typeInfo(Field).@"struct".fields.len,
1275                );
1276                for (aggregate_info.fields) |field| pool.addHashedExtraAssumeCapacity(&hasher, Field, .{
1277                    .name = field.name.index,
1278                    .ctype = field.ctype.index,
1279                    .flags = .{ .alignas = field.alignas },
1280                });
1281                hasher.updateExtra(Aggregate, extra, pool);
1282                return pool.tagTrailingExtra(allocator, hasher, switch (aggregate_info.tag) {
1283                    .@"struct" => switch (aggregate_info.@"packed") {
1284                        false => .aggregate_struct,
1285                        true => .aggregate_struct_packed,
1286                    },
1287                    .@"union" => switch (aggregate_info.@"packed") {
1288                        false => .aggregate_union,
1289                        true => .aggregate_union_packed,
1290                    },
1291                    .@"enum" => unreachable,
1292                }, extra_index);
1293            },
1294        }
1295    }
1296
1297    pub fn getFunction(
1298        pool: *Pool,
1299        allocator: std.mem.Allocator,
1300        function_info: struct {
1301            return_ctype: CType,
1302            param_ctypes: []const CType,
1303            varargs: bool = false,
1304        },
1305    ) !CType {
1306        var hasher = Hasher.init;
1307        const extra: Function = .{
1308            .return_ctype = function_info.return_ctype.index,
1309            .param_ctypes_len = @intCast(function_info.param_ctypes.len),
1310        };
1311        const extra_index = try pool.addExtra(allocator, Function, extra, function_info.param_ctypes.len);
1312        for (function_info.param_ctypes) |param_ctype| {
1313            hasher.update(param_ctype.hash(pool));
1314            pool.extra.appendAssumeCapacity(@intFromEnum(param_ctype.index));
1315        }
1316        hasher.updateExtra(Function, extra, pool);
1317        return pool.tagTrailingExtra(allocator, hasher, switch (function_info.varargs) {
1318            false => .function,
1319            true => .function_varargs,
1320        }, extra_index);
1321    }
1322
1323    pub fn fromFields(
1324        pool: *Pool,
1325        allocator: std.mem.Allocator,
1326        tag: Info.AggregateTag,
1327        fields: []Info.Field,
1328        kind: Kind,
1329    ) !CType {
1330        sortFields(fields);
1331        const fwd_decl = try pool.getFwdDecl(allocator, .{
1332            .tag = tag,
1333            .name = .{ .anon = fields },
1334        });
1335        return if (kind.isForward()) fwd_decl else pool.getAggregate(allocator, .{
1336            .tag = tag,
1337            .name = .{ .fwd_decl = fwd_decl },
1338            .fields = fields,
1339        });
1340    }
1341
1342    pub fn fromIntInfo(
1343        pool: *Pool,
1344        allocator: std.mem.Allocator,
1345        int_info: std.builtin.Type.Int,
1346        mod: *Module,
1347        kind: Kind,
1348    ) !CType {
1349        switch (int_info.bits) {
1350            0 => return .void,
1351            1...8 => switch (int_info.signedness) {
1352                .signed => return .i8,
1353                .unsigned => return .u8,
1354            },
1355            9...16 => switch (int_info.signedness) {
1356                .signed => return .i16,
1357                .unsigned => return .u16,
1358            },
1359            17...32 => switch (int_info.signedness) {
1360                .signed => return .i32,
1361                .unsigned => return .u32,
1362            },
1363            33...64 => switch (int_info.signedness) {
1364                .signed => return .i64,
1365                .unsigned => return .u64,
1366            },
1367            65...128 => switch (int_info.signedness) {
1368                .signed => return .i128,
1369                .unsigned => return .u128,
1370            },
1371            else => {
1372                const target = &mod.resolved_target.result;
1373                const abi_align_bytes = std.zig.target.intAlignment(target, int_info.bits);
1374                const limb_ctype = try pool.fromIntInfo(allocator, .{
1375                    .signedness = .unsigned,
1376                    .bits = @intCast(abi_align_bytes * 8),
1377                }, mod, kind.noParameter());
1378                const array_ctype = try pool.getArray(allocator, .{
1379                    .len = @divExact(std.zig.target.intByteSize(target, int_info.bits), abi_align_bytes),
1380                    .elem_ctype = limb_ctype,
1381                    .nonstring = limb_ctype.isAnyChar(),
1382                });
1383                if (!kind.isParameter()) return array_ctype;
1384                var fields = [_]Info.Field{
1385                    .{
1386                        .name = .{ .index = .array },
1387                        .ctype = array_ctype,
1388                        .alignas = AlignAs.fromAbiAlignment(.fromByteUnits(abi_align_bytes)),
1389                    },
1390                };
1391                return pool.fromFields(allocator, .@"struct", &fields, kind);
1392            },
1393        }
1394    }
1395
1396    pub fn fromType(
1397        pool: *Pool,
1398        allocator: std.mem.Allocator,
1399        scratch: *std.ArrayList(u32),
1400        ty: Type,
1401        pt: Zcu.PerThread,
1402        mod: *Module,
1403        kind: Kind,
1404    ) !CType {
1405        const ip = &pt.zcu.intern_pool;
1406        const zcu = pt.zcu;
1407        switch (ty.toIntern()) {
1408            .u0_type,
1409            .i0_type,
1410            .anyopaque_type,
1411            .void_type,
1412            .empty_tuple_type,
1413            .type_type,
1414            .comptime_int_type,
1415            .comptime_float_type,
1416            .null_type,
1417            .undefined_type,
1418            .enum_literal_type,
1419            .optional_type_type,
1420            .manyptr_const_type_type,
1421            .slice_const_type_type,
1422            => return .void,
1423            .u1_type, .u8_type => return .u8,
1424            .i8_type => return .i8,
1425            .u16_type => return .u16,
1426            .i16_type => return .i16,
1427            .u29_type, .u32_type => return .u32,
1428            .i32_type => return .i32,
1429            .u64_type => return .u64,
1430            .i64_type => return .i64,
1431            .u80_type, .u128_type => return .u128,
1432            .i128_type => return .i128,
1433            .u256_type => return pool.fromIntInfo(allocator, .{
1434                .signedness = .unsigned,
1435                .bits = 256,
1436            }, mod, kind),
1437            .usize_type => return .usize,
1438            .isize_type => return .isize,
1439            .c_char_type => return .{ .index = .char },
1440            .c_short_type => return .{ .index = .short },
1441            .c_ushort_type => return .{ .index = .@"unsigned short" },
1442            .c_int_type => return .{ .index = .int },
1443            .c_uint_type => return .{ .index = .@"unsigned int" },
1444            .c_long_type => return .{ .index = .long },
1445            .c_ulong_type => return .{ .index = .@"unsigned long" },
1446            .c_longlong_type => return .{ .index = .@"long long" },
1447            .c_ulonglong_type => return .{ .index = .@"unsigned long long" },
1448            .c_longdouble_type => return .{ .index = .@"long double" },
1449            .f16_type => return .f16,
1450            .f32_type => return .f32,
1451            .f64_type => return .f64,
1452            .f80_type => return .f80,
1453            .f128_type => return .f128,
1454            .bool_type, .optional_noreturn_type => return .bool,
1455            .noreturn_type,
1456            .anyframe_type,
1457            .generic_poison_type,
1458            => unreachable,
1459            .anyerror_type,
1460            .anyerror_void_error_union_type,
1461            .adhoc_inferred_error_set_type,
1462            => return pool.fromIntInfo(allocator, .{
1463                .signedness = .unsigned,
1464                .bits = pt.zcu.errorSetBits(),
1465            }, mod, kind),
1466
1467            .ptr_usize_type => return pool.getPointer(allocator, .{
1468                .elem_ctype = .usize,
1469            }),
1470            .ptr_const_comptime_int_type => return pool.getPointer(allocator, .{
1471                .elem_ctype = .void,
1472                .@"const" = true,
1473            }),
1474            .manyptr_u8_type => return pool.getPointer(allocator, .{
1475                .elem_ctype = .u8,
1476                .nonstring = true,
1477            }),
1478            .manyptr_const_u8_type => return pool.getPointer(allocator, .{
1479                .elem_ctype = .u8,
1480                .@"const" = true,
1481                .nonstring = true,
1482            }),
1483            .manyptr_const_u8_sentinel_0_type => return pool.getPointer(allocator, .{
1484                .elem_ctype = .u8,
1485                .@"const" = true,
1486            }),
1487            .slice_const_u8_type => {
1488                const target = &mod.resolved_target.result;
1489                var fields = [_]Info.Field{
1490                    .{
1491                        .name = .{ .index = .ptr },
1492                        .ctype = try pool.getPointer(allocator, .{
1493                            .elem_ctype = .u8,
1494                            .@"const" = true,
1495                            .nonstring = true,
1496                        }),
1497                        .alignas = AlignAs.fromAbiAlignment(Type.ptrAbiAlignment(target)),
1498                    },
1499                    .{
1500                        .name = .{ .index = .len },
1501                        .ctype = .usize,
1502                        .alignas = AlignAs.fromAbiAlignment(
1503                            .fromByteUnits(std.zig.target.intAlignment(target, target.ptrBitWidth())),
1504                        ),
1505                    },
1506                };
1507                return pool.fromFields(allocator, .@"struct", &fields, kind);
1508            },
1509            .slice_const_u8_sentinel_0_type => {
1510                const target = &mod.resolved_target.result;
1511                var fields = [_]Info.Field{
1512                    .{
1513                        .name = .{ .index = .ptr },
1514                        .ctype = try pool.getPointer(allocator, .{
1515                            .elem_ctype = .u8,
1516                            .@"const" = true,
1517                        }),
1518                        .alignas = AlignAs.fromAbiAlignment(Type.ptrAbiAlignment(target)),
1519                    },
1520                    .{
1521                        .name = .{ .index = .len },
1522                        .ctype = .usize,
1523                        .alignas = AlignAs.fromAbiAlignment(
1524                            .fromByteUnits(std.zig.target.intAlignment(target, target.ptrBitWidth())),
1525                        ),
1526                    },
1527                };
1528                return pool.fromFields(allocator, .@"struct", &fields, kind);
1529            },
1530
1531            .manyptr_const_slice_const_u8_type => {
1532                const target = &mod.resolved_target.result;
1533                var fields: [2]Info.Field = .{
1534                    .{
1535                        .name = .{ .index = .ptr },
1536                        .ctype = try pool.getPointer(allocator, .{
1537                            .elem_ctype = .u8,
1538                            .@"const" = true,
1539                            .nonstring = true,
1540                        }),
1541                        .alignas = AlignAs.fromAbiAlignment(Type.ptrAbiAlignment(target)),
1542                    },
1543                    .{
1544                        .name = .{ .index = .len },
1545                        .ctype = .usize,
1546                        .alignas = AlignAs.fromAbiAlignment(
1547                            .fromByteUnits(std.zig.target.intAlignment(target, target.ptrBitWidth())),
1548                        ),
1549                    },
1550                };
1551                const slice_const_u8 = try pool.fromFields(allocator, .@"struct", &fields, kind);
1552                return pool.getPointer(allocator, .{
1553                    .elem_ctype = slice_const_u8,
1554                    .@"const" = true,
1555                });
1556            },
1557            .slice_const_slice_const_u8_type => {
1558                const target = &mod.resolved_target.result;
1559                var fields: [2]Info.Field = .{
1560                    .{
1561                        .name = .{ .index = .ptr },
1562                        .ctype = try pool.getPointer(allocator, .{
1563                            .elem_ctype = .u8,
1564                            .@"const" = true,
1565                            .nonstring = true,
1566                        }),
1567                        .alignas = AlignAs.fromAbiAlignment(Type.ptrAbiAlignment(target)),
1568                    },
1569                    .{
1570                        .name = .{ .index = .len },
1571                        .ctype = .usize,
1572                        .alignas = AlignAs.fromAbiAlignment(
1573                            .fromByteUnits(std.zig.target.intAlignment(target, target.ptrBitWidth())),
1574                        ),
1575                    },
1576                };
1577                const slice_const_u8 = try pool.fromFields(allocator, .@"struct", &fields, .forward);
1578                fields = .{
1579                    .{
1580                        .name = .{ .index = .ptr },
1581                        .ctype = try pool.getPointer(allocator, .{
1582                            .elem_ctype = slice_const_u8,
1583                            .@"const" = true,
1584                        }),
1585                        .alignas = AlignAs.fromAbiAlignment(Type.ptrAbiAlignment(target)),
1586                    },
1587                    .{
1588                        .name = .{ .index = .len },
1589                        .ctype = .usize,
1590                        .alignas = AlignAs.fromAbiAlignment(
1591                            .fromByteUnits(std.zig.target.intAlignment(target, target.ptrBitWidth())),
1592                        ),
1593                    },
1594                };
1595                return pool.fromFields(allocator, .@"struct", &fields, kind);
1596            },
1597
1598            .vector_8_i8_type => {
1599                const vector_ctype = try pool.getVector(allocator, .{
1600                    .elem_ctype = .i8,
1601                    .len = 8,
1602                    .nonstring = true,
1603                });
1604                if (!kind.isParameter()) return vector_ctype;
1605                var fields = [_]Info.Field{
1606                    .{
1607                        .name = .{ .index = .array },
1608                        .ctype = vector_ctype,
1609                        .alignas = AlignAs.fromAbiAlignment(Type.i8.abiAlignment(zcu)),
1610                    },
1611                };
1612                return pool.fromFields(allocator, .@"struct", &fields, kind);
1613            },
1614            .vector_16_i8_type => {
1615                const vector_ctype = try pool.getVector(allocator, .{
1616                    .elem_ctype = .i8,
1617                    .len = 16,
1618                    .nonstring = true,
1619                });
1620                if (!kind.isParameter()) return vector_ctype;
1621                var fields = [_]Info.Field{
1622                    .{
1623                        .name = .{ .index = .array },
1624                        .ctype = vector_ctype,
1625                        .alignas = AlignAs.fromAbiAlignment(Type.i8.abiAlignment(zcu)),
1626                    },
1627                };
1628                return pool.fromFields(allocator, .@"struct", &fields, kind);
1629            },
1630            .vector_32_i8_type => {
1631                const vector_ctype = try pool.getVector(allocator, .{
1632                    .elem_ctype = .i8,
1633                    .len = 32,
1634                    .nonstring = true,
1635                });
1636                if (!kind.isParameter()) return vector_ctype;
1637                var fields = [_]Info.Field{
1638                    .{
1639                        .name = .{ .index = .array },
1640                        .ctype = vector_ctype,
1641                        .alignas = AlignAs.fromAbiAlignment(Type.i8.abiAlignment(zcu)),
1642                    },
1643                };
1644                return pool.fromFields(allocator, .@"struct", &fields, kind);
1645            },
1646            .vector_64_i8_type => {
1647                const vector_ctype = try pool.getVector(allocator, .{
1648                    .elem_ctype = .i8,
1649                    .len = 64,
1650                    .nonstring = true,
1651                });
1652                if (!kind.isParameter()) return vector_ctype;
1653                var fields = [_]Info.Field{
1654                    .{
1655                        .name = .{ .index = .array },
1656                        .ctype = vector_ctype,
1657                        .alignas = AlignAs.fromAbiAlignment(Type.i8.abiAlignment(zcu)),
1658                    },
1659                };
1660                return pool.fromFields(allocator, .@"struct", &fields, kind);
1661            },
1662            .vector_1_u8_type => {
1663                const vector_ctype = try pool.getVector(allocator, .{
1664                    .elem_ctype = .u8,
1665                    .len = 1,
1666                    .nonstring = true,
1667                });
1668                if (!kind.isParameter()) return vector_ctype;
1669                var fields = [_]Info.Field{
1670                    .{
1671                        .name = .{ .index = .array },
1672                        .ctype = vector_ctype,
1673                        .alignas = AlignAs.fromAbiAlignment(Type.u8.abiAlignment(zcu)),
1674                    },
1675                };
1676                return pool.fromFields(allocator, .@"struct", &fields, kind);
1677            },
1678            .vector_2_u8_type => {
1679                const vector_ctype = try pool.getVector(allocator, .{
1680                    .elem_ctype = .u8,
1681                    .len = 2,
1682                    .nonstring = true,
1683                });
1684                if (!kind.isParameter()) return vector_ctype;
1685                var fields = [_]Info.Field{
1686                    .{
1687                        .name = .{ .index = .array },
1688                        .ctype = vector_ctype,
1689                        .alignas = AlignAs.fromAbiAlignment(Type.u8.abiAlignment(zcu)),
1690                    },
1691                };
1692                return pool.fromFields(allocator, .@"struct", &fields, kind);
1693            },
1694            .vector_4_u8_type => {
1695                const vector_ctype = try pool.getVector(allocator, .{
1696                    .elem_ctype = .u8,
1697                    .len = 4,
1698                    .nonstring = true,
1699                });
1700                if (!kind.isParameter()) return vector_ctype;
1701                var fields = [_]Info.Field{
1702                    .{
1703                        .name = .{ .index = .array },
1704                        .ctype = vector_ctype,
1705                        .alignas = AlignAs.fromAbiAlignment(Type.u8.abiAlignment(zcu)),
1706                    },
1707                };
1708                return pool.fromFields(allocator, .@"struct", &fields, kind);
1709            },
1710            .vector_8_u8_type => {
1711                const vector_ctype = try pool.getVector(allocator, .{
1712                    .elem_ctype = .u8,
1713                    .len = 8,
1714                    .nonstring = true,
1715                });
1716                if (!kind.isParameter()) return vector_ctype;
1717                var fields = [_]Info.Field{
1718                    .{
1719                        .name = .{ .index = .array },
1720                        .ctype = vector_ctype,
1721                        .alignas = AlignAs.fromAbiAlignment(Type.u8.abiAlignment(zcu)),
1722                    },
1723                };
1724                return pool.fromFields(allocator, .@"struct", &fields, kind);
1725            },
1726            .vector_16_u8_type => {
1727                const vector_ctype = try pool.getVector(allocator, .{
1728                    .elem_ctype = .u8,
1729                    .len = 16,
1730                    .nonstring = true,
1731                });
1732                if (!kind.isParameter()) return vector_ctype;
1733                var fields = [_]Info.Field{
1734                    .{
1735                        .name = .{ .index = .array },
1736                        .ctype = vector_ctype,
1737                        .alignas = AlignAs.fromAbiAlignment(Type.u8.abiAlignment(zcu)),
1738                    },
1739                };
1740                return pool.fromFields(allocator, .@"struct", &fields, kind);
1741            },
1742            .vector_32_u8_type => {
1743                const vector_ctype = try pool.getVector(allocator, .{
1744                    .elem_ctype = .u8,
1745                    .len = 32,
1746                    .nonstring = true,
1747                });
1748                if (!kind.isParameter()) return vector_ctype;
1749                var fields = [_]Info.Field{
1750                    .{
1751                        .name = .{ .index = .array },
1752                        .ctype = vector_ctype,
1753                        .alignas = AlignAs.fromAbiAlignment(Type.u8.abiAlignment(zcu)),
1754                    },
1755                };
1756                return pool.fromFields(allocator, .@"struct", &fields, kind);
1757            },
1758            .vector_64_u8_type => {
1759                const vector_ctype = try pool.getVector(allocator, .{
1760                    .elem_ctype = .u8,
1761                    .len = 64,
1762                    .nonstring = true,
1763                });
1764                if (!kind.isParameter()) return vector_ctype;
1765                var fields = [_]Info.Field{
1766                    .{
1767                        .name = .{ .index = .array },
1768                        .ctype = vector_ctype,
1769                        .alignas = AlignAs.fromAbiAlignment(Type.u8.abiAlignment(zcu)),
1770                    },
1771                };
1772                return pool.fromFields(allocator, .@"struct", &fields, kind);
1773            },
1774            .vector_2_i16_type => {
1775                const vector_ctype = try pool.getVector(allocator, .{
1776                    .elem_ctype = .i16,
1777                    .len = 2,
1778                });
1779                if (!kind.isParameter()) return vector_ctype;
1780                var fields = [_]Info.Field{
1781                    .{
1782                        .name = .{ .index = .array },
1783                        .ctype = vector_ctype,
1784                        .alignas = AlignAs.fromAbiAlignment(Type.i16.abiAlignment(zcu)),
1785                    },
1786                };
1787                return pool.fromFields(allocator, .@"struct", &fields, kind);
1788            },
1789            .vector_4_i16_type => {
1790                const vector_ctype = try pool.getVector(allocator, .{
1791                    .elem_ctype = .i16,
1792                    .len = 4,
1793                });
1794                if (!kind.isParameter()) return vector_ctype;
1795                var fields = [_]Info.Field{
1796                    .{
1797                        .name = .{ .index = .array },
1798                        .ctype = vector_ctype,
1799                        .alignas = AlignAs.fromAbiAlignment(Type.i16.abiAlignment(zcu)),
1800                    },
1801                };
1802                return pool.fromFields(allocator, .@"struct", &fields, kind);
1803            },
1804            .vector_8_i16_type => {
1805                const vector_ctype = try pool.getVector(allocator, .{
1806                    .elem_ctype = .i16,
1807                    .len = 8,
1808                });
1809                if (!kind.isParameter()) return vector_ctype;
1810                var fields = [_]Info.Field{
1811                    .{
1812                        .name = .{ .index = .array },
1813                        .ctype = vector_ctype,
1814                        .alignas = AlignAs.fromAbiAlignment(Type.i16.abiAlignment(zcu)),
1815                    },
1816                };
1817                return pool.fromFields(allocator, .@"struct", &fields, kind);
1818            },
1819            .vector_16_i16_type => {
1820                const vector_ctype = try pool.getVector(allocator, .{
1821                    .elem_ctype = .i16,
1822                    .len = 16,
1823                });
1824                if (!kind.isParameter()) return vector_ctype;
1825                var fields = [_]Info.Field{
1826                    .{
1827                        .name = .{ .index = .array },
1828                        .ctype = vector_ctype,
1829                        .alignas = AlignAs.fromAbiAlignment(Type.i16.abiAlignment(zcu)),
1830                    },
1831                };
1832                return pool.fromFields(allocator, .@"struct", &fields, kind);
1833            },
1834            .vector_32_i16_type => {
1835                const vector_ctype = try pool.getVector(allocator, .{
1836                    .elem_ctype = .i16,
1837                    .len = 32,
1838                });
1839                if (!kind.isParameter()) return vector_ctype;
1840                var fields = [_]Info.Field{
1841                    .{
1842                        .name = .{ .index = .array },
1843                        .ctype = vector_ctype,
1844                        .alignas = AlignAs.fromAbiAlignment(Type.i16.abiAlignment(zcu)),
1845                    },
1846                };
1847                return pool.fromFields(allocator, .@"struct", &fields, kind);
1848            },
1849            .vector_4_u16_type => {
1850                const vector_ctype = try pool.getVector(allocator, .{
1851                    .elem_ctype = .u16,
1852                    .len = 4,
1853                });
1854                if (!kind.isParameter()) return vector_ctype;
1855                var fields = [_]Info.Field{
1856                    .{
1857                        .name = .{ .index = .array },
1858                        .ctype = vector_ctype,
1859                        .alignas = AlignAs.fromAbiAlignment(Type.u16.abiAlignment(zcu)),
1860                    },
1861                };
1862                return pool.fromFields(allocator, .@"struct", &fields, kind);
1863            },
1864            .vector_8_u16_type => {
1865                const vector_ctype = try pool.getVector(allocator, .{
1866                    .elem_ctype = .u16,
1867                    .len = 8,
1868                });
1869                if (!kind.isParameter()) return vector_ctype;
1870                var fields = [_]Info.Field{
1871                    .{
1872                        .name = .{ .index = .array },
1873                        .ctype = vector_ctype,
1874                        .alignas = AlignAs.fromAbiAlignment(Type.u16.abiAlignment(zcu)),
1875                    },
1876                };
1877                return pool.fromFields(allocator, .@"struct", &fields, kind);
1878            },
1879            .vector_16_u16_type => {
1880                const vector_ctype = try pool.getVector(allocator, .{
1881                    .elem_ctype = .u16,
1882                    .len = 16,
1883                });
1884                if (!kind.isParameter()) return vector_ctype;
1885                var fields = [_]Info.Field{
1886                    .{
1887                        .name = .{ .index = .array },
1888                        .ctype = vector_ctype,
1889                        .alignas = AlignAs.fromAbiAlignment(Type.u16.abiAlignment(zcu)),
1890                    },
1891                };
1892                return pool.fromFields(allocator, .@"struct", &fields, kind);
1893            },
1894            .vector_32_u16_type => {
1895                const vector_ctype = try pool.getVector(allocator, .{
1896                    .elem_ctype = .u16,
1897                    .len = 32,
1898                });
1899                if (!kind.isParameter()) return vector_ctype;
1900                var fields = [_]Info.Field{
1901                    .{
1902                        .name = .{ .index = .array },
1903                        .ctype = vector_ctype,
1904                        .alignas = AlignAs.fromAbiAlignment(Type.u16.abiAlignment(zcu)),
1905                    },
1906                };
1907                return pool.fromFields(allocator, .@"struct", &fields, kind);
1908            },
1909            .vector_2_i32_type => {
1910                const vector_ctype = try pool.getVector(allocator, .{
1911                    .elem_ctype = .i32,
1912                    .len = 2,
1913                });
1914                if (!kind.isParameter()) return vector_ctype;
1915                var fields = [_]Info.Field{
1916                    .{
1917                        .name = .{ .index = .array },
1918                        .ctype = vector_ctype,
1919                        .alignas = AlignAs.fromAbiAlignment(Type.i32.abiAlignment(zcu)),
1920                    },
1921                };
1922                return pool.fromFields(allocator, .@"struct", &fields, kind);
1923            },
1924            .vector_4_i32_type => {
1925                const vector_ctype = try pool.getVector(allocator, .{
1926                    .elem_ctype = .i32,
1927                    .len = 4,
1928                });
1929                if (!kind.isParameter()) return vector_ctype;
1930                var fields = [_]Info.Field{
1931                    .{
1932                        .name = .{ .index = .array },
1933                        .ctype = vector_ctype,
1934                        .alignas = AlignAs.fromAbiAlignment(Type.i32.abiAlignment(zcu)),
1935                    },
1936                };
1937                return pool.fromFields(allocator, .@"struct", &fields, kind);
1938            },
1939            .vector_8_i32_type => {
1940                const vector_ctype = try pool.getVector(allocator, .{
1941                    .elem_ctype = .i32,
1942                    .len = 8,
1943                });
1944                if (!kind.isParameter()) return vector_ctype;
1945                var fields = [_]Info.Field{
1946                    .{
1947                        .name = .{ .index = .array },
1948                        .ctype = vector_ctype,
1949                        .alignas = AlignAs.fromAbiAlignment(Type.i32.abiAlignment(zcu)),
1950                    },
1951                };
1952                return pool.fromFields(allocator, .@"struct", &fields, kind);
1953            },
1954            .vector_16_i32_type => {
1955                const vector_ctype = try pool.getVector(allocator, .{
1956                    .elem_ctype = .i32,
1957                    .len = 16,
1958                });
1959                if (!kind.isParameter()) return vector_ctype;
1960                var fields = [_]Info.Field{
1961                    .{
1962                        .name = .{ .index = .array },
1963                        .ctype = vector_ctype,
1964                        .alignas = AlignAs.fromAbiAlignment(Type.i32.abiAlignment(zcu)),
1965                    },
1966                };
1967                return pool.fromFields(allocator, .@"struct", &fields, kind);
1968            },
1969            .vector_4_u32_type => {
1970                const vector_ctype = try pool.getVector(allocator, .{
1971                    .elem_ctype = .u32,
1972                    .len = 4,
1973                });
1974                if (!kind.isParameter()) return vector_ctype;
1975                var fields = [_]Info.Field{
1976                    .{
1977                        .name = .{ .index = .array },
1978                        .ctype = vector_ctype,
1979                        .alignas = AlignAs.fromAbiAlignment(Type.u32.abiAlignment(zcu)),
1980                    },
1981                };
1982                return pool.fromFields(allocator, .@"struct", &fields, kind);
1983            },
1984            .vector_8_u32_type => {
1985                const vector_ctype = try pool.getVector(allocator, .{
1986                    .elem_ctype = .u32,
1987                    .len = 8,
1988                });
1989                if (!kind.isParameter()) return vector_ctype;
1990                var fields = [_]Info.Field{
1991                    .{
1992                        .name = .{ .index = .array },
1993                        .ctype = vector_ctype,
1994                        .alignas = AlignAs.fromAbiAlignment(Type.u32.abiAlignment(zcu)),
1995                    },
1996                };
1997                return pool.fromFields(allocator, .@"struct", &fields, kind);
1998            },
1999            .vector_16_u32_type => {
2000                const vector_ctype = try pool.getVector(allocator, .{
2001                    .elem_ctype = .u32,
2002                    .len = 16,
2003                });
2004                if (!kind.isParameter()) return vector_ctype;
2005                var fields = [_]Info.Field{
2006                    .{
2007                        .name = .{ .index = .array },
2008                        .ctype = vector_ctype,
2009                        .alignas = AlignAs.fromAbiAlignment(Type.u32.abiAlignment(zcu)),
2010                    },
2011                };
2012                return pool.fromFields(allocator, .@"struct", &fields, kind);
2013            },
2014            .vector_2_i64_type => {
2015                const vector_ctype = try pool.getVector(allocator, .{
2016                    .elem_ctype = .i64,
2017                    .len = 2,
2018                });
2019                if (!kind.isParameter()) return vector_ctype;
2020                var fields = [_]Info.Field{
2021                    .{
2022                        .name = .{ .index = .array },
2023                        .ctype = vector_ctype,
2024                        .alignas = AlignAs.fromAbiAlignment(Type.i64.abiAlignment(zcu)),
2025                    },
2026                };
2027                return pool.fromFields(allocator, .@"struct", &fields, kind);
2028            },
2029            .vector_4_i64_type => {
2030                const vector_ctype = try pool.getVector(allocator, .{
2031                    .elem_ctype = .i64,
2032                    .len = 4,
2033                });
2034                if (!kind.isParameter()) return vector_ctype;
2035                var fields = [_]Info.Field{
2036                    .{
2037                        .name = .{ .index = .array },
2038                        .ctype = vector_ctype,
2039                        .alignas = AlignAs.fromAbiAlignment(Type.i64.abiAlignment(zcu)),
2040                    },
2041                };
2042                return pool.fromFields(allocator, .@"struct", &fields, kind);
2043            },
2044            .vector_8_i64_type => {
2045                const vector_ctype = try pool.getVector(allocator, .{
2046                    .elem_ctype = .i64,
2047                    .len = 8,
2048                });
2049                if (!kind.isParameter()) return vector_ctype;
2050                var fields = [_]Info.Field{
2051                    .{
2052                        .name = .{ .index = .array },
2053                        .ctype = vector_ctype,
2054                        .alignas = AlignAs.fromAbiAlignment(Type.i64.abiAlignment(zcu)),
2055                    },
2056                };
2057                return pool.fromFields(allocator, .@"struct", &fields, kind);
2058            },
2059            .vector_2_u64_type => {
2060                const vector_ctype = try pool.getVector(allocator, .{
2061                    .elem_ctype = .u64,
2062                    .len = 2,
2063                });
2064                if (!kind.isParameter()) return vector_ctype;
2065                var fields = [_]Info.Field{
2066                    .{
2067                        .name = .{ .index = .array },
2068                        .ctype = vector_ctype,
2069                        .alignas = AlignAs.fromAbiAlignment(Type.u64.abiAlignment(zcu)),
2070                    },
2071                };
2072                return pool.fromFields(allocator, .@"struct", &fields, kind);
2073            },
2074            .vector_4_u64_type => {
2075                const vector_ctype = try pool.getVector(allocator, .{
2076                    .elem_ctype = .u64,
2077                    .len = 4,
2078                });
2079                if (!kind.isParameter()) return vector_ctype;
2080                var fields = [_]Info.Field{
2081                    .{
2082                        .name = .{ .index = .array },
2083                        .ctype = vector_ctype,
2084                        .alignas = AlignAs.fromAbiAlignment(Type.u64.abiAlignment(zcu)),
2085                    },
2086                };
2087                return pool.fromFields(allocator, .@"struct", &fields, kind);
2088            },
2089            .vector_8_u64_type => {
2090                const vector_ctype = try pool.getVector(allocator, .{
2091                    .elem_ctype = .u64,
2092                    .len = 8,
2093                });
2094                if (!kind.isParameter()) return vector_ctype;
2095                var fields = [_]Info.Field{
2096                    .{
2097                        .name = .{ .index = .array },
2098                        .ctype = vector_ctype,
2099                        .alignas = AlignAs.fromAbiAlignment(Type.u64.abiAlignment(zcu)),
2100                    },
2101                };
2102                return pool.fromFields(allocator, .@"struct", &fields, kind);
2103            },
2104            .vector_1_u128_type => {
2105                const vector_ctype = try pool.getVector(allocator, .{
2106                    .elem_ctype = .u128,
2107                    .len = 1,
2108                });
2109                if (!kind.isParameter()) return vector_ctype;
2110                var fields = [_]Info.Field{
2111                    .{
2112                        .name = .{ .index = .array },
2113                        .ctype = vector_ctype,
2114                        .alignas = AlignAs.fromAbiAlignment(Type.u128.abiAlignment(zcu)),
2115                    },
2116                };
2117                return pool.fromFields(allocator, .@"struct", &fields, kind);
2118            },
2119            .vector_2_u128_type => {
2120                const vector_ctype = try pool.getVector(allocator, .{
2121                    .elem_ctype = .u128,
2122                    .len = 2,
2123                });
2124                if (!kind.isParameter()) return vector_ctype;
2125                var fields = [_]Info.Field{
2126                    .{
2127                        .name = .{ .index = .array },
2128                        .ctype = vector_ctype,
2129                        .alignas = AlignAs.fromAbiAlignment(Type.u128.abiAlignment(zcu)),
2130                    },
2131                };
2132                return pool.fromFields(allocator, .@"struct", &fields, kind);
2133            },
2134            .vector_1_u256_type => {
2135                const vector_ctype = try pool.getVector(allocator, .{
2136                    .elem_ctype = try pool.fromIntInfo(allocator, .{
2137                        .signedness = .unsigned,
2138                        .bits = 256,
2139                    }, mod, kind),
2140                    .len = 1,
2141                });
2142                if (!kind.isParameter()) return vector_ctype;
2143                var fields = [_]Info.Field{
2144                    .{
2145                        .name = .{ .index = .array },
2146                        .ctype = vector_ctype,
2147                        .alignas = AlignAs.fromAbiAlignment(Type.u256.abiAlignment(zcu)),
2148                    },
2149                };
2150                return pool.fromFields(allocator, .@"struct", &fields, kind);
2151            },
2152            .vector_4_f16_type => {
2153                const vector_ctype = try pool.getVector(allocator, .{
2154                    .elem_ctype = .f16,
2155                    .len = 4,
2156                });
2157                if (!kind.isParameter()) return vector_ctype;
2158                var fields = [_]Info.Field{
2159                    .{
2160                        .name = .{ .index = .array },
2161                        .ctype = vector_ctype,
2162                        .alignas = AlignAs.fromAbiAlignment(Type.f16.abiAlignment(zcu)),
2163                    },
2164                };
2165                return pool.fromFields(allocator, .@"struct", &fields, kind);
2166            },
2167            .vector_8_f16_type => {
2168                const vector_ctype = try pool.getVector(allocator, .{
2169                    .elem_ctype = .f16,
2170                    .len = 8,
2171                });
2172                if (!kind.isParameter()) return vector_ctype;
2173                var fields = [_]Info.Field{
2174                    .{
2175                        .name = .{ .index = .array },
2176                        .ctype = vector_ctype,
2177                        .alignas = AlignAs.fromAbiAlignment(Type.f16.abiAlignment(zcu)),
2178                    },
2179                };
2180                return pool.fromFields(allocator, .@"struct", &fields, kind);
2181            },
2182            .vector_16_f16_type => {
2183                const vector_ctype = try pool.getVector(allocator, .{
2184                    .elem_ctype = .f16,
2185                    .len = 16,
2186                });
2187                if (!kind.isParameter()) return vector_ctype;
2188                var fields = [_]Info.Field{
2189                    .{
2190                        .name = .{ .index = .array },
2191                        .ctype = vector_ctype,
2192                        .alignas = AlignAs.fromAbiAlignment(Type.f16.abiAlignment(zcu)),
2193                    },
2194                };
2195                return pool.fromFields(allocator, .@"struct", &fields, kind);
2196            },
2197            .vector_32_f16_type => {
2198                const vector_ctype = try pool.getVector(allocator, .{
2199                    .elem_ctype = .f16,
2200                    .len = 32,
2201                });
2202                if (!kind.isParameter()) return vector_ctype;
2203                var fields = [_]Info.Field{
2204                    .{
2205                        .name = .{ .index = .array },
2206                        .ctype = vector_ctype,
2207                        .alignas = AlignAs.fromAbiAlignment(Type.f16.abiAlignment(zcu)),
2208                    },
2209                };
2210                return pool.fromFields(allocator, .@"struct", &fields, kind);
2211            },
2212            .vector_2_f32_type => {
2213                const vector_ctype = try pool.getVector(allocator, .{
2214                    .elem_ctype = .f32,
2215                    .len = 2,
2216                });
2217                if (!kind.isParameter()) return vector_ctype;
2218                var fields = [_]Info.Field{
2219                    .{
2220                        .name = .{ .index = .array },
2221                        .ctype = vector_ctype,
2222                        .alignas = AlignAs.fromAbiAlignment(Type.f32.abiAlignment(zcu)),
2223                    },
2224                };
2225                return pool.fromFields(allocator, .@"struct", &fields, kind);
2226            },
2227            .vector_4_f32_type => {
2228                const vector_ctype = try pool.getVector(allocator, .{
2229                    .elem_ctype = .f32,
2230                    .len = 4,
2231                });
2232                if (!kind.isParameter()) return vector_ctype;
2233                var fields = [_]Info.Field{
2234                    .{
2235                        .name = .{ .index = .array },
2236                        .ctype = vector_ctype,
2237                        .alignas = AlignAs.fromAbiAlignment(Type.f32.abiAlignment(zcu)),
2238                    },
2239                };
2240                return pool.fromFields(allocator, .@"struct", &fields, kind);
2241            },
2242            .vector_8_f32_type => {
2243                const vector_ctype = try pool.getVector(allocator, .{
2244                    .elem_ctype = .f32,
2245                    .len = 8,
2246                });
2247                if (!kind.isParameter()) return vector_ctype;
2248                var fields = [_]Info.Field{
2249                    .{
2250                        .name = .{ .index = .array },
2251                        .ctype = vector_ctype,
2252                        .alignas = AlignAs.fromAbiAlignment(Type.f32.abiAlignment(zcu)),
2253                    },
2254                };
2255                return pool.fromFields(allocator, .@"struct", &fields, kind);
2256            },
2257            .vector_16_f32_type => {
2258                const vector_ctype = try pool.getVector(allocator, .{
2259                    .elem_ctype = .f32,
2260                    .len = 16,
2261                });
2262                if (!kind.isParameter()) return vector_ctype;
2263                var fields = [_]Info.Field{
2264                    .{
2265                        .name = .{ .index = .array },
2266                        .ctype = vector_ctype,
2267                        .alignas = AlignAs.fromAbiAlignment(Type.f32.abiAlignment(zcu)),
2268                    },
2269                };
2270                return pool.fromFields(allocator, .@"struct", &fields, kind);
2271            },
2272            .vector_2_f64_type => {
2273                const vector_ctype = try pool.getVector(allocator, .{
2274                    .elem_ctype = .f64,
2275                    .len = 2,
2276                });
2277                if (!kind.isParameter()) return vector_ctype;
2278                var fields = [_]Info.Field{
2279                    .{
2280                        .name = .{ .index = .array },
2281                        .ctype = vector_ctype,
2282                        .alignas = AlignAs.fromAbiAlignment(Type.f64.abiAlignment(zcu)),
2283                    },
2284                };
2285                return pool.fromFields(allocator, .@"struct", &fields, kind);
2286            },
2287            .vector_4_f64_type => {
2288                const vector_ctype = try pool.getVector(allocator, .{
2289                    .elem_ctype = .f64,
2290                    .len = 4,
2291                });
2292                if (!kind.isParameter()) return vector_ctype;
2293                var fields = [_]Info.Field{
2294                    .{
2295                        .name = .{ .index = .array },
2296                        .ctype = vector_ctype,
2297                        .alignas = AlignAs.fromAbiAlignment(Type.f64.abiAlignment(zcu)),
2298                    },
2299                };
2300                return pool.fromFields(allocator, .@"struct", &fields, kind);
2301            },
2302            .vector_8_f64_type => {
2303                const vector_ctype = try pool.getVector(allocator, .{
2304                    .elem_ctype = .f64,
2305                    .len = 8,
2306                });
2307                if (!kind.isParameter()) return vector_ctype;
2308                var fields = [_]Info.Field{
2309                    .{
2310                        .name = .{ .index = .array },
2311                        .ctype = vector_ctype,
2312                        .alignas = AlignAs.fromAbiAlignment(Type.f64.abiAlignment(zcu)),
2313                    },
2314                };
2315                return pool.fromFields(allocator, .@"struct", &fields, kind);
2316            },
2317
2318            .undef,
2319            .undef_bool,
2320            .undef_usize,
2321            .undef_u1,
2322            .zero,
2323            .zero_usize,
2324            .zero_u1,
2325            .zero_u8,
2326            .one,
2327            .one_usize,
2328            .one_u1,
2329            .one_u8,
2330            .four_u8,
2331            .negative_one,
2332            .void_value,
2333            .unreachable_value,
2334            .null_value,
2335            .bool_true,
2336            .bool_false,
2337            .empty_tuple,
2338            .none,
2339            => unreachable, // values, not types
2340
2341            _ => |ip_index| switch (ip.indexToKey(ip_index)) {
2342                .int_type => |int_info| return pool.fromIntInfo(allocator, int_info, mod, kind),
2343                .ptr_type => |ptr_info| switch (ptr_info.flags.size) {
2344                    .one, .many, .c => {
2345                        const elem_ctype = elem_ctype: {
2346                            if (ptr_info.packed_offset.host_size > 0 and
2347                                ptr_info.flags.vector_index == .none)
2348                                break :elem_ctype try pool.fromIntInfo(allocator, .{
2349                                    .signedness = .unsigned,
2350                                    .bits = ptr_info.packed_offset.host_size * 8,
2351                                }, mod, .forward);
2352                            const elem: Info.Aligned = .{
2353                                .ctype = try pool.fromType(
2354                                    allocator,
2355                                    scratch,
2356                                    Type.fromInterned(ptr_info.child),
2357                                    pt,
2358                                    mod,
2359                                    .forward,
2360                                ),
2361                                .alignas = AlignAs.fromAlignment(.{
2362                                    .@"align" = ptr_info.flags.alignment,
2363                                    .abi = Type.fromInterned(ptr_info.child).abiAlignment(zcu),
2364                                }),
2365                            };
2366                            break :elem_ctype if (elem.alignas.abiOrder().compare(.gte))
2367                                elem.ctype
2368                            else
2369                                try pool.getAligned(allocator, elem);
2370                        };
2371                        const elem_tag: Info.Tag = switch (elem_ctype.info(pool)) {
2372                            .aligned => |aligned_info| aligned_info.ctype.info(pool),
2373                            else => |elem_tag| elem_tag,
2374                        };
2375                        return pool.getPointer(allocator, .{
2376                            .elem_ctype = elem_ctype,
2377                            .@"const" = switch (elem_tag) {
2378                                .basic,
2379                                .pointer,
2380                                .aligned,
2381                                .array,
2382                                .vector,
2383                                .fwd_decl,
2384                                .aggregate,
2385                                => ptr_info.flags.is_const,
2386                                .function => false,
2387                            },
2388                            .@"volatile" = ptr_info.flags.is_volatile,
2389                            .nonstring = elem_ctype.isAnyChar() and switch (ptr_info.sentinel) {
2390                                .none => true,
2391                                .zero_u8 => false,
2392                                else => |sentinel| Value.fromInterned(sentinel).orderAgainstZero(zcu).compare(.neq),
2393                            },
2394                        });
2395                    },
2396                    .slice => {
2397                        const target = &mod.resolved_target.result;
2398                        var fields = [_]Info.Field{
2399                            .{
2400                                .name = .{ .index = .ptr },
2401                                .ctype = try pool.fromType(
2402                                    allocator,
2403                                    scratch,
2404                                    Type.fromInterned(ip.slicePtrType(ip_index)),
2405                                    pt,
2406                                    mod,
2407                                    kind,
2408                                ),
2409                                .alignas = AlignAs.fromAbiAlignment(Type.ptrAbiAlignment(target)),
2410                            },
2411                            .{
2412                                .name = .{ .index = .len },
2413                                .ctype = .usize,
2414                                .alignas = AlignAs.fromAbiAlignment(
2415                                    .fromByteUnits(std.zig.target.intAlignment(target, target.ptrBitWidth())),
2416                                ),
2417                            },
2418                        };
2419                        return pool.fromFields(allocator, .@"struct", &fields, kind);
2420                    },
2421                },
2422                .array_type => |array_info| {
2423                    const len = array_info.lenIncludingSentinel();
2424                    if (len == 0) return .void;
2425                    const elem_type = Type.fromInterned(array_info.child);
2426                    const elem_ctype = try pool.fromType(
2427                        allocator,
2428                        scratch,
2429                        elem_type,
2430                        pt,
2431                        mod,
2432                        kind.noParameter().asComplete(),
2433                    );
2434                    if (elem_ctype.index == .void) return .void;
2435                    const array_ctype = try pool.getArray(allocator, .{
2436                        .elem_ctype = elem_ctype,
2437                        .len = len,
2438                        .nonstring = elem_ctype.isAnyChar() and switch (array_info.sentinel) {
2439                            .none => true,
2440                            .zero_u8 => false,
2441                            else => |sentinel| Value.fromInterned(sentinel).orderAgainstZero(zcu).compare(.neq),
2442                        },
2443                    });
2444                    if (!kind.isParameter()) return array_ctype;
2445                    var fields = [_]Info.Field{
2446                        .{
2447                            .name = .{ .index = .array },
2448                            .ctype = array_ctype,
2449                            .alignas = AlignAs.fromAbiAlignment(elem_type.abiAlignment(zcu)),
2450                        },
2451                    };
2452                    return pool.fromFields(allocator, .@"struct", &fields, kind);
2453                },
2454                .vector_type => |vector_info| {
2455                    if (vector_info.len == 0) return .void;
2456                    const elem_type = Type.fromInterned(vector_info.child);
2457                    const elem_ctype = try pool.fromType(
2458                        allocator,
2459                        scratch,
2460                        elem_type,
2461                        pt,
2462                        mod,
2463                        kind.noParameter().asComplete(),
2464                    );
2465                    if (elem_ctype.index == .void) return .void;
2466                    const vector_ctype = try pool.getVector(allocator, .{
2467                        .elem_ctype = elem_ctype,
2468                        .len = vector_info.len,
2469                        .nonstring = elem_ctype.isAnyChar(),
2470                    });
2471                    if (!kind.isParameter()) return vector_ctype;
2472                    var fields = [_]Info.Field{
2473                        .{
2474                            .name = .{ .index = .array },
2475                            .ctype = vector_ctype,
2476                            .alignas = AlignAs.fromAbiAlignment(elem_type.abiAlignment(zcu)),
2477                        },
2478                    };
2479                    return pool.fromFields(allocator, .@"struct", &fields, kind);
2480                },
2481                .opt_type => |payload_type| {
2482                    if (ip.isNoReturn(payload_type)) return .void;
2483                    const payload_ctype = try pool.fromType(
2484                        allocator,
2485                        scratch,
2486                        Type.fromInterned(payload_type),
2487                        pt,
2488                        mod,
2489                        kind.noParameter(),
2490                    );
2491                    if (payload_ctype.index == .void) return .bool;
2492                    switch (payload_type) {
2493                        .anyerror_type => return payload_ctype,
2494                        else => switch (ip.indexToKey(payload_type)) {
2495                            .ptr_type => |payload_ptr_info| if (payload_ptr_info.flags.size != .c and
2496                                !payload_ptr_info.flags.is_allowzero) return payload_ctype,
2497                            .error_set_type, .inferred_error_set_type => return payload_ctype,
2498                            else => {},
2499                        },
2500                    }
2501                    var fields = [_]Info.Field{
2502                        .{
2503                            .name = .{ .index = .is_null },
2504                            .ctype = .bool,
2505                            .alignas = AlignAs.fromAbiAlignment(.@"1"),
2506                        },
2507                        .{
2508                            .name = .{ .index = .payload },
2509                            .ctype = payload_ctype,
2510                            .alignas = AlignAs.fromAbiAlignment(
2511                                Type.fromInterned(payload_type).abiAlignment(zcu),
2512                            ),
2513                        },
2514                    };
2515                    return pool.fromFields(allocator, .@"struct", &fields, kind);
2516                },
2517                .anyframe_type => unreachable,
2518                .error_union_type => |error_union_info| {
2519                    const error_set_bits = pt.zcu.errorSetBits();
2520                    const error_set_ctype = try pool.fromIntInfo(allocator, .{
2521                        .signedness = .unsigned,
2522                        .bits = error_set_bits,
2523                    }, mod, kind);
2524                    if (ip.isNoReturn(error_union_info.payload_type)) return error_set_ctype;
2525                    const payload_type = Type.fromInterned(error_union_info.payload_type);
2526                    const payload_ctype = try pool.fromType(
2527                        allocator,
2528                        scratch,
2529                        payload_type,
2530                        pt,
2531                        mod,
2532                        kind.noParameter(),
2533                    );
2534                    if (payload_ctype.index == .void) return error_set_ctype;
2535                    const target = &mod.resolved_target.result;
2536                    var fields = [_]Info.Field{
2537                        .{
2538                            .name = .{ .index = .@"error" },
2539                            .ctype = error_set_ctype,
2540                            .alignas = AlignAs.fromAbiAlignment(
2541                                .fromByteUnits(std.zig.target.intAlignment(target, error_set_bits)),
2542                            ),
2543                        },
2544                        .{
2545                            .name = .{ .index = .payload },
2546                            .ctype = payload_ctype,
2547                            .alignas = AlignAs.fromAbiAlignment(payload_type.abiAlignment(zcu)),
2548                        },
2549                    };
2550                    return pool.fromFields(allocator, .@"struct", &fields, kind);
2551                },
2552                .simple_type => unreachable,
2553                .struct_type => {
2554                    const loaded_struct = ip.loadStructType(ip_index);
2555                    switch (loaded_struct.layout) {
2556                        .auto, .@"extern" => {
2557                            const fwd_decl = try pool.getFwdDecl(allocator, .{
2558                                .tag = .@"struct",
2559                                .name = .{ .index = ip_index },
2560                            });
2561                            if (kind.isForward()) return if (ty.hasRuntimeBitsIgnoreComptime(zcu))
2562                                fwd_decl
2563                            else
2564                                .void;
2565                            const scratch_top = scratch.items.len;
2566                            defer scratch.shrinkRetainingCapacity(scratch_top);
2567                            try scratch.ensureUnusedCapacity(
2568                                allocator,
2569                                loaded_struct.field_types.len * @typeInfo(Field).@"struct".fields.len,
2570                            );
2571                            var hasher = Hasher.init;
2572                            var tag: Pool.Tag = .aggregate_struct;
2573                            var field_it = loaded_struct.iterateRuntimeOrder(ip);
2574                            while (field_it.next()) |field_index| {
2575                                const field_type = Type.fromInterned(
2576                                    loaded_struct.field_types.get(ip)[field_index],
2577                                );
2578                                const field_ctype = try pool.fromType(
2579                                    allocator,
2580                                    scratch,
2581                                    field_type,
2582                                    pt,
2583                                    mod,
2584                                    kind.noParameter(),
2585                                );
2586                                if (field_ctype.index == .void) continue;
2587                                const field_name = try pool.string(allocator, loaded_struct.fieldName(ip, field_index).toSlice(ip));
2588                                const field_alignas = AlignAs.fromAlignment(.{
2589                                    .@"align" = loaded_struct.fieldAlign(ip, field_index),
2590                                    .abi = field_type.abiAlignment(zcu),
2591                                });
2592                                pool.addHashedExtraAssumeCapacityTo(scratch, &hasher, Field, .{
2593                                    .name = field_name.index,
2594                                    .ctype = field_ctype.index,
2595                                    .flags = .{ .alignas = field_alignas },
2596                                });
2597                                if (field_alignas.abiOrder().compare(.lt))
2598                                    tag = .aggregate_struct_packed;
2599                            }
2600                            const fields_len: u32 = @intCast(@divExact(
2601                                scratch.items.len - scratch_top,
2602                                @typeInfo(Field).@"struct".fields.len,
2603                            ));
2604                            if (fields_len == 0) return .void;
2605                            try pool.ensureUnusedCapacity(allocator, 1);
2606                            const extra_index = try pool.addHashedExtra(allocator, &hasher, Aggregate, .{
2607                                .fwd_decl = fwd_decl.index,
2608                                .fields_len = fields_len,
2609                            }, fields_len * @typeInfo(Field).@"struct".fields.len);
2610                            pool.extra.appendSliceAssumeCapacity(scratch.items[scratch_top..]);
2611                            return pool.tagTrailingExtraAssumeCapacity(hasher, tag, extra_index);
2612                        },
2613                        .@"packed" => return pool.fromType(
2614                            allocator,
2615                            scratch,
2616                            Type.fromInterned(loaded_struct.backingIntTypeUnordered(ip)),
2617                            pt,
2618                            mod,
2619                            kind,
2620                        ),
2621                    }
2622                },
2623                .tuple_type => |tuple_info| {
2624                    const scratch_top = scratch.items.len;
2625                    defer scratch.shrinkRetainingCapacity(scratch_top);
2626                    try scratch.ensureUnusedCapacity(allocator, tuple_info.types.len *
2627                        @typeInfo(Field).@"struct".fields.len);
2628                    var hasher = Hasher.init;
2629                    for (0..tuple_info.types.len) |field_index| {
2630                        if (tuple_info.values.get(ip)[field_index] != .none) continue;
2631                        const field_type = Type.fromInterned(
2632                            tuple_info.types.get(ip)[field_index],
2633                        );
2634                        const field_ctype = try pool.fromType(
2635                            allocator,
2636                            scratch,
2637                            field_type,
2638                            pt,
2639                            mod,
2640                            kind.noParameter(),
2641                        );
2642                        if (field_ctype.index == .void) continue;
2643                        const field_name = try pool.fmt(allocator, "f{d}", .{field_index});
2644                        pool.addHashedExtraAssumeCapacityTo(scratch, &hasher, Field, .{
2645                            .name = field_name.index,
2646                            .ctype = field_ctype.index,
2647                            .flags = .{ .alignas = AlignAs.fromAbiAlignment(
2648                                field_type.abiAlignment(zcu),
2649                            ) },
2650                        });
2651                    }
2652                    const fields_len: u32 = @intCast(@divExact(
2653                        scratch.items.len - scratch_top,
2654                        @typeInfo(Field).@"struct".fields.len,
2655                    ));
2656                    if (fields_len == 0) return .void;
2657                    if (kind.isForward()) {
2658                        try pool.ensureUnusedCapacity(allocator, 1);
2659                        const extra_index = try pool.addHashedExtra(
2660                            allocator,
2661                            &hasher,
2662                            FwdDeclAnon,
2663                            .{ .fields_len = fields_len },
2664                            fields_len * @typeInfo(Field).@"struct".fields.len,
2665                        );
2666                        pool.extra.appendSliceAssumeCapacity(scratch.items[scratch_top..]);
2667                        return pool.tagTrailingExtra(
2668                            allocator,
2669                            hasher,
2670                            .fwd_decl_struct_anon,
2671                            extra_index,
2672                        );
2673                    }
2674                    const fwd_decl = try pool.fromType(allocator, scratch, ty, pt, mod, .forward);
2675                    try pool.ensureUnusedCapacity(allocator, 1);
2676                    const extra_index = try pool.addHashedExtra(allocator, &hasher, Aggregate, .{
2677                        .fwd_decl = fwd_decl.index,
2678                        .fields_len = fields_len,
2679                    }, fields_len * @typeInfo(Field).@"struct".fields.len);
2680                    pool.extra.appendSliceAssumeCapacity(scratch.items[scratch_top..]);
2681                    return pool.tagTrailingExtraAssumeCapacity(hasher, .aggregate_struct, extra_index);
2682                },
2683                .union_type => {
2684                    const loaded_union = ip.loadUnionType(ip_index);
2685                    switch (loaded_union.flagsUnordered(ip).layout) {
2686                        .auto, .@"extern" => {
2687                            const has_tag = loaded_union.hasTag(ip);
2688                            const fwd_decl = try pool.getFwdDecl(allocator, .{
2689                                .tag = if (has_tag) .@"struct" else .@"union",
2690                                .name = .{ .index = ip_index },
2691                            });
2692                            if (kind.isForward()) return if (ty.hasRuntimeBitsIgnoreComptime(zcu))
2693                                fwd_decl
2694                            else
2695                                .void;
2696                            const loaded_tag = loaded_union.loadTagType(ip);
2697                            const scratch_top = scratch.items.len;
2698                            defer scratch.shrinkRetainingCapacity(scratch_top);
2699                            try scratch.ensureUnusedCapacity(
2700                                allocator,
2701                                loaded_union.field_types.len * @typeInfo(Field).@"struct".fields.len,
2702                            );
2703                            var hasher = Hasher.init;
2704                            var tag: Pool.Tag = .aggregate_union;
2705                            var payload_align: InternPool.Alignment = .@"1";
2706                            for (0..loaded_union.field_types.len) |field_index| {
2707                                const field_type = Type.fromInterned(
2708                                    loaded_union.field_types.get(ip)[field_index],
2709                                );
2710                                if (ip.isNoReturn(field_type.toIntern())) continue;
2711                                const field_ctype = try pool.fromType(
2712                                    allocator,
2713                                    scratch,
2714                                    field_type,
2715                                    pt,
2716                                    mod,
2717                                    kind.noParameter(),
2718                                );
2719                                if (field_ctype.index == .void) continue;
2720                                const field_name = try pool.string(
2721                                    allocator,
2722                                    loaded_tag.names.get(ip)[field_index].toSlice(ip),
2723                                );
2724                                const field_alignas = AlignAs.fromAlignment(.{
2725                                    .@"align" = loaded_union.fieldAlign(ip, field_index),
2726                                    .abi = field_type.abiAlignment(zcu),
2727                                });
2728                                pool.addHashedExtraAssumeCapacityTo(scratch, &hasher, Field, .{
2729                                    .name = field_name.index,
2730                                    .ctype = field_ctype.index,
2731                                    .flags = .{ .alignas = field_alignas },
2732                                });
2733                                if (field_alignas.abiOrder().compare(.lt))
2734                                    tag = .aggregate_union_packed;
2735                                payload_align = payload_align.maxStrict(field_alignas.@"align");
2736                            }
2737                            const fields_len: u32 = @intCast(@divExact(
2738                                scratch.items.len - scratch_top,
2739                                @typeInfo(Field).@"struct".fields.len,
2740                            ));
2741                            if (!has_tag) {
2742                                if (fields_len == 0) return .void;
2743                                try pool.ensureUnusedCapacity(allocator, 1);
2744                                const extra_index = try pool.addHashedExtra(
2745                                    allocator,
2746                                    &hasher,
2747                                    Aggregate,
2748                                    .{ .fwd_decl = fwd_decl.index, .fields_len = fields_len },
2749                                    fields_len * @typeInfo(Field).@"struct".fields.len,
2750                                );
2751                                pool.extra.appendSliceAssumeCapacity(scratch.items[scratch_top..]);
2752                                return pool.tagTrailingExtraAssumeCapacity(hasher, tag, extra_index);
2753                            }
2754                            try pool.ensureUnusedCapacity(allocator, 2);
2755                            var struct_fields: [2]Info.Field = undefined;
2756                            var struct_fields_len: usize = 0;
2757                            if (loaded_tag.tag_ty != .comptime_int_type) {
2758                                const tag_type = Type.fromInterned(loaded_tag.tag_ty);
2759                                const tag_ctype: CType = try pool.fromType(
2760                                    allocator,
2761                                    scratch,
2762                                    tag_type,
2763                                    pt,
2764                                    mod,
2765                                    kind.noParameter(),
2766                                );
2767                                if (tag_ctype.index != .void) {
2768                                    struct_fields[struct_fields_len] = .{
2769                                        .name = .{ .index = .tag },
2770                                        .ctype = tag_ctype,
2771                                        .alignas = AlignAs.fromAbiAlignment(tag_type.abiAlignment(zcu)),
2772                                    };
2773                                    struct_fields_len += 1;
2774                                }
2775                            }
2776                            if (fields_len > 0) {
2777                                const payload_ctype = payload_ctype: {
2778                                    const extra_index = try pool.addHashedExtra(
2779                                        allocator,
2780                                        &hasher,
2781                                        AggregateAnon,
2782                                        .{
2783                                            .index = ip_index,
2784                                            .id = 0,
2785                                            .fields_len = fields_len,
2786                                        },
2787                                        fields_len * @typeInfo(Field).@"struct".fields.len,
2788                                    );
2789                                    pool.extra.appendSliceAssumeCapacity(scratch.items[scratch_top..]);
2790                                    break :payload_ctype pool.tagTrailingExtraAssumeCapacity(
2791                                        hasher,
2792                                        switch (tag) {
2793                                            .aggregate_union => .aggregate_union_anon,
2794                                            .aggregate_union_packed => .aggregate_union_packed_anon,
2795                                            else => unreachable,
2796                                        },
2797                                        extra_index,
2798                                    );
2799                                };
2800                                if (payload_ctype.index != .void) {
2801                                    struct_fields[struct_fields_len] = .{
2802                                        .name = .{ .index = .payload },
2803                                        .ctype = payload_ctype,
2804                                        .alignas = AlignAs.fromAbiAlignment(payload_align),
2805                                    };
2806                                    struct_fields_len += 1;
2807                                }
2808                            }
2809                            if (struct_fields_len == 0) return .void;
2810                            sortFields(struct_fields[0..struct_fields_len]);
2811                            return pool.getAggregate(allocator, .{
2812                                .tag = .@"struct",
2813                                .name = .{ .fwd_decl = fwd_decl },
2814                                .fields = struct_fields[0..struct_fields_len],
2815                            });
2816                        },
2817                        .@"packed" => return pool.fromIntInfo(allocator, .{
2818                            .signedness = .unsigned,
2819                            .bits = @intCast(ty.bitSize(zcu)),
2820                        }, mod, kind),
2821                    }
2822                },
2823                .opaque_type => return .void,
2824                .enum_type => return pool.fromType(
2825                    allocator,
2826                    scratch,
2827                    Type.fromInterned(ip.loadEnumType(ip_index).tag_ty),
2828                    pt,
2829                    mod,
2830                    kind,
2831                ),
2832                .func_type => |func_info| if (func_info.is_generic) return .void else {
2833                    const scratch_top = scratch.items.len;
2834                    defer scratch.shrinkRetainingCapacity(scratch_top);
2835                    try scratch.ensureUnusedCapacity(allocator, func_info.param_types.len);
2836                    var hasher = Hasher.init;
2837                    const return_type = Type.fromInterned(func_info.return_type);
2838                    const return_ctype: CType =
2839                        if (!ip.isNoReturn(func_info.return_type)) try pool.fromType(
2840                            allocator,
2841                            scratch,
2842                            return_type,
2843                            pt,
2844                            mod,
2845                            kind.asParameter(),
2846                        ) else .void;
2847                    for (0..func_info.param_types.len) |param_index| {
2848                        const param_type = Type.fromInterned(
2849                            func_info.param_types.get(ip)[param_index],
2850                        );
2851                        const param_ctype = try pool.fromType(
2852                            allocator,
2853                            scratch,
2854                            param_type,
2855                            pt,
2856                            mod,
2857                            kind.asParameter(),
2858                        );
2859                        if (param_ctype.index == .void) continue;
2860                        hasher.update(param_ctype.hash(pool));
2861                        scratch.appendAssumeCapacity(@intFromEnum(param_ctype.index));
2862                    }
2863                    const param_ctypes_len: u32 = @intCast(scratch.items.len - scratch_top);
2864                    try pool.ensureUnusedCapacity(allocator, 1);
2865                    const extra_index = try pool.addHashedExtra(allocator, &hasher, Function, .{
2866                        .return_ctype = return_ctype.index,
2867                        .param_ctypes_len = param_ctypes_len,
2868                    }, param_ctypes_len);
2869                    pool.extra.appendSliceAssumeCapacity(scratch.items[scratch_top..]);
2870                    return pool.tagTrailingExtraAssumeCapacity(hasher, switch (func_info.is_var_args) {
2871                        false => .function,
2872                        true => .function_varargs,
2873                    }, extra_index);
2874                },
2875                .error_set_type,
2876                .inferred_error_set_type,
2877                => return pool.fromIntInfo(allocator, .{
2878                    .signedness = .unsigned,
2879                    .bits = pt.zcu.errorSetBits(),
2880                }, mod, kind),
2881
2882                .undef,
2883                .simple_value,
2884                .variable,
2885                .@"extern",
2886                .func,
2887                .int,
2888                .err,
2889                .error_union,
2890                .enum_literal,
2891                .enum_tag,
2892                .empty_enum_value,
2893                .float,
2894                .ptr,
2895                .slice,
2896                .opt,
2897                .aggregate,
2898                .un,
2899                .memoized_call,
2900                => unreachable, // values, not types
2901            },
2902        }
2903    }
2904
2905    pub fn getOrPutAdapted(
2906        pool: *Pool,
2907        allocator: std.mem.Allocator,
2908        source_pool: *const Pool,
2909        source_ctype: CType,
2910        pool_adapter: anytype,
2911    ) !struct { CType, bool } {
2912        const tag = source_pool.items.items(.tag)[
2913            source_ctype.toPoolIndex() orelse return .{ source_ctype, true }
2914        ];
2915        try pool.ensureUnusedCapacity(allocator, 1);
2916        const CTypeAdapter = struct {
2917            pool: *const Pool,
2918            source_pool: *const Pool,
2919            source_info: Info,
2920            pool_adapter: @TypeOf(pool_adapter),
2921            pub fn hash(map_adapter: @This(), key_ctype: CType) Map.Hash {
2922                return key_ctype.hash(map_adapter.source_pool);
2923            }
2924            pub fn eql(map_adapter: @This(), _: CType, _: void, pool_index: usize) bool {
2925                return map_adapter.source_info.eqlAdapted(
2926                    map_adapter.source_pool,
2927                    .fromPoolIndex(pool_index),
2928                    map_adapter.pool,
2929                    map_adapter.pool_adapter,
2930                );
2931            }
2932        };
2933        const source_info = source_ctype.info(source_pool);
2934        const gop = pool.map.getOrPutAssumeCapacityAdapted(source_ctype, CTypeAdapter{
2935            .pool = pool,
2936            .source_pool = source_pool,
2937            .source_info = source_info,
2938            .pool_adapter = pool_adapter,
2939        });
2940        errdefer _ = pool.map.pop();
2941        const ctype: CType = .fromPoolIndex(gop.index);
2942        if (!gop.found_existing) switch (source_info) {
2943            .basic => unreachable,
2944            .pointer => |pointer_info| pool.items.appendAssumeCapacity(switch (pointer_info.nonstring) {
2945                false => .{
2946                    .tag = tag,
2947                    .data = @intFromEnum(pool_adapter.copy(pointer_info.elem_ctype).index),
2948                },
2949                true => .{
2950                    .tag = .nonstring,
2951                    .data = @intFromEnum(pool_adapter.copy(.{ .index = @enumFromInt(
2952                        source_pool.items.items(.data)[source_ctype.toPoolIndex().?],
2953                    ) }).index),
2954                },
2955            }),
2956            .aligned => |aligned_info| pool.items.appendAssumeCapacity(.{
2957                .tag = tag,
2958                .data = try pool.addExtra(allocator, Aligned, .{
2959                    .ctype = pool_adapter.copy(aligned_info.ctype).index,
2960                    .flags = .{ .alignas = aligned_info.alignas },
2961                }, 0),
2962            }),
2963            .array, .vector => |sequence_info| pool.items.appendAssumeCapacity(switch (sequence_info.nonstring) {
2964                false => .{
2965                    .tag = tag,
2966                    .data = switch (tag) {
2967                        .array_small, .vector => try pool.addExtra(allocator, SequenceSmall, .{
2968                            .elem_ctype = pool_adapter.copy(sequence_info.elem_ctype).index,
2969                            .len = @intCast(sequence_info.len),
2970                        }, 0),
2971                        .array_large => try pool.addExtra(allocator, SequenceLarge, .{
2972                            .elem_ctype = pool_adapter.copy(sequence_info.elem_ctype).index,
2973                            .len_lo = @truncate(sequence_info.len >> 0),
2974                            .len_hi = @truncate(sequence_info.len >> 32),
2975                        }, 0),
2976                        else => unreachable,
2977                    },
2978                },
2979                true => .{
2980                    .tag = .nonstring,
2981                    .data = @intFromEnum(pool_adapter.copy(.{ .index = @enumFromInt(
2982                        source_pool.items.items(.data)[source_ctype.toPoolIndex().?],
2983                    ) }).index),
2984                },
2985            }),
2986            .fwd_decl => |fwd_decl_info| switch (fwd_decl_info.name) {
2987                .anon => |fields| {
2988                    pool.items.appendAssumeCapacity(.{
2989                        .tag = tag,
2990                        .data = try pool.addExtra(allocator, FwdDeclAnon, .{
2991                            .fields_len = fields.len,
2992                        }, fields.len * @typeInfo(Field).@"struct".fields.len),
2993                    });
2994                    for (0..fields.len) |field_index| {
2995                        const field = fields.at(field_index, source_pool);
2996                        const field_name = if (field.name.toPoolSlice(source_pool)) |slice|
2997                            try pool.string(allocator, slice)
2998                        else
2999                            field.name;
3000                        pool.addExtraAssumeCapacity(Field, .{
3001                            .name = field_name.index,
3002                            .ctype = pool_adapter.copy(field.ctype).index,
3003                            .flags = .{ .alignas = field.alignas },
3004                        });
3005                    }
3006                },
3007                .index => |index| pool.items.appendAssumeCapacity(.{
3008                    .tag = tag,
3009                    .data = @intFromEnum(index),
3010                }),
3011            },
3012            .aggregate => |aggregate_info| {
3013                pool.items.appendAssumeCapacity(.{
3014                    .tag = tag,
3015                    .data = switch (aggregate_info.name) {
3016                        .anon => |anon| try pool.addExtra(allocator, AggregateAnon, .{
3017                            .index = anon.index,
3018                            .id = anon.id,
3019                            .fields_len = aggregate_info.fields.len,
3020                        }, aggregate_info.fields.len * @typeInfo(Field).@"struct".fields.len),
3021                        .fwd_decl => |fwd_decl| try pool.addExtra(allocator, Aggregate, .{
3022                            .fwd_decl = pool_adapter.copy(fwd_decl).index,
3023                            .fields_len = aggregate_info.fields.len,
3024                        }, aggregate_info.fields.len * @typeInfo(Field).@"struct".fields.len),
3025                    },
3026                });
3027                for (0..aggregate_info.fields.len) |field_index| {
3028                    const field = aggregate_info.fields.at(field_index, source_pool);
3029                    const field_name = if (field.name.toPoolSlice(source_pool)) |slice|
3030                        try pool.string(allocator, slice)
3031                    else
3032                        field.name;
3033                    pool.addExtraAssumeCapacity(Field, .{
3034                        .name = field_name.index,
3035                        .ctype = pool_adapter.copy(field.ctype).index,
3036                        .flags = .{ .alignas = field.alignas },
3037                    });
3038                }
3039            },
3040            .function => |function_info| {
3041                pool.items.appendAssumeCapacity(.{
3042                    .tag = tag,
3043                    .data = try pool.addExtra(allocator, Function, .{
3044                        .return_ctype = pool_adapter.copy(function_info.return_ctype).index,
3045                        .param_ctypes_len = function_info.param_ctypes.len,
3046                    }, function_info.param_ctypes.len),
3047                });
3048                for (0..function_info.param_ctypes.len) |param_index| pool.extra.appendAssumeCapacity(
3049                    @intFromEnum(pool_adapter.copy(
3050                        function_info.param_ctypes.at(param_index, source_pool),
3051                    ).index),
3052                );
3053            },
3054        };
3055        assert(source_info.eqlAdapted(source_pool, ctype, pool, pool_adapter));
3056        assert(source_ctype.hash(source_pool) == ctype.hash(pool));
3057        return .{ ctype, gop.found_existing };
3058    }
3059
3060    pub fn string(pool: *Pool, allocator: std.mem.Allocator, slice: []const u8) !String {
3061        try pool.string_bytes.appendSlice(allocator, slice);
3062        return pool.trailingString(allocator);
3063    }
3064
3065    pub fn fmt(
3066        pool: *Pool,
3067        allocator: std.mem.Allocator,
3068        comptime fmt_str: []const u8,
3069        fmt_args: anytype,
3070    ) !String {
3071        try pool.string_bytes.print(allocator, fmt_str, fmt_args);
3072        return pool.trailingString(allocator);
3073    }
3074
3075    fn ensureUnusedCapacity(pool: *Pool, allocator: std.mem.Allocator, len: u32) !void {
3076        try pool.map.ensureUnusedCapacity(allocator, len);
3077        try pool.items.ensureUnusedCapacity(allocator, len);
3078    }
3079
3080    const Hasher = struct {
3081        const Impl = std.hash.Wyhash;
3082        impl: Impl,
3083
3084        const init: Hasher = .{ .impl = Impl.init(0) };
3085
3086        fn updateExtra(hasher: *Hasher, comptime Extra: type, extra: Extra, pool: *const Pool) void {
3087            inline for (@typeInfo(Extra).@"struct".fields) |field| {
3088                const value = @field(extra, field.name);
3089                switch (field.type) {
3090                    Pool.Tag, String, CType => unreachable,
3091                    CType.Index => hasher.update((CType{ .index = value }).hash(pool)),
3092                    String.Index => if ((String{ .index = value }).toPoolSlice(pool)) |slice|
3093                        hasher.update(slice)
3094                    else
3095                        hasher.update(@intFromEnum(value)),
3096                    else => hasher.update(value),
3097                }
3098            }
3099        }
3100        fn update(hasher: *Hasher, data: anytype) void {
3101            switch (@TypeOf(data)) {
3102                Pool.Tag => @compileError("pass tag to final"),
3103                CType, CType.Index => @compileError("hash ctype.hash(pool) instead"),
3104                String, String.Index => @compileError("hash string.slice(pool) instead"),
3105                u32, InternPool.Index, Aligned.Flags => hasher.impl.update(std.mem.asBytes(&data)),
3106                []const u8 => hasher.impl.update(data),
3107                else => @compileError("unhandled type: " ++ @typeName(@TypeOf(data))),
3108            }
3109        }
3110
3111        fn final(hasher: Hasher, tag: Pool.Tag) Map.Hash {
3112            var impl = hasher.impl;
3113            impl.update(std.mem.asBytes(&tag));
3114            return @truncate(impl.final());
3115        }
3116    };
3117
3118    fn tagData(
3119        pool: *Pool,
3120        allocator: std.mem.Allocator,
3121        hasher: Hasher,
3122        tag: Pool.Tag,
3123        data: u32,
3124    ) !CType {
3125        try pool.ensureUnusedCapacity(allocator, 1);
3126        const Key = struct { hash: Map.Hash, tag: Pool.Tag, data: u32 };
3127        const CTypeAdapter = struct {
3128            pool: *const Pool,
3129            pub fn hash(_: @This(), key: Key) Map.Hash {
3130                return key.hash;
3131            }
3132            pub fn eql(ctype_adapter: @This(), lhs_key: Key, _: void, rhs_index: usize) bool {
3133                const rhs_item = ctype_adapter.pool.items.get(rhs_index);
3134                return lhs_key.tag == rhs_item.tag and lhs_key.data == rhs_item.data;
3135            }
3136        };
3137        const gop = pool.map.getOrPutAssumeCapacityAdapted(
3138            Key{ .hash = hasher.final(tag), .tag = tag, .data = data },
3139            CTypeAdapter{ .pool = pool },
3140        );
3141        if (!gop.found_existing) pool.items.appendAssumeCapacity(.{ .tag = tag, .data = data });
3142        return .fromPoolIndex(gop.index);
3143    }
3144
3145    fn tagExtra(
3146        pool: *Pool,
3147        allocator: std.mem.Allocator,
3148        tag: Pool.Tag,
3149        comptime Extra: type,
3150        extra: Extra,
3151    ) !CType {
3152        var hasher = Hasher.init;
3153        hasher.updateExtra(Extra, extra, pool);
3154        return pool.tagTrailingExtra(
3155            allocator,
3156            hasher,
3157            tag,
3158            try pool.addExtra(allocator, Extra, extra, 0),
3159        );
3160    }
3161
3162    fn tagTrailingExtra(
3163        pool: *Pool,
3164        allocator: std.mem.Allocator,
3165        hasher: Hasher,
3166        tag: Pool.Tag,
3167        extra_index: ExtraIndex,
3168    ) !CType {
3169        try pool.ensureUnusedCapacity(allocator, 1);
3170        return pool.tagTrailingExtraAssumeCapacity(hasher, tag, extra_index);
3171    }
3172
3173    fn tagTrailingExtraAssumeCapacity(
3174        pool: *Pool,
3175        hasher: Hasher,
3176        tag: Pool.Tag,
3177        extra_index: ExtraIndex,
3178    ) CType {
3179        const Key = struct { hash: Map.Hash, tag: Pool.Tag, extra: []const u32 };
3180        const CTypeAdapter = struct {
3181            pool: *const Pool,
3182            pub fn hash(_: @This(), key: Key) Map.Hash {
3183                return key.hash;
3184            }
3185            pub fn eql(ctype_adapter: @This(), lhs_key: Key, _: void, rhs_index: usize) bool {
3186                const rhs_item = ctype_adapter.pool.items.get(rhs_index);
3187                if (lhs_key.tag != rhs_item.tag) return false;
3188                const rhs_extra = ctype_adapter.pool.extra.items[rhs_item.data..];
3189                return std.mem.startsWith(u32, rhs_extra, lhs_key.extra);
3190            }
3191        };
3192        const gop = pool.map.getOrPutAssumeCapacityAdapted(
3193            Key{ .hash = hasher.final(tag), .tag = tag, .extra = pool.extra.items[extra_index..] },
3194            CTypeAdapter{ .pool = pool },
3195        );
3196        if (gop.found_existing)
3197            pool.extra.shrinkRetainingCapacity(extra_index)
3198        else
3199            pool.items.appendAssumeCapacity(.{ .tag = tag, .data = extra_index });
3200        return .fromPoolIndex(gop.index);
3201    }
3202
3203    fn sortFields(fields: []Info.Field) void {
3204        std.mem.sort(Info.Field, fields, {}, struct {
3205            fn before(_: void, lhs_field: Info.Field, rhs_field: Info.Field) bool {
3206                return lhs_field.alignas.order(rhs_field.alignas).compare(.gt);
3207            }
3208        }.before);
3209    }
3210
3211    fn trailingString(pool: *Pool, allocator: std.mem.Allocator) !String {
3212        const start = pool.string_indices.getLast();
3213        const slice: []const u8 = pool.string_bytes.items[start..];
3214        if (slice.len >= 2 and slice[0] == 'f' and switch (slice[1]) {
3215            '0' => slice.len == 2,
3216            '1'...'9' => true,
3217            else => false,
3218        }) if (std.fmt.parseInt(u31, slice[1..], 10)) |unnamed| {
3219            pool.string_bytes.shrinkRetainingCapacity(start);
3220            return String.fromUnnamed(unnamed);
3221        } else |_| {};
3222        if (std.meta.stringToEnum(String.Index, slice)) |index| {
3223            pool.string_bytes.shrinkRetainingCapacity(start);
3224            return .{ .index = index };
3225        }
3226
3227        try pool.string_map.ensureUnusedCapacity(allocator, 1);
3228        try pool.string_indices.ensureUnusedCapacity(allocator, 1);
3229
3230        const gop = pool.string_map.getOrPutAssumeCapacityAdapted(slice, String.Adapter{ .pool = pool });
3231        if (gop.found_existing)
3232            pool.string_bytes.shrinkRetainingCapacity(start)
3233        else
3234            pool.string_indices.appendAssumeCapacity(@intCast(pool.string_bytes.items.len));
3235        return String.fromPoolIndex(gop.index);
3236    }
3237
3238    const Item = struct {
3239        tag: Pool.Tag,
3240        data: u32,
3241    };
3242
3243    const ExtraIndex = u32;
3244
3245    const Tag = enum(u8) {
3246        basic,
3247        pointer,
3248        pointer_const,
3249        pointer_volatile,
3250        pointer_const_volatile,
3251        aligned,
3252        array_small,
3253        array_large,
3254        vector,
3255        nonstring,
3256        fwd_decl_struct_anon,
3257        fwd_decl_union_anon,
3258        fwd_decl_struct,
3259        fwd_decl_union,
3260        aggregate_struct_anon,
3261        aggregate_struct_packed_anon,
3262        aggregate_union_anon,
3263        aggregate_union_packed_anon,
3264        aggregate_struct,
3265        aggregate_struct_packed,
3266        aggregate_union,
3267        aggregate_union_packed,
3268        function,
3269        function_varargs,
3270    };
3271
3272    const Aligned = struct {
3273        ctype: CType.Index,
3274        flags: Flags,
3275
3276        const Flags = packed struct(u32) {
3277            alignas: AlignAs,
3278            _: u20 = 0,
3279        };
3280    };
3281
3282    const SequenceSmall = struct {
3283        elem_ctype: CType.Index,
3284        len: u32,
3285    };
3286
3287    const SequenceLarge = struct {
3288        elem_ctype: CType.Index,
3289        len_lo: u32,
3290        len_hi: u32,
3291
3292        fn len(extra: SequenceLarge) u64 {
3293            return @as(u64, extra.len_lo) << 0 |
3294                @as(u64, extra.len_hi) << 32;
3295        }
3296    };
3297
3298    const Field = struct {
3299        name: String.Index,
3300        ctype: CType.Index,
3301        flags: Flags,
3302
3303        const Flags = Aligned.Flags;
3304    };
3305
3306    const FwdDeclAnon = struct {
3307        fields_len: u32,
3308    };
3309
3310    const AggregateAnon = struct {
3311        index: InternPool.Index,
3312        id: u32,
3313        fields_len: u32,
3314    };
3315
3316    const Aggregate = struct {
3317        fwd_decl: CType.Index,
3318        fields_len: u32,
3319    };
3320
3321    const Function = struct {
3322        return_ctype: CType.Index,
3323        param_ctypes_len: u32,
3324    };
3325
3326    fn addExtra(
3327        pool: *Pool,
3328        allocator: std.mem.Allocator,
3329        comptime Extra: type,
3330        extra: Extra,
3331        trailing_len: usize,
3332    ) !ExtraIndex {
3333        try pool.extra.ensureUnusedCapacity(
3334            allocator,
3335            @typeInfo(Extra).@"struct".fields.len + trailing_len,
3336        );
3337        defer pool.addExtraAssumeCapacity(Extra, extra);
3338        return @intCast(pool.extra.items.len);
3339    }
3340    fn addExtraAssumeCapacity(pool: *Pool, comptime Extra: type, extra: Extra) void {
3341        addExtraAssumeCapacityTo(&pool.extra, Extra, extra);
3342    }
3343    fn addExtraAssumeCapacityTo(
3344        array: *std.ArrayList(u32),
3345        comptime Extra: type,
3346        extra: Extra,
3347    ) void {
3348        inline for (@typeInfo(Extra).@"struct".fields) |field| {
3349            const value = @field(extra, field.name);
3350            array.appendAssumeCapacity(switch (field.type) {
3351                u32 => value,
3352                CType.Index, String.Index, InternPool.Index => @intFromEnum(value),
3353                Aligned.Flags => @bitCast(value),
3354                else => @compileError("bad field type: " ++ field.name ++ ": " ++
3355                    @typeName(field.type)),
3356            });
3357        }
3358    }
3359
3360    fn addHashedExtra(
3361        pool: *Pool,
3362        allocator: std.mem.Allocator,
3363        hasher: *Hasher,
3364        comptime Extra: type,
3365        extra: Extra,
3366        trailing_len: usize,
3367    ) !ExtraIndex {
3368        hasher.updateExtra(Extra, extra, pool);
3369        return pool.addExtra(allocator, Extra, extra, trailing_len);
3370    }
3371    fn addHashedExtraAssumeCapacity(
3372        pool: *Pool,
3373        hasher: *Hasher,
3374        comptime Extra: type,
3375        extra: Extra,
3376    ) void {
3377        hasher.updateExtra(Extra, extra, pool);
3378        pool.addExtraAssumeCapacity(Extra, extra);
3379    }
3380    fn addHashedExtraAssumeCapacityTo(
3381        pool: *Pool,
3382        array: *std.ArrayList(u32),
3383        hasher: *Hasher,
3384        comptime Extra: type,
3385        extra: Extra,
3386    ) void {
3387        hasher.updateExtra(Extra, extra, pool);
3388        addExtraAssumeCapacityTo(array, Extra, extra);
3389    }
3390
3391    const ExtraTrail = struct {
3392        extra_index: ExtraIndex,
3393
3394        fn next(
3395            extra_trail: *ExtraTrail,
3396            len: u32,
3397            comptime Extra: type,
3398            pool: *const Pool,
3399        ) []const Extra {
3400            defer extra_trail.extra_index += @intCast(len);
3401            return @ptrCast(pool.extra.items[extra_trail.extra_index..][0..len]);
3402        }
3403    };
3404
3405    fn getExtraTrail(
3406        pool: *const Pool,
3407        comptime Extra: type,
3408        extra_index: ExtraIndex,
3409    ) struct { extra: Extra, trail: ExtraTrail } {
3410        var extra: Extra = undefined;
3411        const fields = @typeInfo(Extra).@"struct".fields;
3412        inline for (fields, pool.extra.items[extra_index..][0..fields.len]) |field, value|
3413            @field(extra, field.name) = switch (field.type) {
3414                u32 => value,
3415                CType.Index, String.Index, InternPool.Index => @enumFromInt(value),
3416                Aligned.Flags => @bitCast(value),
3417                else => @compileError("bad field type: " ++ field.name ++ ": " ++ @typeName(field.type)),
3418            };
3419        return .{
3420            .extra = extra,
3421            .trail = .{ .extra_index = extra_index + @as(ExtraIndex, @intCast(fields.len)) },
3422        };
3423    }
3424
3425    fn getExtra(pool: *const Pool, comptime Extra: type, extra_index: ExtraIndex) Extra {
3426        return pool.getExtraTrail(Extra, extra_index).extra;
3427    }
3428};
3429
3430pub const AlignAs = packed struct {
3431    @"align": InternPool.Alignment,
3432    abi: InternPool.Alignment,
3433
3434    pub fn fromAlignment(alignas: AlignAs) AlignAs {
3435        assert(alignas.abi != .none);
3436        return .{
3437            .@"align" = if (alignas.@"align" != .none) alignas.@"align" else alignas.abi,
3438            .abi = alignas.abi,
3439        };
3440    }
3441    pub fn fromAbiAlignment(abi: InternPool.Alignment) AlignAs {
3442        assert(abi != .none);
3443        return .{ .@"align" = abi, .abi = abi };
3444    }
3445    pub fn fromByteUnits(@"align": u64, abi: u64) AlignAs {
3446        return fromAlignment(.{
3447            .@"align" = InternPool.Alignment.fromByteUnits(@"align"),
3448            .abi = InternPool.Alignment.fromNonzeroByteUnits(abi),
3449        });
3450    }
3451
3452    pub fn order(lhs: AlignAs, rhs: AlignAs) std.math.Order {
3453        return lhs.@"align".order(rhs.@"align");
3454    }
3455    pub fn abiOrder(alignas: AlignAs) std.math.Order {
3456        return alignas.@"align".order(alignas.abi);
3457    }
3458    pub fn toByteUnits(alignas: AlignAs) u64 {
3459        return alignas.@"align".toByteUnits().?;
3460    }
3461};
3462
3463const std = @import("std");
3464const assert = std.debug.assert;
3465const Writer = std.Io.Writer;
3466
3467const CType = @This();
3468const InternPool = @import("../../InternPool.zig");
3469const Module = @import("../../Package/Module.zig");
3470const Type = @import("../../Type.zig");
3471const Value = @import("../../Value.zig");
3472const Zcu = @import("../../Zcu.zig");