|
18 | 18 | #include "toolchain/check/impl.h"
|
19 | 19 | #include "toolchain/check/import_ref.h"
|
20 | 20 | #include "toolchain/check/inst.h"
|
| 21 | +#include "toolchain/check/name_lookup.h" |
21 | 22 | #include "toolchain/check/subst.h"
|
22 | 23 | #include "toolchain/check/type.h"
|
23 | 24 | #include "toolchain/check/type_completion.h"
|
|
26 | 27 | #include "toolchain/sem_ir/ids.h"
|
27 | 28 | #include "toolchain/sem_ir/impl.h"
|
28 | 29 | #include "toolchain/sem_ir/inst.h"
|
| 30 | +#include "toolchain/sem_ir/specific_interface.h" |
29 | 31 | #include "toolchain/sem_ir/typed_insts.h"
|
30 | 32 |
|
31 | 33 | namespace Carbon::Check {
|
@@ -523,48 +525,192 @@ static auto GetOrAddLookupImplWitness(Context& context, SemIR::LocId loc_id,
|
523 | 525 | return context.constant_values().GetInstId(witness_const_id);
|
524 | 526 | }
|
525 | 527 |
|
526 |
| -// Returns true if the `Self` should impl `Destroy`. |
527 |
| -static auto TypeCanDestroy(Context& context, |
528 |
| - SemIR::ConstantId query_self_const_id) -> bool { |
529 |
| - auto inst = context.insts().Get(context.constant_values().GetInstId( |
530 |
| - GetCanonicalFacetOrTypeValue(context, query_self_const_id))); |
531 |
| - |
532 |
| - // For facet values, look if the FacetType provides the same. |
533 |
| - if (auto facet_type = |
534 |
| - context.types().TryGetAs<SemIR::FacetType>(inst.type_id())) { |
535 |
| - const auto& info = context.facet_types().Get(facet_type->facet_type_id); |
536 |
| - if (info.builtin_constraint_mask.HasAnyOf( |
537 |
| - SemIR::BuiltinConstraintMask::TypeCanDestroy)) { |
538 |
| - return true; |
| 528 | +// Determines whether `type_id` is a `FacetType` which impls `Destroy`. |
| 529 | +// |
| 530 | +// This handles cases where the facet type provides either `CanDestroy` or |
| 531 | +// `Destroy`; the former case will always have a `Destroy` implementation. |
| 532 | +static auto CanDestroyFacetType(Context& context, SemIR::LocId loc_id, |
| 533 | + SemIR::FacetType facet_type, |
| 534 | + SemIR::InterfaceId& destroy_interface_id) |
| 535 | + -> bool { |
| 536 | + const auto& info = context.facet_types().Get(facet_type.facet_type_id); |
| 537 | + if (info.builtin_constraint_mask.HasAnyOf( |
| 538 | + SemIR::BuiltinConstraintMask::TypeCanDestroy)) { |
| 539 | + return true; |
| 540 | + } |
| 541 | + |
| 542 | + if (info.extend_constraints.empty()) { |
| 543 | + return false; |
| 544 | + } |
| 545 | + |
| 546 | + // Fetch `Core.Destroy` if it isn't yet known. |
| 547 | + if (!destroy_interface_id.has_value()) { |
| 548 | + auto destroy_id = LookupNameInCore(context, loc_id, "Destroy"); |
| 549 | + auto destroy_facet_type = |
| 550 | + context.insts().TryGetAs<SemIR::FacetType>(destroy_id); |
| 551 | + if (!destroy_facet_type) { |
| 552 | + return false; |
| 553 | + } |
| 554 | + const auto& destroy_facet_type_info = |
| 555 | + context.facet_types().Get(destroy_facet_type->facet_type_id); |
| 556 | + auto destroy_specific_interface = |
| 557 | + destroy_facet_type_info.TryAsSingleInterface(); |
| 558 | + if (!destroy_specific_interface) { |
| 559 | + return false; |
539 | 560 | }
|
| 561 | + |
| 562 | + destroy_interface_id = destroy_specific_interface->interface_id; |
540 | 563 | }
|
541 | 564 |
|
542 |
| - CARBON_KIND_SWITCH(inst) { |
543 |
| - case CARBON_KIND(SemIR::ClassType class_type): { |
544 |
| - auto class_info = context.classes().Get(class_type.class_id); |
545 |
| - // Incomplete and abstract classes can't be destroyed. |
546 |
| - // TODO: Return false if the object repr doesn't impl `Destroy`. |
547 |
| - // TODO: Return false for C++ types that lack a destructor. |
548 |
| - return class_info.is_complete() && |
549 |
| - class_info.inheritance_kind != |
550 |
| - SemIR::Class::InheritanceKind::Abstract; |
551 |
| - } |
552 |
| - case SemIR::ArrayType::Kind: |
553 |
| - case SemIR::ConstType::Kind: |
554 |
| - case SemIR::MaybeUnformedType::Kind: |
555 |
| - case SemIR::PartialType::Kind: |
556 |
| - case SemIR::StructType::Kind: |
557 |
| - case SemIR::TupleType::Kind: |
558 |
| - // TODO: Return false for types that indirectly reference a type that |
559 |
| - // doesn't impl `Destroy`. |
| 565 | + for (const auto& constraint : info.extend_constraints) { |
| 566 | + if (constraint.interface_id == destroy_interface_id) { |
560 | 567 | return true;
|
561 |
| - case SemIR::BoolType::Kind: |
562 |
| - case SemIR::PointerType::Kind: |
563 |
| - // Trivially destructible. |
564 |
| - return true; |
565 |
| - default: |
566 |
| - return false; |
| 568 | + } |
| 569 | + } |
| 570 | + |
| 571 | + return false; |
| 572 | +} |
| 573 | + |
| 574 | +// Returns true if the `Self` should impl `Destroy`. |
| 575 | +static auto TypeCanDestroy(Context& context, SemIR::LocId loc_id, |
| 576 | + SemIR::ConstantId query_self_const_id) -> bool { |
| 577 | + // A lazily cached `Destroy` interface. |
| 578 | + auto destroy_interface_id = SemIR::InterfaceId::None; |
| 579 | + |
| 580 | + auto query_inst_id = context.constant_values().GetInstId( |
| 581 | + GetCanonicalFacetOrTypeValue(context, query_self_const_id)); |
| 582 | + |
| 583 | + // When querying a facet type, we can return early. |
| 584 | + if (auto facet_type = context.types().TryGetAs<SemIR::FacetType>( |
| 585 | + context.insts().Get(query_inst_id).type_id())) { |
| 586 | + return CanDestroyFacetType(context, loc_id, *facet_type, |
| 587 | + destroy_interface_id); |
| 588 | + } |
| 589 | + |
| 590 | + // A stack of IDs to check (ordering shouldn't be important). |
| 591 | + struct WorkItem { |
| 592 | + SemIR::InstId id; |
| 593 | + // The only case `abstract` is allowed during a type walk is when looking at |
| 594 | + // a `base` type of a class. |
| 595 | + bool allow_abstract = false; |
| 596 | + }; |
| 597 | + llvm::SmallVector<WorkItem> work; |
| 598 | + |
| 599 | + // Start by checking the queried instruction. |
| 600 | + work.push_back({.id = query_inst_id}); |
| 601 | + |
| 602 | + // Loop through type structures recursively, looking for any types |
| 603 | + // incompatible with `Destroy`. |
| 604 | + while (!work.empty()) { |
| 605 | + auto work_item = work.pop_back_val(); |
| 606 | + auto inst = context.insts().Get(work_item.id); |
| 607 | + |
| 608 | + CARBON_KIND_SWITCH(inst) { |
| 609 | + case CARBON_KIND(SemIR::ArrayType array_type): { |
| 610 | + work.push_back({.id = array_type.element_type_inst_id}); |
| 611 | + break; |
| 612 | + } |
| 613 | + |
| 614 | + case CARBON_KIND(SemIR::BaseDecl base_decl): { |
| 615 | + work.push_back( |
| 616 | + {.id = base_decl.base_type_inst_id, .allow_abstract = true}); |
| 617 | + break; |
| 618 | + } |
| 619 | + |
| 620 | + case CARBON_KIND(SemIR::ConstType const_type): { |
| 621 | + work.push_back({.id = const_type.inner_id, |
| 622 | + .allow_abstract = work_item.allow_abstract}); |
| 623 | + break; |
| 624 | + } |
| 625 | + |
| 626 | + case CARBON_KIND(SemIR::ClassType class_type): { |
| 627 | + auto class_info = context.classes().Get(class_type.class_id); |
| 628 | + // Incomplete and abstract classes can't be destroyed. |
| 629 | + if (!class_info.is_complete() || |
| 630 | + (!work_item.allow_abstract && |
| 631 | + class_info.inheritance_kind == |
| 632 | + SemIR::Class::InheritanceKind::Abstract)) { |
| 633 | + return false; |
| 634 | + } |
| 635 | + |
| 636 | + // For C++ types, use Clang to determine whether they can be destructed. |
| 637 | + // TODO: Needs appropriate calls. |
| 638 | + if (context.name_scopes().Get(class_info.scope_id).is_cpp_scope()) { |
| 639 | + break; |
| 640 | + } |
| 641 | + |
| 642 | + auto obj_repr_id = |
| 643 | + class_info.GetObjectRepr(context.sem_ir(), SemIR::SpecificId::None); |
| 644 | + work.push_back({.id = context.types().GetInstId(obj_repr_id)}); |
| 645 | + break; |
| 646 | + } |
| 647 | + |
| 648 | + case CARBON_KIND(SemIR::FacetAccessType facet_access_type): { |
| 649 | + // For facet types, see if they impl `Core.Destroy` directly. |
| 650 | + if (!facet_access_type.facet_value_inst_id.has_value()) { |
| 651 | + return false; |
| 652 | + } |
| 653 | + auto facet_value = |
| 654 | + context.insts().Get(facet_access_type.facet_value_inst_id); |
| 655 | + auto facet_type = |
| 656 | + context.types().GetAs<SemIR::FacetType>(facet_value.type_id()); |
| 657 | + if (!CanDestroyFacetType(context, loc_id, facet_type, |
| 658 | + destroy_interface_id)) { |
| 659 | + return false; |
| 660 | + } |
| 661 | + break; |
| 662 | + } |
| 663 | + |
| 664 | + case CARBON_KIND(SemIR::MaybeUnformedType maybe_unformed_type): { |
| 665 | + break; |
| 666 | + (void)maybe_unformed_type; |
| 667 | + } |
| 668 | + |
| 669 | + case CARBON_KIND(SemIR::StructType struct_type): { |
| 670 | + for (auto field : |
| 671 | + context.struct_type_fields().Get(struct_type.fields_id)) { |
| 672 | + work.push_back({.id = field.type_inst_id}); |
| 673 | + } |
| 674 | + break; |
| 675 | + } |
| 676 | + |
| 677 | + case CARBON_KIND(SemIR::TupleType tuple_type): { |
| 678 | + for (auto element_id : |
| 679 | + context.inst_blocks().Get(tuple_type.type_elements_id)) { |
| 680 | + work.push_back({.id = element_id}); |
| 681 | + } |
| 682 | + break; |
| 683 | + } |
| 684 | + |
| 685 | + case SemIR::FloatLiteralType::Kind: |
| 686 | + case SemIR::FloatType::Kind: |
| 687 | + case SemIR::IntLiteralType::Kind: |
| 688 | + case SemIR::IntType::Kind: |
| 689 | + case SemIR::BoolType::Kind: |
| 690 | + case SemIR::PointerType::Kind: |
| 691 | + case SemIR::TypeType::Kind: |
| 692 | + // Trivially destructible. |
| 693 | + break; |
| 694 | + |
| 695 | + case CARBON_KIND(SemIR::BindSymbolicName binding): { |
| 696 | + (void)binding; |
| 697 | + return false; |
| 698 | + } |
| 699 | + |
| 700 | + case SemIR::ImplWitnessAccess::Kind: |
| 701 | + // TODO: Figure out what to do with these. |
| 702 | + return false; |
| 703 | + |
| 704 | + case SemIR::PartialType::Kind: |
| 705 | + // Never destructible. |
| 706 | + return false; |
| 707 | + |
| 708 | + default: |
| 709 | + CARBON_FATAL("TypeCanDestroy found unexpected inst: {0}", inst); |
| 710 | + } |
567 | 711 | }
|
| 712 | + |
| 713 | + return true; |
568 | 714 | }
|
569 | 715 |
|
570 | 716 | auto LookupImplWitness(Context& context, SemIR::LocId loc_id,
|
@@ -597,7 +743,7 @@ auto LookupImplWitness(Context& context, SemIR::LocId loc_id,
|
597 | 743 | }
|
598 | 744 | if (builtin_constraint_mask.HasAnyOf(
|
599 | 745 | SemIR::BuiltinConstraintMask::TypeCanDestroy) &&
|
600 |
| - !TypeCanDestroy(context, query_self_const_id)) { |
| 746 | + !TypeCanDestroy(context, loc_id, query_self_const_id)) { |
601 | 747 | return SemIR::InstBlockId::None;
|
602 | 748 | }
|
603 | 749 | if (interfaces.empty()) {
|
|
0 commit comments