Skip to content

Commit 723c9a9

Browse files
committed
Expand TypeCanDestroy to handle more types
1 parent b99bc00 commit 723c9a9

30 files changed

+1981
-2179
lines changed

core/prelude/iterate.carbon

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,13 @@ export import library "prelude/operators";
1111

1212
interface Iterate {
1313
// TODO: Support iterating ranges of non-copyable values.
14-
let ElementType:! Copy;
14+
let ElementType:! Copy & Destroy;
1515
let CursorType:! type;
1616
fn NewCursor[self: Self]() -> CursorType;
1717
fn Next[self: Self](cursor: CursorType*) -> Optional(ElementType);
1818
}
1919

20-
impl forall [T:! Copy, N:! IntLiteral()]
20+
impl forall [T:! Copy & Destroy, N:! IntLiteral()]
2121
array(T, N) as Iterate
2222
where .ElementType = T and .CursorType = i32 {
2323
fn NewCursor[self: Self]() -> i32 { return 0; }

core/prelude/types/maybe_unformed.carbon

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import library "prelude/destroy";
88

99
private fn MakeMaybeUnformed(t: type) -> type = "maybe_unformed.make_type";
1010

11-
class MaybeUnformed(T:! type) {
11+
// TODO: There should probably support for non-destructible types.
12+
class MaybeUnformed(T:! Destroy) {
1213
adapt MakeMaybeUnformed(T);
1314
}

core/prelude/types/optional.carbon

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ package Core library "prelude/types/optional";
66

77
import library "prelude/copy";
88
import library "prelude/destroy";
9+
import library "prelude/operators/bitwise";
910
import library "prelude/types/bool";
1011

1112
// For now, an `Optional(T)` is stored as a pair of a `bool` and a `T`, with
@@ -16,7 +17,7 @@ import library "prelude/types/bool";
1617
//
1718
// TODO: We don't have an approved design for an `Optional` type yet, but it's
1819
// used by the design for `Iterate`. The API here is a placeholder.
19-
class Optional(T:! Copy) {
20+
class Optional(T:! Copy & Destroy) {
2021
fn None() -> Self {
2122
returned var me: Self;
2223
me.has_value = false;

toolchain/check/impl_lookup.cpp

Lines changed: 179 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
#include "toolchain/check/impl.h"
1919
#include "toolchain/check/import_ref.h"
2020
#include "toolchain/check/inst.h"
21+
#include "toolchain/check/name_lookup.h"
2122
#include "toolchain/check/subst.h"
2223
#include "toolchain/check/type.h"
2324
#include "toolchain/check/type_completion.h"
@@ -26,6 +27,7 @@
2627
#include "toolchain/sem_ir/ids.h"
2728
#include "toolchain/sem_ir/impl.h"
2829
#include "toolchain/sem_ir/inst.h"
30+
#include "toolchain/sem_ir/specific_interface.h"
2931
#include "toolchain/sem_ir/typed_insts.h"
3032

3133
namespace Carbon::Check {
@@ -523,48 +525,187 @@ static auto GetOrAddLookupImplWitness(Context& context, SemIR::LocId loc_id,
523525
return context.constant_values().GetInstId(witness_const_id);
524526
}
525527

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;
539553
}
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;
560+
}
561+
562+
destroy_interface_id = destroy_specific_interface->interface_id;
540563
}
541564

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`.
560-
return true;
561-
case SemIR::BoolType::Kind:
562-
case SemIR::PointerType::Kind:
563-
// Trivially destructible.
565+
for (const auto& constraint : info.extend_constraints) {
566+
if (constraint.interface_id == destroy_interface_id) {
564567
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);
567588
}
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::StructType struct_type): {
665+
for (auto field :
666+
context.struct_type_fields().Get(struct_type.fields_id)) {
667+
work.push_back({.id = field.type_inst_id});
668+
}
669+
break;
670+
}
671+
672+
case CARBON_KIND(SemIR::TupleType tuple_type): {
673+
for (auto element_id :
674+
context.inst_blocks().Get(tuple_type.type_elements_id)) {
675+
work.push_back({.id = element_id});
676+
}
677+
break;
678+
}
679+
680+
case SemIR::FloatLiteralType::Kind:
681+
case SemIR::FloatType::Kind:
682+
case SemIR::IntLiteralType::Kind:
683+
case SemIR::IntType::Kind:
684+
case SemIR::BoolType::Kind:
685+
case SemIR::PointerType::Kind:
686+
case SemIR::TypeType::Kind:
687+
// Trivially destructible.
688+
break;
689+
690+
case SemIR::MaybeUnformedType::Kind:
691+
// TODO: Need to dig into the type to ensure it does `Destroy`.
692+
break;
693+
694+
case SemIR::BindSymbolicName::Kind:
695+
case SemIR::ImplWitnessAccess::Kind:
696+
// TODO: Figure out what to do with these.
697+
return false;
698+
699+
case SemIR::PartialType::Kind:
700+
// Never destructible.
701+
return false;
702+
703+
default:
704+
CARBON_FATAL("TypeCanDestroy found unexpected inst: {0}", inst);
705+
}
706+
}
707+
708+
return true;
568709
}
569710

570711
auto LookupImplWitness(Context& context, SemIR::LocId loc_id,
@@ -597,7 +738,7 @@ auto LookupImplWitness(Context& context, SemIR::LocId loc_id,
597738
}
598739
if (builtin_constraint_mask.HasAnyOf(
599740
SemIR::BuiltinConstraintMask::TypeCanDestroy) &&
600-
!TypeCanDestroy(context, query_self_const_id)) {
741+
!TypeCanDestroy(context, loc_id, query_self_const_id)) {
601742
return SemIR::InstBlockId::None;
602743
}
603744
if (interfaces.empty()) {

0 commit comments

Comments
 (0)