zig

fork of https://codeberg.org/ziglang/zig
Log | Files | Refs | README | LICENSE

Interner.zig (26780B) - Raw


      1 const std = @import("std");
      2 const Allocator = std.mem.Allocator;
      3 const assert = std.debug.assert;
      4 const BigIntConst = std.math.big.int.Const;
      5 const BigIntMutable = std.math.big.int.Mutable;
      6 const Hash = std.hash.Wyhash;
      7 const Limb = std.math.big.Limb;
      8 
      9 const Interner = @This();
     10 
     11 map: std.AutoArrayHashMapUnmanaged(void, void) = .empty,
     12 items: std.MultiArrayList(struct {
     13     tag: Tag,
     14     data: u32,
     15 }) = .{},
     16 extra: std.ArrayListUnmanaged(u32) = .empty,
     17 limbs: std.ArrayListUnmanaged(Limb) = .empty,
     18 strings: std.ArrayListUnmanaged(u8) = .empty,
     19 
     20 const KeyAdapter = struct {
     21     interner: *const Interner,
     22 
     23     pub fn eql(adapter: KeyAdapter, a: Key, b_void: void, b_map_index: usize) bool {
     24         _ = b_void;
     25         return adapter.interner.get(@as(Ref, @enumFromInt(b_map_index))).eql(a);
     26     }
     27 
     28     pub fn hash(adapter: KeyAdapter, a: Key) u32 {
     29         _ = adapter;
     30         return a.hash();
     31     }
     32 };
     33 
     34 pub const Key = union(enum) {
     35     int_ty: u16,
     36     float_ty: u16,
     37     complex_ty: u16,
     38     ptr_ty,
     39     noreturn_ty,
     40     void_ty,
     41     func_ty,
     42     array_ty: struct {
     43         len: u64,
     44         child: Ref,
     45     },
     46     vector_ty: struct {
     47         len: u32,
     48         child: Ref,
     49     },
     50     record_ty: []const Ref,
     51     /// May not be zero
     52     null,
     53     int: union(enum) {
     54         u64: u64,
     55         i64: i64,
     56         big_int: BigIntConst,
     57 
     58         pub fn toBigInt(repr: @This(), space: *Tag.Int.BigIntSpace) BigIntConst {
     59             return switch (repr) {
     60                 .big_int => |x| x,
     61                 inline .u64, .i64 => |x| BigIntMutable.init(&space.limbs, x).toConst(),
     62             };
     63         }
     64     },
     65     float: Float,
     66     complex: Complex,
     67     bytes: []const u8,
     68 
     69     pub const Float = union(enum) {
     70         f16: f16,
     71         f32: f32,
     72         f64: f64,
     73         f80: f80,
     74         f128: f128,
     75     };
     76     pub const Complex = union(enum) {
     77         cf16: [2]f16,
     78         cf32: [2]f32,
     79         cf64: [2]f64,
     80         cf80: [2]f80,
     81         cf128: [2]f128,
     82     };
     83 
     84     pub fn hash(key: Key) u32 {
     85         var hasher = Hash.init(0);
     86         const tag = std.meta.activeTag(key);
     87         std.hash.autoHash(&hasher, tag);
     88         switch (key) {
     89             .bytes => |bytes| {
     90                 hasher.update(bytes);
     91             },
     92             .record_ty => |elems| for (elems) |elem| {
     93                 std.hash.autoHash(&hasher, elem);
     94             },
     95             .float => |repr| switch (repr) {
     96                 inline else => |data| std.hash.autoHash(
     97                     &hasher,
     98                     @as(std.meta.Int(.unsigned, @bitSizeOf(@TypeOf(data))), @bitCast(data)),
     99                 ),
    100             },
    101             .complex => |repr| switch (repr) {
    102                 inline else => |data| std.hash.autoHash(
    103                     &hasher,
    104                     @as(std.meta.Int(.unsigned, @bitSizeOf(@TypeOf(data))), @bitCast(data)),
    105                 ),
    106             },
    107             .int => |repr| {
    108                 var space: Tag.Int.BigIntSpace = undefined;
    109                 const big = repr.toBigInt(&space);
    110                 std.hash.autoHash(&hasher, big.positive);
    111                 for (big.limbs) |limb| std.hash.autoHash(&hasher, limb);
    112             },
    113             inline else => |info| {
    114                 std.hash.autoHash(&hasher, info);
    115             },
    116         }
    117         return @truncate(hasher.final());
    118     }
    119 
    120     pub fn eql(a: Key, b: Key) bool {
    121         const KeyTag = std.meta.Tag(Key);
    122         const a_tag: KeyTag = a;
    123         const b_tag: KeyTag = b;
    124         if (a_tag != b_tag) return false;
    125         switch (a) {
    126             .record_ty => |a_elems| {
    127                 const b_elems = b.record_ty;
    128                 if (a_elems.len != b_elems.len) return false;
    129                 for (a_elems, b_elems) |a_elem, b_elem| {
    130                     if (a_elem != b_elem) return false;
    131                 }
    132                 return true;
    133             },
    134             .bytes => |a_bytes| {
    135                 const b_bytes = b.bytes;
    136                 return std.mem.eql(u8, a_bytes, b_bytes);
    137             },
    138             .int => |a_repr| {
    139                 var a_space: Tag.Int.BigIntSpace = undefined;
    140                 const a_big = a_repr.toBigInt(&a_space);
    141                 var b_space: Tag.Int.BigIntSpace = undefined;
    142                 const b_big = b.int.toBigInt(&b_space);
    143 
    144                 return a_big.eql(b_big);
    145             },
    146             inline else => |a_info, tag| {
    147                 const b_info = @field(b, @tagName(tag));
    148                 return std.meta.eql(a_info, b_info);
    149             },
    150         }
    151     }
    152 
    153     fn toRef(key: Key) ?Ref {
    154         switch (key) {
    155             .int_ty => |bits| switch (bits) {
    156                 1 => return .i1,
    157                 8 => return .i8,
    158                 16 => return .i16,
    159                 32 => return .i32,
    160                 64 => return .i64,
    161                 128 => return .i128,
    162                 else => {},
    163             },
    164             .float_ty => |bits| switch (bits) {
    165                 16 => return .f16,
    166                 32 => return .f32,
    167                 64 => return .f64,
    168                 80 => return .f80,
    169                 128 => return .f128,
    170                 else => unreachable,
    171             },
    172             .complex_ty => |bits| switch (bits) {
    173                 16 => return .cf16,
    174                 32 => return .cf32,
    175                 64 => return .cf64,
    176                 80 => return .cf80,
    177                 128 => return .cf128,
    178                 else => unreachable,
    179             },
    180             .ptr_ty => return .ptr,
    181             .func_ty => return .func,
    182             .noreturn_ty => return .noreturn,
    183             .void_ty => return .void,
    184             .int => |repr| {
    185                 var space: Tag.Int.BigIntSpace = undefined;
    186                 const big = repr.toBigInt(&space);
    187                 if (big.eqlZero()) return .zero;
    188                 const big_one = BigIntConst{ .limbs = &.{1}, .positive = true };
    189                 if (big.eql(big_one)) return .one;
    190             },
    191             .float => |repr| switch (repr) {
    192                 inline else => |data| {
    193                     if (std.math.isPositiveZero(data)) return .zero;
    194                     if (data == 1) return .one;
    195                 },
    196             },
    197             .null => return .null,
    198             else => {},
    199         }
    200         return null;
    201     }
    202 };
    203 
    204 pub const Ref = enum(u32) {
    205     const max = std.math.maxInt(u32);
    206 
    207     ptr = max - 1,
    208     noreturn = max - 2,
    209     void = max - 3,
    210     i1 = max - 4,
    211     i8 = max - 5,
    212     i16 = max - 6,
    213     i32 = max - 7,
    214     i64 = max - 8,
    215     i128 = max - 9,
    216     f16 = max - 10,
    217     f32 = max - 11,
    218     f64 = max - 12,
    219     f80 = max - 13,
    220     f128 = max - 14,
    221     func = max - 15,
    222     zero = max - 16,
    223     one = max - 17,
    224     null = max - 18,
    225     cf16 = max - 19,
    226     cf32 = max - 20,
    227     cf64 = max - 21,
    228     cf80 = max - 22,
    229     cf128 = max - 23,
    230     _,
    231 };
    232 
    233 pub const OptRef = enum(u32) {
    234     const max = std.math.maxInt(u32);
    235 
    236     none = max - 0,
    237     ptr = max - 1,
    238     noreturn = max - 2,
    239     void = max - 3,
    240     i1 = max - 4,
    241     i8 = max - 5,
    242     i16 = max - 6,
    243     i32 = max - 7,
    244     i64 = max - 8,
    245     i128 = max - 9,
    246     f16 = max - 10,
    247     f32 = max - 11,
    248     f64 = max - 12,
    249     f80 = max - 13,
    250     f128 = max - 14,
    251     func = max - 15,
    252     zero = max - 16,
    253     one = max - 17,
    254     null = max - 18,
    255     cf16 = max - 19,
    256     cf32 = max - 20,
    257     cf64 = max - 21,
    258     cf80 = max - 22,
    259     cf128 = max - 23,
    260     _,
    261 };
    262 
    263 pub const Tag = enum(u8) {
    264     /// `data` is `u16`
    265     int_ty,
    266     /// `data` is `u16`
    267     float_ty,
    268     /// `data` is `u16`
    269     complex_ty,
    270     /// `data` is index to `Array`
    271     array_ty,
    272     /// `data` is index to `Vector`
    273     vector_ty,
    274     /// `data` is `u32`
    275     u32,
    276     /// `data` is `i32`
    277     i32,
    278     /// `data` is `Int`
    279     int_positive,
    280     /// `data` is `Int`
    281     int_negative,
    282     /// `data` is `f16`
    283     f16,
    284     /// `data` is `f32`
    285     f32,
    286     /// `data` is `F64`
    287     f64,
    288     /// `data` is `F80`
    289     f80,
    290     /// `data` is `F128`
    291     f128,
    292     /// `data` is `CF16`
    293     cf16,
    294     /// `data` is `CF32`
    295     cf32,
    296     /// `data` is `CF64`
    297     cf64,
    298     /// `data` is `CF80`
    299     cf80,
    300     /// `data` is `CF128`
    301     cf128,
    302     /// `data` is `Bytes`
    303     bytes,
    304     /// `data` is `Record`
    305     record_ty,
    306 
    307     pub const Array = struct {
    308         len0: u32,
    309         len1: u32,
    310         child: Ref,
    311 
    312         pub fn getLen(a: Array) u64 {
    313             return (PackedU64{
    314                 .a = a.len0,
    315                 .b = a.len1,
    316             }).get();
    317         }
    318     };
    319 
    320     pub const Vector = struct {
    321         len: u32,
    322         child: Ref,
    323     };
    324 
    325     pub const Int = struct {
    326         limbs_index: u32,
    327         limbs_len: u32,
    328 
    329         /// Big enough to fit any non-BigInt value
    330         pub const BigIntSpace = struct {
    331             /// The +1 is headroom so that operations such as incrementing once
    332             /// or decrementing once are possible without using an allocator.
    333             limbs: [(@sizeOf(u64) / @sizeOf(std.math.big.Limb)) + 1]std.math.big.Limb,
    334         };
    335     };
    336 
    337     pub const F64 = struct {
    338         piece0: u32,
    339         piece1: u32,
    340 
    341         pub fn get(self: F64) f64 {
    342             const int_bits = @as(u64, self.piece0) | (@as(u64, self.piece1) << 32);
    343             return @bitCast(int_bits);
    344         }
    345 
    346         fn pack(val: f64) F64 {
    347             const bits = @as(u64, @bitCast(val));
    348             return .{
    349                 .piece0 = @as(u32, @truncate(bits)),
    350                 .piece1 = @as(u32, @truncate(bits >> 32)),
    351             };
    352         }
    353     };
    354 
    355     pub const F80 = struct {
    356         piece0: u32,
    357         piece1: u32,
    358         piece2: u32, // u16 part, top bits
    359 
    360         pub fn get(self: F80) f80 {
    361             const int_bits = @as(u80, self.piece0) |
    362                 (@as(u80, self.piece1) << 32) |
    363                 (@as(u80, self.piece2) << 64);
    364             return @bitCast(int_bits);
    365         }
    366 
    367         fn pack(val: f80) F80 {
    368             const bits = @as(u80, @bitCast(val));
    369             return .{
    370                 .piece0 = @as(u32, @truncate(bits)),
    371                 .piece1 = @as(u32, @truncate(bits >> 32)),
    372                 .piece2 = @as(u16, @truncate(bits >> 64)),
    373             };
    374         }
    375     };
    376 
    377     pub const F128 = struct {
    378         piece0: u32,
    379         piece1: u32,
    380         piece2: u32,
    381         piece3: u32,
    382 
    383         pub fn get(self: F128) f128 {
    384             const int_bits = @as(u128, self.piece0) |
    385                 (@as(u128, self.piece1) << 32) |
    386                 (@as(u128, self.piece2) << 64) |
    387                 (@as(u128, self.piece3) << 96);
    388             return @bitCast(int_bits);
    389         }
    390 
    391         fn pack(val: f128) F128 {
    392             const bits = @as(u128, @bitCast(val));
    393             return .{
    394                 .piece0 = @as(u32, @truncate(bits)),
    395                 .piece1 = @as(u32, @truncate(bits >> 32)),
    396                 .piece2 = @as(u32, @truncate(bits >> 64)),
    397                 .piece3 = @as(u32, @truncate(bits >> 96)),
    398             };
    399         }
    400     };
    401 
    402     pub const CF16 = struct {
    403         piece0: u32,
    404 
    405         pub fn get(self: CF16) [2]f16 {
    406             const real: f16 = @bitCast(@as(u16, @truncate(self.piece0 >> 16)));
    407             const imag: f16 = @bitCast(@as(u16, @truncate(self.piece0)));
    408             return .{
    409                 real,
    410                 imag,
    411             };
    412         }
    413 
    414         fn pack(val: [2]f16) CF16 {
    415             const real: u16 = @bitCast(val[0]);
    416             const imag: u16 = @bitCast(val[1]);
    417             return .{
    418                 .piece0 = (@as(u32, real) << 16) | @as(u32, imag),
    419             };
    420         }
    421     };
    422 
    423     pub const CF32 = struct {
    424         piece0: u32,
    425         piece1: u32,
    426 
    427         pub fn get(self: CF32) [2]f32 {
    428             return .{
    429                 @bitCast(self.piece0),
    430                 @bitCast(self.piece1),
    431             };
    432         }
    433 
    434         fn pack(val: [2]f32) CF32 {
    435             return .{
    436                 .piece0 = @bitCast(val[0]),
    437                 .piece1 = @bitCast(val[1]),
    438             };
    439         }
    440     };
    441 
    442     pub const CF64 = struct {
    443         piece0: u32,
    444         piece1: u32,
    445         piece2: u32,
    446         piece3: u32,
    447 
    448         pub fn get(self: CF64) [2]f64 {
    449             return .{
    450                 (F64{ .piece0 = self.piece0, .piece1 = self.piece1 }).get(),
    451                 (F64{ .piece0 = self.piece2, .piece1 = self.piece3 }).get(),
    452             };
    453         }
    454 
    455         fn pack(val: [2]f64) CF64 {
    456             const real = F64.pack(val[0]);
    457             const imag = F64.pack(val[1]);
    458             return .{
    459                 .piece0 = real.piece0,
    460                 .piece1 = real.piece1,
    461                 .piece2 = imag.piece0,
    462                 .piece3 = imag.piece1,
    463             };
    464         }
    465     };
    466 
    467     /// TODO pack into 5 pieces
    468     pub const CF80 = struct {
    469         piece0: u32,
    470         piece1: u32,
    471         piece2: u32, // u16 part, top bits
    472         piece3: u32,
    473         piece4: u32,
    474         piece5: u32, // u16 part, top bits
    475 
    476         pub fn get(self: CF80) [2]f80 {
    477             return .{
    478                 (F80{ .piece0 = self.piece0, .piece1 = self.piece1, .piece2 = self.piece2 }).get(),
    479                 (F80{ .piece0 = self.piece3, .piece1 = self.piece4, .piece2 = self.piece5 }).get(),
    480             };
    481         }
    482 
    483         fn pack(val: [2]f80) CF80 {
    484             const real = F80.pack(val[0]);
    485             const imag = F80.pack(val[1]);
    486             return .{
    487                 .piece0 = real.piece0,
    488                 .piece1 = real.piece1,
    489                 .piece2 = real.piece2,
    490                 .piece3 = imag.piece0,
    491                 .piece4 = imag.piece1,
    492                 .piece5 = imag.piece2,
    493             };
    494         }
    495     };
    496 
    497     pub const CF128 = struct {
    498         piece0: u32,
    499         piece1: u32,
    500         piece2: u32,
    501         piece3: u32,
    502         piece4: u32,
    503         piece5: u32,
    504         piece6: u32,
    505         piece7: u32,
    506 
    507         pub fn get(self: CF128) [2]f128 {
    508             return .{
    509                 (F128{ .piece0 = self.piece0, .piece1 = self.piece1, .piece2 = self.piece2, .piece3 = self.piece3 }).get(),
    510                 (F128{ .piece0 = self.piece4, .piece1 = self.piece5, .piece2 = self.piece6, .piece3 = self.piece7 }).get(),
    511             };
    512         }
    513 
    514         fn pack(val: [2]f128) CF128 {
    515             const real = F128.pack(val[0]);
    516             const imag = F128.pack(val[1]);
    517             return .{
    518                 .piece0 = real.piece0,
    519                 .piece1 = real.piece1,
    520                 .piece2 = real.piece2,
    521                 .piece3 = real.piece3,
    522                 .piece4 = imag.piece0,
    523                 .piece5 = imag.piece1,
    524                 .piece6 = imag.piece2,
    525                 .piece7 = imag.piece3,
    526             };
    527         }
    528     };
    529 
    530     pub const Bytes = struct {
    531         strings_index: u32,
    532         len: u32,
    533     };
    534 
    535     pub const Record = struct {
    536         elements_len: u32,
    537         // trailing
    538         // [elements_len]Ref
    539     };
    540 };
    541 
    542 pub const PackedU64 = packed struct(u64) {
    543     a: u32,
    544     b: u32,
    545 
    546     pub fn get(x: PackedU64) u64 {
    547         return @bitCast(x);
    548     }
    549 
    550     pub fn init(x: u64) PackedU64 {
    551         return @bitCast(x);
    552     }
    553 };
    554 
    555 pub fn deinit(i: *Interner, gpa: Allocator) void {
    556     i.map.deinit(gpa);
    557     i.items.deinit(gpa);
    558     i.extra.deinit(gpa);
    559     i.limbs.deinit(gpa);
    560     i.strings.deinit(gpa);
    561 }
    562 
    563 pub fn put(i: *Interner, gpa: Allocator, key: Key) !Ref {
    564     if (key.toRef()) |some| return some;
    565     const adapter: KeyAdapter = .{ .interner = i };
    566     const gop = try i.map.getOrPutAdapted(gpa, key, adapter);
    567     if (gop.found_existing) return @enumFromInt(gop.index);
    568     try i.items.ensureUnusedCapacity(gpa, 1);
    569 
    570     switch (key) {
    571         .int_ty => |bits| {
    572             i.items.appendAssumeCapacity(.{
    573                 .tag = .int_ty,
    574                 .data = bits,
    575             });
    576         },
    577         .float_ty => |bits| {
    578             i.items.appendAssumeCapacity(.{
    579                 .tag = .float_ty,
    580                 .data = bits,
    581             });
    582         },
    583         .complex_ty => |bits| {
    584             i.items.appendAssumeCapacity(.{
    585                 .tag = .complex_ty,
    586                 .data = bits,
    587             });
    588         },
    589         .array_ty => |info| {
    590             const split_len = PackedU64.init(info.len);
    591             i.items.appendAssumeCapacity(.{
    592                 .tag = .array_ty,
    593                 .data = try i.addExtra(gpa, Tag.Array{
    594                     .len0 = split_len.a,
    595                     .len1 = split_len.b,
    596                     .child = info.child,
    597                 }),
    598             });
    599         },
    600         .vector_ty => |info| {
    601             i.items.appendAssumeCapacity(.{
    602                 .tag = .vector_ty,
    603                 .data = try i.addExtra(gpa, Tag.Vector{
    604                     .len = info.len,
    605                     .child = info.child,
    606                 }),
    607             });
    608         },
    609         .int => |repr| int: {
    610             var space: Tag.Int.BigIntSpace = undefined;
    611             const big = repr.toBigInt(&space);
    612             switch (repr) {
    613                 .u64 => |data| if (std.math.cast(u32, data)) |small| {
    614                     i.items.appendAssumeCapacity(.{
    615                         .tag = .u32,
    616                         .data = small,
    617                     });
    618                     break :int;
    619                 },
    620                 .i64 => |data| if (std.math.cast(i32, data)) |small| {
    621                     i.items.appendAssumeCapacity(.{
    622                         .tag = .i32,
    623                         .data = @bitCast(small),
    624                     });
    625                     break :int;
    626                 },
    627                 .big_int => |data| {
    628                     if (data.fitsInTwosComp(.unsigned, 32)) {
    629                         i.items.appendAssumeCapacity(.{
    630                             .tag = .u32,
    631                             .data = data.toInt(u32) catch unreachable,
    632                         });
    633                         break :int;
    634                     } else if (data.fitsInTwosComp(.signed, 32)) {
    635                         i.items.appendAssumeCapacity(.{
    636                             .tag = .i32,
    637                             .data = @bitCast(data.toInt(i32) catch unreachable),
    638                         });
    639                         break :int;
    640                     }
    641                 },
    642             }
    643             const limbs_index: u32 = @intCast(i.limbs.items.len);
    644             try i.limbs.appendSlice(gpa, big.limbs);
    645             i.items.appendAssumeCapacity(.{
    646                 .tag = if (big.positive) .int_positive else .int_negative,
    647                 .data = try i.addExtra(gpa, Tag.Int{
    648                     .limbs_index = limbs_index,
    649                     .limbs_len = @intCast(big.limbs.len),
    650                 }),
    651             });
    652         },
    653         .float => |repr| switch (repr) {
    654             .f16 => |data| i.items.appendAssumeCapacity(.{
    655                 .tag = .f16,
    656                 .data = @as(u16, @bitCast(data)),
    657             }),
    658             .f32 => |data| i.items.appendAssumeCapacity(.{
    659                 .tag = .f32,
    660                 .data = @as(u32, @bitCast(data)),
    661             }),
    662             .f64 => |data| i.items.appendAssumeCapacity(.{
    663                 .tag = .f64,
    664                 .data = try i.addExtra(gpa, Tag.F64.pack(data)),
    665             }),
    666             .f80 => |data| i.items.appendAssumeCapacity(.{
    667                 .tag = .f80,
    668                 .data = try i.addExtra(gpa, Tag.F80.pack(data)),
    669             }),
    670             .f128 => |data| i.items.appendAssumeCapacity(.{
    671                 .tag = .f128,
    672                 .data = try i.addExtra(gpa, Tag.F128.pack(data)),
    673             }),
    674         },
    675         .complex => |repr| switch (repr) {
    676             .cf16 => |data| i.items.appendAssumeCapacity(.{
    677                 .tag = .cf16,
    678                 .data = try i.addExtra(gpa, Tag.CF16.pack(data)),
    679             }),
    680             .cf32 => |data| i.items.appendAssumeCapacity(.{
    681                 .tag = .cf32,
    682                 .data = try i.addExtra(gpa, Tag.CF32.pack(data)),
    683             }),
    684             .cf64 => |data| i.items.appendAssumeCapacity(.{
    685                 .tag = .cf64,
    686                 .data = try i.addExtra(gpa, Tag.CF64.pack(data)),
    687             }),
    688             .cf80 => |data| i.items.appendAssumeCapacity(.{
    689                 .tag = .cf80,
    690                 .data = try i.addExtra(gpa, Tag.CF80.pack(data)),
    691             }),
    692             .cf128 => |data| i.items.appendAssumeCapacity(.{
    693                 .tag = .cf128,
    694                 .data = try i.addExtra(gpa, Tag.CF128.pack(data)),
    695             }),
    696         },
    697         .bytes => |bytes| {
    698             const strings_index: u32 = @intCast(i.strings.items.len);
    699             try i.strings.appendSlice(gpa, bytes);
    700             i.items.appendAssumeCapacity(.{
    701                 .tag = .bytes,
    702                 .data = try i.addExtra(gpa, Tag.Bytes{
    703                     .strings_index = strings_index,
    704                     .len = @intCast(bytes.len),
    705                 }),
    706             });
    707         },
    708         .record_ty => |elems| {
    709             try i.extra.ensureUnusedCapacity(gpa, @typeInfo(Tag.Record).@"struct".fields.len +
    710                 elems.len);
    711             i.items.appendAssumeCapacity(.{
    712                 .tag = .record_ty,
    713                 .data = i.addExtraAssumeCapacity(Tag.Record{
    714                     .elements_len = @intCast(elems.len),
    715                 }),
    716             });
    717             i.extra.appendSliceAssumeCapacity(@ptrCast(elems));
    718         },
    719         .ptr_ty,
    720         .noreturn_ty,
    721         .void_ty,
    722         .func_ty,
    723         .null,
    724         => unreachable,
    725     }
    726 
    727     return @enumFromInt(gop.index);
    728 }
    729 
    730 fn addExtra(i: *Interner, gpa: Allocator, extra: anytype) Allocator.Error!u32 {
    731     const fields = @typeInfo(@TypeOf(extra)).@"struct".fields;
    732     try i.extra.ensureUnusedCapacity(gpa, fields.len);
    733     return i.addExtraAssumeCapacity(extra);
    734 }
    735 
    736 fn addExtraAssumeCapacity(i: *Interner, extra: anytype) u32 {
    737     const result = @as(u32, @intCast(i.extra.items.len));
    738     inline for (@typeInfo(@TypeOf(extra)).@"struct".fields) |field| {
    739         i.extra.appendAssumeCapacity(switch (field.type) {
    740             Ref => @intFromEnum(@field(extra, field.name)),
    741             u32 => @field(extra, field.name),
    742             else => @compileError("bad field type: " ++ @typeName(field.type)),
    743         });
    744     }
    745     return result;
    746 }
    747 
    748 pub fn get(i: *const Interner, ref: Ref) Key {
    749     switch (ref) {
    750         .ptr => return .ptr_ty,
    751         .func => return .func_ty,
    752         .noreturn => return .noreturn_ty,
    753         .void => return .void_ty,
    754         .i1 => return .{ .int_ty = 1 },
    755         .i8 => return .{ .int_ty = 8 },
    756         .i16 => return .{ .int_ty = 16 },
    757         .i32 => return .{ .int_ty = 32 },
    758         .i64 => return .{ .int_ty = 64 },
    759         .i128 => return .{ .int_ty = 128 },
    760         .f16 => return .{ .float_ty = 16 },
    761         .f32 => return .{ .float_ty = 32 },
    762         .f64 => return .{ .float_ty = 64 },
    763         .f80 => return .{ .float_ty = 80 },
    764         .f128 => return .{ .float_ty = 128 },
    765         .zero => return .{ .int = .{ .u64 = 0 } },
    766         .one => return .{ .int = .{ .u64 = 1 } },
    767         .null => return .null,
    768         .cf16 => return .{ .complex_ty = 16 },
    769         .cf32 => return .{ .complex_ty = 32 },
    770         .cf64 => return .{ .complex_ty = 64 },
    771         .cf80 => return .{ .complex_ty = 80 },
    772         else => {},
    773     }
    774 
    775     const item = i.items.get(@intFromEnum(ref));
    776     const data = item.data;
    777     return switch (item.tag) {
    778         .int_ty => .{ .int_ty = @intCast(data) },
    779         .float_ty => .{ .float_ty = @intCast(data) },
    780         .complex_ty => .{ .complex_ty = @intCast(data) },
    781         .array_ty => {
    782             const array_ty = i.extraData(Tag.Array, data);
    783             return .{ .array_ty = .{
    784                 .len = array_ty.getLen(),
    785                 .child = array_ty.child,
    786             } };
    787         },
    788         .vector_ty => {
    789             const vector_ty = i.extraData(Tag.Vector, data);
    790             return .{ .vector_ty = .{
    791                 .len = vector_ty.len,
    792                 .child = vector_ty.child,
    793             } };
    794         },
    795         .u32 => .{ .int = .{ .u64 = data } },
    796         .i32 => .{ .int = .{ .i64 = @as(i32, @bitCast(data)) } },
    797         .int_positive, .int_negative => {
    798             const int_info = i.extraData(Tag.Int, data);
    799             const limbs = i.limbs.items[int_info.limbs_index..][0..int_info.limbs_len];
    800             return .{ .int = .{
    801                 .big_int = .{
    802                     .positive = item.tag == .int_positive,
    803                     .limbs = limbs,
    804                 },
    805             } };
    806         },
    807         .f16 => .{ .float = .{ .f16 = @bitCast(@as(u16, @intCast(data))) } },
    808         .f32 => .{ .float = .{ .f32 = @bitCast(data) } },
    809         .f64 => {
    810             const float = i.extraData(Tag.F64, data);
    811             return .{ .float = .{ .f64 = float.get() } };
    812         },
    813         .f80 => {
    814             const float = i.extraData(Tag.F80, data);
    815             return .{ .float = .{ .f80 = float.get() } };
    816         },
    817         .f128 => {
    818             const float = i.extraData(Tag.F128, data);
    819             return .{ .float = .{ .f128 = float.get() } };
    820         },
    821         .cf16 => {
    822             const components = i.extraData(Tag.CF16, data);
    823             return .{ .complex = .{ .cf16 = components.get() } };
    824         },
    825         .cf32 => {
    826             const components = i.extraData(Tag.CF32, data);
    827             return .{ .complex = .{ .cf32 = components.get() } };
    828         },
    829         .cf64 => {
    830             const components = i.extraData(Tag.CF64, data);
    831             return .{ .complex = .{ .cf64 = components.get() } };
    832         },
    833         .cf80 => {
    834             const components = i.extraData(Tag.CF80, data);
    835             return .{ .complex = .{ .cf80 = components.get() } };
    836         },
    837         .cf128 => {
    838             const components = i.extraData(Tag.CF128, data);
    839             return .{ .complex = .{ .cf128 = components.get() } };
    840         },
    841         .bytes => {
    842             const bytes = i.extraData(Tag.Bytes, data);
    843             return .{ .bytes = i.strings.items[bytes.strings_index..][0..bytes.len] };
    844         },
    845         .record_ty => {
    846             const extra = i.extraDataTrail(Tag.Record, data);
    847             return .{
    848                 .record_ty = @ptrCast(i.extra.items[extra.end..][0..extra.data.elements_len]),
    849             };
    850         },
    851     };
    852 }
    853 
    854 fn extraData(i: *const Interner, comptime T: type, index: usize) T {
    855     return i.extraDataTrail(T, index).data;
    856 }
    857 
    858 fn extraDataTrail(i: *const Interner, comptime T: type, index: usize) struct { data: T, end: u32 } {
    859     var result: T = undefined;
    860     const fields = @typeInfo(T).@"struct".fields;
    861     inline for (fields, 0..) |field, field_i| {
    862         const int32 = i.extra.items[field_i + index];
    863         @field(result, field.name) = switch (field.type) {
    864             Ref => @enumFromInt(int32),
    865             u32 => int32,
    866             else => @compileError("bad field type: " ++ @typeName(field.type)),
    867         };
    868     }
    869     return .{
    870         .data = result,
    871         .end = @intCast(index + fields.len),
    872     };
    873 }