|
19 | 19 | #include "toolchain/check/impl.h" |
20 | 20 | #include "toolchain/check/import_ref.h" |
21 | 21 | #include "toolchain/check/inst.h" |
| 22 | +#include "toolchain/check/name_lookup.h" |
22 | 23 | #include "toolchain/check/subst.h" |
23 | 24 | #include "toolchain/check/type.h" |
24 | 25 | #include "toolchain/check/type_completion.h" |
|
27 | 28 | #include "toolchain/sem_ir/ids.h" |
28 | 29 | #include "toolchain/sem_ir/impl.h" |
29 | 30 | #include "toolchain/sem_ir/inst.h" |
| 31 | +#include "toolchain/sem_ir/specific_interface.h" |
30 | 32 | #include "toolchain/sem_ir/typed_insts.h" |
31 | 33 |
|
32 | 34 | namespace Carbon::Check { |
@@ -566,48 +568,208 @@ static auto GetOrAddLookupImplWitness(Context& context, SemIR::LocId loc_id, |
566 | 568 | return context.constant_values().GetInstId(witness_const_id); |
567 | 569 | } |
568 | 570 |
|
569 | | -// Returns true if the `Self` should impl `Destroy`. |
570 | | -static auto TypeCanDestroy(Context& context, |
571 | | - SemIR::ConstantId query_self_const_id) -> bool { |
572 | | - auto inst = context.insts().Get(context.constant_values().GetInstId( |
573 | | - GetCanonicalFacetOrTypeValue(context, query_self_const_id))); |
574 | | - |
575 | | - // For facet values, look if the FacetType provides the same. |
576 | | - if (auto facet_type = |
577 | | - context.types().TryGetAs<SemIR::FacetType>(inst.type_id())) { |
578 | | - const auto& info = context.facet_types().Get(facet_type->facet_type_id); |
579 | | - if (info.builtin_constraint_mask.HasAnyOf( |
580 | | - SemIR::BuiltinConstraintMask::TypeCanDestroy)) { |
| 571 | +// Determines whether `type_id` is a `FacetType` which impls `Destroy`. |
| 572 | +// |
| 573 | +// This handles cases where the facet type provides either `CanDestroy` or |
| 574 | +// `Destroy`; the former case will always have a `Destroy` implementation. |
| 575 | +static auto CanDestroyFacetType(Context& context, SemIR::LocId loc_id, |
| 576 | + SemIR::FacetType facet_type, |
| 577 | + SemIR::InterfaceId& destroy_interface_id) |
| 578 | + -> bool { |
| 579 | + const auto& info = context.facet_types().Get(facet_type.facet_type_id); |
| 580 | + if (info.builtin_constraint_mask.HasAnyOf( |
| 581 | + SemIR::BuiltinConstraintMask::TypeCanDestroy)) { |
| 582 | + return true; |
| 583 | + } |
| 584 | + |
| 585 | + if (info.extend_constraints.empty()) { |
| 586 | + return false; |
| 587 | + } |
| 588 | + |
| 589 | + // Fetch `Core.Destroy` if it isn't yet known. |
| 590 | + if (!destroy_interface_id.has_value()) { |
| 591 | + auto destroy_id = LookupNameInCore(context, loc_id, "Destroy"); |
| 592 | + auto destroy_facet_type = |
| 593 | + context.insts().TryGetAs<SemIR::FacetType>(destroy_id); |
| 594 | + if (!destroy_facet_type) { |
| 595 | + return false; |
| 596 | + } |
| 597 | + const auto& destroy_facet_type_info = |
| 598 | + context.facet_types().Get(destroy_facet_type->facet_type_id); |
| 599 | + auto destroy_extend = destroy_facet_type_info.TryAsSingleExtend(); |
| 600 | + if (!destroy_extend || |
| 601 | + !std::holds_alternative<SemIR::SpecificInterface>(*destroy_extend)) { |
| 602 | + return false; |
| 603 | + } |
| 604 | + |
| 605 | + destroy_interface_id = |
| 606 | + std::get<SemIR::SpecificInterface>(*destroy_extend).interface_id; |
| 607 | + } |
| 608 | + |
| 609 | + for (const auto& constraint : info.extend_constraints) { |
| 610 | + if (constraint.interface_id == destroy_interface_id) { |
581 | 611 | return true; |
582 | 612 | } |
583 | 613 | } |
584 | 614 |
|
585 | | - CARBON_KIND_SWITCH(inst) { |
586 | | - case CARBON_KIND(SemIR::ClassType class_type): { |
587 | | - auto class_info = context.classes().Get(class_type.class_id); |
588 | | - // Incomplete and abstract classes can't be destroyed. |
589 | | - // TODO: Return false if the object repr doesn't impl `Destroy`. |
590 | | - // TODO: Return false for C++ types that lack a destructor. |
591 | | - return class_info.is_complete() && |
592 | | - class_info.inheritance_kind != |
593 | | - SemIR::Class::InheritanceKind::Abstract; |
| 615 | + return false; |
| 616 | +} |
| 617 | + |
| 618 | +// Returns true if an instruction that should be a `FacetValue` is destructible. |
| 619 | +static auto CanDestroyFacetValue(Context& context, SemIR::LocId loc_id, |
| 620 | + SemIR::InstId facet_value_inst_id, |
| 621 | + SemIR::InterfaceId& destroy_interface_id) |
| 622 | + -> bool { |
| 623 | + if (!facet_value_inst_id.has_value()) { |
| 624 | + return false; |
| 625 | + } |
| 626 | + auto facet_value = context.insts().Get(facet_value_inst_id); |
| 627 | + auto facet_type = |
| 628 | + context.types().GetAs<SemIR::FacetType>(facet_value.type_id()); |
| 629 | + return CanDestroyFacetType(context, loc_id, facet_type, destroy_interface_id); |
| 630 | +} |
| 631 | + |
| 632 | +// Returns true if the `Self` should impl `Destroy`. |
| 633 | +static auto TypeCanDestroy(Context& context, SemIR::LocId loc_id, |
| 634 | + SemIR::ConstantId query_self_const_id) -> bool { |
| 635 | + // A lazily cached `Destroy` interface. |
| 636 | + auto destroy_interface_id = SemIR::InterfaceId::None; |
| 637 | + |
| 638 | + auto query_inst_id = context.constant_values().GetInstId( |
| 639 | + GetCanonicalFacetOrTypeValue(context, query_self_const_id)); |
| 640 | + |
| 641 | + // When querying a facet type, we can return early. |
| 642 | + if (auto facet_type = context.types().TryGetAs<SemIR::FacetType>( |
| 643 | + context.insts().Get(query_inst_id).type_id())) { |
| 644 | + return CanDestroyFacetType(context, loc_id, *facet_type, |
| 645 | + destroy_interface_id); |
| 646 | + } |
| 647 | + |
| 648 | + // A stack of IDs to check (ordering shouldn't be important). |
| 649 | + struct WorkItem { |
| 650 | + SemIR::InstId id; |
| 651 | + // The only case `abstract` is allowed during a type walk is when looking at |
| 652 | + // a `base` type of a class. |
| 653 | + bool allow_abstract = false; |
| 654 | + }; |
| 655 | + llvm::SmallVector<WorkItem> work; |
| 656 | + |
| 657 | + // Start by checking the queried instruction. |
| 658 | + work.push_back({.id = query_inst_id}); |
| 659 | + |
| 660 | + // Loop through type structures recursively, looking for any types |
| 661 | + // incompatible with `Destroy`. |
| 662 | + while (!work.empty()) { |
| 663 | + auto work_item = work.pop_back_val(); |
| 664 | + auto inst = context.insts().Get(work_item.id); |
| 665 | + |
| 666 | + CARBON_KIND_SWITCH(inst) { |
| 667 | + case CARBON_KIND(SemIR::ArrayType array_type): { |
| 668 | + work.push_back({.id = array_type.element_type_inst_id}); |
| 669 | + break; |
| 670 | + } |
| 671 | + |
| 672 | + case CARBON_KIND(SemIR::BaseDecl base_decl): { |
| 673 | + work.push_back( |
| 674 | + {.id = base_decl.base_type_inst_id, .allow_abstract = true}); |
| 675 | + break; |
| 676 | + } |
| 677 | + |
| 678 | + case CARBON_KIND(SemIR::ClassType class_type): { |
| 679 | + auto class_info = context.classes().Get(class_type.class_id); |
| 680 | + // Incomplete and abstract classes can't be destroyed. |
| 681 | + if (!class_info.is_complete() || |
| 682 | + (!work_item.allow_abstract && |
| 683 | + class_info.inheritance_kind == |
| 684 | + SemIR::Class::InheritanceKind::Abstract)) { |
| 685 | + return false; |
| 686 | + } |
| 687 | + |
| 688 | + // For C++ types, use Clang to determine whether they can be destructed. |
| 689 | + // TODO: Needs appropriate calls. |
| 690 | + if (context.name_scopes().Get(class_info.scope_id).is_cpp_scope()) { |
| 691 | + break; |
| 692 | + } |
| 693 | + |
| 694 | + auto obj_repr_id = |
| 695 | + class_info.GetObjectRepr(context.sem_ir(), SemIR::SpecificId::None); |
| 696 | + work.push_back({.id = context.types().GetInstId(obj_repr_id)}); |
| 697 | + break; |
| 698 | + } |
| 699 | + |
| 700 | + case CARBON_KIND(SemIR::ConstType const_type): { |
| 701 | + work.push_back({.id = const_type.inner_id, |
| 702 | + .allow_abstract = work_item.allow_abstract}); |
| 703 | + break; |
| 704 | + } |
| 705 | + |
| 706 | + case CARBON_KIND(SemIR::FacetAccessType facet_access_type): { |
| 707 | + if (!CanDestroyFacetValue(context, loc_id, |
| 708 | + facet_access_type.facet_value_inst_id, |
| 709 | + destroy_interface_id)) { |
| 710 | + return false; |
| 711 | + } |
| 712 | + break; |
| 713 | + } |
| 714 | + |
| 715 | + case CARBON_KIND(SemIR::PartialType partial_type): { |
| 716 | + // TODO: This may be adjusted per |
| 717 | + // https://github.com/carbon-language/carbon-lang/issues/6161. |
| 718 | + work.push_back({.id = partial_type.inner_id, .allow_abstract = true}); |
| 719 | + break; |
| 720 | + } |
| 721 | + |
| 722 | + case CARBON_KIND(SemIR::StructType struct_type): { |
| 723 | + for (auto field : |
| 724 | + context.struct_type_fields().Get(struct_type.fields_id)) { |
| 725 | + work.push_back({.id = field.type_inst_id}); |
| 726 | + } |
| 727 | + break; |
| 728 | + } |
| 729 | + |
| 730 | + case CARBON_KIND(SemIR::SymbolicBindingType facet_access_type): { |
| 731 | + if (!CanDestroyFacetValue(context, loc_id, |
| 732 | + facet_access_type.facet_value_inst_id, |
| 733 | + destroy_interface_id)) { |
| 734 | + return false; |
| 735 | + } |
| 736 | + break; |
| 737 | + } |
| 738 | + |
| 739 | + case CARBON_KIND(SemIR::TupleType tuple_type): { |
| 740 | + for (auto element_id : |
| 741 | + context.inst_blocks().Get(tuple_type.type_elements_id)) { |
| 742 | + work.push_back({.id = element_id}); |
| 743 | + } |
| 744 | + break; |
| 745 | + } |
| 746 | + |
| 747 | + case SemIR::FloatLiteralType::Kind: |
| 748 | + case SemIR::FloatType::Kind: |
| 749 | + case SemIR::IntLiteralType::Kind: |
| 750 | + case SemIR::IntType::Kind: |
| 751 | + case SemIR::BoolType::Kind: |
| 752 | + case SemIR::PointerType::Kind: |
| 753 | + case SemIR::TypeType::Kind: |
| 754 | + // Trivially destructible. |
| 755 | + break; |
| 756 | + |
| 757 | + case SemIR::ImplWitnessAccess::Kind: |
| 758 | + case SemIR::MaybeUnformedType::Kind: |
| 759 | + // TODO: Need to dig into the type to ensure it does `Destroy`; |
| 760 | + // providing `Destroy` for now for compatibility. |
| 761 | + break; |
| 762 | + |
| 763 | + case SemIR::SymbolicBinding::Kind: |
| 764 | + // TODO: Figure out what to do with these. |
| 765 | + return false; |
| 766 | + |
| 767 | + default: |
| 768 | + CARBON_FATAL("TypeCanDestroy found unexpected inst: {0}", inst); |
594 | 769 | } |
595 | | - case SemIR::ArrayType::Kind: |
596 | | - case SemIR::ConstType::Kind: |
597 | | - case SemIR::MaybeUnformedType::Kind: |
598 | | - case SemIR::PartialType::Kind: |
599 | | - case SemIR::StructType::Kind: |
600 | | - case SemIR::TupleType::Kind: |
601 | | - // TODO: Return false for types that indirectly reference a type that |
602 | | - // doesn't impl `Destroy`. |
603 | | - return true; |
604 | | - case SemIR::BoolType::Kind: |
605 | | - case SemIR::PointerType::Kind: |
606 | | - // Trivially destructible. |
607 | | - return true; |
608 | | - default: |
609 | | - return false; |
610 | 770 | } |
| 771 | + |
| 772 | + return true; |
611 | 773 | } |
612 | 774 |
|
613 | 775 | auto LookupImplWitness(Context& context, SemIR::LocId loc_id, |
@@ -645,7 +807,7 @@ auto LookupImplWitness(Context& context, SemIR::LocId loc_id, |
645 | 807 | } |
646 | 808 | if (builtin_constraint_mask.HasAnyOf( |
647 | 809 | SemIR::BuiltinConstraintMask::TypeCanDestroy) && |
648 | | - !TypeCanDestroy(context, query_self_const_id)) { |
| 810 | + !TypeCanDestroy(context, loc_id, query_self_const_id)) { |
649 | 811 | return SemIR::InstBlockId::None; |
650 | 812 | } |
651 | 813 | if (interfaces.empty()) { |
|
0 commit comments