| ... | ... | @@ -0,0 +1,1457 @@ |
| 1 | const std = @import("std"); |
| 2 | const cstr = std.cstr; |
| 3 | const mem = std.mem; |
| 4 | const Allocator = mem.Allocator; |
| 5 | const assert = std.debug.assert; |
| 6 | const autoHash = std.hash.autoHash; |
| 7 | const Target = std.Target; |
| 8 | |
| 9 | const Module = @import("../../Module.zig"); |
| 10 | const Type = @import("../../type.zig").Type; |
| 11 | |
| 12 | pub const CType = extern union { |
| 13 | /// If the tag value is less than Tag.no_payload_count, then no pointer |
| 14 | /// dereference is needed. |
| 15 | tag_if_small_enough: Tag, |
| 16 | ptr_otherwise: *const Payload, |
| 17 | |
| 18 | pub fn initTag(small_tag: Tag) CType { |
| 19 | assert(!small_tag.hasPayload()); |
| 20 | return .{ .tag_if_small_enough = small_tag }; |
| 21 | } |
| 22 | |
| 23 | pub fn initPayload(pl: anytype) CType { |
| 24 | const T = @typeInfo(@TypeOf(pl)).Pointer.child; |
| 25 | return switch (pl.base.tag) { |
| 26 | inline else => |t| if (comptime t.hasPayload() and t.Type() == T) .{ |
| 27 | .ptr_otherwise = &pl.base, |
| 28 | } else unreachable, |
| 29 | }; |
| 30 | } |
| 31 | |
| 32 | pub fn hasPayload(self: CType) bool { |
| 33 | return self.tag_if_small_enough.hasPayload(); |
| 34 | } |
| 35 | |
| 36 | pub fn tag(self: CType) Tag { |
| 37 | return if (self.hasPayload()) self.ptr_otherwise.tag else self.tag_if_small_enough; |
| 38 | } |
| 39 | |
| 40 | pub fn cast(self: CType, comptime T: type) ?*const T { |
| 41 | if (!self.hasPayload()) return null; |
| 42 | const pl = self.ptr_otherwise; |
| 43 | return switch (pl.tag) { |
| 44 | inline else => |t| if (comptime t.hasPayload() and t.Type() == T) |
| 45 | @fieldParentPtr(T, "base", pl) |
| 46 | else |
| 47 | null, |
| 48 | }; |
| 49 | } |
| 50 | |
| 51 | pub fn castTag(self: CType, comptime t: Tag) ?*const t.Type() { |
| 52 | return if (self.tag() == t) @fieldParentPtr(t.Type(), "base", self.ptr_otherwise) else null; |
| 53 | } |
| 54 | |
| 55 | pub const Tag = enum(usize) { |
| 56 | // The first section of this enum are tags that require no payload. |
| 57 | void, |
| 58 | |
| 59 | // C basic types |
| 60 | char, |
| 61 | |
| 62 | @"signed char", |
| 63 | short, |
| 64 | int, |
| 65 | long, |
| 66 | @"long long", |
| 67 | |
| 68 | _Bool, |
| 69 | @"unsigned char", |
| 70 | @"unsigned short", |
| 71 | @"unsigned int", |
| 72 | @"unsigned long", |
| 73 | @"unsigned long long", |
| 74 | |
| 75 | float, |
| 76 | double, |
| 77 | @"long double", |
| 78 | |
| 79 | // C header types |
| 80 | bool, // stdbool.h |
| 81 | size_t, // stddef.h |
| 82 | ptrdiff_t, // stddef.h |
| 83 | |
| 84 | // zig.h types |
| 85 | zig_u8, |
| 86 | zig_i8, |
| 87 | zig_u16, |
| 88 | zig_i16, |
| 89 | zig_u32, |
| 90 | zig_i32, |
| 91 | zig_u64, |
| 92 | zig_i64, |
| 93 | zig_u128, |
| 94 | zig_i128, |
| 95 | zig_f16, |
| 96 | zig_f32, |
| 97 | zig_f64, |
| 98 | zig_f80, |
| 99 | zig_f128, |
| 100 | |
| 101 | // After this, the tag requires a payload. |
| 102 | pointer, |
| 103 | pointer_const, |
| 104 | pointer_volatile, |
| 105 | pointer_const_volatile, |
| 106 | array, |
| 107 | vector, |
| 108 | fwd_struct, |
| 109 | fwd_union, |
| 110 | anon_struct, |
| 111 | packed_anon_struct, |
| 112 | @"struct", |
| 113 | @"union", |
| 114 | packed_struct, |
| 115 | packed_union, |
| 116 | function, |
| 117 | varargs_function, |
| 118 | |
| 119 | pub const last_no_payload_tag = Tag.zig_f128; |
| 120 | pub const no_payload_count = @enumToInt(last_no_payload_tag) + 1; |
| 121 | |
| 122 | pub fn hasPayload(self: Tag) bool { |
| 123 | return @enumToInt(self) >= no_payload_count; |
| 124 | } |
| 125 | |
| 126 | pub fn toIndex(self: Tag) Index { |
| 127 | assert(!self.hasPayload()); |
| 128 | return @intCast(Index, @enumToInt(self)); |
| 129 | } |
| 130 | |
| 131 | pub fn Type(comptime self: Tag) type { |
| 132 | return switch (self) { |
| 133 | .void, |
| 134 | .char, |
| 135 | .@"signed char", |
| 136 | .short, |
| 137 | .int, |
| 138 | .long, |
| 139 | .@"long long", |
| 140 | ._Bool, |
| 141 | .@"unsigned char", |
| 142 | .@"unsigned short", |
| 143 | .@"unsigned int", |
| 144 | .@"unsigned long", |
| 145 | .@"unsigned long long", |
| 146 | .float, |
| 147 | .double, |
| 148 | .@"long double", |
| 149 | .bool, |
| 150 | .size_t, |
| 151 | .ptrdiff_t, |
| 152 | .zig_u8, |
| 153 | .zig_i8, |
| 154 | .zig_u16, |
| 155 | .zig_i16, |
| 156 | .zig_u32, |
| 157 | .zig_i32, |
| 158 | .zig_u64, |
| 159 | .zig_i64, |
| 160 | .zig_u128, |
| 161 | .zig_i128, |
| 162 | .zig_f16, |
| 163 | .zig_f32, |
| 164 | .zig_f64, |
| 165 | .zig_f80, |
| 166 | .zig_f128, |
| 167 | => @compileError("Type Tag " ++ @tagName(self) ++ " has no payload"), |
| 168 | |
| 169 | .pointer, |
| 170 | .pointer_const, |
| 171 | .pointer_volatile, |
| 172 | .pointer_const_volatile, |
| 173 | => Payload.Child, |
| 174 | |
| 175 | .array, |
| 176 | .vector, |
| 177 | => Payload.Sequence, |
| 178 | |
| 179 | .fwd_struct, |
| 180 | .fwd_union, |
| 181 | => Payload.FwdDecl, |
| 182 | |
| 183 | .anon_struct, |
| 184 | .packed_anon_struct, |
| 185 | => Payload.Fields, |
| 186 | |
| 187 | .@"struct", |
| 188 | .@"union", |
| 189 | .packed_struct, |
| 190 | .packed_union, |
| 191 | => Payload.Aggregate, |
| 192 | |
| 193 | .function, |
| 194 | .varargs_function, |
| 195 | => Payload.Function, |
| 196 | }; |
| 197 | } |
| 198 | }; |
| 199 | |
| 200 | pub const Payload = struct { |
| 201 | tag: Tag, |
| 202 | |
| 203 | pub const Child = struct { |
| 204 | base: Payload, |
| 205 | data: Index, |
| 206 | }; |
| 207 | |
| 208 | pub const Sequence = struct { |
| 209 | base: Payload, |
| 210 | data: struct { |
| 211 | len: u64, |
| 212 | elem_type: Index, |
| 213 | }, |
| 214 | }; |
| 215 | |
| 216 | pub const FwdDecl = struct { |
| 217 | base: Payload, |
| 218 | data: Module.Decl.Index, |
| 219 | }; |
| 220 | |
| 221 | pub const Fields = struct { |
| 222 | base: Payload, |
| 223 | data: Data, |
| 224 | |
| 225 | const Data = []const Field; |
| 226 | const Field = struct { |
| 227 | name: [*:0]const u8, |
| 228 | type: Index, |
| 229 | alignas: u32, |
| 230 | }; |
| 231 | }; |
| 232 | |
| 233 | pub const Aggregate = struct { |
| 234 | base: Payload, |
| 235 | data: struct { |
| 236 | fields: Fields.Data, |
| 237 | fwd_decl: Index, |
| 238 | }, |
| 239 | }; |
| 240 | |
| 241 | pub const Function = struct { |
| 242 | base: Payload, |
| 243 | data: struct { |
| 244 | return_type: Index, |
| 245 | param_types: []const Index, |
| 246 | }, |
| 247 | }; |
| 248 | }; |
| 249 | |
| 250 | pub const Index = u32; |
| 251 | pub const Store = struct { |
| 252 | arena: std.heap.ArenaAllocator.State = .{}, |
| 253 | set: Set = .{}, |
| 254 | |
| 255 | const Set = struct { |
| 256 | const Map = std.ArrayHashMapUnmanaged(CType, void, HashContext32, true); |
| 257 | |
| 258 | map: Map = .{}, |
| 259 | |
| 260 | fn indexToCType(self: Set, index: Index) CType { |
| 261 | if (index < Tag.no_payload_count) return initTag(@intToEnum(Tag, index)); |
| 262 | return self.map.keys()[index - Tag.no_payload_count]; |
| 263 | } |
| 264 | |
| 265 | fn indexToHash(self: Set, index: Index) Map.Hash { |
| 266 | if (index < Tag.no_payload_count) return self.indexToCType(index).hash(self); |
| 267 | return self.map.entries.items(.hash)[index - Tag.no_payload_count]; |
| 268 | } |
| 269 | |
| 270 | fn typeToIndex(self: Set, ty: Type, target: Target, kind: Kind) ?Index { |
| 271 | const lookup = Convert.Lookup{ .imm = .{ .set = &self, .target = target } }; |
| 272 | |
| 273 | var convert: Convert = undefined; |
| 274 | convert.initType(ty, kind, lookup) catch unreachable; |
| 275 | |
| 276 | const t = convert.tag(); |
| 277 | if (!t.hasPayload()) return t.toIndex(); |
| 278 | |
| 279 | return if (self.map.getIndexAdapted( |
| 280 | ty, |
| 281 | TypeAdapter32{ .kind = kind, .lookup = lookup, .convert = &convert }, |
| 282 | )) |idx| @intCast(Index, Tag.no_payload_count + idx) else null; |
| 283 | } |
| 284 | }; |
| 285 | |
| 286 | const Promoted = struct { |
| 287 | arena: std.heap.ArenaAllocator, |
| 288 | set: Set, |
| 289 | |
| 290 | fn gpa(self: *Promoted) Allocator { |
| 291 | return self.arena.child_allocator; |
| 292 | } |
| 293 | |
| 294 | fn cTypeToIndex(self: *Promoted, cty: CType) Allocator.Error!Index { |
| 295 | const t = cty.tag(); |
| 296 | if (@enumToInt(t) < Tag.no_payload_count) return @intCast(Index, @enumToInt(t)); |
| 297 | |
| 298 | const gop = try self.set.map.getOrPutContext(self.gpa(), cty, .{ .store = &self.set }); |
| 299 | if (!gop.found_existing) gop.key_ptr.* = cty; |
| 300 | if (std.debug.runtime_safety) { |
| 301 | const key = self.set.map.entries.items(.key)[gop.index]; |
| 302 | assert(key.eql(cty)); |
| 303 | assert(cty.hash(self.set) == key.hash(self.set)); |
| 304 | } |
| 305 | return @intCast(Index, Tag.no_payload_count + gop.index); |
| 306 | } |
| 307 | |
| 308 | fn typeToIndex(self: *Promoted, ty: Type, mod: *Module, kind: Kind) Allocator.Error!Index { |
| 309 | const lookup = Convert.Lookup{ .mut = .{ .promoted = self, .mod = mod } }; |
| 310 | |
| 311 | var convert: Convert = undefined; |
| 312 | try convert.initType(ty, kind, lookup); |
| 313 | |
| 314 | const t = convert.tag(); |
| 315 | if (!t.hasPayload()) return t.toIndex(); |
| 316 | |
| 317 | const gop = try self.set.map.getOrPutContextAdapted( |
| 318 | self.gpa(), |
| 319 | ty, |
| 320 | TypeAdapter32{ .kind = kind, .lookup = lookup.freeze(), .convert = &convert }, |
| 321 | .{ .store = &self.set }, |
| 322 | ); |
| 323 | if (!gop.found_existing) { |
| 324 | errdefer _ = self.set.map.pop(); |
| 325 | gop.key_ptr.* = try createFromConvert(self, ty, lookup.getTarget(), kind, convert); |
| 326 | } |
| 327 | if (std.debug.runtime_safety) { |
| 328 | const adapter = TypeAdapter64{ |
| 329 | .kind = kind, |
| 330 | .lookup = lookup.freeze(), |
| 331 | .convert = &convert, |
| 332 | }; |
| 333 | const key = self.set.map.entries.items(.key)[gop.index]; |
| 334 | assert(adapter.eql(ty, key)); |
| 335 | assert(adapter.hash(ty) == key.hash(self.set)); |
| 336 | } |
| 337 | return @intCast(Index, Tag.no_payload_count + gop.index); |
| 338 | } |
| 339 | }; |
| 340 | |
| 341 | fn promote(self: Store, gpa: Allocator) Promoted { |
| 342 | return .{ .arena = self.arena.promote(gpa), .set = self.set }; |
| 343 | } |
| 344 | |
| 345 | fn demote(self: *Store, promoted: Promoted) void { |
| 346 | self.arena = promoted.arena.state; |
| 347 | self.set = promoted.set; |
| 348 | } |
| 349 | |
| 350 | pub fn indexToCType(self: Store, index: Index) CType { |
| 351 | return self.set.indexToCType(index); |
| 352 | } |
| 353 | |
| 354 | pub fn cTypeToIndex(self: *Store, gpa: Allocator, cty: CType) !Index { |
| 355 | var promoted = self.promote(gpa); |
| 356 | defer self.demote(promoted); |
| 357 | return promoted.cTypeToIndex(cty); |
| 358 | } |
| 359 | |
| 360 | pub fn typeToCType(self: *Store, gpa: Allocator, ty: Type, mod: *Module) !CType { |
| 361 | const idx = try self.typeToIndex(gpa, ty, mod); |
| 362 | return self.indexToCType(idx); |
| 363 | } |
| 364 | |
| 365 | pub fn typeToIndex(self: *Store, gpa: Allocator, ty: Type, mod: *Module) !Index { |
| 366 | var promoted = self.promote(gpa); |
| 367 | defer self.demote(promoted); |
| 368 | return promoted.typeToIndex(ty, mod, .complete); |
| 369 | } |
| 370 | |
| 371 | pub fn clearRetainingCapacity(self: *Store, gpa: Allocator) void { |
| 372 | var promoted = self.promote(gpa); |
| 373 | defer self.demote(promoted); |
| 374 | promoted.set.map.clearRetainingCapacity(); |
| 375 | _ = promoted.arena.reset(.retain_capacity); |
| 376 | } |
| 377 | |
| 378 | pub fn shrinkToFit(self: *Store, gpa: Allocator) void { |
| 379 | self.map.shrinkAndFree(gpa, self.map.entries.len); |
| 380 | } |
| 381 | |
| 382 | pub fn shrinkAndFree(self: *Store, gpa: Allocator) void { |
| 383 | var promoted = self.promote(gpa); |
| 384 | defer self.demote(promoted); |
| 385 | promoted.set.map.clearAndFree(gpa); |
| 386 | _ = promoted.arena.reset(.free_all); |
| 387 | } |
| 388 | |
| 389 | pub fn move(self: *Store) Store { |
| 390 | const moved = self.*; |
| 391 | self.* = .{}; |
| 392 | return moved; |
| 393 | } |
| 394 | |
| 395 | pub fn deinit(self: *Store, gpa: Allocator) void { |
| 396 | var promoted = self.promote(gpa); |
| 397 | promoted.set.map.deinit(gpa); |
| 398 | _ = promoted.arena.deinit(); |
| 399 | self.* = undefined; |
| 400 | } |
| 401 | }; |
| 402 | |
| 403 | pub fn eql(lhs: CType, rhs: CType) bool { |
| 404 | // As a shortcut, if the small tags / addresses match, we're done. |
| 405 | if (lhs.tag_if_small_enough == rhs.tag_if_small_enough) return true; |
| 406 | |
| 407 | const lhs_tag = lhs.tag(); |
| 408 | const rhs_tag = rhs.tag(); |
| 409 | if (lhs_tag != rhs_tag) return false; |
| 410 | |
| 411 | return switch (lhs_tag) { |
| 412 | .void, |
| 413 | .char, |
| 414 | .@"signed char", |
| 415 | .short, |
| 416 | .int, |
| 417 | .long, |
| 418 | .@"long long", |
| 419 | ._Bool, |
| 420 | .@"unsigned char", |
| 421 | .@"unsigned short", |
| 422 | .@"unsigned int", |
| 423 | .@"unsigned long", |
| 424 | .@"unsigned long long", |
| 425 | .float, |
| 426 | .double, |
| 427 | .@"long double", |
| 428 | .bool, |
| 429 | .size_t, |
| 430 | .ptrdiff_t, |
| 431 | .zig_u8, |
| 432 | .zig_i8, |
| 433 | .zig_u16, |
| 434 | .zig_i16, |
| 435 | .zig_u32, |
| 436 | .zig_i32, |
| 437 | .zig_u64, |
| 438 | .zig_i64, |
| 439 | .zig_u128, |
| 440 | .zig_i128, |
| 441 | .zig_f16, |
| 442 | .zig_f32, |
| 443 | .zig_f64, |
| 444 | .zig_f80, |
| 445 | .zig_f128, |
| 446 | => false, |
| 447 | |
| 448 | .pointer, |
| 449 | .pointer_const, |
| 450 | .pointer_volatile, |
| 451 | .pointer_const_volatile, |
| 452 | => lhs.cast(Payload.Child).?.data == rhs.cast(Payload.Child).?.data, |
| 453 | |
| 454 | .array, |
| 455 | .vector, |
| 456 | => std.meta.eql(lhs.cast(Payload.Sequence).?.data, rhs.cast(Payload.Sequence).?.data), |
| 457 | |
| 458 | .fwd_struct, |
| 459 | .fwd_union, |
| 460 | => lhs.cast(Payload.FwdDecl).?.data == rhs.cast(Payload.FwdDecl).?.data, |
| 461 | |
| 462 | .anon_struct, |
| 463 | .packed_anon_struct, |
| 464 | => { |
| 465 | const lhs_data = lhs.cast(Payload.Fields).?.data; |
| 466 | const rhs_data = rhs.cast(Payload.Fields).?.data; |
| 467 | if (lhs_data.len != rhs_data.len) return false; |
| 468 | for (lhs_data, rhs_data) |lhs_field, rhs_field| { |
| 469 | if (lhs_field.type != rhs_field.type) return false; |
| 470 | if (lhs_field.alignas != rhs_field.alignas) return false; |
| 471 | if (cstr.cmp(lhs_field.name, rhs_field.name) != 0) return false; |
| 472 | } |
| 473 | return true; |
| 474 | }, |
| 475 | |
| 476 | .@"struct", |
| 477 | .@"union", |
| 478 | .packed_struct, |
| 479 | .packed_union, |
| 480 | => std.meta.eql( |
| 481 | lhs.cast(Payload.Aggregate).?.data.fwd_decl, |
| 482 | rhs.cast(Payload.Aggregate).?.data.fwd_decl, |
| 483 | ), |
| 484 | |
| 485 | .function, |
| 486 | .varargs_function, |
| 487 | => { |
| 488 | const lhs_data = lhs.cast(Payload.Function).?.data; |
| 489 | const rhs_data = rhs.cast(Payload.Function).?.data; |
| 490 | if (lhs_data.return_type != rhs_data.return_type) return false; |
| 491 | if (lhs_data.param_types.len != rhs_data.param_types.len) return false; |
| 492 | for (lhs_data.param_types, rhs_data.param_types) |lhs_param_cty, rhs_param_cty| { |
| 493 | if (lhs_param_cty != rhs_param_cty) return false; |
| 494 | } |
| 495 | return true; |
| 496 | }, |
| 497 | }; |
| 498 | } |
| 499 | |
| 500 | pub fn hash(self: CType, store: Store.Set) u64 { |
| 501 | var hasher = std.hash.Wyhash.init(0); |
| 502 | self.updateHasher(&hasher, store); |
| 503 | return hasher.final(); |
| 504 | } |
| 505 | |
| 506 | pub fn updateHasher(self: CType, hasher: anytype, store: Store.Set) void { |
| 507 | const t = self.tag(); |
| 508 | autoHash(hasher, t); |
| 509 | switch (t) { |
| 510 | .void, |
| 511 | .char, |
| 512 | .@"signed char", |
| 513 | .short, |
| 514 | .int, |
| 515 | .long, |
| 516 | .@"long long", |
| 517 | ._Bool, |
| 518 | .@"unsigned char", |
| 519 | .@"unsigned short", |
| 520 | .@"unsigned int", |
| 521 | .@"unsigned long", |
| 522 | .@"unsigned long long", |
| 523 | .float, |
| 524 | .double, |
| 525 | .@"long double", |
| 526 | .bool, |
| 527 | .size_t, |
| 528 | .ptrdiff_t, |
| 529 | .zig_u8, |
| 530 | .zig_i8, |
| 531 | .zig_u16, |
| 532 | .zig_i16, |
| 533 | .zig_u32, |
| 534 | .zig_i32, |
| 535 | .zig_u64, |
| 536 | .zig_i64, |
| 537 | .zig_u128, |
| 538 | .zig_i128, |
| 539 | .zig_f16, |
| 540 | .zig_f32, |
| 541 | .zig_f64, |
| 542 | .zig_f80, |
| 543 | .zig_f128, |
| 544 | => {}, |
| 545 | |
| 546 | .pointer, |
| 547 | .pointer_const, |
| 548 | .pointer_volatile, |
| 549 | .pointer_const_volatile, |
| 550 | => store.indexToCType(self.cast(Payload.Child).?.data).updateHasher(hasher, store), |
| 551 | |
| 552 | .array, |
| 553 | .vector, |
| 554 | => { |
| 555 | const data = self.cast(Payload.Sequence).?.data; |
| 556 | autoHash(hasher, data.len); |
| 557 | store.indexToCType(data.elem_type).updateHasher(hasher, store); |
| 558 | }, |
| 559 | |
| 560 | .fwd_struct, |
| 561 | .fwd_union, |
| 562 | => autoHash(hasher, self.cast(Payload.FwdDecl).?.data), |
| 563 | |
| 564 | .anon_struct, |
| 565 | .packed_anon_struct, |
| 566 | => for (self.cast(Payload.Fields).?.data) |field| { |
| 567 | store.indexToCType(field.type).updateHasher(hasher, store); |
| 568 | hasher.update(mem.span(field.name)); |
| 569 | autoHash(hasher, field.alignas); |
| 570 | }, |
| 571 | |
| 572 | .@"struct", |
| 573 | .@"union", |
| 574 | .packed_struct, |
| 575 | .packed_union, |
| 576 | => store.indexToCType(self.cast(Payload.Aggregate).?.data.fwd_decl) |
| 577 | .updateHasher(hasher, store), |
| 578 | |
| 579 | .function, |
| 580 | .varargs_function, |
| 581 | => { |
| 582 | const data = self.cast(Payload.Function).?.data; |
| 583 | store.indexToCType(data.return_type).updateHasher(hasher, store); |
| 584 | for (data.param_types) |param_ty| { |
| 585 | store.indexToCType(param_ty).updateHasher(hasher, store); |
| 586 | } |
| 587 | }, |
| 588 | } |
| 589 | } |
| 590 | |
| 591 | pub const Kind = enum { forward, complete, global, parameter }; |
| 592 | |
| 593 | const Convert = struct { |
| 594 | storage: union { |
| 595 | none: void, |
| 596 | child: Payload.Child, |
| 597 | seq: Payload.Sequence, |
| 598 | fwd: Payload.FwdDecl, |
| 599 | anon: struct { |
| 600 | fields: [2]Payload.Fields.Field, |
| 601 | pl: Payload.Fields, |
| 602 | }, |
| 603 | agg: Payload.Aggregate, |
| 604 | }, |
| 605 | value: union(enum) { |
| 606 | tag: Tag, |
| 607 | cty: CType, |
| 608 | }, |
| 609 | |
| 610 | pub fn init(self: *@This(), t: Tag) void { |
| 611 | self.* = if (t.hasPayload()) .{ |
| 612 | .storage = .{ .none = {} }, |
| 613 | .value = .{ .tag = t }, |
| 614 | } else .{ |
| 615 | .storage = .{ .none = {} }, |
| 616 | .value = .{ .cty = initTag(t) }, |
| 617 | }; |
| 618 | } |
| 619 | |
| 620 | pub fn tag(self: @This()) Tag { |
| 621 | return switch (self.value) { |
| 622 | .tag => |t| t, |
| 623 | .cty => |c| c.tag(), |
| 624 | }; |
| 625 | } |
| 626 | |
| 627 | fn tagFromIntInfo(signedness: std.builtin.Signedness, bits: u16) Tag { |
| 628 | return switch (bits) { |
| 629 | 0 => .void, |
| 630 | 1...8 => switch (signedness) { |
| 631 | .unsigned => .zig_u8, |
| 632 | .signed => .zig_i8, |
| 633 | }, |
| 634 | 9...16 => switch (signedness) { |
| 635 | .unsigned => .zig_u16, |
| 636 | .signed => .zig_i16, |
| 637 | }, |
| 638 | 17...32 => switch (signedness) { |
| 639 | .unsigned => .zig_u32, |
| 640 | .signed => .zig_i32, |
| 641 | }, |
| 642 | 33...64 => switch (signedness) { |
| 643 | .unsigned => .zig_u64, |
| 644 | .signed => .zig_i64, |
| 645 | }, |
| 646 | 65...128 => switch (signedness) { |
| 647 | .unsigned => .zig_u128, |
| 648 | .signed => .zig_i128, |
| 649 | }, |
| 650 | else => .array, |
| 651 | }; |
| 652 | } |
| 653 | |
| 654 | pub const Lookup = union(enum) { |
| 655 | fail: Target, |
| 656 | imm: struct { |
| 657 | set: *const Store.Set, |
| 658 | target: Target, |
| 659 | }, |
| 660 | mut: struct { |
| 661 | promoted: *Store.Promoted, |
| 662 | mod: *Module, |
| 663 | }, |
| 664 | |
| 665 | pub fn isMutable(self: @This()) bool { |
| 666 | return switch (self) { |
| 667 | .fail, .imm => false, |
| 668 | .mut => true, |
| 669 | }; |
| 670 | } |
| 671 | |
| 672 | pub fn getTarget(self: @This()) Target { |
| 673 | return switch (self) { |
| 674 | .fail => |target| target, |
| 675 | .imm => |imm| imm.target, |
| 676 | .mut => |mut| mut.mod.getTarget(), |
| 677 | }; |
| 678 | } |
| 679 | |
| 680 | pub fn getSet(self: @This()) ?*const Store.Set { |
| 681 | return switch (self) { |
| 682 | .fail => null, |
| 683 | .imm => |imm| imm.set, |
| 684 | .mut => |mut| &mut.promoted.set, |
| 685 | }; |
| 686 | } |
| 687 | |
| 688 | pub fn typeToIndex(self: @This(), ty: Type, kind: Kind) !?Index { |
| 689 | return switch (self) { |
| 690 | .fail => null, |
| 691 | .imm => |imm| imm.set.typeToIndex(ty, imm.target, kind), |
| 692 | .mut => |mut| try mut.promoted.typeToIndex(ty, mut.mod, kind), |
| 693 | }; |
| 694 | } |
| 695 | |
| 696 | pub fn indexToCType(self: @This(), index: Index) ?CType { |
| 697 | return if (self.getSet()) |set| set.indexToCType(index) else null; |
| 698 | } |
| 699 | |
| 700 | pub fn freeze(self: @This()) @This() { |
| 701 | return switch (self) { |
| 702 | .fail, .imm => self, |
| 703 | .mut => |mut| .{ .imm = .{ .set = &mut.promoted.set, .target = self.getTarget() } }, |
| 704 | }; |
| 705 | } |
| 706 | }; |
| 707 | |
| 708 | pub fn initType(self: *@This(), ty: Type, kind: Kind, lookup: Lookup) !void { |
| 709 | const target = lookup.getTarget(); |
| 710 | |
| 711 | self.* = undefined; |
| 712 | if (!ty.isFnOrHasRuntimeBitsIgnoreComptime()) |
| 713 | self.init(.void) |
| 714 | else if (ty.isAbiInt()) switch (ty.tag()) { |
| 715 | .usize => self.init(.size_t), |
| 716 | .isize => self.init(.ptrdiff_t), |
| 717 | .c_short => self.init(.short), |
| 718 | .c_ushort => self.init(.@"unsigned short"), |
| 719 | .c_int => self.init(.int), |
| 720 | .c_uint => self.init(.@"unsigned int"), |
| 721 | .c_long => self.init(.long), |
| 722 | .c_ulong => self.init(.@"unsigned long"), |
| 723 | .c_longlong => self.init(.@"long long"), |
| 724 | .c_ulonglong => self.init(.@"unsigned long long"), |
| 725 | else => { |
| 726 | const info = ty.intInfo(target); |
| 727 | const t = tagFromIntInfo(info.signedness, info.bits); |
| 728 | switch (t) { |
| 729 | .void => unreachable, |
| 730 | else => self.init(t), |
| 731 | .array => { |
| 732 | const abi_size = ty.abiSize(target); |
| 733 | const abi_align = ty.abiAlignment(target); |
| 734 | self.storage = .{ .seq = .{ .base = .{ .tag = .array }, .data = .{ |
| 735 | .len = @divExact(abi_size, abi_align), |
| 736 | .elem_type = tagFromIntInfo( |
| 737 | .unsigned, |
| 738 | @intCast(u16, abi_align * 8), |
| 739 | ).toIndex(), |
| 740 | } } }; |
| 741 | self.value = .{ .cty = initPayload(&self.storage.seq) }; |
| 742 | }, |
| 743 | } |
| 744 | }, |
| 745 | } else switch (ty.zigTypeTag()) { |
| 746 | .Frame => unreachable, |
| 747 | .AnyFrame => unreachable, |
| 748 | |
| 749 | .Int, |
| 750 | .Enum, |
| 751 | .ErrorSet, |
| 752 | .Type, |
| 753 | .Void, |
| 754 | .NoReturn, |
| 755 | .ComptimeFloat, |
| 756 | .ComptimeInt, |
| 757 | .Undefined, |
| 758 | .Null, |
| 759 | .EnumLiteral, |
| 760 | => unreachable, |
| 761 | |
| 762 | .Bool => self.init(.bool), |
| 763 | |
| 764 | .Float => self.init(switch (ty.tag()) { |
| 765 | .f16 => .zig_f16, |
| 766 | .f32 => .zig_f32, |
| 767 | .f64 => .zig_f64, |
| 768 | .f80 => .zig_f80, |
| 769 | .f128 => .zig_f128, |
| 770 | .c_longdouble => .@"long double", |
| 771 | else => unreachable, |
| 772 | }), |
| 773 | |
| 774 | .Pointer => switch (ty.ptrSize()) { |
| 775 | .Slice => { |
| 776 | var buf: Type.SlicePtrFieldTypeBuffer = undefined; |
| 777 | const ptr_ty = ty.slicePtrFieldType(&buf); |
| 778 | if (try lookup.typeToIndex(ptr_ty, kind)) |ptr_idx| { |
| 779 | self.storage = .{ .anon = .{ .fields = .{ |
| 780 | .{ |
| 781 | .name = "ptr", |
| 782 | .type = ptr_idx, |
| 783 | .alignas = ptr_ty.abiAlignment(target), |
| 784 | }, |
| 785 | .{ |
| 786 | .name = "len", |
| 787 | .type = Tag.size_t.toIndex(), |
| 788 | .alignas = Type.usize.abiAlignment(target), |
| 789 | }, |
| 790 | }, .pl = undefined } }; |
| 791 | self.storage.anon.pl = .{ |
| 792 | .base = .{ .tag = .anon_struct }, |
| 793 | .data = self.storage.anon.fields[0..2], |
| 794 | }; |
| 795 | self.value = .{ .cty = initPayload(&self.storage.anon.pl) }; |
| 796 | } else self.init(.anon_struct); |
| 797 | }, |
| 798 | |
| 799 | .One, .Many, .C => { |
| 800 | const t: Tag = switch (ty.isVolatilePtr()) { |
| 801 | false => switch (ty.isConstPtr()) { |
| 802 | false => .pointer, |
| 803 | true => .pointer_const, |
| 804 | }, |
| 805 | true => switch (ty.isConstPtr()) { |
| 806 | false => .pointer_volatile, |
| 807 | true => .pointer_const_volatile, |
| 808 | }, |
| 809 | }; |
| 810 | if (try lookup.typeToIndex(ty.childType(), .forward)) |child_idx| { |
| 811 | self.storage = .{ .child = .{ .base = .{ .tag = t }, .data = child_idx } }; |
| 812 | self.value = .{ .cty = initPayload(&self.storage.child) }; |
| 813 | } else self.init(t); |
| 814 | }, |
| 815 | }, |
| 816 | |
| 817 | .Struct, .Union => |zig_tag| if (ty.isTupleOrAnonStruct()) { |
| 818 | if (lookup.isMutable()) { |
| 819 | for (0..ty.structFieldCount()) |field_i| { |
| 820 | if (ty.structFieldIsComptime(field_i)) continue; |
| 821 | _ = try lookup.typeToIndex(ty.structFieldType(field_i), switch (kind) { |
| 822 | .forward, .complete, .parameter => .complete, |
| 823 | .global => .global, |
| 824 | }); |
| 825 | } |
| 826 | } |
| 827 | self.init(.anon_struct); |
| 828 | } else { |
| 829 | const is_struct = zig_tag == .Struct or ty.unionTagTypeSafety() != null; |
| 830 | switch (kind) { |
| 831 | .forward => { |
| 832 | self.storage = .{ .fwd = .{ |
| 833 | .base = .{ .tag = if (is_struct) .fwd_struct else .fwd_union }, |
| 834 | .data = ty.getOwnerDecl(), |
| 835 | } }; |
| 836 | self.value = .{ .cty = initPayload(&self.storage.fwd) }; |
| 837 | }, |
| 838 | else => { |
| 839 | if (lookup.isMutable()) { |
| 840 | for (0..switch (zig_tag) { |
| 841 | .Struct => ty.structFieldCount(), |
| 842 | .Union => ty.cast(Type.Payload.Union).?.data.fields.count(), |
| 843 | else => unreachable, |
| 844 | }) |field_i| { |
| 845 | if (zig_tag == .Struct and ty.structFieldIsComptime(field_i)) |
| 846 | continue; |
| 847 | _ = try lookup.typeToIndex( |
| 848 | ty.structFieldType(field_i), |
| 849 | switch (kind) { |
| 850 | .forward => unreachable, |
| 851 | .complete, .parameter => .complete, |
| 852 | .global => .global, |
| 853 | }, |
| 854 | ); |
| 855 | } |
| 856 | _ = try lookup.typeToIndex(ty, .forward); |
| 857 | } |
| 858 | self.init(if (is_struct) .@"struct" else .@"union"); |
| 859 | }, |
| 860 | } |
| 861 | }, |
| 862 | |
| 863 | .Array, .Vector => |zig_tag| { |
| 864 | const t: Tag = switch (zig_tag) { |
| 865 | .Array => .array, |
| 866 | .Vector => .vector, |
| 867 | else => unreachable, |
| 868 | }; |
| 869 | if (try lookup.typeToIndex(ty.childType(), kind)) |child_idx| { |
| 870 | self.storage = .{ .seq = .{ .base = .{ .tag = t }, .data = .{ |
| 871 | .len = ty.arrayLenIncludingSentinel(), |
| 872 | .elem_type = child_idx, |
| 873 | } } }; |
| 874 | self.value = .{ .cty = initPayload(&self.storage.seq) }; |
| 875 | } else self.init(t); |
| 876 | }, |
| 877 | |
| 878 | .Optional => { |
| 879 | var buf: Type.Payload.ElemType = undefined; |
| 880 | const payload_ty = ty.optionalChild(&buf); |
| 881 | if (payload_ty.hasRuntimeBitsIgnoreComptime()) { |
| 882 | if (ty.optionalReprIsPayload()) |
| 883 | try self.initType(payload_ty, kind, lookup) |
| 884 | else if (try lookup.typeToIndex(payload_ty, kind)) |payload_idx| { |
| 885 | self.storage = .{ .anon = .{ .fields = .{ |
| 886 | .{ |
| 887 | .name = "payload", |
| 888 | .type = payload_idx, |
| 889 | .alignas = payload_ty.abiAlignment(target), |
| 890 | }, |
| 891 | .{ |
| 892 | .name = "is_null", |
| 893 | .type = Tag.bool.toIndex(), |
| 894 | .alignas = Type.bool.abiAlignment(target), |
| 895 | }, |
| 896 | }, .pl = undefined } }; |
| 897 | self.storage.anon.pl = .{ |
| 898 | .base = .{ .tag = .anon_struct }, |
| 899 | .data = self.storage.anon.fields[0..2], |
| 900 | }; |
| 901 | self.value = .{ .cty = initPayload(&self.storage.anon.pl) }; |
| 902 | } else self.init(.anon_struct); |
| 903 | } else self.init(.bool); |
| 904 | }, |
| 905 | |
| 906 | .ErrorUnion => { |
| 907 | const payload_ty = ty.errorUnionPayload(); |
| 908 | if (try lookup.typeToIndex(payload_ty, switch (kind) { |
| 909 | .forward, .complete, .parameter => .complete, |
| 910 | .global => .global, |
| 911 | })) |payload_idx| { |
| 912 | const error_ty = ty.errorUnionSet(); |
| 913 | if (payload_idx == Tag.void.toIndex()) |
| 914 | try self.initType(error_ty, kind, lookup) |
| 915 | else if (try lookup.typeToIndex(error_ty, kind)) |error_idx| { |
| 916 | self.storage = .{ .anon = .{ .fields = .{ |
| 917 | .{ |
| 918 | .name = "payload", |
| 919 | .type = payload_idx, |
| 920 | .alignas = payload_ty.abiAlignment(target), |
| 921 | }, |
| 922 | .{ |
| 923 | .name = "error", |
| 924 | .type = error_idx, |
| 925 | .alignas = error_ty.abiAlignment(target), |
| 926 | }, |
| 927 | }, .pl = undefined } }; |
| 928 | self.storage.anon.pl = .{ |
| 929 | .base = .{ .tag = .anon_struct }, |
| 930 | .data = self.storage.anon.fields[0..2], |
| 931 | }; |
| 932 | self.value = .{ .cty = initPayload(&self.storage.anon.pl) }; |
| 933 | } else self.init(.anon_struct); |
| 934 | } else self.init(.anon_struct); |
| 935 | }, |
| 936 | |
| 937 | .Opaque => switch (ty.tag()) { |
| 938 | .anyopaque => self.init(.void), |
| 939 | .@"opaque" => { |
| 940 | self.storage = .{ .fwd = .{ |
| 941 | .base = .{ .tag = .fwd_struct }, |
| 942 | .data = ty.getOwnerDecl(), |
| 943 | } }; |
| 944 | self.value = .{ .cty = initPayload(&self.storage.fwd) }; |
| 945 | }, |
| 946 | else => unreachable, |
| 947 | }, |
| 948 | |
| 949 | .Fn => { |
| 950 | const info = ty.fnInfo(); |
| 951 | if (lookup.isMutable()) { |
| 952 | _ = try lookup.typeToIndex(info.return_type, switch (kind) { |
| 953 | .forward => .forward, |
| 954 | .complete, .parameter, .global => .complete, |
| 955 | }); |
| 956 | for (info.param_types, 0..) |param_ty, param_i| { |
| 957 | if (info.paramIsComptime(param_i)) continue; |
| 958 | _ = try lookup.typeToIndex(param_ty, switch (kind) { |
| 959 | .forward => .forward, |
| 960 | .complete, .parameter, .global => unreachable, |
| 961 | }); |
| 962 | } |
| 963 | } |
| 964 | self.init(if (info.is_var_args) .varargs_function else .function); |
| 965 | }, |
| 966 | } |
| 967 | } |
| 968 | }; |
| 969 | |
| 970 | fn copyFields(arena: Allocator, fields: Payload.Fields.Data) !Payload.Fields.Data { |
| 971 | const new_fields = try arena.dupe(Payload.Fields.Field, fields); |
| 972 | for (new_fields) |*new_field| { |
| 973 | new_field.name = try arena.dupeZ(u8, mem.span(new_field.name)); |
| 974 | new_field.type = new_field.type; |
| 975 | } |
| 976 | return new_fields; |
| 977 | } |
| 978 | |
| 979 | pub fn copy(self: CType, arena: Allocator) !CType { |
| 980 | switch (self.tag()) { |
| 981 | .void, |
| 982 | .char, |
| 983 | .@"signed char", |
| 984 | .short, |
| 985 | .int, |
| 986 | .long, |
| 987 | .@"long long", |
| 988 | ._Bool, |
| 989 | .@"unsigned char", |
| 990 | .@"unsigned short", |
| 991 | .@"unsigned int", |
| 992 | .@"unsigned long", |
| 993 | .@"unsigned long long", |
| 994 | .float, |
| 995 | .double, |
| 996 | .@"long double", |
| 997 | .bool, |
| 998 | .size_t, |
| 999 | .ptrdiff_t, |
| 1000 | .zig_u8, |
| 1001 | .zig_i8, |
| 1002 | .zig_u16, |
| 1003 | .zig_i16, |
| 1004 | .zig_u32, |
| 1005 | .zig_i32, |
| 1006 | .zig_u64, |
| 1007 | .zig_i64, |
| 1008 | .zig_u128, |
| 1009 | .zig_i128, |
| 1010 | .zig_f16, |
| 1011 | .zig_f32, |
| 1012 | .zig_f64, |
| 1013 | .zig_f80, |
| 1014 | .zig_f128, |
| 1015 | => return self, |
| 1016 | |
| 1017 | .pointer, |
| 1018 | .pointer_const, |
| 1019 | .pointer_volatile, |
| 1020 | .pointer_const_volatile, |
| 1021 | => { |
| 1022 | const pl = self.cast(Payload.Child).?; |
| 1023 | const new_pl = try arena.create(Payload.Child); |
| 1024 | new_pl.* = .{ .base = .{ .tag = pl.base.tag }, .data = pl.data }; |
| 1025 | return initPayload(new_pl); |
| 1026 | }, |
| 1027 | |
| 1028 | .array, |
| 1029 | .vector, |
| 1030 | => { |
| 1031 | const pl = self.cast(Payload.Sequence).?; |
| 1032 | const new_pl = try arena.create(Payload.Sequence); |
| 1033 | new_pl.* = .{ |
| 1034 | .base = .{ .tag = pl.base.tag }, |
| 1035 | .data = .{ .len = pl.data.len, .elem_type = pl.data.elem_type }, |
| 1036 | }; |
| 1037 | return initPayload(new_pl); |
| 1038 | }, |
| 1039 | |
| 1040 | .fwd_struct, |
| 1041 | .fwd_union, |
| 1042 | => { |
| 1043 | const pl = self.cast(Payload.FwdDecl).?; |
| 1044 | const new_pl = try arena.create(Payload.FwdDecl); |
| 1045 | new_pl.* = .{ |
| 1046 | .base = .{ .tag = pl.base.tag }, |
| 1047 | .data = pl.data, |
| 1048 | }; |
| 1049 | return initPayload(new_pl); |
| 1050 | }, |
| 1051 | |
| 1052 | .anon_struct, |
| 1053 | .packed_anon_struct, |
| 1054 | => { |
| 1055 | const pl = self.cast(Payload.Fields).?; |
| 1056 | const new_pl = try arena.create(Payload.Fields); |
| 1057 | new_pl.* = .{ |
| 1058 | .base = .{ .tag = pl.base.tag }, |
| 1059 | .data = try copyFields(arena, pl.data), |
| 1060 | }; |
| 1061 | return initPayload(new_pl); |
| 1062 | }, |
| 1063 | |
| 1064 | .@"struct", |
| 1065 | .@"union", |
| 1066 | .packed_struct, |
| 1067 | .packed_union, |
| 1068 | => { |
| 1069 | const pl = self.cast(Payload.Aggregate).?; |
| 1070 | const new_pl = try arena.create(Payload.Aggregate); |
| 1071 | new_pl.* = .{ .base = .{ .tag = pl.base.tag }, .data = .{ |
| 1072 | .fields = try copyFields(arena, pl.data.fields), |
| 1073 | .fwd_decl = pl.data.fwd_decl, |
| 1074 | } }; |
| 1075 | return initPayload(new_pl); |
| 1076 | }, |
| 1077 | |
| 1078 | .function, |
| 1079 | .varargs_function, |
| 1080 | => { |
| 1081 | const pl = self.cast(Payload.Function).?; |
| 1082 | const new_pl = try arena.create(Payload.Function); |
| 1083 | new_pl.* = .{ .base = .{ .tag = pl.base.tag }, .data = .{ |
| 1084 | .return_type = pl.data.return_type, |
| 1085 | .param_types = try arena.dupe(Index, pl.data.param_types), |
| 1086 | } }; |
| 1087 | return initPayload(new_pl); |
| 1088 | }, |
| 1089 | } |
| 1090 | } |
| 1091 | |
| 1092 | fn createFromType(store: *Store.Promoted, ty: Type, target: Target, kind: Kind) !CType { |
| 1093 | var convert: Convert = undefined; |
| 1094 | try convert.initType(ty, kind, .{ .imm = .{ .set = &store.set, .target = target } }); |
| 1095 | return createFromConvert(store, ty, target, kind, &convert); |
| 1096 | } |
| 1097 | |
| 1098 | fn createFromConvert( |
| 1099 | store: *Store.Promoted, |
| 1100 | ty: Type, |
| 1101 | target: Target, |
| 1102 | kind: Kind, |
| 1103 | convert: Convert, |
| 1104 | ) !CType { |
| 1105 | const arena = store.arena.allocator(); |
| 1106 | switch (convert.value) { |
| 1107 | .cty => |c| return c.copy(arena), |
| 1108 | .tag => |t| switch (t) { |
| 1109 | .anon_struct, |
| 1110 | .packed_anon_struct, |
| 1111 | .@"struct", |
| 1112 | .@"union", |
| 1113 | .packed_struct, |
| 1114 | .packed_union, |
| 1115 | => switch (ty.zigTypeTag()) { |
| 1116 | .Struct => { |
| 1117 | const fields_len = ty.structFieldCount(); |
| 1118 | |
| 1119 | var c_fields_len: usize = 0; |
| 1120 | for (0..fields_len) |field_i| { |
| 1121 | if (ty.structFieldIsComptime(field_i)) continue; |
| 1122 | c_fields_len += 1; |
| 1123 | } |
| 1124 | |
| 1125 | const fields_pl = try arena.alloc(Payload.Fields.Field, c_fields_len); |
| 1126 | var c_field_i: usize = 0; |
| 1127 | for (0..fields_len) |field_i| { |
| 1128 | if (ty.structFieldIsComptime(field_i)) continue; |
| 1129 | |
| 1130 | fields_pl[c_field_i] = .{ |
| 1131 | .name = try if (ty.isSimpleTuple()) |
| 1132 | std.fmt.allocPrintZ(arena, "f{}", .{field_i}) |
| 1133 | else |
| 1134 | arena.dupeZ(u8, ty.structFieldName(field_i)), |
| 1135 | .type = store.set.typeToIndex( |
| 1136 | ty.structFieldType(field_i), |
| 1137 | target, |
| 1138 | switch (kind) { |
| 1139 | .forward, .complete, .parameter => .complete, |
| 1140 | .global => .global, |
| 1141 | }, |
| 1142 | ).?, |
| 1143 | .alignas = ty.structFieldAlign(field_i, target), |
| 1144 | }; |
| 1145 | c_field_i += 1; |
| 1146 | } |
| 1147 | |
| 1148 | if (ty.isTupleOrAnonStruct()) { |
| 1149 | const anon_pl = try arena.create(Payload.Fields); |
| 1150 | anon_pl.* = .{ .base = .{ .tag = .anon_struct }, .data = fields_pl }; |
| 1151 | return initPayload(anon_pl); |
| 1152 | } |
| 1153 | |
| 1154 | const struct_pl = try arena.create(Payload.Aggregate); |
| 1155 | struct_pl.* = .{ .base = .{ .tag = t }, .data = .{ |
| 1156 | .fields = fields_pl, |
| 1157 | .fwd_decl = store.set.typeToIndex(ty, target, .forward).?, |
| 1158 | } }; |
| 1159 | return initPayload(struct_pl); |
| 1160 | }, |
| 1161 | |
| 1162 | .Union => { |
| 1163 | const fields = ty.unionFields(); |
| 1164 | const fields_len = fields.count(); |
| 1165 | |
| 1166 | var c_fields_len: usize = 0; |
| 1167 | for (0..fields_len) |field_i| { |
| 1168 | const field_ty = ty.structFieldType(field_i); |
| 1169 | if (!field_ty.hasRuntimeBitsIgnoreComptime()) continue; |
| 1170 | c_fields_len += 1; |
| 1171 | } |
| 1172 | |
| 1173 | const fields_pl = try arena.alloc(Payload.Fields.Field, c_fields_len); |
| 1174 | var field_i: usize = 0; |
| 1175 | var c_field_i: usize = 0; |
| 1176 | var field_it = fields.iterator(); |
| 1177 | while (field_it.next()) |field| { |
| 1178 | defer field_i += 1; |
| 1179 | if (!field.value_ptr.ty.hasRuntimeBitsIgnoreComptime()) continue; |
| 1180 | |
| 1181 | fields_pl[c_field_i] = .{ |
| 1182 | .name = try arena.dupeZ(u8, field.key_ptr.*), |
| 1183 | .type = store.set.typeToIndex(field.value_ptr.ty, target, switch (kind) { |
| 1184 | .forward => unreachable, |
| 1185 | .complete, .parameter => .complete, |
| 1186 | .global => .global, |
| 1187 | }).?, |
| 1188 | .alignas = ty.structFieldAlign(field_i, target), |
| 1189 | }; |
| 1190 | c_field_i += 1; |
| 1191 | } |
| 1192 | |
| 1193 | const union_pl = try arena.create(Payload.Aggregate); |
| 1194 | union_pl.* = .{ .base = .{ .tag = t }, .data = .{ |
| 1195 | .fields = fields_pl, |
| 1196 | .fwd_decl = store.set.typeToIndex(ty, target, .forward).?, |
| 1197 | } }; |
| 1198 | return initPayload(union_pl); |
| 1199 | }, |
| 1200 | |
| 1201 | else => unreachable, |
| 1202 | }, |
| 1203 | |
| 1204 | .function, |
| 1205 | .varargs_function, |
| 1206 | => { |
| 1207 | const info = ty.fnInfo(); |
| 1208 | const recurse_kind: Kind = switch (kind) { |
| 1209 | .forward => .forward, |
| 1210 | .complete, .parameter, .global => unreachable, |
| 1211 | }; |
| 1212 | |
| 1213 | var c_params_len: usize = 0; |
| 1214 | for (0..info.param_types.len) |param_i| { |
| 1215 | if (info.paramIsComptime(param_i)) continue; |
| 1216 | c_params_len += 1; |
| 1217 | } |
| 1218 | |
| 1219 | const params_pl = try arena.alloc(Index, c_params_len); |
| 1220 | var c_param_i: usize = 0; |
| 1221 | for (info.param_types, 0..) |param_ty, param_i| { |
| 1222 | if (info.paramIsComptime(param_i)) continue; |
| 1223 | params_pl[c_param_i] = store.set.typeToIndex(param_ty, target, recurse_kind).?; |
| 1224 | c_param_i += 1; |
| 1225 | } |
| 1226 | |
| 1227 | const fn_pl = try arena.create(Payload.Function); |
| 1228 | fn_pl.* = .{ .base = .{ .tag = t }, .data = .{ |
| 1229 | .return_type = store.set.typeToIndex(info.return_type, target, recurse_kind).?, |
| 1230 | .param_types = params_pl, |
| 1231 | } }; |
| 1232 | return initPayload(fn_pl); |
| 1233 | }, |
| 1234 | |
| 1235 | else => unreachable, |
| 1236 | }, |
| 1237 | } |
| 1238 | } |
| 1239 | |
| 1240 | pub const HashContext64 = struct { |
| 1241 | store: *const Store.Set, |
| 1242 | |
| 1243 | pub fn hash(_: @This(), cty: CType) u64 { |
| 1244 | return cty.hash(); |
| 1245 | } |
| 1246 | pub fn eql(_: @This(), lhs: CType, rhs: CType) bool { |
| 1247 | return lhs.eql(rhs); |
| 1248 | } |
| 1249 | }; |
| 1250 | |
| 1251 | pub const HashContext32 = struct { |
| 1252 | store: *const Store.Set, |
| 1253 | |
| 1254 | pub fn hash(self: @This(), cty: CType) u32 { |
| 1255 | return @truncate(u32, cty.hash(self.store.*)); |
| 1256 | } |
| 1257 | pub fn eql(_: @This(), lhs: CType, rhs: CType, _: usize) bool { |
| 1258 | return lhs.eql(rhs); |
| 1259 | } |
| 1260 | }; |
| 1261 | |
| 1262 | pub const TypeAdapter64 = struct { |
| 1263 | kind: Kind, |
| 1264 | lookup: Convert.Lookup, |
| 1265 | convert: *const Convert, |
| 1266 | |
| 1267 | fn eqlRecurse(self: @This(), ty: Type, cty: Index, kind: Kind) bool { |
| 1268 | assert(!self.lookup.isMutable()); |
| 1269 | |
| 1270 | var convert: Convert = undefined; |
| 1271 | convert.initType(ty, kind, self.lookup) catch unreachable; |
| 1272 | |
| 1273 | const self_recurse = @This(){ .kind = kind, .lookup = self.lookup, .convert = &convert }; |
| 1274 | return self_recurse.eql(ty, self.lookup.indexToCType(cty).?); |
| 1275 | } |
| 1276 | |
| 1277 | pub fn eql(self: @This(), ty: Type, cty: CType) bool { |
| 1278 | switch (self.convert.value) { |
| 1279 | .cty => |c| return c.eql(cty), |
| 1280 | .tag => |t| { |
| 1281 | if (t != cty.tag()) return false; |
| 1282 | |
| 1283 | const target = self.lookup.getTarget(); |
| 1284 | switch (t) { |
| 1285 | .anon_struct, |
| 1286 | .packed_anon_struct, |
| 1287 | => { |
| 1288 | if (!ty.isTupleOrAnonStruct()) return false; |
| 1289 | |
| 1290 | var name_buf: [ |
| 1291 | std.fmt.count("f{}", .{std.math.maxInt(usize)}) |
| 1292 | ]u8 = undefined; |
| 1293 | const c_fields = cty.cast(Payload.Fields).?.data; |
| 1294 | |
| 1295 | var c_field_i: usize = 0; |
| 1296 | for (0..ty.structFieldCount()) |field_i| { |
| 1297 | if (ty.structFieldIsComptime(field_i)) continue; |
| 1298 | |
| 1299 | const c_field = &c_fields[c_field_i]; |
| 1300 | c_field_i += 1; |
| 1301 | |
| 1302 | if (!self.eqlRecurse( |
| 1303 | ty.structFieldType(field_i), |
| 1304 | c_field.type, |
| 1305 | switch (self.kind) { |
| 1306 | .forward, .complete, .parameter => .complete, |
| 1307 | .global => .global, |
| 1308 | }, |
| 1309 | ) or !mem.eql( |
| 1310 | u8, |
| 1311 | if (ty.isSimpleTuple()) |
| 1312 | std.fmt.bufPrint(&name_buf, "f{}", .{field_i}) catch unreachable |
| 1313 | else |
| 1314 | ty.structFieldName(field_i), |
| 1315 | mem.span(c_field.name), |
| 1316 | ) or ty.structFieldAlign(field_i, target) != c_field.alignas) |
| 1317 | return false; |
| 1318 | } |
| 1319 | return true; |
| 1320 | }, |
| 1321 | |
| 1322 | .@"struct", |
| 1323 | .@"union", |
| 1324 | .packed_struct, |
| 1325 | .packed_union, |
| 1326 | => return self.eqlRecurse( |
| 1327 | ty, |
| 1328 | cty.cast(Payload.Aggregate).?.data.fwd_decl, |
| 1329 | .forward, |
| 1330 | ), |
| 1331 | |
| 1332 | .function, |
| 1333 | .varargs_function, |
| 1334 | => { |
| 1335 | if (ty.zigTypeTag() != .Fn) return false; |
| 1336 | |
| 1337 | const info = ty.fnInfo(); |
| 1338 | const data = cty.cast(Payload.Function).?.data; |
| 1339 | const recurse_kind: Kind = switch (self.kind) { |
| 1340 | .forward => .forward, |
| 1341 | .complete, .parameter, .global => unreachable, |
| 1342 | }; |
| 1343 | |
| 1344 | if (info.param_types.len != data.param_types.len or |
| 1345 | !self.eqlRecurse(info.return_type, data.return_type, recurse_kind)) |
| 1346 | return false; |
| 1347 | for (info.param_types, data.param_types, 0..) |param_ty, param_cty, param_i| { |
| 1348 | if (info.paramIsComptime(param_i)) continue; |
| 1349 | if (!self.eqlRecurse(param_ty, param_cty, recurse_kind)) |
| 1350 | return false; |
| 1351 | } |
| 1352 | return true; |
| 1353 | }, |
| 1354 | |
| 1355 | else => unreachable, |
| 1356 | } |
| 1357 | }, |
| 1358 | } |
| 1359 | } |
| 1360 | |
| 1361 | pub fn hash(self: @This(), ty: Type) u64 { |
| 1362 | var hasher = std.hash.Wyhash.init(0); |
| 1363 | self.updateHasher(&hasher, ty); |
| 1364 | return hasher.final(); |
| 1365 | } |
| 1366 | |
| 1367 | fn updateHasherRecurse(self: @This(), hasher: anytype, ty: Type, kind: Kind) void { |
| 1368 | assert(!self.lookup.isMutable()); |
| 1369 | |
| 1370 | var convert: Convert = undefined; |
| 1371 | convert.initType(ty, kind, self.lookup) catch unreachable; |
| 1372 | |
| 1373 | const self_recurse = @This(){ .kind = kind, .lookup = self.lookup, .convert = &convert }; |
| 1374 | self_recurse.updateHasher(hasher, ty); |
| 1375 | } |
| 1376 | |
| 1377 | pub fn updateHasher(self: @This(), hasher: anytype, ty: Type) void { |
| 1378 | switch (self.convert.value) { |
| 1379 | .cty => |c| return c.updateHasher(hasher, self.lookup.getSet().?.*), |
| 1380 | .tag => |t| { |
| 1381 | autoHash(hasher, t); |
| 1382 | |
| 1383 | const target = self.lookup.getTarget(); |
| 1384 | switch (t) { |
| 1385 | .anon_struct, |
| 1386 | .packed_anon_struct, |
| 1387 | => { |
| 1388 | var name_buf: [ |
| 1389 | std.fmt.count("f{}", .{std.math.maxInt(usize)}) |
| 1390 | ]u8 = undefined; |
| 1391 | for (0..ty.structFieldCount()) |field_i| { |
| 1392 | if (ty.structFieldIsComptime(field_i)) continue; |
| 1393 | |
| 1394 | self.updateHasherRecurse( |
| 1395 | hasher, |
| 1396 | ty.structFieldType(field_i), |
| 1397 | switch (self.kind) { |
| 1398 | .forward, .complete, .parameter => .complete, |
| 1399 | .global => .global, |
| 1400 | }, |
| 1401 | ); |
| 1402 | hasher.update(if (ty.isSimpleTuple()) |
| 1403 | std.fmt.bufPrint(&name_buf, "f{}", .{field_i}) catch unreachable |
| 1404 | else |
| 1405 | ty.structFieldName(field_i)); |
| 1406 | autoHash(hasher, ty.structFieldAlign(field_i, target)); |
| 1407 | } |
| 1408 | }, |
| 1409 | |
| 1410 | .@"struct", |
| 1411 | .@"union", |
| 1412 | .packed_struct, |
| 1413 | .packed_union, |
| 1414 | => self.updateHasherRecurse(hasher, ty, .forward), |
| 1415 | |
| 1416 | .function, |
| 1417 | .varargs_function, |
| 1418 | => { |
| 1419 | const info = ty.fnInfo(); |
| 1420 | const recurse_kind: Kind = switch (self.kind) { |
| 1421 | .forward => .forward, |
| 1422 | .complete, .parameter, .global => unreachable, |
| 1423 | }; |
| 1424 | |
| 1425 | self.updateHasherRecurse(hasher, info.return_type, recurse_kind); |
| 1426 | for (info.param_types, 0..) |param_ty, param_i| { |
| 1427 | if (info.paramIsComptime(param_i)) continue; |
| 1428 | self.updateHasherRecurse(hasher, param_ty, recurse_kind); |
| 1429 | } |
| 1430 | }, |
| 1431 | |
| 1432 | else => unreachable, |
| 1433 | } |
| 1434 | }, |
| 1435 | } |
| 1436 | } |
| 1437 | }; |
| 1438 | |
| 1439 | pub const TypeAdapter32 = struct { |
| 1440 | kind: Kind, |
| 1441 | lookup: Convert.Lookup, |
| 1442 | convert: *const Convert, |
| 1443 | |
| 1444 | fn to64(self: @This()) TypeAdapter64 { |
| 1445 | return .{ .kind = self.kind, .lookup = self.lookup, .convert = self.convert }; |
| 1446 | } |
| 1447 | |
| 1448 | pub fn eql(self: @This(), ty: Type, cty: CType, cty_index: usize) bool { |
| 1449 | _ = cty_index; |
| 1450 | return self.to64().eql(ty, cty); |
| 1451 | } |
| 1452 | |
| 1453 | pub fn hash(self: @This(), ty: Type) u32 { |
| 1454 | return @truncate(u32, self.to64().hash(ty)); |
| 1455 | } |
| 1456 | }; |
| 1457 | }; |