diff --git a/toolchain/check/action.cpp b/toolchain/check/action.cpp index b25d172a9958..719d0f60bb59 100644 --- a/toolchain/check/action.cpp +++ b/toolchain/check/action.cpp @@ -136,7 +136,7 @@ static auto RefineOperands(Context& context, SemIR::LocId loc_id, SemIR::Inst action) -> SemIR::Inst { auto [arg0_kind, arg1_kind] = action.ArgKinds(); auto arg0 = RefineOperand(context, loc_id, arg0_kind, action.arg0()); - auto arg1 = RefineOperand(context, loc_id, arg0_kind, action.arg1()); + auto arg1 = RefineOperand(context, loc_id, arg1_kind, action.arg1()); action.SetArgs(arg0, arg1); return action; } diff --git a/toolchain/check/action.h b/toolchain/check/action.h index 450c09c255d6..6609d403d58c 100644 --- a/toolchain/check/action.h +++ b/toolchain/check/action.h @@ -15,6 +15,8 @@ namespace Carbon::Check { // Performs a member access action. Defined in member_access.cpp. auto PerformAction(Context& context, SemIR::LocId loc_id, SemIR::AccessMemberAction action) -> SemIR::InstId; +auto PerformAction(Context& context, SemIR::LocId loc_id, + SemIR::AccessOptionalMemberAction action) -> SemIR::InstId; // Performs a conversion action. Defined in convert.cpp. auto PerformAction(Context& context, SemIR::LocId loc_id, diff --git a/toolchain/check/call.cpp b/toolchain/check/call.cpp index 972ab9c82462..88e512058fe0 100644 --- a/toolchain/check/call.cpp +++ b/toolchain/check/call.cpp @@ -6,6 +6,7 @@ #include "toolchain/base/kind_switch.h" #include "toolchain/check/context.h" +#include "toolchain/check/control_flow.h" #include "toolchain/check/convert.h" #include "toolchain/check/deduce.h" #include "toolchain/check/facet_type.h" @@ -233,7 +234,7 @@ auto PerformCall(Context& context, SemIR::LocId loc_id, SemIR::InstId callee_id, case SemIR::InitRepr::InPlace: // Tentatively put storage for a temporary in the function's return slot. // This will be replaced if necessary when we perform initialization. - return_slot_arg_id = AddInst( + return_slot_arg_id = AddInstWithCleanup( context, loc_id, {.type_id = return_info.type_id}); break; case SemIR::InitRepr::None: diff --git a/toolchain/check/control_flow.cpp b/toolchain/check/control_flow.cpp index 20118868c84f..258d930ff20d 100644 --- a/toolchain/check/control_flow.cpp +++ b/toolchain/check/control_flow.cpp @@ -4,7 +4,12 @@ #include "toolchain/check/control_flow.h" +#include "toolchain/base/kind_switch.h" +#include "toolchain/check/call.h" #include "toolchain/check/inst.h" +#include "toolchain/check/member_access.h" +#include "toolchain/check/name_lookup.h" +#include "toolchain/sem_ir/ids.h" #include "toolchain/sem_ir/typed_insts.h" namespace Carbon::Check { @@ -130,4 +135,58 @@ auto IsCurrentPositionReachable(Context& context) -> bool { SemIR::TerminatorKind::Terminator; } +auto MaybeAddCleanupForInst(Context& context, SemIR::TypeId type_id, + SemIR::InstId inst_id) -> void { + if (!context.scope_stack().PeekIsLexicalScope()) { + // Cleanup can only occur in lexical scopes. + return; + } + + // TODO: Add destruction of members of ArrayType, StructType, and + // TupleType. Includes refactoring MaybeAddCleanupForInst to remain + // non-recursive. + + auto type_inst = context.types().GetAsInst(type_id); + CARBON_KIND_SWITCH(type_inst) { + case SemIR::ClassType::Kind: { + // TODO: Figure out what destruction of classes with destroyable members + // should look like (maybe an implicit `fn destroy` added by + // `handle_class.cpp`?). + auto destroy_id = + PerformMemberAccess(context, SemIR::LocId::None, inst_id, + SemIR::NameId::Destroy, /*required=*/false); + if (destroy_id.has_value()) { + context.scope_stack().destroy_id_stack().AppendToTop(destroy_id); + } + break; + } + + default: + // Not interesting storage for destruction. + return; + } +} + +// Common support for cleanup blocks. +static auto AddCleanupBlock(Context& context) -> void { + auto destroy_ids = context.scope_stack().destroy_id_stack().PeekArray(); + + // If there's nothing to destroy, add the final instruction to the current + // block. + if (destroy_ids.empty()) { + return; + } + + for (auto destroy_id : llvm::reverse(destroy_ids)) { + PerformCall(context, SemIR::LocId::None, destroy_id, {}); + } +} + +auto AddReturnCleanupBlock( + Context& context, + typename decltype(SemIR::Return::Kind)::TypedNodeId node_id) -> void { + AddCleanupBlock(context); + AddInst(context, node_id, SemIR::Return{}); +} + } // namespace Carbon::Check diff --git a/toolchain/check/control_flow.h b/toolchain/check/control_flow.h index 4a776d10979a..7aad2da2a540 100644 --- a/toolchain/check/control_flow.h +++ b/toolchain/check/control_flow.h @@ -6,6 +6,8 @@ #define CARBON_TOOLCHAIN_CHECK_CONTROL_FLOW_H_ #include "toolchain/check/context.h" +#include "toolchain/check/inst.h" +#include "toolchain/parse/typed_nodes.h" #include "toolchain/sem_ir/ids.h" namespace Carbon::Check { @@ -62,6 +64,63 @@ auto SetBlockArgResultBeforeConstantUse(Context& context, // Returns whether the current position in the current block is reachable. auto IsCurrentPositionReachable(Context& context) -> bool; +// Determines whether the instruction requires cleanup and, if so, adds it for +// cleanup blocks. Note for example that a class may not need destruction when +// neither it nor its members have `destroy` functions. +auto MaybeAddCleanupForInst(Context& context, SemIR::TypeId type_id, + SemIR::InstId inst_id) -> void; + +// Adds an instruction that has cleanup associated. +template + requires(InstT::Kind.has_cleanup()) +auto AddInstWithCleanup(Context& context, LocT loc, InstT inst) + -> SemIR::InstId { + auto inst_id = AddInst(context, SemIR::LocIdAndInst(loc, inst)); + MaybeAddCleanupForInst(context, inst.type_id, inst_id); + return inst_id; +} + +// Adds an instruction that has cleanup associated. +template + requires(InstT::Kind.has_cleanup()) +auto AddInstWithCleanupInNoBlock(Context& context, LocT loc, InstT inst) + -> SemIR::InstId { + auto inst_id = AddInstInNoBlock(context, SemIR::LocIdAndInst(loc, inst)); + MaybeAddCleanupForInst(context, inst.type_id, inst_id); + return inst_id; +} + +// Cleanup blocks are an effort to share cleanup instructions across equivalent +// scope-ending instructions (for example, all `return;` instructions are +// equivalent). Structurally, they should first run non-shared cleanup, then +// either dispatch to a cleanup block that includes shared cleanup, or invoke +// the control flow instruction. +// +// For example: +// +// fn F() { +// var a: C; +// if (...) { +// // Cleanup block 1: destroy a, return +// return; +// } +// +// var b: C; +// if (...) { +// // Cleanup block 2: destroy b, reuse cleanup block 1. +// return; +// } +// +// DoSomethingMore(); +// // Cleanup block 3: reuse cleanup block 2. +// } +// +// TODO: Add support for `return;`, `return ;`, `break;`,and `continue;`; +// also, add reuse (described above but not done). +auto AddReturnCleanupBlock( + Context& context, + typename decltype(SemIR::Return::Kind)::TypedNodeId node_id) -> void; + } // namespace Carbon::Check #endif // CARBON_TOOLCHAIN_CHECK_CONTROL_FLOW_H_ diff --git a/toolchain/check/convert.cpp b/toolchain/check/convert.cpp index f6313ea7b859..f1376511f227 100644 --- a/toolchain/check/convert.cpp +++ b/toolchain/check/convert.cpp @@ -13,6 +13,7 @@ #include "toolchain/base/kind_switch.h" #include "toolchain/check/action.h" #include "toolchain/check/context.h" +#include "toolchain/check/control_flow.h" #include "toolchain/check/diagnostic_helpers.h" #include "toolchain/check/eval.h" #include "toolchain/check/impl_lookup.h" @@ -134,7 +135,7 @@ static auto FinalizeTemporary(Context& context, SemIR::InstId init_id, // initialize a temporary, rather than two separate instructions. auto init = sem_ir.insts().Get(init_id); auto loc_id = sem_ir.insts().GetLocId(init_id); - auto temporary_id = AddInst( + auto temporary_id = AddInstWithCleanup( context, loc_id, {.type_id = init.type_id()}); return AddInst(context, loc_id, {.type_id = init.type_id(), @@ -293,8 +294,9 @@ static auto ConvertTupleToArray(Context& context, SemIR::TupleType tuple_type, // destination for the array initialization if we weren't given one. SemIR::InstId return_slot_arg_id = target.init_id; if (!target.init_id.has_value()) { - return_slot_arg_id = target_block->AddInst( - value_loc_id, {.type_id = target.type_id}); + return_slot_arg_id = + target_block->AddInstWithCleanup( + value_loc_id, {.type_id = target.type_id}); } // Initialize each element of the array from the corresponding element of the @@ -619,7 +621,7 @@ static auto ConvertStructToClass(Context& context, SemIR::StructType src_type, if (need_temporary) { target.kind = ConversionTarget::Initializer; target.init_block = &target_block; - target.init_id = target_block.AddInst( + target.init_id = target_block.AddInstWithCleanup( context.insts().GetLocId(value_id), {.type_id = target.type_id}); } diff --git a/toolchain/check/handle_function.cpp b/toolchain/check/handle_function.cpp index 118549dafc1c..e631becec43a 100644 --- a/toolchain/check/handle_function.cpp +++ b/toolchain/check/handle_function.cpp @@ -29,6 +29,7 @@ #include "toolchain/sem_ir/entry_point.h" #include "toolchain/sem_ir/function.h" #include "toolchain/sem_ir/ids.h" +#include "toolchain/sem_ir/inst.h" #include "toolchain/sem_ir/pattern.h" #include "toolchain/sem_ir/typed_insts.h" @@ -641,7 +642,7 @@ auto HandleParseNode(Context& context, Parse::FunctionDefinitionId node_id) "missing `return` at end of function with declared return type"); context.emitter().Emit(TokenOnly(node_id), MissingReturnStatement); } else { - AddInst(context, node_id, {}); + AddReturnCleanupBlock(context, node_id); } } diff --git a/toolchain/check/handle_let_and_var.cpp b/toolchain/check/handle_let_and_var.cpp index e9bdb4a60e4e..5a277d7d69d5 100644 --- a/toolchain/check/handle_let_and_var.cpp +++ b/toolchain/check/handle_let_and_var.cpp @@ -3,6 +3,7 @@ // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #include "toolchain/check/context.h" +#include "toolchain/check/control_flow.h" #include "toolchain/check/convert.h" #include "toolchain/check/decl_introducer_state.h" #include "toolchain/check/generic.h" @@ -119,12 +120,13 @@ static auto GetOrAddStorage(Context& context, SemIR::InstId var_pattern_id) } auto pattern = context.insts().GetWithLocId(var_pattern_id); - return AddInst(context, pattern.loc_id, - SemIR::VarStorage{ - .type_id = pattern.inst.type_id(), - .pretty_name_id = SemIR::GetPrettyNameFromPatternId( - context.sem_ir(), - pattern.inst.As().subpattern_id)}); + return AddInstWithCleanup( + context, pattern.loc_id, + SemIR::VarStorage{ + .type_id = pattern.inst.type_id(), + .pretty_name_id = SemIR::GetPrettyNameFromPatternId( + context.sem_ir(), + pattern.inst.As().subpattern_id)}); } auto HandleParseNode(Context& context, Parse::VariablePatternId node_id) diff --git a/toolchain/check/inst.h b/toolchain/check/inst.h index f69c17ce31e1..d79e5c22b759 100644 --- a/toolchain/check/inst.h +++ b/toolchain/check/inst.h @@ -16,15 +16,20 @@ auto AddInst(Context& context, SemIR::LocIdAndInst loc_id_and_inst) -> SemIR::InstId; // Convenience for AddInst with typed nodes. +// +// As a safety check, prevent use with storage insts (see `AddInstWithCleanup`). template + requires(!InstT::Kind.has_cleanup()) auto AddInst(Context& context, LocT loc, InstT inst) -> SemIR::InstId { return AddInst(context, SemIR::LocIdAndInst(loc, inst)); } // Pushes a parse tree node onto the stack, storing the SemIR::Inst as the // result. +// +// As a safety check, prevent use with storage insts (see `AddInstWithCleanup`). template - requires(SemIR::Internal::HasNodeId) + requires(SemIR::Internal::HasNodeId && !InstT::Kind.has_cleanup()) auto AddInstAndPush(Context& context, typename decltype(InstT::Kind)::TypedNodeId node_id, InstT inst) -> void { @@ -37,7 +42,10 @@ auto AddInstInNoBlock(Context& context, SemIR::LocIdAndInst loc_id_and_inst) -> SemIR::InstId; // Convenience for AddInstInNoBlock with typed nodes. +// +// As a safety check, prevent use with storage insts (see `AddInstWithCleanup`). template + requires(!InstT::Kind.has_cleanup()) auto AddInstInNoBlock(Context& context, LocT loc, InstT inst) -> SemIR::InstId { return AddInstInNoBlock(context, SemIR::LocIdAndInst(loc, inst)); } @@ -48,7 +56,10 @@ auto GetOrAddInst(Context& context, SemIR::LocIdAndInst loc_id_and_inst) -> SemIR::InstId; // Convenience for GetOrAddInst with typed nodes. +// +// As a safety check, prevent use with storage insts (see `AddInstWithCleanup`). template + requires(!InstT::Kind.has_cleanup()) auto GetOrAddInst(Context& context, LocT loc, InstT inst) -> SemIR::InstId { return GetOrAddInst(context, SemIR::LocIdAndInst(loc, inst)); } @@ -76,8 +87,10 @@ auto AddPatternInst(Context& context, SemIR::LocIdAndInst loc_id_and_inst) -> SemIR::InstId; // Convenience for AddPatternInst with typed nodes. +// +// As a safety check, prevent use with storage insts (see `AddInstWithCleanup`). template - requires(SemIR::Internal::HasNodeId) + requires(SemIR::Internal::HasNodeId && !InstT::Kind.has_cleanup()) auto AddPatternInst(Context& context, typename decltype(InstT::Kind)::TypedNodeId node_id, InstT inst) -> SemIR::InstId { @@ -91,7 +104,10 @@ auto AddPlaceholderInst(Context& context, SemIR::LocIdAndInst loc_id_and_inst) -> SemIR::InstId; // Convenience for AddPlaceholderInst with typed nodes. +// +// As a safety check, prevent use with storage insts (see `AddInstWithCleanup`). template + requires(!InstT::Kind.has_cleanup()) auto AddPlaceholderInst(Context& context, LocT loc, InstT inst) -> SemIR::InstId { return AddPlaceholderInst(context, SemIR::LocIdAndInst(loc, inst)); @@ -105,7 +121,10 @@ auto AddPlaceholderInstInNoBlock(Context& context, -> SemIR::InstId; // Convenience for AddPlaceholderInstInNoBlock with typed nodes. +// +// As a safety check, prevent use with storage insts (see `AddInstWithCleanup`). template + requires(!InstT::Kind.has_cleanup()) auto AddPlaceholderInstInNoBlock(Context& context, LocT loc, InstT inst) -> SemIR::InstId { return AddPlaceholderInstInNoBlock(context, SemIR::LocIdAndInst(loc, inst)); diff --git a/toolchain/check/member_access.cpp b/toolchain/check/member_access.cpp index a2cd7e5c5ab3..d35902e7ffda 100644 --- a/toolchain/check/member_access.cpp +++ b/toolchain/check/member_access.cpp @@ -245,16 +245,15 @@ static auto LookupMemberNameInScope(Context& context, SemIR::LocId loc_id, SemIR::NameId name_id, SemIR::ConstantId name_scope_const_id, llvm::ArrayRef lookup_scopes, - bool lookup_in_type_of_base) + bool lookup_in_type_of_base, bool required) -> SemIR::InstId { AccessInfo access_info = { .constant_id = name_scope_const_id, .highest_allowed_access = GetHighestAllowedAccess(context, loc_id, name_scope_const_id), }; - LookupResult result = - LookupQualifiedName(context, loc_id, name_id, lookup_scopes, - /*required=*/true, access_info); + LookupResult result = LookupQualifiedName( + context, loc_id, name_id, lookup_scopes, required, access_info); if (!result.scope_result.is_found()) { return SemIR::ErrorInst::SingletonInstId; @@ -458,24 +457,31 @@ static auto ValidateTupleIndex(Context& context, SemIR::LocId loc_id, } auto PerformMemberAccess(Context& context, SemIR::LocId loc_id, - SemIR::InstId base_id, SemIR::NameId name_id) - -> SemIR::InstId { + SemIR::InstId base_id, SemIR::NameId name_id, + bool required) -> SemIR::InstId { // TODO: Member access for dependent member names is supposed to perform a // lookup in both the template definition context and the template // instantiation context, and reject if both succeed but find different // things. - return HandleAction( - context, loc_id, - {.type_id = SemIR::InstType::SingletonTypeId, - .base_id = base_id, - .name_id = name_id}); + if (required) { + return HandleAction( + context, loc_id, + {.type_id = SemIR::InstType::SingletonTypeId, + .base_id = base_id, + .name_id = name_id}); + } else { + return HandleAction( + context, loc_id, + {.type_id = SemIR::InstType::SingletonTypeId, + .base_id = base_id, + .name_id = name_id}); + } } -auto PerformAction(Context& context, SemIR::LocId loc_id, - SemIR::AccessMemberAction action) -> SemIR::InstId { - SemIR::InstId base_id = action.base_id; - SemIR::NameId name_id = action.name_id; - +// Common logic for `AccessMemberAction` and `AccessOptionalMemberAction`. +static auto PerformActionHelper(Context& context, SemIR::LocId loc_id, + SemIR::InstId base_id, SemIR::NameId name_id, + bool required) -> SemIR::InstId { // If the base is a name scope, such as a class or namespace, perform lookup // into that scope. if (auto base_const_id = context.constant_values().Get(base_id); @@ -483,23 +489,22 @@ auto PerformAction(Context& context, SemIR::LocId loc_id, llvm::SmallVector lookup_scopes; if (AppendLookupScopesForConstant(context, loc_id, base_const_id, &lookup_scopes)) { - return LookupMemberNameInScope(context, loc_id, base_id, name_id, - base_const_id, lookup_scopes, - /*lookup_in_type_of_base=*/false); + return LookupMemberNameInScope( + context, loc_id, base_id, name_id, base_const_id, lookup_scopes, + /*lookup_in_type_of_base=*/false, /*required=*/required); } } // If the base isn't a scope, it must have a complete type. auto base_type_id = context.insts().Get(base_id).type_id(); - if (!RequireCompleteType( - context, base_type_id, context.insts().GetLocId(base_id), [&] { - CARBON_DIAGNOSTIC( - IncompleteTypeInMemberAccess, Error, - "member access into object of incomplete type {0}", - TypeOfInstId); - return context.emitter().Build( - base_id, IncompleteTypeInMemberAccess, base_id); - })) { + auto base_loc_id = context.insts().GetLocId(base_id); + if (!RequireCompleteType(context, base_type_id, base_loc_id, [&] { + CARBON_DIAGNOSTIC(IncompleteTypeInMemberAccess, Error, + "member access into object of incomplete type {0}", + TypeOfInstId); + return context.emitter().Build(base_id, IncompleteTypeInMemberAccess, + base_id); + })) { return SemIR::ErrorInst::SingletonInstId; } @@ -528,12 +533,16 @@ auto PerformAction(Context& context, SemIR::LocId loc_id, .index = SemIR::ElementIndex(i)}); } } - CARBON_DIAGNOSTIC(QualifiedExprNameNotFound, Error, - "type {0} does not have a member `{1}`", TypeOfInstId, - SemIR::NameId); - context.emitter().Emit(loc_id, QualifiedExprNameNotFound, base_id, - name_id); - return SemIR::ErrorInst::SingletonInstId; + if (required) { + CARBON_DIAGNOSTIC(QualifiedExprNameNotFound, Error, + "type {0} does not have a member `{1}`", TypeOfInstId, + SemIR::NameId); + context.emitter().Emit(loc_id, QualifiedExprNameNotFound, base_id, + name_id); + return SemIR::ErrorInst::SingletonInstId; + } else { + return SemIR::InstId::None; + } } if (base_type_id != SemIR::ErrorInst::SingletonTypeId) { @@ -546,9 +555,9 @@ auto PerformAction(Context& context, SemIR::LocId loc_id, } // Perform lookup into the base type. - auto member_id = LookupMemberNameInScope(context, loc_id, base_id, name_id, - base_type_const_id, lookup_scopes, - /*lookup_in_type_of_base=*/true); + auto member_id = LookupMemberNameInScope( + context, loc_id, base_id, name_id, base_type_const_id, lookup_scopes, + /*lookup_in_type_of_base=*/true, /*required=*/required); // For name lookup into a facet, never perform instance binding. // TODO: According to the design, this should be a "lookup in base" lookup, @@ -564,6 +573,18 @@ auto PerformAction(Context& context, SemIR::LocId loc_id, return member_id; } +auto PerformAction(Context& context, SemIR::LocId loc_id, + SemIR::AccessMemberAction action) -> SemIR::InstId { + return PerformActionHelper(context, loc_id, action.base_id, action.name_id, + /*required=*/true); +} + +auto PerformAction(Context& context, SemIR::LocId loc_id, + SemIR::AccessOptionalMemberAction action) -> SemIR::InstId { + return PerformActionHelper(context, loc_id, action.base_id, action.name_id, + /*required=*/false); +} + // Logic shared by GetAssociatedValue() and PerformCompoundMemberAccess(). static auto GetAssociatedValueImpl(Context& context, SemIR::LocId loc_id, SemIR::InstId base_id, diff --git a/toolchain/check/member_access.h b/toolchain/check/member_access.h index 8fda71d8ea28..0d99eb299002 100644 --- a/toolchain/check/member_access.h +++ b/toolchain/check/member_access.h @@ -11,10 +11,12 @@ namespace Carbon::Check { // Creates SemIR to perform a member access with base expression `base_id` and -// member name `name_id`. Returns the result of the access. +// member name `name_id`. When `required`, failing to find the name is a +// diagnosed error; otherwise, `None` is returned. Returns the result of the +// access. auto PerformMemberAccess(Context& context, SemIR::LocId loc_id, - SemIR::InstId base_id, SemIR::NameId name_id) - -> SemIR::InstId; + SemIR::InstId base_id, SemIR::NameId name_id, + bool required = true) -> SemIR::InstId; // Creates SemIR to perform a compound member access with base expression // `base_id` and member name expression `member_expr_id`. Returns the result of diff --git a/toolchain/check/pattern_match.cpp b/toolchain/check/pattern_match.cpp index a0313b4defc4..c0f4646c64a6 100644 --- a/toolchain/check/pattern_match.cpp +++ b/toolchain/check/pattern_match.cpp @@ -11,6 +11,7 @@ #include "llvm/ADT/SmallVector.h" #include "toolchain/base/kind_switch.h" #include "toolchain/check/context.h" +#include "toolchain/check/control_flow.h" #include "toolchain/check/convert.h" #include "toolchain/check/subpattern.h" #include "toolchain/check/type.h" @@ -257,7 +258,7 @@ auto MatchContext::DoEmitPatternMatch(Context& context, AddrSelfIsNonRef); // Add fake reference expression to preserve invariants. auto scrutinee = context.insts().GetWithLocId(entry.scrutinee_id); - scrutinee_ref_id = AddInst( + scrutinee_ref_id = AddInstWithCleanup( context, scrutinee.loc_id, {.type_id = scrutinee.inst.type_id()}); } auto scrutinee_ref = context.insts().Get(scrutinee_ref_id); @@ -436,7 +437,7 @@ auto MatchContext::DoEmitPatternMatch(Context& context, break; } case MatchKind::Caller: { - storage_id = AddInst( + storage_id = AddInstWithCleanup( context, pattern_loc_id, {.type_id = var_pattern.type_id}); CARBON_CHECK(entry.scrutinee_id.has_value()); break; diff --git a/toolchain/check/pending_block.h b/toolchain/check/pending_block.h index 3475e083a987..e665b728bb98 100644 --- a/toolchain/check/pending_block.h +++ b/toolchain/check/pending_block.h @@ -49,6 +49,13 @@ class PendingBlock { return inst_id; } + template + auto AddInstWithCleanup(LocT loc_id, InstT inst) -> SemIR::InstId { + auto inst_id = AddInstWithCleanupInNoBlock(context_, loc_id, inst); + insts_.push_back(inst_id); + return inst_id; + } + // Insert the pending block of code at the current position. auto InsertHere() -> void { for (auto id : insts_) { diff --git a/toolchain/check/scope_stack.cpp b/toolchain/check/scope_stack.cpp index 0723b99a6923..27af13544444 100644 --- a/toolchain/check/scope_stack.cpp +++ b/toolchain/check/scope_stack.cpp @@ -10,7 +10,16 @@ namespace Carbon::Check { auto ScopeStack::VerifyOnFinish() const -> void { + CARBON_CHECK(return_scope_stack_.empty(), "{0}", return_scope_stack_.size()); + CARBON_CHECK(break_continue_stack_.empty(), "{0}", + break_continue_stack_.size()); CARBON_CHECK(scope_stack_.empty(), "{0}", scope_stack_.size()); + CARBON_CHECK(destroy_id_stack_.empty(), "{0}", + destroy_id_stack_.all_values_size()); + CARBON_CHECK(non_lexical_scope_stack_.empty(), "{0}", + non_lexical_scope_stack_.size()); + CARBON_CHECK(compile_time_binding_stack_.empty(), "{0}", + compile_time_binding_stack_.all_values_size()); full_pattern_stack_.VerifyOnFinish(); } @@ -46,16 +55,18 @@ auto ScopeStack::Push(SemIR::InstId scope_inst_id, SemIR::NameScopeId scope_id, compile_time_binding_stack_.all_values_size()), .lexical_lookup_has_load_error = LexicalLookupHasLoadError() || lexical_lookup_has_load_error}); - if (scope_id.has_value()) { - non_lexical_scope_stack_.push_back({.scope_index = next_scope_index_, - .name_scope_id = scope_id, - .specific_id = enclosing_specific_id}); - } else { + if (scope_stack_.back().is_lexical_scope()) { // For lexical lookups, unqualified lookup doesn't know how to find the // associated specific, so if we start adding lexical scopes associated with // specifics, we'll need to somehow track them in lookup. CARBON_CHECK(!specific_id.has_value(), "Lexical scope should not have an associated specific."); + + destroy_id_stack_.PushArray(); + } else { + non_lexical_scope_stack_.push_back({.scope_index = next_scope_index_, + .name_scope_id = scope_id, + .specific_id = enclosing_specific_id}); } // TODO: Handle this case more gracefully. @@ -76,7 +87,9 @@ auto ScopeStack::Pop() -> void { lexical_results.pop_back(); }); - if (scope.scope_id.has_value()) { + if (scope.is_lexical_scope()) { + destroy_id_stack_.PopArray(); + } else { CARBON_CHECK(non_lexical_scope_stack_.back().scope_index == scope.index); non_lexical_scope_stack_.pop_back(); } @@ -203,10 +216,13 @@ auto ScopeStack::Suspend() -> SuspendedScope { CARBON_CHECK(!scope_stack_.empty(), "No scope to suspend"); SuspendedScope result = {.entry = scope_stack_.pop_back_val(), .suspended_items = {}}; - if (result.entry.scope_id.has_value()) { + if (result.entry.is_lexical_scope()) { + CARBON_CHECK(destroy_id_stack_.PeekArray().empty(), + "Missing support to suspend scopes with destructed storage"); + destroy_id_stack_.PopArray(); + } else { non_lexical_scope_stack_.pop_back(); } - auto peek_compile_time_bindings = compile_time_binding_stack_.PeekArray(); result.suspended_items.reserve(result.entry.num_names + peek_compile_time_bindings.size()); @@ -247,7 +263,9 @@ auto ScopeStack::Restore(SuspendedScope scope) -> void { VerifyNextCompileTimeBindIndex("Restore", scope.entry); - if (scope.entry.scope_id.has_value()) { + if (scope.entry.is_lexical_scope()) { + destroy_id_stack_.PushArray(); + } else { non_lexical_scope_stack_.push_back( {.scope_index = scope.entry.index, .name_scope_id = scope.entry.scope_id, diff --git a/toolchain/check/scope_stack.h b/toolchain/check/scope_stack.h index cae4d4b90260..a0a5c08d523b 100644 --- a/toolchain/check/scope_stack.h +++ b/toolchain/check/scope_stack.h @@ -98,6 +98,9 @@ class ScopeStack { return Peek().specific_id; } + // Returns true if current scope is lexical. + auto PeekIsLexicalScope() const -> bool { return Peek().is_lexical_scope(); } + // Returns the current scope, if it is of the specified kind. Otherwise, // returns nullopt. template @@ -165,6 +168,10 @@ class ScopeStack { return break_continue_stack_; } + auto destroy_id_stack() -> ArrayStack& { + return destroy_id_stack_; + } + auto compile_time_bindings_stack() -> ArrayStack& { return compile_time_binding_stack_; } @@ -174,6 +181,8 @@ class ScopeStack { private: // An entry in scope_stack_. struct ScopeStackEntry { + auto is_lexical_scope() const -> bool { return !scope_id.has_value(); } + // The sequential index of this scope entry within the file. ScopeIndex index; @@ -208,8 +217,6 @@ class ScopeStack { // Names which are registered with lexical_lookup_, and will need to be // unregistered when the scope ends. Set names = {}; - - // TODO: This likely needs to track things which need to be destructed. }; auto Peek() const -> const ScopeStackEntry& { return scope_stack_.back(); } @@ -239,6 +246,10 @@ class ScopeStack { // A stack for scope context. llvm::SmallVector scope_stack_; + // A stack of `destroy` functions to call. This only has entries for lexical + // scopes, because non-lexical scopes don't have destruction on scope exit. + ArrayStack destroy_id_stack_; + // Information about non-lexical scopes. This is a subset of the entries and // the information in scope_stack_. llvm::SmallVector non_lexical_scope_stack_; diff --git a/toolchain/check/testdata/class/no_prelude/destroy.carbon b/toolchain/check/testdata/class/no_prelude/destroy.carbon deleted file mode 100644 index a56443c26b0a..000000000000 --- a/toolchain/check/testdata/class/no_prelude/destroy.carbon +++ /dev/null @@ -1,734 +0,0 @@ -// Part of the Carbon Language project, under the Apache License v2.0 with LLVM -// Exceptions. See /LICENSE for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -// AUTOUPDATE -// TIP: To test this file alone, run: -// TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/class/no_prelude/destroy.carbon -// TIP: To dump output, run: -// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/class/no_prelude/destroy.carbon - -// --- self.carbon - -library "[[@TEST_NAME]]"; - -class C { - fn destroy[self: Self](); -} - -// --- addr_self.carbon - -library "[[@TEST_NAME]]"; - -class C { - fn destroy[addr self: Self*](); -} - -// --- explicit_return.carbon - -library "[[@TEST_NAME]]"; - -class C { - fn destroy[self: Self]() -> (); -} - -// --- fail_class_function.carbon - -library "[[@TEST_NAME]]"; - -class C { - // CHECK:STDERR: fail_class_function.carbon:[[@LINE+4]]:3: error: missing implicit `self` parameter [DestroyFunctionMissingSelf] - // CHECK:STDERR: fn destroy(); - // CHECK:STDERR: ^~~~~~~~~~~~~ - // CHECK:STDERR: - fn destroy(); -} - -// --- fail_extra_implicit_params_second.carbon - -library "[[@TEST_NAME]]"; - -class C { - // CHECK:STDERR: fail_extra_implicit_params_second.carbon:[[@LINE+4]]:26: error: unexpected implicit parameter [DestroyFunctionUnexpectedImplicitParam] - // CHECK:STDERR: fn destroy[self: Self, T:! type](); - // CHECK:STDERR: ^ - // CHECK:STDERR: - fn destroy[self: Self, T:! type](); -} - -// --- fail_extra_implicit_params_first.carbon - -library "[[@TEST_NAME]]"; - -class C { - // CHECK:STDERR: fail_extra_implicit_params_first.carbon:[[@LINE+4]]:14: error: unexpected implicit parameter [DestroyFunctionUnexpectedImplicitParam] - // CHECK:STDERR: fn destroy[T:! type, self: Self](); - // CHECK:STDERR: ^ - // CHECK:STDERR: - fn destroy[T:! type, self: Self](); -} - -// --- fail_positional_params.carbon - -library "[[@TEST_NAME]]"; - -class C { - // CHECK:STDERR: fail_positional_params.carbon:[[@LINE+4]]:3: error: missing empty explicit parameter list [DestroyFunctionPositionalParams] - // CHECK:STDERR: fn destroy[self: Self]; - // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~ - // CHECK:STDERR: - fn destroy[self: Self]; -} - -// --- fail_explicit_params.carbon - -library "[[@TEST_NAME]]"; - -class C { - // CHECK:STDERR: fail_explicit_params.carbon:[[@LINE+4]]:26: error: unexpected parameter [DestroyFunctionNonEmptyExplicitParams] - // CHECK:STDERR: fn destroy[self: Self](x: ()); - // CHECK:STDERR: ^~~~~ - // CHECK:STDERR: - fn destroy[self: Self](x: ()); -} - -// --- fail_return_type.carbon - -library "[[@TEST_NAME]]"; - -class C { - // CHECK:STDERR: fail_return_type.carbon:[[@LINE+4]]:28: error: incorrect return type; must be unspecified or `()` [DestroyFunctionIncorrectReturnType] - // CHECK:STDERR: fn destroy[self: Self]() -> {}; - // CHECK:STDERR: ^~~~~ - // CHECK:STDERR: - fn destroy[self: Self]() -> {}; -} - -// --- fail_out_of_line_missing_params.carbon - -library "[[@TEST_NAME]]"; - - -class C { - fn destroy[self: Self](); -} - -// CHECK:STDERR: fail_out_of_line_missing_params.carbon:[[@LINE+7]]:1: error: redeclaration differs because of missing implicit parameter list [RedeclParamListDiffers] -// CHECK:STDERR: fn C.destroy {} -// CHECK:STDERR: ^~~~~~~~~~~~~~ -// CHECK:STDERR: fail_out_of_line_missing_params.carbon:[[@LINE-6]]:3: note: previously declared with implicit parameter list [RedeclParamListPrevious] -// CHECK:STDERR: fn destroy[self: Self](); -// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~ -// CHECK:STDERR: -fn C.destroy {} - -// --- fail_destroy_in_file_scope.carbon - -library "[[@TEST_NAME]]"; - -// CHECK:STDERR: fail_destroy_in_file_scope.carbon:[[@LINE+4]]:1: error: declaring `fn destroy` in non-class scope [DestroyFunctionOutsideClass] -// CHECK:STDERR: fn destroy[self: ()](); -// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~ -// CHECK:STDERR: -fn destroy[self: ()](); - -// --- fail_destroy_in_namespace_scope.carbon - -library "[[@TEST_NAME]]"; - -namespace NS; - -// CHECK:STDERR: fail_destroy_in_namespace_scope.carbon:[[@LINE+4]]:1: error: declaring `fn destroy` in non-class scope [DestroyFunctionOutsideClass] -// CHECK:STDERR: fn NS.destroy(); -// CHECK:STDERR: ^~~~~~~~~~~~~~~~ -// CHECK:STDERR: -fn NS.destroy(); - -// --- fail_invalid_qualifier_with_params.carbon - -library "[[@TEST_NAME]]"; - -class C { - fn destroy[self: Self](); -} - -// CHECK:STDERR: fail_invalid_qualifier_with_params.carbon:[[@LINE+7]]:6: error: name qualifiers are only allowed for entities that provide a scope [QualifiedNameInNonScope] -// CHECK:STDERR: fn C.destroy[self: Self]().Foo() {} -// CHECK:STDERR: ^~~~~~~ -// CHECK:STDERR: fail_invalid_qualifier_with_params.carbon:[[@LINE-6]]:3: note: referenced non-scope entity declared here [QualifiedNameNonScopeEntity] -// CHECK:STDERR: fn destroy[self: Self](); -// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~ -// CHECK:STDERR: -fn C.destroy[self: Self]().Foo() {} - -// --- fail_invalid_qualifier_no_params.carbon - -library "[[@TEST_NAME]]"; - -class C { - fn destroy[self: Self](); -} - -// CHECK:STDERR: fail_invalid_qualifier_no_params.carbon:[[@LINE+7]]:6: error: name qualifiers are only allowed for entities that provide a scope [QualifiedNameInNonScope] -// CHECK:STDERR: fn C.destroy.Foo() {} -// CHECK:STDERR: ^~~~~~~ -// CHECK:STDERR: fail_invalid_qualifier_no_params.carbon:[[@LINE-6]]:3: note: referenced non-scope entity declared here [QualifiedNameNonScopeEntity] -// CHECK:STDERR: fn destroy[self: Self](); -// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~ -// CHECK:STDERR: -fn C.destroy.Foo() {} - -// CHECK:STDOUT: --- self.carbon -// CHECK:STDOUT: -// CHECK:STDOUT: constants { -// CHECK:STDOUT: %C: type = class_type @C [concrete] -// CHECK:STDOUT: %destroy.type: type = fn_type @destroy [concrete] -// CHECK:STDOUT: %destroy: %destroy.type = struct_value () [concrete] -// CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] -// CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] -// CHECK:STDOUT: } -// CHECK:STDOUT: -// CHECK:STDOUT: file { -// CHECK:STDOUT: package: = namespace [concrete] { -// CHECK:STDOUT: .C = %C.decl -// CHECK:STDOUT: } -// CHECK:STDOUT: %C.decl: type = class_decl @C [concrete = constants.%C] {} {} -// CHECK:STDOUT: } -// CHECK:STDOUT: -// CHECK:STDOUT: class @C { -// CHECK:STDOUT: %destroy.decl: %destroy.type = fn_decl @destroy [concrete = constants.%destroy] { -// CHECK:STDOUT: %self.patt: %C = binding_pattern self -// CHECK:STDOUT: %self.param_patt: %C = value_param_pattern %self.patt, call_param0 -// CHECK:STDOUT: } { -// CHECK:STDOUT: %self.param: %C = value_param call_param0 -// CHECK:STDOUT: %Self.ref: type = name_ref Self, constants.%C [concrete = constants.%C] -// CHECK:STDOUT: %self: %C = bind_name self, %self.param -// CHECK:STDOUT: } -// CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete = constants.%complete_type] -// CHECK:STDOUT: complete_type_witness = %complete_type -// CHECK:STDOUT: -// CHECK:STDOUT: !members: -// CHECK:STDOUT: .Self = constants.%C -// CHECK:STDOUT: .destroy = %destroy.decl -// CHECK:STDOUT: } -// CHECK:STDOUT: -// CHECK:STDOUT: fn @destroy[%self.param_patt: %C](); -// CHECK:STDOUT: -// CHECK:STDOUT: --- addr_self.carbon -// CHECK:STDOUT: -// CHECK:STDOUT: constants { -// CHECK:STDOUT: %C: type = class_type @C [concrete] -// CHECK:STDOUT: %ptr: type = ptr_type %C [concrete] -// CHECK:STDOUT: %destroy.type: type = fn_type @destroy [concrete] -// CHECK:STDOUT: %destroy: %destroy.type = struct_value () [concrete] -// CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] -// CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] -// CHECK:STDOUT: } -// CHECK:STDOUT: -// CHECK:STDOUT: file { -// CHECK:STDOUT: package: = namespace [concrete] { -// CHECK:STDOUT: .C = %C.decl -// CHECK:STDOUT: } -// CHECK:STDOUT: %C.decl: type = class_decl @C [concrete = constants.%C] {} {} -// CHECK:STDOUT: } -// CHECK:STDOUT: -// CHECK:STDOUT: class @C { -// CHECK:STDOUT: %destroy.decl: %destroy.type = fn_decl @destroy [concrete = constants.%destroy] { -// CHECK:STDOUT: %self.patt: %ptr = binding_pattern self -// CHECK:STDOUT: %self.param_patt: %ptr = value_param_pattern %self.patt, call_param0 -// CHECK:STDOUT: %.loc5_14: auto = addr_pattern %self.param_patt -// CHECK:STDOUT: } { -// CHECK:STDOUT: %self.param: %ptr = value_param call_param0 -// CHECK:STDOUT: %.loc5_29: type = splice_block %ptr [concrete = constants.%ptr] { -// CHECK:STDOUT: %Self.ref: type = name_ref Self, constants.%C [concrete = constants.%C] -// CHECK:STDOUT: %ptr: type = ptr_type %C [concrete = constants.%ptr] -// CHECK:STDOUT: } -// CHECK:STDOUT: %self: %ptr = bind_name self, %self.param -// CHECK:STDOUT: } -// CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete = constants.%complete_type] -// CHECK:STDOUT: complete_type_witness = %complete_type -// CHECK:STDOUT: -// CHECK:STDOUT: !members: -// CHECK:STDOUT: .Self = constants.%C -// CHECK:STDOUT: .destroy = %destroy.decl -// CHECK:STDOUT: } -// CHECK:STDOUT: -// CHECK:STDOUT: fn @destroy[addr %self.param_patt: %ptr](); -// CHECK:STDOUT: -// CHECK:STDOUT: --- explicit_return.carbon -// CHECK:STDOUT: -// CHECK:STDOUT: constants { -// CHECK:STDOUT: %C: type = class_type @C [concrete] -// CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] -// CHECK:STDOUT: %destroy.type: type = fn_type @destroy [concrete] -// CHECK:STDOUT: %destroy: %destroy.type = struct_value () [concrete] -// CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] -// CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] -// CHECK:STDOUT: } -// CHECK:STDOUT: -// CHECK:STDOUT: file { -// CHECK:STDOUT: package: = namespace [concrete] { -// CHECK:STDOUT: .C = %C.decl -// CHECK:STDOUT: } -// CHECK:STDOUT: %C.decl: type = class_decl @C [concrete = constants.%C] {} {} -// CHECK:STDOUT: } -// CHECK:STDOUT: -// CHECK:STDOUT: class @C { -// CHECK:STDOUT: %destroy.decl: %destroy.type = fn_decl @destroy [concrete = constants.%destroy] { -// CHECK:STDOUT: %self.patt: %C = binding_pattern self -// CHECK:STDOUT: %self.param_patt: %C = value_param_pattern %self.patt, call_param0 -// CHECK:STDOUT: %return.patt: %empty_tuple.type = return_slot_pattern -// CHECK:STDOUT: %return.param_patt: %empty_tuple.type = out_param_pattern %return.patt, call_param1 -// CHECK:STDOUT: } { -// CHECK:STDOUT: %.loc5_32.1: %empty_tuple.type = tuple_literal () -// CHECK:STDOUT: %.loc5_32.2: type = converted %.loc5_32.1, constants.%empty_tuple.type [concrete = constants.%empty_tuple.type] -// CHECK:STDOUT: %self.param: %C = value_param call_param0 -// CHECK:STDOUT: %Self.ref: type = name_ref Self, constants.%C [concrete = constants.%C] -// CHECK:STDOUT: %self: %C = bind_name self, %self.param -// CHECK:STDOUT: %return.param: ref %empty_tuple.type = out_param call_param1 -// CHECK:STDOUT: %return: ref %empty_tuple.type = return_slot %return.param -// CHECK:STDOUT: } -// CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete = constants.%complete_type] -// CHECK:STDOUT: complete_type_witness = %complete_type -// CHECK:STDOUT: -// CHECK:STDOUT: !members: -// CHECK:STDOUT: .Self = constants.%C -// CHECK:STDOUT: .destroy = %destroy.decl -// CHECK:STDOUT: } -// CHECK:STDOUT: -// CHECK:STDOUT: fn @destroy[%self.param_patt: %C]() -> %empty_tuple.type; -// CHECK:STDOUT: -// CHECK:STDOUT: --- fail_class_function.carbon -// CHECK:STDOUT: -// CHECK:STDOUT: constants { -// CHECK:STDOUT: %C: type = class_type @C [concrete] -// CHECK:STDOUT: %destroy.type: type = fn_type @destroy [concrete] -// CHECK:STDOUT: %destroy: %destroy.type = struct_value () [concrete] -// CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] -// CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] -// CHECK:STDOUT: } -// CHECK:STDOUT: -// CHECK:STDOUT: file { -// CHECK:STDOUT: package: = namespace [concrete] { -// CHECK:STDOUT: .C = %C.decl -// CHECK:STDOUT: } -// CHECK:STDOUT: %C.decl: type = class_decl @C [concrete = constants.%C] {} {} -// CHECK:STDOUT: } -// CHECK:STDOUT: -// CHECK:STDOUT: class @C { -// CHECK:STDOUT: %destroy.decl: %destroy.type = fn_decl @destroy [concrete = constants.%destroy] {} {} -// CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete = constants.%complete_type] -// CHECK:STDOUT: complete_type_witness = %complete_type -// CHECK:STDOUT: -// CHECK:STDOUT: !members: -// CHECK:STDOUT: .Self = constants.%C -// CHECK:STDOUT: .destroy = %destroy.decl -// CHECK:STDOUT: } -// CHECK:STDOUT: -// CHECK:STDOUT: fn @destroy(); -// CHECK:STDOUT: -// CHECK:STDOUT: --- fail_extra_implicit_params_second.carbon -// CHECK:STDOUT: -// CHECK:STDOUT: constants { -// CHECK:STDOUT: %C: type = class_type @C [concrete] -// CHECK:STDOUT: %T: type = bind_symbolic_name T, 0 [symbolic] -// CHECK:STDOUT: %T.patt: type = symbolic_binding_pattern T, 0 [symbolic] -// CHECK:STDOUT: %destroy.type: type = fn_type @destroy [concrete] -// CHECK:STDOUT: %destroy: %destroy.type = struct_value () [concrete] -// CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] -// CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] -// CHECK:STDOUT: } -// CHECK:STDOUT: -// CHECK:STDOUT: file { -// CHECK:STDOUT: package: = namespace [concrete] { -// CHECK:STDOUT: .C = %C.decl -// CHECK:STDOUT: } -// CHECK:STDOUT: %C.decl: type = class_decl @C [concrete = constants.%C] {} {} -// CHECK:STDOUT: } -// CHECK:STDOUT: -// CHECK:STDOUT: class @C { -// CHECK:STDOUT: %destroy.decl: %destroy.type = fn_decl @destroy [concrete = constants.%destroy] { -// CHECK:STDOUT: %self.patt: %C = binding_pattern self -// CHECK:STDOUT: %self.param_patt: %C = value_param_pattern %self.patt, call_param0 -// CHECK:STDOUT: %T.patt.loc9_26.1: type = symbolic_binding_pattern T, 0 [symbolic = %T.patt.loc9_26.2 (constants.%T.patt)] -// CHECK:STDOUT: } { -// CHECK:STDOUT: %self.param: %C = value_param call_param0 -// CHECK:STDOUT: %Self.ref: type = name_ref Self, constants.%C [concrete = constants.%C] -// CHECK:STDOUT: %self: %C = bind_name self, %self.param -// CHECK:STDOUT: %T.loc9_26.2: type = bind_symbolic_name T, 0 [symbolic = %T.loc9_26.1 (constants.%T)] -// CHECK:STDOUT: } -// CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete = constants.%complete_type] -// CHECK:STDOUT: complete_type_witness = %complete_type -// CHECK:STDOUT: -// CHECK:STDOUT: !members: -// CHECK:STDOUT: .Self = constants.%C -// CHECK:STDOUT: .destroy = %destroy.decl -// CHECK:STDOUT: } -// CHECK:STDOUT: -// CHECK:STDOUT: generic fn @destroy(%T.loc9_26.2: type) { -// CHECK:STDOUT: %T.loc9_26.1: type = bind_symbolic_name T, 0 [symbolic = %T.loc9_26.1 (constants.%T)] -// CHECK:STDOUT: %T.patt.loc9_26.2: type = symbolic_binding_pattern T, 0 [symbolic = %T.patt.loc9_26.2 (constants.%T.patt)] -// CHECK:STDOUT: -// CHECK:STDOUT: fn[%self.param_patt: %C, %T.patt.loc9_26.1: type](); -// CHECK:STDOUT: } -// CHECK:STDOUT: -// CHECK:STDOUT: specific @destroy(constants.%T) { -// CHECK:STDOUT: %T.loc9_26.1 => constants.%T -// CHECK:STDOUT: %T.patt.loc9_26.2 => constants.%T -// CHECK:STDOUT: } -// CHECK:STDOUT: -// CHECK:STDOUT: --- fail_extra_implicit_params_first.carbon -// CHECK:STDOUT: -// CHECK:STDOUT: constants { -// CHECK:STDOUT: %C: type = class_type @C [concrete] -// CHECK:STDOUT: %T: type = bind_symbolic_name T, 0 [symbolic] -// CHECK:STDOUT: %T.patt: type = symbolic_binding_pattern T, 0 [symbolic] -// CHECK:STDOUT: %destroy.type: type = fn_type @destroy [concrete] -// CHECK:STDOUT: %destroy: %destroy.type = struct_value () [concrete] -// CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] -// CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] -// CHECK:STDOUT: } -// CHECK:STDOUT: -// CHECK:STDOUT: file { -// CHECK:STDOUT: package: = namespace [concrete] { -// CHECK:STDOUT: .C = %C.decl -// CHECK:STDOUT: } -// CHECK:STDOUT: %C.decl: type = class_decl @C [concrete = constants.%C] {} {} -// CHECK:STDOUT: } -// CHECK:STDOUT: -// CHECK:STDOUT: class @C { -// CHECK:STDOUT: %destroy.decl: %destroy.type = fn_decl @destroy [concrete = constants.%destroy] { -// CHECK:STDOUT: %T.patt.loc9_14.1: type = symbolic_binding_pattern T, 0 [symbolic = %T.patt.loc9_14.2 (constants.%T.patt)] -// CHECK:STDOUT: %self.patt: %C = binding_pattern self -// CHECK:STDOUT: %self.param_patt: %C = value_param_pattern %self.patt, call_param0 -// CHECK:STDOUT: } { -// CHECK:STDOUT: %T.loc9_14.2: type = bind_symbolic_name T, 0 [symbolic = %T.loc9_14.1 (constants.%T)] -// CHECK:STDOUT: %self.param: %C = value_param call_param0 -// CHECK:STDOUT: %Self.ref: type = name_ref Self, constants.%C [concrete = constants.%C] -// CHECK:STDOUT: %self: %C = bind_name self, %self.param -// CHECK:STDOUT: } -// CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete = constants.%complete_type] -// CHECK:STDOUT: complete_type_witness = %complete_type -// CHECK:STDOUT: -// CHECK:STDOUT: !members: -// CHECK:STDOUT: .Self = constants.%C -// CHECK:STDOUT: .destroy = %destroy.decl -// CHECK:STDOUT: } -// CHECK:STDOUT: -// CHECK:STDOUT: generic fn @destroy(%T.loc9_14.2: type) { -// CHECK:STDOUT: %T.loc9_14.1: type = bind_symbolic_name T, 0 [symbolic = %T.loc9_14.1 (constants.%T)] -// CHECK:STDOUT: %T.patt.loc9_14.2: type = symbolic_binding_pattern T, 0 [symbolic = %T.patt.loc9_14.2 (constants.%T.patt)] -// CHECK:STDOUT: -// CHECK:STDOUT: fn[%T.patt.loc9_14.1: type, %self.param_patt: %C](); -// CHECK:STDOUT: } -// CHECK:STDOUT: -// CHECK:STDOUT: specific @destroy(constants.%T) { -// CHECK:STDOUT: %T.loc9_14.1 => constants.%T -// CHECK:STDOUT: %T.patt.loc9_14.2 => constants.%T -// CHECK:STDOUT: } -// CHECK:STDOUT: -// CHECK:STDOUT: --- fail_positional_params.carbon -// CHECK:STDOUT: -// CHECK:STDOUT: constants { -// CHECK:STDOUT: %C: type = class_type @C [concrete] -// CHECK:STDOUT: %destroy.type: type = fn_type @destroy [concrete] -// CHECK:STDOUT: %destroy: %destroy.type = struct_value () [concrete] -// CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] -// CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] -// CHECK:STDOUT: } -// CHECK:STDOUT: -// CHECK:STDOUT: file { -// CHECK:STDOUT: package: = namespace [concrete] { -// CHECK:STDOUT: .C = %C.decl -// CHECK:STDOUT: } -// CHECK:STDOUT: %C.decl: type = class_decl @C [concrete = constants.%C] {} {} -// CHECK:STDOUT: } -// CHECK:STDOUT: -// CHECK:STDOUT: class @C { -// CHECK:STDOUT: %destroy.decl: %destroy.type = fn_decl @destroy [concrete = constants.%destroy] { -// CHECK:STDOUT: %self.patt: %C = binding_pattern self -// CHECK:STDOUT: %self.param_patt: %C = value_param_pattern %self.patt, call_param0 -// CHECK:STDOUT: } { -// CHECK:STDOUT: %self.param: %C = value_param call_param0 -// CHECK:STDOUT: %Self.ref: type = name_ref Self, constants.%C [concrete = constants.%C] -// CHECK:STDOUT: %self: %C = bind_name self, %self.param -// CHECK:STDOUT: } -// CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete = constants.%complete_type] -// CHECK:STDOUT: complete_type_witness = %complete_type -// CHECK:STDOUT: -// CHECK:STDOUT: !members: -// CHECK:STDOUT: .Self = constants.%C -// CHECK:STDOUT: .destroy = %destroy.decl -// CHECK:STDOUT: } -// CHECK:STDOUT: -// CHECK:STDOUT: fn @destroy[%self.param_patt: %C](); -// CHECK:STDOUT: -// CHECK:STDOUT: --- fail_explicit_params.carbon -// CHECK:STDOUT: -// CHECK:STDOUT: constants { -// CHECK:STDOUT: %C: type = class_type @C [concrete] -// CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] -// CHECK:STDOUT: %destroy.type: type = fn_type @destroy [concrete] -// CHECK:STDOUT: %destroy: %destroy.type = struct_value () [concrete] -// CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] -// CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] -// CHECK:STDOUT: } -// CHECK:STDOUT: -// CHECK:STDOUT: file { -// CHECK:STDOUT: package: = namespace [concrete] { -// CHECK:STDOUT: .C = %C.decl -// CHECK:STDOUT: } -// CHECK:STDOUT: %C.decl: type = class_decl @C [concrete = constants.%C] {} {} -// CHECK:STDOUT: } -// CHECK:STDOUT: -// CHECK:STDOUT: class @C { -// CHECK:STDOUT: %destroy.decl: %destroy.type = fn_decl @destroy [concrete = constants.%destroy] { -// CHECK:STDOUT: %self.patt: %C = binding_pattern self -// CHECK:STDOUT: %self.param_patt: %C = value_param_pattern %self.patt, call_param0 -// CHECK:STDOUT: %x.patt: %empty_tuple.type = binding_pattern x -// CHECK:STDOUT: %x.param_patt: %empty_tuple.type = value_param_pattern %x.patt, call_param1 -// CHECK:STDOUT: } { -// CHECK:STDOUT: %self.param: %C = value_param call_param0 -// CHECK:STDOUT: %Self.ref: type = name_ref Self, constants.%C [concrete = constants.%C] -// CHECK:STDOUT: %self: %C = bind_name self, %self.param -// CHECK:STDOUT: %x.param: %empty_tuple.type = value_param call_param1 -// CHECK:STDOUT: %.loc9_30.1: type = splice_block %.loc9_30.3 [concrete = constants.%empty_tuple.type] { -// CHECK:STDOUT: %.loc9_30.2: %empty_tuple.type = tuple_literal () -// CHECK:STDOUT: %.loc9_30.3: type = converted %.loc9_30.2, constants.%empty_tuple.type [concrete = constants.%empty_tuple.type] -// CHECK:STDOUT: } -// CHECK:STDOUT: %x: %empty_tuple.type = bind_name x, %x.param -// CHECK:STDOUT: } -// CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete = constants.%complete_type] -// CHECK:STDOUT: complete_type_witness = %complete_type -// CHECK:STDOUT: -// CHECK:STDOUT: !members: -// CHECK:STDOUT: .Self = constants.%C -// CHECK:STDOUT: .destroy = %destroy.decl -// CHECK:STDOUT: } -// CHECK:STDOUT: -// CHECK:STDOUT: fn @destroy[%self.param_patt: %C](); -// CHECK:STDOUT: -// CHECK:STDOUT: --- fail_return_type.carbon -// CHECK:STDOUT: -// CHECK:STDOUT: constants { -// CHECK:STDOUT: %C: type = class_type @C [concrete] -// CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] -// CHECK:STDOUT: %destroy.type: type = fn_type @destroy [concrete] -// CHECK:STDOUT: %destroy: %destroy.type = struct_value () [concrete] -// CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] -// CHECK:STDOUT: } -// CHECK:STDOUT: -// CHECK:STDOUT: file { -// CHECK:STDOUT: package: = namespace [concrete] { -// CHECK:STDOUT: .C = %C.decl -// CHECK:STDOUT: } -// CHECK:STDOUT: %C.decl: type = class_decl @C [concrete = constants.%C] {} {} -// CHECK:STDOUT: } -// CHECK:STDOUT: -// CHECK:STDOUT: class @C { -// CHECK:STDOUT: %destroy.decl: %destroy.type = fn_decl @destroy [concrete = constants.%destroy] { -// CHECK:STDOUT: %self.patt: %C = binding_pattern self -// CHECK:STDOUT: %self.param_patt: %C = value_param_pattern %self.patt, call_param0 -// CHECK:STDOUT: %return.patt: %empty_struct_type = return_slot_pattern -// CHECK:STDOUT: %return.param_patt: %empty_struct_type = out_param_pattern %return.patt, call_param1 -// CHECK:STDOUT: } { -// CHECK:STDOUT: %.loc9_32.1: %empty_struct_type = struct_literal () -// CHECK:STDOUT: %.loc9_32.2: type = converted %.loc9_32.1, constants.%empty_struct_type [concrete = constants.%empty_struct_type] -// CHECK:STDOUT: %self.param: %C = value_param call_param0 -// CHECK:STDOUT: %Self.ref: type = name_ref Self, constants.%C [concrete = constants.%C] -// CHECK:STDOUT: %self: %C = bind_name self, %self.param -// CHECK:STDOUT: %return.param: ref %empty_struct_type = out_param call_param1 -// CHECK:STDOUT: %return: ref %empty_struct_type = return_slot %return.param -// CHECK:STDOUT: } -// CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete = constants.%complete_type] -// CHECK:STDOUT: complete_type_witness = %complete_type -// CHECK:STDOUT: -// CHECK:STDOUT: !members: -// CHECK:STDOUT: .Self = constants.%C -// CHECK:STDOUT: .destroy = %destroy.decl -// CHECK:STDOUT: } -// CHECK:STDOUT: -// CHECK:STDOUT: fn @destroy[%self.param_patt: %C]() -> %empty_struct_type; -// CHECK:STDOUT: -// CHECK:STDOUT: --- fail_out_of_line_missing_params.carbon -// CHECK:STDOUT: -// CHECK:STDOUT: constants { -// CHECK:STDOUT: %C: type = class_type @C [concrete] -// CHECK:STDOUT: %destroy.type.bd30b6.1: type = fn_type @destroy.1 [concrete] -// CHECK:STDOUT: %destroy.357925.1: %destroy.type.bd30b6.1 = struct_value () [concrete] -// CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] -// CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] -// CHECK:STDOUT: %destroy.type.bd30b6.2: type = fn_type @destroy.2 [concrete] -// CHECK:STDOUT: %destroy.357925.2: %destroy.type.bd30b6.2 = struct_value () [concrete] -// CHECK:STDOUT: } -// CHECK:STDOUT: -// CHECK:STDOUT: file { -// CHECK:STDOUT: package: = namespace [concrete] { -// CHECK:STDOUT: .C = %C.decl -// CHECK:STDOUT: } -// CHECK:STDOUT: %C.decl: type = class_decl @C [concrete = constants.%C] {} {} -// CHECK:STDOUT: %destroy.decl: %destroy.type.bd30b6.2 = fn_decl @destroy.2 [concrete = constants.%destroy.357925.2] {} {} -// CHECK:STDOUT: } -// CHECK:STDOUT: -// CHECK:STDOUT: class @C { -// CHECK:STDOUT: %destroy.decl: %destroy.type.bd30b6.1 = fn_decl @destroy.1 [concrete = constants.%destroy.357925.1] { -// CHECK:STDOUT: %self.patt: %C = binding_pattern self -// CHECK:STDOUT: %self.param_patt: %C = value_param_pattern %self.patt, call_param0 -// CHECK:STDOUT: } { -// CHECK:STDOUT: %self.param: %C = value_param call_param0 -// CHECK:STDOUT: %Self.ref: type = name_ref Self, constants.%C [concrete = constants.%C] -// CHECK:STDOUT: %self: %C = bind_name self, %self.param -// CHECK:STDOUT: } -// CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete = constants.%complete_type] -// CHECK:STDOUT: complete_type_witness = %complete_type -// CHECK:STDOUT: -// CHECK:STDOUT: !members: -// CHECK:STDOUT: .Self = constants.%C -// CHECK:STDOUT: .destroy = %destroy.decl -// CHECK:STDOUT: } -// CHECK:STDOUT: -// CHECK:STDOUT: fn @destroy.1[%self.param_patt: %C](); -// CHECK:STDOUT: -// CHECK:STDOUT: fn @destroy.2() { -// CHECK:STDOUT: !entry: -// CHECK:STDOUT: return -// CHECK:STDOUT: } -// CHECK:STDOUT: -// CHECK:STDOUT: --- fail_destroy_in_file_scope.carbon -// CHECK:STDOUT: -// CHECK:STDOUT: constants { -// CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] -// CHECK:STDOUT: %destroy.type: type = fn_type @destroy [concrete] -// CHECK:STDOUT: %destroy: %destroy.type = struct_value () [concrete] -// CHECK:STDOUT: } -// CHECK:STDOUT: -// CHECK:STDOUT: file { -// CHECK:STDOUT: package: = namespace [concrete] { -// CHECK:STDOUT: .destroy = %destroy.decl -// CHECK:STDOUT: } -// CHECK:STDOUT: %destroy.decl: %destroy.type = fn_decl @destroy [concrete = constants.%destroy] { -// CHECK:STDOUT: %self.patt: %empty_tuple.type = binding_pattern self -// CHECK:STDOUT: %self.param_patt: %empty_tuple.type = value_param_pattern %self.patt, call_param0 -// CHECK:STDOUT: } { -// CHECK:STDOUT: %self.param: %empty_tuple.type = value_param call_param0 -// CHECK:STDOUT: %.loc8_19.1: type = splice_block %.loc8_19.3 [concrete = constants.%empty_tuple.type] { -// CHECK:STDOUT: %.loc8_19.2: %empty_tuple.type = tuple_literal () -// CHECK:STDOUT: %.loc8_19.3: type = converted %.loc8_19.2, constants.%empty_tuple.type [concrete = constants.%empty_tuple.type] -// CHECK:STDOUT: } -// CHECK:STDOUT: %self: %empty_tuple.type = bind_name self, %self.param -// CHECK:STDOUT: } -// CHECK:STDOUT: } -// CHECK:STDOUT: -// CHECK:STDOUT: fn @destroy[%self.param_patt: %empty_tuple.type](); -// CHECK:STDOUT: -// CHECK:STDOUT: --- fail_destroy_in_namespace_scope.carbon -// CHECK:STDOUT: -// CHECK:STDOUT: constants { -// CHECK:STDOUT: %destroy.type: type = fn_type @destroy [concrete] -// CHECK:STDOUT: %destroy: %destroy.type = struct_value () [concrete] -// CHECK:STDOUT: } -// CHECK:STDOUT: -// CHECK:STDOUT: file { -// CHECK:STDOUT: package: = namespace [concrete] { -// CHECK:STDOUT: .NS = %NS -// CHECK:STDOUT: } -// CHECK:STDOUT: %NS: = namespace [concrete] { -// CHECK:STDOUT: .destroy = %destroy.decl -// CHECK:STDOUT: } -// CHECK:STDOUT: %destroy.decl: %destroy.type = fn_decl @destroy [concrete = constants.%destroy] {} {} -// CHECK:STDOUT: } -// CHECK:STDOUT: -// CHECK:STDOUT: fn @destroy(); -// CHECK:STDOUT: -// CHECK:STDOUT: --- fail_invalid_qualifier_with_params.carbon -// CHECK:STDOUT: -// CHECK:STDOUT: constants { -// CHECK:STDOUT: %C: type = class_type @C [concrete] -// CHECK:STDOUT: %destroy.type: type = fn_type @destroy [concrete] -// CHECK:STDOUT: %destroy: %destroy.type = struct_value () [concrete] -// CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] -// CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] -// CHECK:STDOUT: %Foo.type: type = fn_type @Foo [concrete] -// CHECK:STDOUT: %Foo: %Foo.type = struct_value () [concrete] -// CHECK:STDOUT: } -// CHECK:STDOUT: -// CHECK:STDOUT: file { -// CHECK:STDOUT: package: = namespace [concrete] { -// CHECK:STDOUT: .C = %C.decl -// CHECK:STDOUT: } -// CHECK:STDOUT: %C.decl: type = class_decl @C [concrete = constants.%C] {} {} -// CHECK:STDOUT: %Foo.decl: %Foo.type = fn_decl @Foo [concrete = constants.%Foo] {} { -// CHECK:STDOUT: %self.param: %C = value_param call_param0 -// CHECK:STDOUT: %Self.ref: type = name_ref Self, constants.%C [concrete = constants.%C] -// CHECK:STDOUT: %self: %C = bind_name self, %self.param -// CHECK:STDOUT: } -// CHECK:STDOUT: } -// CHECK:STDOUT: -// CHECK:STDOUT: class @C { -// CHECK:STDOUT: %destroy.decl: %destroy.type = fn_decl @destroy [concrete = constants.%destroy] { -// CHECK:STDOUT: %self.patt: %C = binding_pattern self -// CHECK:STDOUT: %self.param_patt: %C = value_param_pattern %self.patt, call_param0 -// CHECK:STDOUT: } { -// CHECK:STDOUT: %self.param: %C = value_param call_param0 -// CHECK:STDOUT: %Self.ref: type = name_ref Self, constants.%C [concrete = constants.%C] -// CHECK:STDOUT: %self: %C = bind_name self, %self.param -// CHECK:STDOUT: } -// CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete = constants.%complete_type] -// CHECK:STDOUT: complete_type_witness = %complete_type -// CHECK:STDOUT: -// CHECK:STDOUT: !members: -// CHECK:STDOUT: .Self = constants.%C -// CHECK:STDOUT: .destroy = %destroy.decl -// CHECK:STDOUT: } -// CHECK:STDOUT: -// CHECK:STDOUT: fn @destroy[%self.param_patt: %C](); -// CHECK:STDOUT: -// CHECK:STDOUT: fn @Foo() { -// CHECK:STDOUT: !entry: -// CHECK:STDOUT: return -// CHECK:STDOUT: } -// CHECK:STDOUT: -// CHECK:STDOUT: --- fail_invalid_qualifier_no_params.carbon -// CHECK:STDOUT: -// CHECK:STDOUT: constants { -// CHECK:STDOUT: %C: type = class_type @C [concrete] -// CHECK:STDOUT: %destroy.type: type = fn_type @destroy [concrete] -// CHECK:STDOUT: %destroy: %destroy.type = struct_value () [concrete] -// CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] -// CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] -// CHECK:STDOUT: %Foo.type: type = fn_type @Foo [concrete] -// CHECK:STDOUT: %Foo: %Foo.type = struct_value () [concrete] -// CHECK:STDOUT: } -// CHECK:STDOUT: -// CHECK:STDOUT: file { -// CHECK:STDOUT: package: = namespace [concrete] { -// CHECK:STDOUT: .C = %C.decl -// CHECK:STDOUT: } -// CHECK:STDOUT: %C.decl: type = class_decl @C [concrete = constants.%C] {} {} -// CHECK:STDOUT: %Foo.decl: %Foo.type = fn_decl @Foo [concrete = constants.%Foo] {} {} -// CHECK:STDOUT: } -// CHECK:STDOUT: -// CHECK:STDOUT: class @C { -// CHECK:STDOUT: %destroy.decl: %destroy.type = fn_decl @destroy [concrete = constants.%destroy] { -// CHECK:STDOUT: %self.patt: %C = binding_pattern self -// CHECK:STDOUT: %self.param_patt: %C = value_param_pattern %self.patt, call_param0 -// CHECK:STDOUT: } { -// CHECK:STDOUT: %self.param: %C = value_param call_param0 -// CHECK:STDOUT: %Self.ref: type = name_ref Self, constants.%C [concrete = constants.%C] -// CHECK:STDOUT: %self: %C = bind_name self, %self.param -// CHECK:STDOUT: } -// CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete = constants.%complete_type] -// CHECK:STDOUT: complete_type_witness = %complete_type -// CHECK:STDOUT: -// CHECK:STDOUT: !members: -// CHECK:STDOUT: .Self = constants.%C -// CHECK:STDOUT: .destroy = %destroy.decl -// CHECK:STDOUT: } -// CHECK:STDOUT: -// CHECK:STDOUT: fn @destroy[%self.param_patt: %C](); -// CHECK:STDOUT: -// CHECK:STDOUT: fn @Foo() { -// CHECK:STDOUT: !entry: -// CHECK:STDOUT: return -// CHECK:STDOUT: } -// CHECK:STDOUT: diff --git a/toolchain/check/testdata/class/no_prelude/destroy_calls.carbon b/toolchain/check/testdata/class/no_prelude/destroy_calls.carbon new file mode 100644 index 000000000000..2f0813554011 --- /dev/null +++ b/toolchain/check/testdata/class/no_prelude/destroy_calls.carbon @@ -0,0 +1,850 @@ +// Part of the Carbon Language project, under the Apache License v2.0 with LLVM +// Exceptions. See /LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// AUTOUPDATE +// TIP: To test this file alone, run: +// TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/class/no_prelude/destroy_calls.carbon +// TIP: To dump output, run: +// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/class/no_prelude/destroy_calls.carbon + +// --- types.carbon + +library "[[@TEST_NAME]]"; + +class NoAddr { + fn Make() -> NoAddr; + fn destroy[self: Self](); +} + +class ExplicitReturn { + fn Make() -> ExplicitReturn; + fn destroy[self: Self]() -> (); +} + +class WithAddr { + fn Make() -> WithAddr; + fn destroy[addr self: Self*](); +} + +// --- implicit_return.carbon + +library "[[@TEST_NAME]]"; +import library "types"; + +fn F() { + var no_addr: NoAddr; + var explicit_return: ExplicitReturn; + var with_addr: WithAddr; +} + +// --- nested_scope.carbon + +library "[[@TEST_NAME]]"; +import library "types"; + +fn F() { + var no_addr: NoAddr; + var explicit_return: ExplicitReturn; + var with_addr: WithAddr; + if (true) { + // TODO: Destroy in nested scope. + var in_scope: NoAddr; + } +} + +// --- temp.carbon + +library "[[@TEST_NAME]]"; +import library "types"; + +fn F() { + // TODO: The scoping of these destroy calls is incorrect. Maybe we need to + // establish statement scopes? + NoAddr.Make(); + ExplicitReturn.Make(); + WithAddr.Make(); +} + +// --- fail_recovery.carbon + +library "[[@TEST_NAME]]"; + +class NoSelf { + // CHECK:STDERR: fail_recovery.carbon:[[@LINE+4]]:3: error: missing implicit `self` parameter [DestroyFunctionMissingSelf] + // CHECK:STDERR: fn destroy(); + // CHECK:STDERR: ^~~~~~~~~~~~~ + // CHECK:STDERR: + fn destroy(); +} + +class Args { + // CHECK:STDERR: fail_recovery.carbon:[[@LINE+4]]:26: error: unexpected parameter [DestroyFunctionNonEmptyExplicitParams] + // CHECK:STDERR: fn destroy[self: Self](x: ()); + // CHECK:STDERR: ^~~~~ + // CHECK:STDERR: + fn destroy[self: Self](x: ()); +} + +fn F() { + var a: NoSelf; + var b: Args; +} + +// --- fail_not_breaking_generics.carbon +// CHECK:STDERR: fail_not_breaking_generics.carbon: error: value of type `` is not callable [CallToNonCallable] +// CHECK:STDERR: + +class C(template T:! type) {} + +fn F(template T:! type) { + var v: C(T); +} + +fn G() { F({}); } + +// CHECK:STDOUT: --- types.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %NoAddr: type = class_type @NoAddr [concrete] +// CHECK:STDOUT: %Make.type.bc9: type = fn_type @Make.1 [concrete] +// CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] +// CHECK:STDOUT: %Make.1be: %Make.type.bc9 = struct_value () [concrete] +// CHECK:STDOUT: %destroy.type.bc5: type = fn_type @destroy.1 [concrete] +// CHECK:STDOUT: %destroy.60f: %destroy.type.bc5 = struct_value () [concrete] +// CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] +// CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] +// CHECK:STDOUT: %ExplicitReturn: type = class_type @ExplicitReturn [concrete] +// CHECK:STDOUT: %Make.type.378: type = fn_type @Make.2 [concrete] +// CHECK:STDOUT: %Make.960: %Make.type.378 = struct_value () [concrete] +// CHECK:STDOUT: %destroy.type.dfa: type = fn_type @destroy.2 [concrete] +// CHECK:STDOUT: %destroy.539: %destroy.type.dfa = struct_value () [concrete] +// CHECK:STDOUT: %WithAddr: type = class_type @WithAddr [concrete] +// CHECK:STDOUT: %Make.type.e14: type = fn_type @Make.3 [concrete] +// CHECK:STDOUT: %Make.b0a: %Make.type.e14 = struct_value () [concrete] +// CHECK:STDOUT: %ptr: type = ptr_type %WithAddr [concrete] +// CHECK:STDOUT: %destroy.type.02f: type = fn_type @destroy.3 [concrete] +// CHECK:STDOUT: %destroy.8d0: %destroy.type.02f = struct_value () [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [concrete] { +// CHECK:STDOUT: .NoAddr = %NoAddr.decl +// CHECK:STDOUT: .ExplicitReturn = %ExplicitReturn.decl +// CHECK:STDOUT: .WithAddr = %WithAddr.decl +// CHECK:STDOUT: } +// CHECK:STDOUT: %NoAddr.decl: type = class_decl @NoAddr [concrete = constants.%NoAddr] {} {} +// CHECK:STDOUT: %ExplicitReturn.decl: type = class_decl @ExplicitReturn [concrete = constants.%ExplicitReturn] {} {} +// CHECK:STDOUT: %WithAddr.decl: type = class_decl @WithAddr [concrete = constants.%WithAddr] {} {} +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: class @NoAddr { +// CHECK:STDOUT: %Make.decl: %Make.type.bc9 = fn_decl @Make.1 [concrete = constants.%Make.1be] { +// CHECK:STDOUT: %return.patt: %NoAddr = return_slot_pattern +// CHECK:STDOUT: %return.param_patt: %NoAddr = out_param_pattern %return.patt, call_param0 +// CHECK:STDOUT: } { +// CHECK:STDOUT: %NoAddr.ref: type = name_ref NoAddr, file.%NoAddr.decl [concrete = constants.%NoAddr] +// CHECK:STDOUT: %return.param: ref %NoAddr = out_param call_param0 +// CHECK:STDOUT: %return: ref %NoAddr = return_slot %return.param +// CHECK:STDOUT: } +// CHECK:STDOUT: %destroy.decl: %destroy.type.bc5 = fn_decl @destroy.1 [concrete = constants.%destroy.60f] { +// CHECK:STDOUT: %self.patt: %NoAddr = binding_pattern self +// CHECK:STDOUT: %self.param_patt: %NoAddr = value_param_pattern %self.patt, call_param0 +// CHECK:STDOUT: } { +// CHECK:STDOUT: %self.param: %NoAddr = value_param call_param0 +// CHECK:STDOUT: %Self.ref: type = name_ref Self, constants.%NoAddr [concrete = constants.%NoAddr] +// CHECK:STDOUT: %self: %NoAddr = bind_name self, %self.param +// CHECK:STDOUT: } +// CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete = constants.%complete_type] +// CHECK:STDOUT: complete_type_witness = %complete_type +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .Self = constants.%NoAddr +// CHECK:STDOUT: .NoAddr = +// CHECK:STDOUT: .Make = %Make.decl +// CHECK:STDOUT: .destroy = %destroy.decl +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: class @ExplicitReturn { +// CHECK:STDOUT: %Make.decl: %Make.type.378 = fn_decl @Make.2 [concrete = constants.%Make.960] { +// CHECK:STDOUT: %return.patt: %ExplicitReturn = return_slot_pattern +// CHECK:STDOUT: %return.param_patt: %ExplicitReturn = out_param_pattern %return.patt, call_param0 +// CHECK:STDOUT: } { +// CHECK:STDOUT: %ExplicitReturn.ref: type = name_ref ExplicitReturn, file.%ExplicitReturn.decl [concrete = constants.%ExplicitReturn] +// CHECK:STDOUT: %return.param: ref %ExplicitReturn = out_param call_param0 +// CHECK:STDOUT: %return: ref %ExplicitReturn = return_slot %return.param +// CHECK:STDOUT: } +// CHECK:STDOUT: %destroy.decl: %destroy.type.dfa = fn_decl @destroy.2 [concrete = constants.%destroy.539] { +// CHECK:STDOUT: %self.patt: %ExplicitReturn = binding_pattern self +// CHECK:STDOUT: %self.param_patt: %ExplicitReturn = value_param_pattern %self.patt, call_param0 +// CHECK:STDOUT: %return.patt: %empty_tuple.type = return_slot_pattern +// CHECK:STDOUT: %return.param_patt: %empty_tuple.type = out_param_pattern %return.patt, call_param1 +// CHECK:STDOUT: } { +// CHECK:STDOUT: %.loc11_32.1: %empty_tuple.type = tuple_literal () +// CHECK:STDOUT: %.loc11_32.2: type = converted %.loc11_32.1, constants.%empty_tuple.type [concrete = constants.%empty_tuple.type] +// CHECK:STDOUT: %self.param: %ExplicitReturn = value_param call_param0 +// CHECK:STDOUT: %Self.ref: type = name_ref Self, constants.%ExplicitReturn [concrete = constants.%ExplicitReturn] +// CHECK:STDOUT: %self: %ExplicitReturn = bind_name self, %self.param +// CHECK:STDOUT: %return.param: ref %empty_tuple.type = out_param call_param1 +// CHECK:STDOUT: %return: ref %empty_tuple.type = return_slot %return.param +// CHECK:STDOUT: } +// CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete = constants.%complete_type] +// CHECK:STDOUT: complete_type_witness = %complete_type +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .Self = constants.%ExplicitReturn +// CHECK:STDOUT: .ExplicitReturn = +// CHECK:STDOUT: .Make = %Make.decl +// CHECK:STDOUT: .destroy = %destroy.decl +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: class @WithAddr { +// CHECK:STDOUT: %Make.decl: %Make.type.e14 = fn_decl @Make.3 [concrete = constants.%Make.b0a] { +// CHECK:STDOUT: %return.patt: %WithAddr = return_slot_pattern +// CHECK:STDOUT: %return.param_patt: %WithAddr = out_param_pattern %return.patt, call_param0 +// CHECK:STDOUT: } { +// CHECK:STDOUT: %WithAddr.ref: type = name_ref WithAddr, file.%WithAddr.decl [concrete = constants.%WithAddr] +// CHECK:STDOUT: %return.param: ref %WithAddr = out_param call_param0 +// CHECK:STDOUT: %return: ref %WithAddr = return_slot %return.param +// CHECK:STDOUT: } +// CHECK:STDOUT: %destroy.decl: %destroy.type.02f = fn_decl @destroy.3 [concrete = constants.%destroy.8d0] { +// CHECK:STDOUT: %self.patt: %ptr = binding_pattern self +// CHECK:STDOUT: %self.param_patt: %ptr = value_param_pattern %self.patt, call_param0 +// CHECK:STDOUT: %.loc16_14: auto = addr_pattern %self.param_patt +// CHECK:STDOUT: } { +// CHECK:STDOUT: %self.param: %ptr = value_param call_param0 +// CHECK:STDOUT: %.loc16_29: type = splice_block %ptr [concrete = constants.%ptr] { +// CHECK:STDOUT: %Self.ref: type = name_ref Self, constants.%WithAddr [concrete = constants.%WithAddr] +// CHECK:STDOUT: %ptr: type = ptr_type %WithAddr [concrete = constants.%ptr] +// CHECK:STDOUT: } +// CHECK:STDOUT: %self: %ptr = bind_name self, %self.param +// CHECK:STDOUT: } +// CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete = constants.%complete_type] +// CHECK:STDOUT: complete_type_witness = %complete_type +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .Self = constants.%WithAddr +// CHECK:STDOUT: .WithAddr = +// CHECK:STDOUT: .Make = %Make.decl +// CHECK:STDOUT: .destroy = %destroy.decl +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @Make.1() -> %NoAddr; +// CHECK:STDOUT: +// CHECK:STDOUT: fn @destroy.1[%self.param_patt: %NoAddr](); +// CHECK:STDOUT: +// CHECK:STDOUT: fn @Make.2() -> %ExplicitReturn; +// CHECK:STDOUT: +// CHECK:STDOUT: fn @destroy.2[%self.param_patt: %ExplicitReturn]() -> %empty_tuple.type; +// CHECK:STDOUT: +// CHECK:STDOUT: fn @Make.3() -> %WithAddr; +// CHECK:STDOUT: +// CHECK:STDOUT: fn @destroy.3[addr %self.param_patt: %ptr](); +// CHECK:STDOUT: +// CHECK:STDOUT: --- implicit_return.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %F.type: type = fn_type @F [concrete] +// CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] +// CHECK:STDOUT: %F: %F.type = struct_value () [concrete] +// CHECK:STDOUT: %NoAddr: type = class_type @NoAddr [concrete] +// CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] +// CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] +// CHECK:STDOUT: %destroy.type.bc5: type = fn_type @destroy.1 [concrete] +// CHECK:STDOUT: %destroy.60f: %destroy.type.bc5 = struct_value () [concrete] +// CHECK:STDOUT: %ExplicitReturn: type = class_type @ExplicitReturn [concrete] +// CHECK:STDOUT: %destroy.type.dfa: type = fn_type @destroy.2 [concrete] +// CHECK:STDOUT: %destroy.539: %destroy.type.dfa = struct_value () [concrete] +// CHECK:STDOUT: %WithAddr: type = class_type @WithAddr [concrete] +// CHECK:STDOUT: %destroy.type.02f: type = fn_type @destroy.3 [concrete] +// CHECK:STDOUT: %destroy.8d0: %destroy.type.02f = struct_value () [concrete] +// CHECK:STDOUT: %ptr.b4e: type = ptr_type %WithAddr [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: imports { +// CHECK:STDOUT: %Main.NoAddr: type = import_ref Main//types, NoAddr, loaded [concrete = constants.%NoAddr] +// CHECK:STDOUT: %Main.ExplicitReturn: type = import_ref Main//types, ExplicitReturn, loaded [concrete = constants.%ExplicitReturn] +// CHECK:STDOUT: %Main.WithAddr: type = import_ref Main//types, WithAddr, loaded [concrete = constants.%WithAddr] +// CHECK:STDOUT: %Main.import_ref.8f24d3.1: = import_ref Main//types, loc7_1, loaded [concrete = constants.%complete_type] +// CHECK:STDOUT: %Main.import_ref.f42 = import_ref Main//types, inst15 [no loc], unloaded +// CHECK:STDOUT: %Main.import_ref.b5b = import_ref Main//types, loc5_22, unloaded +// CHECK:STDOUT: %Main.import_ref.7fe: %destroy.type.bc5 = import_ref Main//types, loc6_27, loaded [concrete = constants.%destroy.60f] +// CHECK:STDOUT: %Main.import_ref.8f24d3.2: = import_ref Main//types, loc12_1, loaded [concrete = constants.%complete_type] +// CHECK:STDOUT: %Main.import_ref.ee7 = import_ref Main//types, inst37 [no loc], unloaded +// CHECK:STDOUT: %Main.import_ref.b14 = import_ref Main//types, loc10_30, unloaded +// CHECK:STDOUT: %Main.import_ref.83a: %destroy.type.dfa = import_ref Main//types, loc11_33, loaded [concrete = constants.%destroy.539] +// CHECK:STDOUT: %Main.import_ref.8f24d3.3: = import_ref Main//types, loc17_1, loaded [concrete = constants.%complete_type] +// CHECK:STDOUT: %Main.import_ref.95d = import_ref Main//types, inst62 [no loc], unloaded +// CHECK:STDOUT: %Main.import_ref.1cb = import_ref Main//types, loc15_24, unloaded +// CHECK:STDOUT: %Main.import_ref.54e: %destroy.type.02f = import_ref Main//types, loc16_33, loaded [concrete = constants.%destroy.8d0] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [concrete] { +// CHECK:STDOUT: .NoAddr = imports.%Main.NoAddr +// CHECK:STDOUT: .ExplicitReturn = imports.%Main.ExplicitReturn +// CHECK:STDOUT: .WithAddr = imports.%Main.WithAddr +// CHECK:STDOUT: .F = %F.decl +// CHECK:STDOUT: } +// CHECK:STDOUT: %default.import = import +// CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] {} {} +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: class @NoAddr [from "types.carbon"] { +// CHECK:STDOUT: complete_type_witness = imports.%Main.import_ref.8f24d3.1 +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .Self = imports.%Main.import_ref.f42 +// CHECK:STDOUT: .Make = imports.%Main.import_ref.b5b +// CHECK:STDOUT: .destroy = imports.%Main.import_ref.7fe +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: class @ExplicitReturn [from "types.carbon"] { +// CHECK:STDOUT: complete_type_witness = imports.%Main.import_ref.8f24d3.2 +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .Self = imports.%Main.import_ref.ee7 +// CHECK:STDOUT: .Make = imports.%Main.import_ref.b14 +// CHECK:STDOUT: .destroy = imports.%Main.import_ref.83a +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: class @WithAddr [from "types.carbon"] { +// CHECK:STDOUT: complete_type_witness = imports.%Main.import_ref.8f24d3.3 +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .Self = imports.%Main.import_ref.95d +// CHECK:STDOUT: .Make = imports.%Main.import_ref.1cb +// CHECK:STDOUT: .destroy = imports.%Main.import_ref.54e +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @F() { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: name_binding_decl { +// CHECK:STDOUT: %no_addr.patt: %NoAddr = binding_pattern no_addr +// CHECK:STDOUT: %.loc6_3.1: %NoAddr = var_pattern %no_addr.patt +// CHECK:STDOUT: } +// CHECK:STDOUT: %no_addr.var: ref %NoAddr = var no_addr +// CHECK:STDOUT: %destroy.ref.1: %destroy.type.bc5 = name_ref destroy, imports.%Main.import_ref.7fe [concrete = constants.%destroy.60f] +// CHECK:STDOUT: %destroy.bound.1: = bound_method %no_addr.var, %destroy.ref.1 +// CHECK:STDOUT: %NoAddr.ref: type = name_ref NoAddr, imports.%Main.NoAddr [concrete = constants.%NoAddr] +// CHECK:STDOUT: %no_addr: ref %NoAddr = bind_name no_addr, %no_addr.var +// CHECK:STDOUT: name_binding_decl { +// CHECK:STDOUT: %explicit_return.patt: %ExplicitReturn = binding_pattern explicit_return +// CHECK:STDOUT: %.loc7_3.1: %ExplicitReturn = var_pattern %explicit_return.patt +// CHECK:STDOUT: } +// CHECK:STDOUT: %explicit_return.var: ref %ExplicitReturn = var explicit_return +// CHECK:STDOUT: %destroy.ref.2: %destroy.type.dfa = name_ref destroy, imports.%Main.import_ref.83a [concrete = constants.%destroy.539] +// CHECK:STDOUT: %destroy.bound.2: = bound_method %explicit_return.var, %destroy.ref.2 +// CHECK:STDOUT: %ExplicitReturn.ref: type = name_ref ExplicitReturn, imports.%Main.ExplicitReturn [concrete = constants.%ExplicitReturn] +// CHECK:STDOUT: %explicit_return: ref %ExplicitReturn = bind_name explicit_return, %explicit_return.var +// CHECK:STDOUT: name_binding_decl { +// CHECK:STDOUT: %with_addr.patt: %WithAddr = binding_pattern with_addr +// CHECK:STDOUT: %.loc8: %WithAddr = var_pattern %with_addr.patt +// CHECK:STDOUT: } +// CHECK:STDOUT: %with_addr.var: ref %WithAddr = var with_addr +// CHECK:STDOUT: %destroy.ref.3: %destroy.type.02f = name_ref destroy, imports.%Main.import_ref.54e [concrete = constants.%destroy.8d0] +// CHECK:STDOUT: %destroy.bound.3: = bound_method %with_addr.var, %destroy.ref.3 +// CHECK:STDOUT: %WithAddr.ref: type = name_ref WithAddr, imports.%Main.WithAddr [concrete = constants.%WithAddr] +// CHECK:STDOUT: %with_addr: ref %WithAddr = bind_name with_addr, %with_addr.var +// CHECK:STDOUT: %addr: %ptr.b4e = addr_of %with_addr.var +// CHECK:STDOUT: %destroy.call.1: init %empty_tuple.type = call %destroy.bound.3(%addr) +// CHECK:STDOUT: %.loc7_3.2: %ExplicitReturn = bind_value %explicit_return.var +// CHECK:STDOUT: %destroy.call.2: init %empty_tuple.type = call %destroy.bound.2(%.loc7_3.2) +// CHECK:STDOUT: %.loc6_3.2: %NoAddr = bind_value %no_addr.var +// CHECK:STDOUT: %destroy.call.3: init %empty_tuple.type = call %destroy.bound.1(%.loc6_3.2) +// CHECK:STDOUT: return +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @destroy.1[%self.param_patt: %NoAddr]() [from "types.carbon"]; +// CHECK:STDOUT: +// CHECK:STDOUT: fn @destroy.2[%self.param_patt: %ExplicitReturn]() -> %empty_tuple.type [from "types.carbon"]; +// CHECK:STDOUT: +// CHECK:STDOUT: fn @destroy.3[addr .inst82: %ptr.b4e]() [from "types.carbon"]; +// CHECK:STDOUT: +// CHECK:STDOUT: --- nested_scope.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %F.type: type = fn_type @F [concrete] +// CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] +// CHECK:STDOUT: %F: %F.type = struct_value () [concrete] +// CHECK:STDOUT: %NoAddr: type = class_type @NoAddr [concrete] +// CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] +// CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] +// CHECK:STDOUT: %destroy.type.bc5: type = fn_type @destroy.1 [concrete] +// CHECK:STDOUT: %destroy.60f: %destroy.type.bc5 = struct_value () [concrete] +// CHECK:STDOUT: %ExplicitReturn: type = class_type @ExplicitReturn [concrete] +// CHECK:STDOUT: %destroy.type.dfa: type = fn_type @destroy.2 [concrete] +// CHECK:STDOUT: %destroy.539: %destroy.type.dfa = struct_value () [concrete] +// CHECK:STDOUT: %WithAddr: type = class_type @WithAddr [concrete] +// CHECK:STDOUT: %destroy.type.02f: type = fn_type @destroy.3 [concrete] +// CHECK:STDOUT: %destroy.8d0: %destroy.type.02f = struct_value () [concrete] +// CHECK:STDOUT: %ptr.b4e: type = ptr_type %WithAddr [concrete] +// CHECK:STDOUT: %true: bool = bool_literal true [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: imports { +// CHECK:STDOUT: %Main.NoAddr: type = import_ref Main//types, NoAddr, loaded [concrete = constants.%NoAddr] +// CHECK:STDOUT: %Main.ExplicitReturn: type = import_ref Main//types, ExplicitReturn, loaded [concrete = constants.%ExplicitReturn] +// CHECK:STDOUT: %Main.WithAddr: type = import_ref Main//types, WithAddr, loaded [concrete = constants.%WithAddr] +// CHECK:STDOUT: %Main.import_ref.8f24d3.1: = import_ref Main//types, loc7_1, loaded [concrete = constants.%complete_type] +// CHECK:STDOUT: %Main.import_ref.f42 = import_ref Main//types, inst15 [no loc], unloaded +// CHECK:STDOUT: %Main.import_ref.b5b = import_ref Main//types, loc5_22, unloaded +// CHECK:STDOUT: %Main.import_ref.7fe: %destroy.type.bc5 = import_ref Main//types, loc6_27, loaded [concrete = constants.%destroy.60f] +// CHECK:STDOUT: %Main.import_ref.8f24d3.2: = import_ref Main//types, loc12_1, loaded [concrete = constants.%complete_type] +// CHECK:STDOUT: %Main.import_ref.ee7 = import_ref Main//types, inst37 [no loc], unloaded +// CHECK:STDOUT: %Main.import_ref.b14 = import_ref Main//types, loc10_30, unloaded +// CHECK:STDOUT: %Main.import_ref.83a: %destroy.type.dfa = import_ref Main//types, loc11_33, loaded [concrete = constants.%destroy.539] +// CHECK:STDOUT: %Main.import_ref.8f24d3.3: = import_ref Main//types, loc17_1, loaded [concrete = constants.%complete_type] +// CHECK:STDOUT: %Main.import_ref.95d = import_ref Main//types, inst62 [no loc], unloaded +// CHECK:STDOUT: %Main.import_ref.1cb = import_ref Main//types, loc15_24, unloaded +// CHECK:STDOUT: %Main.import_ref.54e: %destroy.type.02f = import_ref Main//types, loc16_33, loaded [concrete = constants.%destroy.8d0] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [concrete] { +// CHECK:STDOUT: .NoAddr = imports.%Main.NoAddr +// CHECK:STDOUT: .ExplicitReturn = imports.%Main.ExplicitReturn +// CHECK:STDOUT: .WithAddr = imports.%Main.WithAddr +// CHECK:STDOUT: .F = %F.decl +// CHECK:STDOUT: } +// CHECK:STDOUT: %default.import = import +// CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] {} {} +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: class @NoAddr [from "types.carbon"] { +// CHECK:STDOUT: complete_type_witness = imports.%Main.import_ref.8f24d3.1 +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .Self = imports.%Main.import_ref.f42 +// CHECK:STDOUT: .Make = imports.%Main.import_ref.b5b +// CHECK:STDOUT: .destroy = imports.%Main.import_ref.7fe +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: class @ExplicitReturn [from "types.carbon"] { +// CHECK:STDOUT: complete_type_witness = imports.%Main.import_ref.8f24d3.2 +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .Self = imports.%Main.import_ref.ee7 +// CHECK:STDOUT: .Make = imports.%Main.import_ref.b14 +// CHECK:STDOUT: .destroy = imports.%Main.import_ref.83a +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: class @WithAddr [from "types.carbon"] { +// CHECK:STDOUT: complete_type_witness = imports.%Main.import_ref.8f24d3.3 +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .Self = imports.%Main.import_ref.95d +// CHECK:STDOUT: .Make = imports.%Main.import_ref.1cb +// CHECK:STDOUT: .destroy = imports.%Main.import_ref.54e +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @F() { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: name_binding_decl { +// CHECK:STDOUT: %no_addr.patt: %NoAddr = binding_pattern no_addr +// CHECK:STDOUT: %.loc6_3.1: %NoAddr = var_pattern %no_addr.patt +// CHECK:STDOUT: } +// CHECK:STDOUT: %no_addr.var: ref %NoAddr = var no_addr +// CHECK:STDOUT: %destroy.ref.1: %destroy.type.bc5 = name_ref destroy, imports.%Main.import_ref.7fe [concrete = constants.%destroy.60f] +// CHECK:STDOUT: %destroy.bound.1: = bound_method %no_addr.var, %destroy.ref.1 +// CHECK:STDOUT: %NoAddr.ref.loc6: type = name_ref NoAddr, imports.%Main.NoAddr [concrete = constants.%NoAddr] +// CHECK:STDOUT: %no_addr: ref %NoAddr = bind_name no_addr, %no_addr.var +// CHECK:STDOUT: name_binding_decl { +// CHECK:STDOUT: %explicit_return.patt: %ExplicitReturn = binding_pattern explicit_return +// CHECK:STDOUT: %.loc7_3.1: %ExplicitReturn = var_pattern %explicit_return.patt +// CHECK:STDOUT: } +// CHECK:STDOUT: %explicit_return.var: ref %ExplicitReturn = var explicit_return +// CHECK:STDOUT: %destroy.ref.2: %destroy.type.dfa = name_ref destroy, imports.%Main.import_ref.83a [concrete = constants.%destroy.539] +// CHECK:STDOUT: %destroy.bound.2: = bound_method %explicit_return.var, %destroy.ref.2 +// CHECK:STDOUT: %ExplicitReturn.ref: type = name_ref ExplicitReturn, imports.%Main.ExplicitReturn [concrete = constants.%ExplicitReturn] +// CHECK:STDOUT: %explicit_return: ref %ExplicitReturn = bind_name explicit_return, %explicit_return.var +// CHECK:STDOUT: name_binding_decl { +// CHECK:STDOUT: %with_addr.patt: %WithAddr = binding_pattern with_addr +// CHECK:STDOUT: %.loc8: %WithAddr = var_pattern %with_addr.patt +// CHECK:STDOUT: } +// CHECK:STDOUT: %with_addr.var: ref %WithAddr = var with_addr +// CHECK:STDOUT: %destroy.ref.3: %destroy.type.02f = name_ref destroy, imports.%Main.import_ref.54e [concrete = constants.%destroy.8d0] +// CHECK:STDOUT: %destroy.bound.3: = bound_method %with_addr.var, %destroy.ref.3 +// CHECK:STDOUT: %WithAddr.ref: type = name_ref WithAddr, imports.%Main.WithAddr [concrete = constants.%WithAddr] +// CHECK:STDOUT: %with_addr: ref %WithAddr = bind_name with_addr, %with_addr.var +// CHECK:STDOUT: %true: bool = bool_literal true [concrete = constants.%true] +// CHECK:STDOUT: if %true br !if.then else br !if.else +// CHECK:STDOUT: +// CHECK:STDOUT: !if.then: +// CHECK:STDOUT: name_binding_decl { +// CHECK:STDOUT: %in_scope.patt: %NoAddr = binding_pattern in_scope +// CHECK:STDOUT: %.loc11: %NoAddr = var_pattern %in_scope.patt +// CHECK:STDOUT: } +// CHECK:STDOUT: %in_scope.var: ref %NoAddr = var in_scope +// CHECK:STDOUT: %destroy.ref.4: %destroy.type.bc5 = name_ref destroy, imports.%Main.import_ref.7fe [concrete = constants.%destroy.60f] +// CHECK:STDOUT: %destroy.bound.4: = bound_method %in_scope.var, %destroy.ref.4 +// CHECK:STDOUT: %NoAddr.ref.loc11: type = name_ref NoAddr, imports.%Main.NoAddr [concrete = constants.%NoAddr] +// CHECK:STDOUT: %in_scope: ref %NoAddr = bind_name in_scope, %in_scope.var +// CHECK:STDOUT: br !if.else +// CHECK:STDOUT: +// CHECK:STDOUT: !if.else: +// CHECK:STDOUT: %addr: %ptr.b4e = addr_of %with_addr.var +// CHECK:STDOUT: %destroy.call.1: init %empty_tuple.type = call %destroy.bound.3(%addr) +// CHECK:STDOUT: %.loc7_3.2: %ExplicitReturn = bind_value %explicit_return.var +// CHECK:STDOUT: %destroy.call.2: init %empty_tuple.type = call %destroy.bound.2(%.loc7_3.2) +// CHECK:STDOUT: %.loc6_3.2: %NoAddr = bind_value %no_addr.var +// CHECK:STDOUT: %destroy.call.3: init %empty_tuple.type = call %destroy.bound.1(%.loc6_3.2) +// CHECK:STDOUT: return +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @destroy.1[%self.param_patt: %NoAddr]() [from "types.carbon"]; +// CHECK:STDOUT: +// CHECK:STDOUT: fn @destroy.2[%self.param_patt: %ExplicitReturn]() -> %empty_tuple.type [from "types.carbon"]; +// CHECK:STDOUT: +// CHECK:STDOUT: fn @destroy.3[addr .inst82: %ptr.b4e]() [from "types.carbon"]; +// CHECK:STDOUT: +// CHECK:STDOUT: --- temp.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %F.type: type = fn_type @F [concrete] +// CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] +// CHECK:STDOUT: %F: %F.type = struct_value () [concrete] +// CHECK:STDOUT: %NoAddr: type = class_type @NoAddr [concrete] +// CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] +// CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] +// CHECK:STDOUT: %Make.type.bc9: type = fn_type @Make.1 [concrete] +// CHECK:STDOUT: %Make.1be: %Make.type.bc9 = struct_value () [concrete] +// CHECK:STDOUT: %destroy.type.bc5: type = fn_type @destroy.1 [concrete] +// CHECK:STDOUT: %destroy.60f: %destroy.type.bc5 = struct_value () [concrete] +// CHECK:STDOUT: %ExplicitReturn: type = class_type @ExplicitReturn [concrete] +// CHECK:STDOUT: %Make.type.378: type = fn_type @Make.2 [concrete] +// CHECK:STDOUT: %Make.960: %Make.type.378 = struct_value () [concrete] +// CHECK:STDOUT: %destroy.type.dfa: type = fn_type @destroy.2 [concrete] +// CHECK:STDOUT: %destroy.539: %destroy.type.dfa = struct_value () [concrete] +// CHECK:STDOUT: %WithAddr: type = class_type @WithAddr [concrete] +// CHECK:STDOUT: %Make.type.e14: type = fn_type @Make.3 [concrete] +// CHECK:STDOUT: %Make.b0a: %Make.type.e14 = struct_value () [concrete] +// CHECK:STDOUT: %destroy.type.02f: type = fn_type @destroy.3 [concrete] +// CHECK:STDOUT: %destroy.8d0: %destroy.type.02f = struct_value () [concrete] +// CHECK:STDOUT: %ptr.b4e: type = ptr_type %WithAddr [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: imports { +// CHECK:STDOUT: %Main.NoAddr: type = import_ref Main//types, NoAddr, loaded [concrete = constants.%NoAddr] +// CHECK:STDOUT: %Main.ExplicitReturn: type = import_ref Main//types, ExplicitReturn, loaded [concrete = constants.%ExplicitReturn] +// CHECK:STDOUT: %Main.WithAddr: type = import_ref Main//types, WithAddr, loaded [concrete = constants.%WithAddr] +// CHECK:STDOUT: %Main.import_ref.8f24d3.1: = import_ref Main//types, loc7_1, loaded [concrete = constants.%complete_type] +// CHECK:STDOUT: %Main.import_ref.f42 = import_ref Main//types, inst15 [no loc], unloaded +// CHECK:STDOUT: %Main.import_ref.784: %Make.type.bc9 = import_ref Main//types, loc5_22, loaded [concrete = constants.%Make.1be] +// CHECK:STDOUT: %Main.import_ref.7fe: %destroy.type.bc5 = import_ref Main//types, loc6_27, loaded [concrete = constants.%destroy.60f] +// CHECK:STDOUT: %Main.import_ref.8f24d3.2: = import_ref Main//types, loc12_1, loaded [concrete = constants.%complete_type] +// CHECK:STDOUT: %Main.import_ref.ee7 = import_ref Main//types, inst37 [no loc], unloaded +// CHECK:STDOUT: %Main.import_ref.8e0: %Make.type.378 = import_ref Main//types, loc10_30, loaded [concrete = constants.%Make.960] +// CHECK:STDOUT: %Main.import_ref.83a: %destroy.type.dfa = import_ref Main//types, loc11_33, loaded [concrete = constants.%destroy.539] +// CHECK:STDOUT: %Main.import_ref.8f24d3.3: = import_ref Main//types, loc17_1, loaded [concrete = constants.%complete_type] +// CHECK:STDOUT: %Main.import_ref.95d = import_ref Main//types, inst62 [no loc], unloaded +// CHECK:STDOUT: %Main.import_ref.974: %Make.type.e14 = import_ref Main//types, loc15_24, loaded [concrete = constants.%Make.b0a] +// CHECK:STDOUT: %Main.import_ref.54e: %destroy.type.02f = import_ref Main//types, loc16_33, loaded [concrete = constants.%destroy.8d0] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [concrete] { +// CHECK:STDOUT: .NoAddr = imports.%Main.NoAddr +// CHECK:STDOUT: .ExplicitReturn = imports.%Main.ExplicitReturn +// CHECK:STDOUT: .WithAddr = imports.%Main.WithAddr +// CHECK:STDOUT: .F = %F.decl +// CHECK:STDOUT: } +// CHECK:STDOUT: %default.import = import +// CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] {} {} +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: class @NoAddr [from "types.carbon"] { +// CHECK:STDOUT: complete_type_witness = imports.%Main.import_ref.8f24d3.1 +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .Self = imports.%Main.import_ref.f42 +// CHECK:STDOUT: .Make = imports.%Main.import_ref.784 +// CHECK:STDOUT: .destroy = imports.%Main.import_ref.7fe +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: class @ExplicitReturn [from "types.carbon"] { +// CHECK:STDOUT: complete_type_witness = imports.%Main.import_ref.8f24d3.2 +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .Self = imports.%Main.import_ref.ee7 +// CHECK:STDOUT: .Make = imports.%Main.import_ref.8e0 +// CHECK:STDOUT: .destroy = imports.%Main.import_ref.83a +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: class @WithAddr [from "types.carbon"] { +// CHECK:STDOUT: complete_type_witness = imports.%Main.import_ref.8f24d3.3 +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .Self = imports.%Main.import_ref.95d +// CHECK:STDOUT: .Make = imports.%Main.import_ref.974 +// CHECK:STDOUT: .destroy = imports.%Main.import_ref.54e +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @F() { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: %NoAddr.ref: type = name_ref NoAddr, imports.%Main.NoAddr [concrete = constants.%NoAddr] +// CHECK:STDOUT: %Make.ref.loc8: %Make.type.bc9 = name_ref Make, imports.%Main.import_ref.784 [concrete = constants.%Make.1be] +// CHECK:STDOUT: %.loc8_15.1: ref %NoAddr = temporary_storage +// CHECK:STDOUT: %destroy.ref.1: %destroy.type.bc5 = name_ref destroy, imports.%Main.import_ref.7fe [concrete = constants.%destroy.60f] +// CHECK:STDOUT: %destroy.bound.1: = bound_method %.loc8_15.1, %destroy.ref.1 +// CHECK:STDOUT: %Make.call.loc8: init %NoAddr = call %Make.ref.loc8() to %.loc8_15.1 +// CHECK:STDOUT: %.loc8_15.2: ref %NoAddr = temporary %.loc8_15.1, %Make.call.loc8 +// CHECK:STDOUT: %ExplicitReturn.ref: type = name_ref ExplicitReturn, imports.%Main.ExplicitReturn [concrete = constants.%ExplicitReturn] +// CHECK:STDOUT: %Make.ref.loc9: %Make.type.378 = name_ref Make, imports.%Main.import_ref.8e0 [concrete = constants.%Make.960] +// CHECK:STDOUT: %.loc9_23.1: ref %ExplicitReturn = temporary_storage +// CHECK:STDOUT: %destroy.ref.2: %destroy.type.dfa = name_ref destroy, imports.%Main.import_ref.83a [concrete = constants.%destroy.539] +// CHECK:STDOUT: %destroy.bound.2: = bound_method %.loc9_23.1, %destroy.ref.2 +// CHECK:STDOUT: %Make.call.loc9: init %ExplicitReturn = call %Make.ref.loc9() to %.loc9_23.1 +// CHECK:STDOUT: %.loc9_23.2: ref %ExplicitReturn = temporary %.loc9_23.1, %Make.call.loc9 +// CHECK:STDOUT: %WithAddr.ref: type = name_ref WithAddr, imports.%Main.WithAddr [concrete = constants.%WithAddr] +// CHECK:STDOUT: %Make.ref.loc10: %Make.type.e14 = name_ref Make, imports.%Main.import_ref.974 [concrete = constants.%Make.b0a] +// CHECK:STDOUT: %.loc10_17.1: ref %WithAddr = temporary_storage +// CHECK:STDOUT: %destroy.ref.3: %destroy.type.02f = name_ref destroy, imports.%Main.import_ref.54e [concrete = constants.%destroy.8d0] +// CHECK:STDOUT: %destroy.bound.3: = bound_method %.loc10_17.1, %destroy.ref.3 +// CHECK:STDOUT: %Make.call.loc10: init %WithAddr = call %Make.ref.loc10() to %.loc10_17.1 +// CHECK:STDOUT: %.loc10_17.2: ref %WithAddr = temporary %.loc10_17.1, %Make.call.loc10 +// CHECK:STDOUT: %addr: %ptr.b4e = addr_of %.loc10_17.1 +// CHECK:STDOUT: %destroy.call.1: init %empty_tuple.type = call %destroy.bound.3(%addr) +// CHECK:STDOUT: %.loc9_23.3: %ExplicitReturn = bind_value %.loc9_23.1 +// CHECK:STDOUT: %destroy.call.2: init %empty_tuple.type = call %destroy.bound.2(%.loc9_23.3) +// CHECK:STDOUT: %.loc8_15.3: %NoAddr = bind_value %.loc8_15.1 +// CHECK:STDOUT: %destroy.call.3: init %empty_tuple.type = call %destroy.bound.1(%.loc8_15.3) +// CHECK:STDOUT: return +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @Make.1() -> %NoAddr [from "types.carbon"]; +// CHECK:STDOUT: +// CHECK:STDOUT: fn @destroy.1[%self.param_patt: %NoAddr]() [from "types.carbon"]; +// CHECK:STDOUT: +// CHECK:STDOUT: fn @Make.2() -> %ExplicitReturn [from "types.carbon"]; +// CHECK:STDOUT: +// CHECK:STDOUT: fn @destroy.2[%self.param_patt: %ExplicitReturn]() -> %empty_tuple.type [from "types.carbon"]; +// CHECK:STDOUT: +// CHECK:STDOUT: fn @Make.3() -> %WithAddr [from "types.carbon"]; +// CHECK:STDOUT: +// CHECK:STDOUT: fn @destroy.3[addr .inst92: %ptr.b4e]() [from "types.carbon"]; +// CHECK:STDOUT: +// CHECK:STDOUT: --- fail_recovery.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %NoSelf: type = class_type @NoSelf [concrete] +// CHECK:STDOUT: %destroy.type.d69: type = fn_type @destroy.1 [concrete] +// CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] +// CHECK:STDOUT: %destroy.ab7: %destroy.type.d69 = struct_value () [concrete] +// CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] +// CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] +// CHECK:STDOUT: %Args: type = class_type @Args [concrete] +// CHECK:STDOUT: %destroy.type.ba0: type = fn_type @destroy.2 [concrete] +// CHECK:STDOUT: %destroy.723: %destroy.type.ba0 = struct_value () [concrete] +// CHECK:STDOUT: %F.type: type = fn_type @F [concrete] +// CHECK:STDOUT: %F: %F.type = struct_value () [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [concrete] { +// CHECK:STDOUT: .NoSelf = %NoSelf.decl +// CHECK:STDOUT: .Args = %Args.decl +// CHECK:STDOUT: .F = %F.decl +// CHECK:STDOUT: } +// CHECK:STDOUT: %NoSelf.decl: type = class_decl @NoSelf [concrete = constants.%NoSelf] {} {} +// CHECK:STDOUT: %Args.decl: type = class_decl @Args [concrete = constants.%Args] {} {} +// CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] {} {} +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: class @NoSelf { +// CHECK:STDOUT: %destroy.decl: %destroy.type.d69 = fn_decl @destroy.1 [concrete = constants.%destroy.ab7] {} {} +// CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete = constants.%complete_type] +// CHECK:STDOUT: complete_type_witness = %complete_type +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .Self = constants.%NoSelf +// CHECK:STDOUT: .destroy = %destroy.decl +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: class @Args { +// CHECK:STDOUT: %destroy.decl: %destroy.type.ba0 = fn_decl @destroy.2 [concrete = constants.%destroy.723] { +// CHECK:STDOUT: %self.patt: %Args = binding_pattern self +// CHECK:STDOUT: %self.param_patt: %Args = value_param_pattern %self.patt, call_param0 +// CHECK:STDOUT: %x.patt: %empty_tuple.type = binding_pattern x +// CHECK:STDOUT: %x.param_patt: %empty_tuple.type = value_param_pattern %x.patt, call_param1 +// CHECK:STDOUT: } { +// CHECK:STDOUT: %self.param: %Args = value_param call_param0 +// CHECK:STDOUT: %Self.ref: type = name_ref Self, constants.%Args [concrete = constants.%Args] +// CHECK:STDOUT: %self: %Args = bind_name self, %self.param +// CHECK:STDOUT: %x.param: %empty_tuple.type = value_param call_param1 +// CHECK:STDOUT: %.loc17_30.1: type = splice_block %.loc17_30.3 [concrete = constants.%empty_tuple.type] { +// CHECK:STDOUT: %.loc17_30.2: %empty_tuple.type = tuple_literal () +// CHECK:STDOUT: %.loc17_30.3: type = converted %.loc17_30.2, constants.%empty_tuple.type [concrete = constants.%empty_tuple.type] +// CHECK:STDOUT: } +// CHECK:STDOUT: %x: %empty_tuple.type = bind_name x, %x.param +// CHECK:STDOUT: } +// CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete = constants.%complete_type] +// CHECK:STDOUT: complete_type_witness = %complete_type +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .Self = constants.%Args +// CHECK:STDOUT: .destroy = %destroy.decl +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @destroy.1(); +// CHECK:STDOUT: +// CHECK:STDOUT: fn @destroy.2[%self.param_patt: %Args](); +// CHECK:STDOUT: +// CHECK:STDOUT: fn @F() { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: name_binding_decl { +// CHECK:STDOUT: %a.patt: %NoSelf = binding_pattern a +// CHECK:STDOUT: %.loc21: %NoSelf = var_pattern %a.patt +// CHECK:STDOUT: } +// CHECK:STDOUT: %a.var: ref %NoSelf = var a +// CHECK:STDOUT: %destroy.ref.1: %destroy.type.d69 = name_ref destroy, @NoSelf.%destroy.decl [concrete = constants.%destroy.ab7] +// CHECK:STDOUT: %NoSelf.ref: type = name_ref NoSelf, file.%NoSelf.decl [concrete = constants.%NoSelf] +// CHECK:STDOUT: %a: ref %NoSelf = bind_name a, %a.var +// CHECK:STDOUT: name_binding_decl { +// CHECK:STDOUT: %b.patt: %Args = binding_pattern b +// CHECK:STDOUT: %.loc22_3.1: %Args = var_pattern %b.patt +// CHECK:STDOUT: } +// CHECK:STDOUT: %b.var: ref %Args = var b +// CHECK:STDOUT: %destroy.ref.2: %destroy.type.ba0 = name_ref destroy, @Args.%destroy.decl [concrete = constants.%destroy.723] +// CHECK:STDOUT: %destroy.bound: = bound_method %b.var, %destroy.ref.2 +// CHECK:STDOUT: %Args.ref: type = name_ref Args, file.%Args.decl [concrete = constants.%Args] +// CHECK:STDOUT: %b: ref %Args = bind_name b, %b.var +// CHECK:STDOUT: %.loc22_3.2: %Args = bind_value %b.var +// CHECK:STDOUT: %destroy.call.1: init %empty_tuple.type = call %destroy.bound(%.loc22_3.2) +// CHECK:STDOUT: %destroy.call.2: init %empty_tuple.type = call %destroy.ref.1() +// CHECK:STDOUT: return +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: --- fail_not_breaking_generics.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %T: type = bind_symbolic_name T, 0, template [template] +// CHECK:STDOUT: %T.patt: type = symbolic_binding_pattern T, 0, template [template] +// CHECK:STDOUT: %C.type: type = generic_class_type @C [concrete] +// CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] +// CHECK:STDOUT: %C.generic: %C.type = struct_value () [concrete] +// CHECK:STDOUT: %C.f2e: type = class_type @C, @C(%T) [template] +// CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] +// CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] +// CHECK:STDOUT: %F.type: type = fn_type @F [concrete] +// CHECK:STDOUT: %F: %F.type = struct_value () [concrete] +// CHECK:STDOUT: %require_complete: = require_complete_type %C.f2e [template] +// CHECK:STDOUT: %G.type: type = fn_type @G [concrete] +// CHECK:STDOUT: %G: %G.type = struct_value () [concrete] +// CHECK:STDOUT: %F.specific_fn: = specific_function %F, @F(%empty_struct_type) [concrete] +// CHECK:STDOUT: %C.7a7: type = class_type @C, @C(%empty_struct_type) [concrete] +// CHECK:STDOUT: %inst.as_compatible: = inst_value [concrete] { +// CHECK:STDOUT: %.6f0: ref %C.7a7 = as_compatible @F.%v.var +// CHECK:STDOUT: } +// CHECK:STDOUT: %inst.splice_block: = inst_value [concrete] { +// CHECK:STDOUT: %.432: = splice_block [concrete = ] {} +// CHECK:STDOUT: } +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [concrete] { +// CHECK:STDOUT: .C = %C.decl +// CHECK:STDOUT: .F = %F.decl +// CHECK:STDOUT: .G = %G.decl +// CHECK:STDOUT: } +// CHECK:STDOUT: %C.decl: %C.type = class_decl @C [concrete = constants.%C.generic] { +// CHECK:STDOUT: %T.patt.loc4_18.1: type = symbolic_binding_pattern T, 0, template [template = %T.patt.loc4_18.2 (constants.%T.patt)] +// CHECK:STDOUT: } { +// CHECK:STDOUT: %T.loc4_18.1: type = bind_symbolic_name T, 0, template [template = %T.loc4_18.2 (constants.%T)] +// CHECK:STDOUT: } +// CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] { +// CHECK:STDOUT: %T.patt.loc6_15.1: type = symbolic_binding_pattern T, 0, template [template = %T.patt.loc6_15.2 (constants.%T.patt)] +// CHECK:STDOUT: } { +// CHECK:STDOUT: %T.loc6_15.1: type = bind_symbolic_name T, 0, template [template = %T.loc6_15.2 (constants.%T)] +// CHECK:STDOUT: } +// CHECK:STDOUT: %G.decl: %G.type = fn_decl @G [concrete = constants.%G] {} {} +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: generic class @C(%T.loc4_18.1: type) { +// CHECK:STDOUT: %T.loc4_18.2: type = bind_symbolic_name T, 0, template [template = %T.loc4_18.2 (constants.%T)] +// CHECK:STDOUT: %T.patt.loc4_18.2: type = symbolic_binding_pattern T, 0, template [template = %T.patt.loc4_18.2 (constants.%T.patt)] +// CHECK:STDOUT: +// CHECK:STDOUT: !definition: +// CHECK:STDOUT: +// CHECK:STDOUT: class { +// CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete = constants.%complete_type] +// CHECK:STDOUT: complete_type_witness = %complete_type +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .Self = constants.%C.f2e +// CHECK:STDOUT: } +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: generic fn @F(%T.loc6_15.1: type) { +// CHECK:STDOUT: %T.loc6_15.2: type = bind_symbolic_name T, 0, template [template = %T.loc6_15.2 (constants.%T)] +// CHECK:STDOUT: %T.patt.loc6_15.2: type = symbolic_binding_pattern T, 0, template [template = %T.patt.loc6_15.2 (constants.%T.patt)] +// CHECK:STDOUT: +// CHECK:STDOUT: !definition: +// CHECK:STDOUT: %C.loc7_13.2: type = class_type @C, @C(%T.loc6_15.2) [template = %C.loc7_13.2 (constants.%C.f2e)] +// CHECK:STDOUT: %require_complete: = require_complete_type @F.%C.loc7_13.2 (%C.f2e) [template = %require_complete (constants.%require_complete)] +// CHECK:STDOUT: %.3: = refine_type_action %v.var, @F.%C.loc7_13.2 (%C.f2e) [template] +// CHECK:STDOUT: %.4: = access_optional_member_action %.1, destroy [template] +// CHECK:STDOUT: %.5: type = type_of_inst %.4 [template] +// CHECK:STDOUT: +// CHECK:STDOUT: fn(%T.patt.loc6_15.1: type) { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: name_binding_decl { +// CHECK:STDOUT: %v.patt: @F.%C.loc7_13.2 (%C.f2e) = binding_pattern v +// CHECK:STDOUT: %.loc7_3: @F.%C.loc7_13.2 (%C.f2e) = var_pattern %v.patt +// CHECK:STDOUT: } +// CHECK:STDOUT: %v.var: ref @F.%C.loc7_13.2 (%C.f2e) = var v +// CHECK:STDOUT: %.1: @F.%C.loc7_13.2 (%C.f2e) = splice_inst %.3 +// CHECK:STDOUT: %.2: @F.%.5 (@F.%.5) = splice_inst %.4 +// CHECK:STDOUT: %.loc7_13: type = splice_block %C.loc7_13.1 [template = %C.loc7_13.2 (constants.%C.f2e)] { +// CHECK:STDOUT: %C.ref: %C.type = name_ref C, file.%C.decl [concrete = constants.%C.generic] +// CHECK:STDOUT: %T.ref: type = name_ref T, %T.loc6_15.1 [template = %T.loc6_15.2 (constants.%T)] +// CHECK:STDOUT: %C.loc7_13.1: type = class_type @C, @C(constants.%T) [template = %C.loc7_13.2 (constants.%C.f2e)] +// CHECK:STDOUT: } +// CHECK:STDOUT: %v: ref @F.%C.loc7_13.2 (%C.f2e) = bind_name v, %v.var +// CHECK:STDOUT: return +// CHECK:STDOUT: } +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @G() { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: %F.ref: %F.type = name_ref F, file.%F.decl [concrete = constants.%F] +// CHECK:STDOUT: %.loc10_13: %empty_struct_type = struct_literal () +// CHECK:STDOUT: %.loc10_14: type = converted %.loc10_13, constants.%empty_struct_type [concrete = constants.%empty_struct_type] +// CHECK:STDOUT: %F.specific_fn: = specific_function %F.ref, @F(constants.%empty_struct_type) [concrete = constants.%F.specific_fn] +// CHECK:STDOUT: %F.call: init %empty_tuple.type = call %F.specific_fn() +// CHECK:STDOUT: return +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: specific @C(constants.%T) { +// CHECK:STDOUT: %T.loc4_18.2 => constants.%T +// CHECK:STDOUT: %T.patt.loc4_18.2 => constants.%T +// CHECK:STDOUT: +// CHECK:STDOUT: !definition: +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: specific @F(constants.%T) { +// CHECK:STDOUT: %T.loc6_15.2 => constants.%T +// CHECK:STDOUT: %T.patt.loc6_15.2 => constants.%T +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: specific @C(@F.%T.loc6_15.2) {} +// CHECK:STDOUT: +// CHECK:STDOUT: specific @F(constants.%empty_struct_type) { +// CHECK:STDOUT: %T.loc6_15.2 => constants.%empty_struct_type +// CHECK:STDOUT: %T.patt.loc6_15.2 => constants.%empty_struct_type +// CHECK:STDOUT: +// CHECK:STDOUT: !definition: +// CHECK:STDOUT: %C.loc7_13.2 => constants.%C.7a7 +// CHECK:STDOUT: %require_complete => constants.%complete_type +// CHECK:STDOUT: %.3 => constants.%inst.as_compatible +// CHECK:STDOUT: %.4 => constants.%inst.splice_block +// CHECK:STDOUT: %.5 => +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: specific @C(constants.%empty_struct_type) { +// CHECK:STDOUT: %T.loc4_18.2 => constants.%empty_struct_type +// CHECK:STDOUT: %T.patt.loc4_18.2 => constants.%empty_struct_type +// CHECK:STDOUT: +// CHECK:STDOUT: !definition: +// CHECK:STDOUT: } +// CHECK:STDOUT: diff --git a/toolchain/check/testdata/class/no_prelude/destroy_decl.carbon b/toolchain/check/testdata/class/no_prelude/destroy_decl.carbon new file mode 100644 index 000000000000..195c7e685fbf --- /dev/null +++ b/toolchain/check/testdata/class/no_prelude/destroy_decl.carbon @@ -0,0 +1,181 @@ +// Part of the Carbon Language project, under the Apache License v2.0 with LLVM +// Exceptions. See /LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// EXTRA-ARGS: --no-dump-sem-ir +// +// AUTOUPDATE +// TIP: To test this file alone, run: +// TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/class/no_prelude/destroy_decl.carbon +// TIP: To dump output, run: +// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/class/no_prelude/destroy_decl.carbon + +// --- self.carbon + +library "[[@TEST_NAME]]"; + +class C { + fn destroy[self: Self](); +} + +// --- addr_self.carbon + +library "[[@TEST_NAME]]"; + +class C { + fn destroy[addr self: Self*](); +} + +// --- explicit_return.carbon + +library "[[@TEST_NAME]]"; + +class C { + fn destroy[self: Self]() -> (); +} + +// --- fail_class_function.carbon + +library "[[@TEST_NAME]]"; + +class C { + // CHECK:STDERR: fail_class_function.carbon:[[@LINE+4]]:3: error: missing implicit `self` parameter [DestroyFunctionMissingSelf] + // CHECK:STDERR: fn destroy(); + // CHECK:STDERR: ^~~~~~~~~~~~~ + // CHECK:STDERR: + fn destroy(); +} + +// --- fail_extra_implicit_params_second.carbon + +library "[[@TEST_NAME]]"; + +class C { + // CHECK:STDERR: fail_extra_implicit_params_second.carbon:[[@LINE+4]]:26: error: unexpected implicit parameter [DestroyFunctionUnexpectedImplicitParam] + // CHECK:STDERR: fn destroy[self: Self, T:! type](); + // CHECK:STDERR: ^ + // CHECK:STDERR: + fn destroy[self: Self, T:! type](); +} + +// --- fail_extra_implicit_params_first.carbon + +library "[[@TEST_NAME]]"; + +class C { + // CHECK:STDERR: fail_extra_implicit_params_first.carbon:[[@LINE+4]]:14: error: unexpected implicit parameter [DestroyFunctionUnexpectedImplicitParam] + // CHECK:STDERR: fn destroy[T:! type, self: Self](); + // CHECK:STDERR: ^ + // CHECK:STDERR: + fn destroy[T:! type, self: Self](); +} + +// --- fail_positional_params.carbon + +library "[[@TEST_NAME]]"; + +class C { + // CHECK:STDERR: fail_positional_params.carbon:[[@LINE+4]]:3: error: missing empty explicit parameter list [DestroyFunctionPositionalParams] + // CHECK:STDERR: fn destroy[self: Self]; + // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~ + // CHECK:STDERR: + fn destroy[self: Self]; +} + +// --- fail_explicit_params.carbon + +library "[[@TEST_NAME]]"; + +class C { + // CHECK:STDERR: fail_explicit_params.carbon:[[@LINE+4]]:26: error: unexpected parameter [DestroyFunctionNonEmptyExplicitParams] + // CHECK:STDERR: fn destroy[self: Self](x: ()); + // CHECK:STDERR: ^~~~~ + // CHECK:STDERR: + fn destroy[self: Self](x: ()); +} + +// --- fail_return_type.carbon + +library "[[@TEST_NAME]]"; + +class C { + // CHECK:STDERR: fail_return_type.carbon:[[@LINE+4]]:28: error: incorrect return type; must be unspecified or `()` [DestroyFunctionIncorrectReturnType] + // CHECK:STDERR: fn destroy[self: Self]() -> {}; + // CHECK:STDERR: ^~~~~ + // CHECK:STDERR: + fn destroy[self: Self]() -> {}; +} + +// --- fail_out_of_line_missing_params.carbon + +library "[[@TEST_NAME]]"; + + +class C { + fn destroy[self: Self](); +} + +// CHECK:STDERR: fail_out_of_line_missing_params.carbon:[[@LINE+7]]:1: error: redeclaration differs because of missing implicit parameter list [RedeclParamListDiffers] +// CHECK:STDERR: fn C.destroy {} +// CHECK:STDERR: ^~~~~~~~~~~~~~ +// CHECK:STDERR: fail_out_of_line_missing_params.carbon:[[@LINE-6]]:3: note: previously declared with implicit parameter list [RedeclParamListPrevious] +// CHECK:STDERR: fn destroy[self: Self](); +// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~ +// CHECK:STDERR: +fn C.destroy {} + +// --- fail_destroy_in_file_scope.carbon + +library "[[@TEST_NAME]]"; + +// CHECK:STDERR: fail_destroy_in_file_scope.carbon:[[@LINE+4]]:1: error: declaring `fn destroy` in non-class scope [DestroyFunctionOutsideClass] +// CHECK:STDERR: fn destroy[self: ()](); +// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~ +// CHECK:STDERR: +fn destroy[self: ()](); + +// --- fail_destroy_in_namespace_scope.carbon + +library "[[@TEST_NAME]]"; + +namespace NS; + +// CHECK:STDERR: fail_destroy_in_namespace_scope.carbon:[[@LINE+4]]:1: error: declaring `fn destroy` in non-class scope [DestroyFunctionOutsideClass] +// CHECK:STDERR: fn NS.destroy(); +// CHECK:STDERR: ^~~~~~~~~~~~~~~~ +// CHECK:STDERR: +fn NS.destroy(); + +// --- fail_invalid_qualifier_with_params.carbon + +library "[[@TEST_NAME]]"; + +class C { + fn destroy[self: Self](); +} + +// CHECK:STDERR: fail_invalid_qualifier_with_params.carbon:[[@LINE+7]]:6: error: name qualifiers are only allowed for entities that provide a scope [QualifiedNameInNonScope] +// CHECK:STDERR: fn C.destroy[self: Self]().Foo() {} +// CHECK:STDERR: ^~~~~~~ +// CHECK:STDERR: fail_invalid_qualifier_with_params.carbon:[[@LINE-6]]:3: note: referenced non-scope entity declared here [QualifiedNameNonScopeEntity] +// CHECK:STDERR: fn destroy[self: Self](); +// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~ +// CHECK:STDERR: +fn C.destroy[self: Self]().Foo() {} + +// --- fail_invalid_qualifier_no_params.carbon + +library "[[@TEST_NAME]]"; + +class C { + fn destroy[self: Self](); +} + +// CHECK:STDERR: fail_invalid_qualifier_no_params.carbon:[[@LINE+7]]:6: error: name qualifiers are only allowed for entities that provide a scope [QualifiedNameInNonScope] +// CHECK:STDERR: fn C.destroy.Foo() {} +// CHECK:STDERR: ^~~~~~~ +// CHECK:STDERR: fail_invalid_qualifier_no_params.carbon:[[@LINE-6]]:3: note: referenced non-scope entity declared here [QualifiedNameNonScopeEntity] +// CHECK:STDERR: fn destroy[self: Self](); +// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~ +// CHECK:STDERR: +fn C.destroy.Foo() {} diff --git a/toolchain/sem_ir/file.cpp b/toolchain/sem_ir/file.cpp index 41fa4d9530e2..d6cfbf0fb08c 100644 --- a/toolchain/sem_ir/file.cpp +++ b/toolchain/sem_ir/file.cpp @@ -242,6 +242,7 @@ auto GetExprCategory(const File& file, InstId inst_id) -> ExprCategory { } case AccessMemberAction::Kind: + case AccessOptionalMemberAction::Kind: case AddrOf::Kind: case ArrayType::Kind: case AssociatedConstantDecl::Kind: diff --git a/toolchain/sem_ir/inst_kind.def b/toolchain/sem_ir/inst_kind.def index 65d532abb761..daa0ef2bc9da 100644 --- a/toolchain/sem_ir/inst_kind.def +++ b/toolchain/sem_ir/inst_kind.def @@ -19,6 +19,7 @@ // For each instruction kind declared here there is a matching definition in // `typed_insts.h`. CARBON_SEM_IR_INST_KIND(AccessMemberAction) +CARBON_SEM_IR_INST_KIND(AccessOptionalMemberAction) CARBON_SEM_IR_INST_KIND(AdaptDecl) CARBON_SEM_IR_INST_KIND(AddrOf) CARBON_SEM_IR_INST_KIND(AddrPattern) diff --git a/toolchain/sem_ir/inst_kind.h b/toolchain/sem_ir/inst_kind.h index d4b4272e9601..c3ec558e4bda 100644 --- a/toolchain/sem_ir/inst_kind.h +++ b/toolchain/sem_ir/inst_kind.h @@ -122,6 +122,7 @@ class InstKind : public CARBON_ENUM_BASE(InstKind) { TerminatorKind terminator_kind = TerminatorKind::NotTerminator; bool is_lowered = true; bool deduce_through = false; + bool has_cleanup = false; }; // Provides a definition for this instruction kind. Should only be called @@ -222,6 +223,10 @@ class InstKind::Definition : public InstKind { // conclude `A` == `B`. constexpr auto deduce_through() const -> bool { return info_.deduce_through; } + // Returns true if this instruction has scoped cleanup associated, typically a + // destructor. + constexpr auto has_cleanup() const -> bool { return info_.has_cleanup; } + private: friend class InstKind; diff --git a/toolchain/sem_ir/typed_insts.h b/toolchain/sem_ir/typed_insts.h index 1ed77d3e6ecc..27cead440f44 100644 --- a/toolchain/sem_ir/typed_insts.h +++ b/toolchain/sem_ir/typed_insts.h @@ -61,6 +61,20 @@ struct AccessMemberAction { NameId name_id; }; +// An action that performs member access which should fail silently. For +// example, `base.destroy`. +struct AccessOptionalMemberAction { + static constexpr auto Kind = + InstKind::AccessOptionalMemberAction.Define( + {.ir_name = "access_optional_member_action", + .constant_kind = InstConstantKind::InstAction, + .is_lowered = false}); + + TypeId type_id; + MetaInstId base_id; + NameId name_id; +}; + // Common representation for declarations describing the foundation type of a // class -- either its adapted type or its base class. struct AnyFoundationDecl { @@ -1576,7 +1590,8 @@ struct TemporaryStorage { // TODO: Make Parse::NodeId more specific. static constexpr auto Kind = InstKind::TemporaryStorage.Define( {.ir_name = "temporary_storage", - .constant_kind = InstConstantKind::Never}); + .constant_kind = InstConstantKind::Never, + .has_cleanup = true}); TypeId type_id; }; @@ -1750,7 +1765,9 @@ struct VarPattern { struct VarStorage { // TODO: Make Parse::NodeId more specific. static constexpr auto Kind = InstKind::VarStorage.Define( - {.ir_name = "var", .constant_kind = InstConstantKind::Never}); + {.ir_name = "var", + .constant_kind = InstConstantKind::Never, + .has_cleanup = true}); TypeId type_id;