From a5df8ad736d1e02c1756e662ff908fead94982bb Mon Sep 17 00:00:00 2001 From: Jon Ross-Perkins Date: Thu, 27 Mar 2025 17:29:17 -0700 Subject: [PATCH] Support destruction of storage (#5171) What this does: - Adds tracking where storage is allocated. - Determines if that storage supports destruction and, if so, records the `destroy` function for it. - Calls any found `destroy` functions when going out-of-scope. What this does not do: - Precise scope tracking of temporaries. We currently don't define temporary scopes, which would probably be the solution. - Destruction for anything but a `class` with `fn destroy`, in an implicit return. That excludes: - Classes with members that need destruction, particularly in the absence of `fn destroy`. - Structs, tuples, and arrays. - Explicit returns, break, continue, nested scopes. Noting the exclusions in particular, I think those will need work to support, but this should set the right framework. The cleanup block concept stems from clang and trying to share code across cleanups, from discussion with chandlerc. Note in this implementation I try to find `destroy` functions early on: that's so that, when destruction is present on multiple paths, particularly non-shared paths, we only bind the `destroy` method once. Implementation-wise, I'll note this adds a `has_cleanup` flag to `TemporaryStorage` and `VarStorage`. There are several related options, but this felt similar to other information we're trying to track on instructions. My goal with this is to mitigate the chance of accidental calls where the storage may not be tracked for destruction. Alternatives I considered were to not add the flag (I was worried about heightened risk of errors), or to just add a concept for the relevant `requires` (which just felt inconsistent). Cleanup logic ends up in control_flow in this change because I thought it was a reasonably consistent place for the cleanup block concept and its pretty direct control flow interactions. --- toolchain/check/action.cpp | 2 +- toolchain/check/action.h | 2 + toolchain/check/call.cpp | 3 +- toolchain/check/control_flow.cpp | 59 ++ toolchain/check/control_flow.h | 59 ++ toolchain/check/convert.cpp | 10 +- toolchain/check/handle_function.cpp | 3 +- toolchain/check/handle_let_and_var.cpp | 14 +- toolchain/check/inst.h | 23 +- toolchain/check/member_access.cpp | 95 +- toolchain/check/member_access.h | 8 +- toolchain/check/pattern_match.cpp | 5 +- toolchain/check/pending_block.h | 7 + toolchain/check/scope_stack.cpp | 36 +- toolchain/check/scope_stack.h | 15 +- .../testdata/class/no_prelude/destroy.carbon | 734 --------------- .../class/no_prelude/destroy_calls.carbon | 850 ++++++++++++++++++ .../class/no_prelude/destroy_decl.carbon | 181 ++++ toolchain/sem_ir/file.cpp | 1 + toolchain/sem_ir/inst_kind.def | 1 + toolchain/sem_ir/inst_kind.h | 5 + toolchain/sem_ir/typed_insts.h | 21 +- 22 files changed, 1330 insertions(+), 804 deletions(-) delete mode 100644 toolchain/check/testdata/class/no_prelude/destroy.carbon create mode 100644 toolchain/check/testdata/class/no_prelude/destroy_calls.carbon create mode 100644 toolchain/check/testdata/class/no_prelude/destroy_decl.carbon 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;