Skip to content

Commit 0a7390e

Browse files
committed
Expand TypeCanDestroy to handle more types
1 parent e3b4482 commit 0a7390e

30 files changed

+1987
-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 be a 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: 184 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,192 @@ 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;
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;
539560
}
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`.
565+
for (const auto& constraint : info.extend_constraints) {
566+
if (constraint.interface_id == destroy_interface_id) {
560567
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+
}
567711
}
712+
713+
return true;
568714
}
569715

570716
auto LookupImplWitness(Context& context, SemIR::LocId loc_id,
@@ -597,7 +743,7 @@ auto LookupImplWitness(Context& context, SemIR::LocId loc_id,
597743
}
598744
if (builtin_constraint_mask.HasAnyOf(
599745
SemIR::BuiltinConstraintMask::TypeCanDestroy) &&
600-
!TypeCanDestroy(context, query_self_const_id)) {
746+
!TypeCanDestroy(context, loc_id, query_self_const_id)) {
601747
return SemIR::InstBlockId::None;
602748
}
603749
if (interfaces.empty()) {

0 commit comments

Comments
 (0)