├── .gitattributes ├── .gitignore ├── LICENSE ├── PeerType.zig └── README.md /.gitattributes: -------------------------------------------------------------------------------- 1 | *.zig text=auto eol=lf 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /zig-cache 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Alexandros Naskos 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /PeerType.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | 3 | fn getSrcPtrType(comptime T: type) ?type { 4 | return switch (@typeInfo(T)) { 5 | .Pointer => T, 6 | .Fn => T, 7 | .AnyFrame => T, 8 | .Optional => |o| switch (@typeInfo(o.child)) { 9 | .Pointer => |p| if (p.is_allowzero) null else o.child, 10 | .Fn => o.child, 11 | .AnyFrame => o.child, 12 | else => null, 13 | }, 14 | else => null, 15 | }; 16 | } 17 | 18 | fn isAllowZeroPtr(comptime T: type) bool { 19 | return switch (@typeInfo(T)) { 20 | .Pointer => |p| p.is_allowzero, 21 | .Optional => true, 22 | else => false, 23 | }; 24 | } 25 | 26 | fn genericArgCount(comptime fn_info: std.builtin.TypeInfo.Fn) comptime_int { 27 | for (fn_info.args) |arg, i| { 28 | if (arg.is_generic) 29 | return fn_info.args.len - i; 30 | } 31 | return 0; 32 | } 33 | 34 | fn sentinelEql(comptime left: anytype, comptime right: anytype) bool { 35 | if (@TypeOf(left) != @TypeOf(right)) { 36 | return false; 37 | } 38 | return std.meta.eql(left, right); 39 | } 40 | 41 | /// Translated from ir.cpp:types_match_const_cast_only 42 | fn typesMatchConstCastOnly(comptime wanted: type, comptime actual: type, comptime wanted_is_mutable: bool) bool { 43 | comptime { 44 | const wanted_info = @typeInfo(wanted); 45 | const actual_info = @typeInfo(actual); 46 | 47 | if (wanted == actual) 48 | return true; 49 | 50 | const wanted_ptr_type = getSrcPtrType(wanted); 51 | const actual_ptr_type = getSrcPtrType(actual); 52 | const wanted_allows_zero = isAllowZeroPtr(wanted); 53 | const actual_allows_zero = isAllowZeroPtr(actual); 54 | const wanted_is_c_ptr = wanted_info == .Pointer and wanted_info.Pointer.size == .C; 55 | const actual_is_c_ptr = actual_info == .Pointer and actual_info.Pointer.size == .C; 56 | const wanted_opt_or_ptr = wanted_ptr_type != null and @typeInfo(wanted_ptr_type.?) == .Pointer; 57 | const actual_opt_or_ptr = actual_ptr_type != null and @typeInfo(actual_ptr_type.?) == .Pointer; 58 | 59 | if (wanted_opt_or_ptr and actual_opt_or_ptr) { 60 | const wanted_ptr_info = @typeInfo(wanted_ptr_type.?).Pointer; 61 | const actual_ptr_info = @typeInfo(actual_ptr_type.?).Pointer; 62 | const ok_null_term_ptrs = wanted_ptr_info.sentinel == null or 63 | (actual_ptr_info.sentinel != null and 64 | sentinelEql(wanted_ptr_info.sentinel, actual_ptr_info.sentinel)); 65 | 66 | if (!ok_null_term_ptrs) { 67 | return false; 68 | } 69 | const ptr_sizes_eql = actual_ptr_info.size == wanted_ptr_info.size; 70 | if (!(ptr_sizes_eql or wanted_is_c_ptr or actual_is_c_ptr)) { 71 | return false; 72 | } 73 | const ok_cv_qualifiers = (!actual_ptr_info.is_const or wanted_ptr_info.is_const) and 74 | (!actual_ptr_info.is_volatile or wanted_ptr_info.is_volatile); 75 | if (!ok_cv_qualifiers) { 76 | return false; 77 | } 78 | if (!typesMatchConstCastOnly( 79 | wanted_ptr_info.child, 80 | actual_ptr_info.child, 81 | !wanted_ptr_info.is_const, 82 | )) { 83 | return false; 84 | } 85 | const ok_allows_zero = (wanted_allows_zero and 86 | (actual_allows_zero or !wanted_is_mutable)) or 87 | (!wanted_allows_zero and !actual_allows_zero); 88 | if (!ok_allows_zero) { 89 | return false; 90 | } 91 | if ((@sizeOf(wanted) > 0 and @sizeOf(actual) > 0) and 92 | actual_ptr_info.alignment >= wanted_ptr_info.alignment) 93 | { 94 | return true; 95 | } 96 | } 97 | 98 | // arrays 99 | if (wanted_info == .Array and actual_info == .Array and 100 | wanted_info.Array.len == actual_info.Array.len) 101 | { 102 | if (!typesMatchConstCastOnly( 103 | wanted_info.Array.child, 104 | actual_info.Array.child, 105 | wanted_is_mutable, 106 | )) { 107 | return false; 108 | } 109 | const ok_sentinels = wanted_info.Array.sentinel == null or 110 | (actual_info.Array.sentinel != null and 111 | sentinelEql(wanted_info.Array.sentinel, actual_info.Array.sentinel)); 112 | if (!ok_sentinels) { 113 | return false; 114 | } 115 | return true; 116 | } 117 | 118 | // const slice 119 | if (isSlice(wanted) and isSlice(actual)) { 120 | const wanted_slice_info = @typeInfo(wanted).Pointer; 121 | const actual_slice_info = @typeInfo(actual).Pointer; 122 | const ok_sentinels = wanted_slice_info.sentinel == null or 123 | (actual_slice_info.sentinel != null and 124 | sentinelEql(wanted_slice_info.sentinel, actual_slice_info.sentinel)); 125 | if (!ok_sentinels) { 126 | return false; 127 | } 128 | const ok_cv_qualifiers = (!actual_slice_info.is_const or wanted_slice_info.is_const) and 129 | (!actual_slice_info.is_volatile or wanted_slice_info.is_volatile); 130 | if (!ok_cv_qualifiers) { 131 | return false; 132 | } 133 | if (actual_slice_info.alignment < wanted_slice_info.alignment) { 134 | return false; 135 | } 136 | if (!typesMatchConstCastOnly( 137 | wanted_slice_info.child, 138 | actual_slice_info.child, 139 | !wanted_slice_info.is_const, 140 | )) { 141 | return false; 142 | } 143 | return true; 144 | } 145 | 146 | // optional types 147 | if (wanted_info == .Optional and actual_info == .Optional) { 148 | if ((wanted_ptr_type != null) != (actual_ptr_type != null)) { 149 | return false; 150 | } 151 | if (!typesMatchConstCastOnly( 152 | wanted_info.Optional.child, 153 | actual_info.Optional.child, 154 | wanted_is_mutable, 155 | )) { 156 | return false; 157 | } 158 | return true; 159 | } 160 | 161 | // error union 162 | if (wanted_info == .ErrorUnion and actual_info == .ErrorUnion) { 163 | if (!typesMatchConstCastOnly( 164 | wanted_info.ErrorUnion.payload, 165 | actual_info.ErrorUnion.payload, 166 | wanted_is_mutable, 167 | )) { 168 | return false; 169 | } 170 | if (!typesMatchConstCastOnly( 171 | wanted_info.ErrorUnion.error_set, 172 | actual_info.ErrorUnion.error_set, 173 | wanted_is_mutable, 174 | )) { 175 | return false; 176 | } 177 | return true; 178 | } 179 | 180 | // error set 181 | if (wanted_info == .ErrorSet and actual_info == .ErrorSet) { 182 | return isSuperset(wanted, actual); 183 | } 184 | 185 | // fn 186 | if (wanted_info == .Fn and actual_info == .Fn) { 187 | if (wanted_info.Fn.alignment > actual_info.Fn.alignment) { 188 | return false; 189 | } 190 | if (wanted_info.Fn.is_var_args != actual_info.Fn.is_var_args) { 191 | return false; 192 | } 193 | if (wanted_info.Fn.is_generic != actual_info.Fn.is_generic) { 194 | return false; 195 | } 196 | if (!wanted_info.Fn.is_generic and 197 | actual_info.Fn.return_type != null) 198 | { 199 | if (!typesMatchConstCastOnly( 200 | wanted_info.Fn.return_type.?, 201 | actual_info.Fn.return_type.?, 202 | false, 203 | )) { 204 | return false; 205 | } 206 | } 207 | if (wanted_info.Fn.args.len != actual_info.Fn.args.len) { 208 | return false; 209 | } 210 | if (genericArgCount(wanted_info.Fn) != genericArgCount(actual_info.Fn)) { 211 | return false; 212 | } 213 | if (wanted_info.Fn.calling_convention != actual_info.Fn.calling_convention) { 214 | return false; 215 | } 216 | var i = 0; 217 | while (i < wanted_info.Fn.args.len) : (i += 1) { 218 | const actual_arg_info = actual_info.Fn.args[i]; 219 | const wanted_arg_info = wanted_info.Fn.args[i]; 220 | 221 | if (actual_arg_info.is_generic != wanted_arg_info.is_generic) { 222 | return false; 223 | } 224 | if (actual_arg_info.is_noalias != wanted_arg_info.is_noalias) { 225 | return false; 226 | } 227 | if (actual_arg_info.is_generic) { 228 | continue; 229 | } 230 | if (!typesMatchConstCastOnly( 231 | actual_arg_info.arg_type.?, 232 | wanted_arg_info.arg_type.?, 233 | false, 234 | )) { 235 | return false; 236 | } 237 | } 238 | return true; 239 | } 240 | 241 | if (wanted_info == .Int and actual_info == .Int) { 242 | if (wanted_info.Int.signedness != actual_info.Int.signedness or 243 | wanted_info.Int.bits != actual_info.Int.bits) 244 | { 245 | return false; 246 | } 247 | return true; 248 | } 249 | 250 | if (wanted_info == .Vector and actual_info == .Vector) { 251 | if (wanted_info.Vector.len != actual_info.Vector.len) { 252 | return false; 253 | } 254 | if (!typesMatchConstCastOnly( 255 | wanted_info.Vector.child, 256 | actual_info.Vector.child, 257 | false, 258 | )) { 259 | return false; 260 | } 261 | return true; 262 | } 263 | return false; 264 | } 265 | } 266 | 267 | fn isSuperset(comptime A: type, comptime B: type) bool { 268 | const a_info = @typeInfo(A).ErrorSet.?; 269 | const b_info = @typeInfo(B).ErrorSet.?; 270 | 271 | for (b_info) |b_err| { 272 | var found = false; 273 | for (a_info) |a_err| { 274 | if (std.mem.eql(u8, a_err.name, b_err.name)) { 275 | found = true; 276 | break; 277 | } 278 | } 279 | if (!found) { 280 | return false; 281 | } 282 | } 283 | return true; 284 | } 285 | 286 | fn errSetEql(comptime A: type, comptime B: type) bool { 287 | if (A == B) return true; 288 | 289 | const a_info = @typeInfo(A).ErrorSet.?; 290 | const b_info = @typeInfo(B).ErrorSet.?; 291 | 292 | if (a_info.len != b_info.len) return false; 293 | return isSuperset(A, B); 294 | } 295 | 296 | /// Translated from ir.cpp:ir_resolve_peer_types 297 | pub fn PeerType(comptime types: anytype) ?type { 298 | var prev_type: type = undefined; 299 | var prev_info: std.builtin.TypeInfo = undefined; 300 | 301 | var i = 0; 302 | while (true) { 303 | prev_type = types[i]; 304 | prev_info = @typeInfo(prev_type); 305 | 306 | if (prev_info == .NoReturn) { 307 | i += 1; 308 | if (i == types.len) { 309 | return prev_type; 310 | } 311 | continue; 312 | } 313 | break; 314 | } 315 | 316 | // Differences with stage1 implementation: 317 | // we need only keep the type and use || 318 | // to update it, no need to separately keep 319 | // an error entry table. 320 | var err_set_type: ?type = null; 321 | if (prev_info == .ErrorSet) { 322 | err_set_type = prev_type; 323 | } 324 | 325 | var any_are_null = prev_info == .Null; 326 | var convert_to_const_slice = false; 327 | var make_the_slice_const = false; 328 | var make_the_pointer_const = false; 329 | 330 | while (i < types.len) : (i += 1) { 331 | const cur_type = types[i]; 332 | const cur_info = @typeInfo(cur_type); 333 | prev_info = @typeInfo(prev_type); 334 | 335 | if (prev_type == cur_type) 336 | continue; 337 | 338 | if (prev_info == .NoReturn) { 339 | prev_type = cur_type; 340 | continue; 341 | } 342 | 343 | if (cur_info == .NoReturn) { 344 | continue; 345 | } 346 | 347 | if (prev_info == .ErrorSet) { 348 | switch (cur_info) { 349 | .ErrorSet => { 350 | if (err_set_type == anyerror) { 351 | continue; 352 | } 353 | 354 | if (cur_type == anyerror) { 355 | prev_type = cur_type; 356 | err_set_type = anyerror; 357 | continue; 358 | } 359 | 360 | if (isSuperset(err_set_type.?, cur_type)) { 361 | continue; 362 | } 363 | 364 | if (isSuperset(cur_type, err_set_type.?)) { 365 | err_set_type = cur_type; 366 | prev_type = cur_type; 367 | continue; 368 | } 369 | err_set_type = err_set_type.? || cur_type; 370 | continue; 371 | }, 372 | .ErrorUnion => |cur_err_union| { 373 | if (err_set_type == anyerror) { 374 | prev_type = cur_type; 375 | continue; 376 | } 377 | 378 | const cur_err_set_type = cur_err_union.error_set; 379 | if (cur_err_set_type == anyerror) { 380 | err_set_type = anyerror; 381 | prev_type = cur_type; 382 | continue; 383 | } 384 | 385 | if (isSuperset(cur_err_set_type, err_set_type.?)) { 386 | err_set_type = cur_err_set_type; 387 | prev_type = cur_type; 388 | errors = cur_errors; 389 | continue; 390 | } 391 | 392 | err_set_type = err_set_type.? || cur_err_set_type; 393 | prev_type = cur_type; 394 | continue; 395 | }, 396 | else => { 397 | prev_type = cur_type; 398 | continue; 399 | }, 400 | } 401 | } 402 | 403 | if (cur_info == .ErrorSet) { 404 | if (cur_type == anyerror) { 405 | err_set_type = anyerror; 406 | continue; 407 | } 408 | if (err_set_type == anyerror) { 409 | continue; 410 | } 411 | 412 | if (err_set_type == null) { 413 | err_set_type = cur_type; 414 | continue; 415 | } 416 | 417 | if (isSuperset(err_set_type.?, cur_type)) { 418 | continue; 419 | } 420 | err_set_type = err_set_type.? || cur_type; 421 | continue; 422 | } 423 | 424 | if (prev_info == .ErrorUnion and cur_info == .ErrorUnion) { 425 | const prev_payload_type = prev_info.ErrorUnion.payload; 426 | const cur_payload_type = cur_info.ErrorUnion.payload; 427 | const const_cast_prev = typesMatchConstCastOnly(prev_payload_type, cur_payload_type, false); 428 | const const_cast_cur = typesMatchConstCastOnly(cur_payload_type, prev_payload_type, false); 429 | 430 | if (const_cast_cur or const_cast_prev) { 431 | if (const_cast_cur) { 432 | prev_type = cur_type; 433 | } 434 | 435 | const prev_err_set_type = if (err_set_type) |s| 436 | s 437 | else 438 | prev_info.ErrorUnion.error_set; 439 | 440 | const cur_err_set_type = cur_info.ErrorUnion.error_set; 441 | if (errSetEql(prev_err_set_type, cur_err_set_type)) { 442 | continue; 443 | } 444 | 445 | if (prev_err_set_type == anyerror or cur_err_set_type == anyerror) { 446 | err_set_type = anyerror; 447 | continue; 448 | } 449 | 450 | if (err_set_type == null) { 451 | err_set_type = prev_err_set_type; 452 | } 453 | 454 | if (isSuperset(err_set_type, cur_err_set_type)) { 455 | continue; 456 | } 457 | 458 | if (isSuperset(cur_err_set_type, err_set_type)) { 459 | err_set_type = cur_err_set_type; 460 | continue; 461 | } 462 | 463 | err_set_type = err_set_type.? || cur_err_set_type; 464 | continue; 465 | } 466 | } 467 | 468 | if (prev_info == .Null) { 469 | prev_type = cur_type; 470 | any_are_null = true; 471 | continue; 472 | } 473 | 474 | if (cur_info == .Null) { 475 | any_are_null = true; 476 | continue; 477 | } 478 | 479 | if (prev_info == .Enum and cur_info == .EnumLiteral) { 480 | // We assume the enum literal is coercible to any enum type 481 | continue; 482 | } 483 | 484 | if (prev_info == .Union and prev_info.Union.tag_type != null and cur_info == .EnumLiteral) { 485 | // Same as above 486 | continue; 487 | } 488 | 489 | if (cur_info == .Enum and prev_info == .EnumLiteral) { 490 | // Same as above 491 | prev_type = cur_type; 492 | continue; 493 | } 494 | 495 | if (cur_info == .Union and cur_info.Union.tag_type != null and prev_info == .EnumLiteral) { 496 | // Same as above 497 | prev_type = cur_type; 498 | continue; 499 | } 500 | 501 | if (prev_info == .Pointer and prev_info.Pointer.size == .C and 502 | (cur_info == .ComptimeInt or cur_info == .Int)) 503 | { 504 | continue; 505 | } 506 | 507 | if (cur_info == .Pointer and cur_info.Pointer.size == .C and 508 | (prev_info == .ComptimeInt or prev_info == .Int)) 509 | { 510 | prev_type = cur_type; 511 | continue; 512 | } 513 | 514 | if (prev_info == .Pointer and cur_info == .Pointer) { 515 | if (prev_info.Pointer.size == .C and 516 | typesMatchConstCastOnly( 517 | prev_info.Pointer.child, 518 | cur_info.Pointer.child, 519 | !prev_info.Pointer.is_const, 520 | )) { 521 | continue; 522 | } 523 | 524 | if (cur_info.Pointer.size == .C and 525 | typesMatchConstCastOnly( 526 | cur_info.Pointer.child, 527 | prev_info.Pointer.child, 528 | !cur_info.Pointer.is_const, 529 | )) { 530 | prev_type = cur_type; 531 | continue; 532 | } 533 | } 534 | 535 | if (typesMatchConstCastOnly(prev_type, cur_type, false)) 536 | continue; 537 | 538 | if (typesMatchConstCastOnly(cur_type, prev_type, false)) { 539 | prev_type = cur_type; 540 | continue; 541 | } 542 | 543 | if (prev_info == .Int and cur_info == .Int and 544 | prev_info.Int.signedness == cur_info.Int.signedness) 545 | { 546 | if (cur_info.Int.bits > prev_info.Int.bits) { 547 | prev_type = cur_type; 548 | } 549 | continue; 550 | } 551 | 552 | if (prev_info == .Float and cur_info == .Float) { 553 | if (cur_info.Float.bits > prev_info.Float.bits) { 554 | prev_type = cur_type; 555 | } 556 | continue; 557 | } 558 | 559 | if (prev_info == .ErrorUnion and 560 | typesMatchConstCastOnly(prev_info.ErrorUnion.payload, cur_type, false)) 561 | { 562 | continue; 563 | } 564 | 565 | if (cur_info == .ErrorUnion and 566 | typesMatchConstCastOnly(cur_info.ErrorUnion.payload, prev_type, false)) 567 | { 568 | if (err_set_type) |err_set| { 569 | const cur_err_set_type = cur_info.ErrorUnion.error_set; 570 | if (err_set_type == anyerror or cur_err_set_type == anyerror) { 571 | err_set_type = anyerror; 572 | prev_type = cur_type; 573 | continue; 574 | } 575 | err_set_type = err_set_type.? || cur_err_set_type; 576 | } 577 | prev_type = cur_type; 578 | continue; 579 | } 580 | 581 | if (prev_info == .Optional and 582 | typesMatchConstCastOnly(prev_info.Optional.child, cur_type, false)) 583 | { 584 | continue; 585 | } 586 | 587 | if (cur_info == .Optional and 588 | typesMatchConstCastOnly(cur_info.Optional.child, prev_type, false)) 589 | { 590 | prev_type = cur_type; 591 | continue; 592 | } 593 | 594 | if (prev_info == .Optional and 595 | typesMatchConstCastOnly(cur_type, prev_info.Optional.child, false)) 596 | { 597 | prev_type = cur_type; 598 | any_are_null = true; 599 | continue; 600 | } 601 | 602 | if (cur_info == .Optional and 603 | typesMatchConstCastOnly(prev_type, cur_info.Optional.child, false)) 604 | { 605 | any_are_null = true; 606 | continue; 607 | } 608 | 609 | if (cur_info == .Undefined) 610 | continue; 611 | 612 | if (prev_info == .Undefined) { 613 | prev_type = cur_type; 614 | continue; 615 | } 616 | 617 | if (prev_info == .ComptimeInt and (cur_info == .Int or cur_info == .ComptimeInt)) { 618 | prev_type = cur_type; 619 | continue; 620 | } 621 | 622 | if (prev_info == .ComptimeFloat and (cur_info == .Float or cur_info == .ComptimeFloat)) { 623 | prev_type = cur_type; 624 | continue; 625 | } 626 | 627 | if (cur_info == .ComptimeInt and (prev_info == .Int or prev_info == .ComptimeInt)) { 628 | continue; 629 | } 630 | 631 | if (cur_info == .ComptimeFloat and (prev_info == .Float or prev_info == .ComptimeFloat)) { 632 | continue; 633 | } 634 | 635 | // *[N]T to [*]T 636 | if (prev_info == .Pointer and prev_info.Pointer.size == .One and 637 | @typeInfo(prev_info.Pointer.child) == .Array and 638 | (cur_info == .Pointer and cur_info.Pointer.size == .Many)) 639 | { 640 | convert_to_const_slice = false; 641 | prev_type = cur_type; 642 | if (prev_info.Pointer.is_const and !cur_info.Pointer.is_const) { 643 | make_the_pointer_const = true; 644 | } 645 | continue; 646 | } 647 | 648 | // *[N]T to [*]T 649 | if (cur_info == .Pointer and cur_info.Pointer.size == .One and 650 | @typeInfo(cur_info.Pointer.child) == .Array and 651 | (prev_info == .Pointer and prev_info.Pointer.size == .Many)) 652 | { 653 | if (cur_info.Pointer.is_const and !prev_info.Pointer.is_const) { 654 | make_the_pointer_const = true; 655 | } 656 | continue; 657 | } 658 | 659 | // *[N]T to []T 660 | // *[N]T to E![]T 661 | if (cur_info == .Pointer and cur_info.Pointer.size == .One and 662 | @typeInfo(cur_info.Pointer.child) == .Array and 663 | ((prev_info == .ErrorUnion and isSlice(prev_info.ErrorUnion.payload)) or 664 | isSlice(prev_type))) 665 | { 666 | const array_type = cur_info.Pointer.child; 667 | const slice_type = if (prev_info == .ErrorUnion) 668 | prev_info.ErrorUnion.payload 669 | else 670 | prev_type; 671 | 672 | const array_info = @typeInfo(array_type).Array; 673 | const slice_ptr_type = slicePtrType(slice_type); 674 | const slice_ptr_info = @typeInfo(slice_ptr_type); 675 | if (typesMatchConstCastOnly( 676 | slice_ptr_info.Pointer.child, 677 | array_info.child, 678 | false, 679 | )) { 680 | const const_ok = slice_ptr_info.Pointer.is_const or 681 | array_info.size == 0 or !cur_info.Pointer.is_const; 682 | if (!const_ok) make_the_slice_const = true; 683 | convert_to_const_slice = false; 684 | continue; 685 | } 686 | } 687 | 688 | // *[N]T to []T 689 | // *[N]T to E![]T 690 | if (prev_info == .Pointer and prev_info.Pointer.size == .One and 691 | @typeInfo(prev_info.Pointer.child) == .Array and 692 | ((cur_info == .ErrorUnion and isSlice(cur_info.ErrorUnion.payload)) or 693 | (cur_info == .Optional and isSlice(cur_info.Optional.child)) or 694 | isSlice(cur_type))) 695 | { 696 | const array_type = prev_info.Pointer.child; 697 | const slice_type = switch (cur_info) { 698 | .ErrorUnion => |error_union_info| error_union_info.payload, 699 | .Optional => |optional_info| optional_info.child, 700 | else => cur_type, 701 | }; 702 | 703 | const array_info = @typeInfo(array_type).Array; 704 | const slice_ptr_type = slicePtrType(slice_type); 705 | const slice_ptr_info = @typeInfo(slice_ptr_type); 706 | if (typesMatchConstCastOnly( 707 | slice_ptr_info.Pointer.child, 708 | array_info.child, 709 | false, 710 | )) { 711 | const const_ok = slice_ptr_info.Pointer.is_const or 712 | array_info.size == 0 or !prev_info.Pointer.is_const; 713 | if (!const_ok) make_the_slice_const = true; 714 | prev_type = cur_type; 715 | convert_to_const_slice = false; 716 | continue; 717 | } 718 | } 719 | 720 | // *[N]T and *[M]T 721 | const both_ptr_to_arr = (cur_info == .Pointer and cur_info.Pointer.size == .One and 722 | @typeInfo(cur_info.Pointer.child) == .Array and 723 | prev_info == .Pointer and prev_info.Pointer.size == .One and 724 | @typeInfo(prev_info.Pointer.child) == .Array); 725 | 726 | if (both_ptr_to_arr) { 727 | const cur_array_info = @typeInfo(cur_info.Pointer.child).Array; 728 | const prev_array_info = @typeInfo(prev_info.Pointer.child).Array; 729 | 730 | if (prev_array_info.sentinel == null or (cur_array_info.sentinel != null and 731 | sentinelEql(prev_array_info.sentinel, cur_array_info.sentinel)) and 732 | typesMatchConstCastOnly( 733 | cur_array_info.child, 734 | prev_array_info.child, 735 | !cur_info.Pointer.is_const, 736 | )) { 737 | const const_ok = cur_info.Pointer.is_const or !prev_info.Pointer.is_const or 738 | prev_array_info.len == 0; 739 | 740 | if (!const_ok) make_the_slice_const = true; 741 | prev_type = cur_type; 742 | convert_to_const_slice = true; 743 | continue; 744 | } 745 | 746 | if (cur_array_info.sentinel == null or (prev_array_info.sentinel != null and 747 | sentinelEql(cur_array_info.sentinel, prev_array_info.sentinel)) and 748 | typesMatchConstCastOnly( 749 | prev_array_info.child, 750 | cur_array_info.child, 751 | !prev_info.Pointer.is_const, 752 | )) { 753 | const const_ok = prev_indo.Pointer.is_const or !cur_info.Pointer.is_const or 754 | cur_array_info.len == 0; 755 | 756 | if (!const_ok) make_the_slice_const = true; 757 | convert_to_const_slice = true; 758 | continue; 759 | } 760 | } 761 | 762 | if (prev_info == .Enum and cur_info == .Union and cur_info.Union.tag_type != null) { 763 | if (cur_info.Union.tag_type.? == prev_type) { 764 | continue; 765 | } 766 | } 767 | 768 | if (cur_info == .Enum and prev_info == .Union and prev_info.Union.tag_type != null) { 769 | if (prev_info.Union.tag_type.? == cur_type) { 770 | prev_type = cur_type; 771 | continue; 772 | } 773 | } 774 | 775 | return null; 776 | } 777 | 778 | if (convert_to_const_slice) { 779 | if (prev_info == .Pointer) { 780 | const array_type = prev_info.Pointer.child; 781 | const array_info = @typeInfo(array_type).Array; 782 | 783 | const slice_type = @Type(.{ 784 | .Pointer = .{ 785 | .child = array_info.child, 786 | .is_const = prev_info.Pointer.is_const or make_the_slice_const, 787 | .is_volatile = false, 788 | .size = .Slice, 789 | .alignment = if (@sizeOf(array_info.child) > 0) @alignOf(array_info.child) else 0, 790 | .is_allowzero = false, 791 | .sentinel = array_info.sentinel, 792 | }, 793 | }); 794 | 795 | if (err_set_type) |err_type| { 796 | return err_type!slice_type; 797 | } 798 | return slice_type; 799 | } 800 | } else if (err_set_type) |err_type| { 801 | return switch (prev_info) { 802 | .ErrorSet => err_type, 803 | .ErrorUnion => |u| err_type!u.payload, 804 | else => null, 805 | }; 806 | } else if (any_are_null and prev_info != .Null) { 807 | if (prev_info == .Optional) { 808 | return prev_type; 809 | } 810 | return ?prev_type; 811 | } else if (make_the_slice_const) { 812 | const slice_type = switch (prev_info) { 813 | .ErrorUnion => |u| u.payload, 814 | .Pointer => |p| if (p.size == .Slice) prev_type else unreachable, 815 | else => unreachable, 816 | }; 817 | 818 | const adjusted_slice_type = blk: { 819 | var ptr_info = @typeInfo(slice_type); 820 | ptr_info.Pointer.is_const = make_the_slice_const; 821 | break :blk @Type(ptr_info); 822 | }; 823 | return switch (prev_info) { 824 | .ErrorUnion => |u| u.error_set!adjusted_slice_type, 825 | .Pointer => |p| if (p.size == .Slice) adjusted_slice_type else unreachable, 826 | else => unreachable, 827 | }; 828 | } else if (make_the_pointer_const) { 829 | return blk: { 830 | var ptr_info = @typeInfo(prev_type); 831 | ptr_info.Pointer.is_const = make_the_pointer_const; 832 | break :blk @Type(ptr_info); 833 | }; 834 | } 835 | return prev_type; 836 | } 837 | 838 | fn slicePtrType(comptime S: type) type { 839 | var info = @typeInfo(S); 840 | info.Pointer.size = .Many; 841 | return @Type(info); 842 | } 843 | 844 | const isSlice = std.meta.trait.isSlice; 845 | 846 | pub fn coercesTo(comptime dst: type, comptime src: type) bool { 847 | return (PeerType(.{ dst, src }) orelse return false) == dst; 848 | } 849 | 850 | const ContainsAnytype = struct { val: anytype }; 851 | const AnyType = std.meta.fieldInfo(ContainsAnytype, .val).field_type; 852 | 853 | pub fn requiresComptime(comptime T: type) bool { 854 | comptime { 855 | switch (T) { 856 | AnyType, 857 | comptime_int, 858 | comptime_float, 859 | type, 860 | @Type(.EnumLiteral), 861 | @Type(.Null), 862 | @Type(.Undefined), 863 | => return true, 864 | else => {}, 865 | } 866 | 867 | const info = @typeInfo(T); 868 | switch (info) { 869 | .BoundFn => return true, 870 | .Array => |a| return requiresComptime(a.child), 871 | .Struct => |s| { 872 | for (s.fields) |f| { 873 | if (requiresComptime(f.field_type)) 874 | return true; 875 | } 876 | return false; 877 | }, 878 | .Union => |u| { 879 | for (u.fields) |f| { 880 | if (requiresComptime(f.field_type)) 881 | return true; 882 | } 883 | return false; 884 | }, 885 | .Optional => |o| return requiresComptime(o.child), 886 | .ErrorUnion => |u| return requiresComptime(u.payload), 887 | .Pointer => |p| { 888 | if (@typeInfo(p.child) == .Opaque) 889 | return false; 890 | return requiresComptime(p.child); 891 | }, 892 | .Fn => |f| return f.is_generic, 893 | else => return false, 894 | } 895 | } 896 | } 897 | 898 | fn testPeerTypeIs(comptime types: anytype, comptime result: type) void { 899 | std.testing.expect((PeerType(types) orelse unreachable) == result); 900 | } 901 | 902 | fn testNoPeerType(comptime types: anytype) void { 903 | std.testing.expect(PeerType(types) == null); 904 | } 905 | 906 | test "PeerType" { 907 | testPeerTypeIs(.{ *const [3:0]u8, *const [15:0]u8 }, [:0]const u8); 908 | testPeerTypeIs(.{ usize, u8 }, usize); 909 | 910 | const E1 = error{OutOfMemory}; 911 | const E2 = error{}; 912 | const S = struct {}; 913 | testPeerTypeIs(.{ E1, E2 }, E1); 914 | testPeerTypeIs(.{ E1, anyerror!S, E2 }, anyerror!S); 915 | 916 | testPeerTypeIs(.{ *align(16) usize, *align(2) usize }, *align(2) usize); 917 | 918 | testNoPeerType(.{ usize, void }); 919 | testNoPeerType(.{ struct {}, struct {} }); 920 | } 921 | 922 | test "coercesTo" { 923 | std.testing.expect(coercesTo([]const u8, *const [123:0]u8)); 924 | std.testing.expect(coercesTo(usize, comptime_int)); 925 | } 926 | 927 | test "requiresComptime" { 928 | std.testing.expect(requiresComptime(comptime_int)); 929 | std.testing.expect(requiresComptime(struct { foo: anytype })); 930 | std.testing.expect(requiresComptime(struct { foo: struct { bar: comptime_float } })); 931 | std.testing.expect(!requiresComptime(struct { foo: void })); 932 | } 933 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PeerType 2 | 3 | ## API 4 | ```zig 5 | /// types must be an iterable of types (tuple, slice, ptr to array) 6 | pub fn PeerType(comptime types: anytype) ?type; 7 | pub fn coercesTo(comptime dst: type, comptime src: type) bool; 8 | pub fn requiresComptime(comptime T: type) bool; 9 | ``` 10 | 11 | ## License 12 | MIT 13 | --------------------------------------------------------------------------------