From 6e62a7d4a24c222d068b620d143f8f50343ae708 Mon Sep 17 00:00:00 2001 From: Richard Smith Date: Mon, 13 Jul 2026 17:59:22 -0700 Subject: [PATCH] Destroy locals at the end of blocks, not only on return (#7448) Destroy local variables and temporaries at each `}`, and when branching with `break` and `continue`. In `for` statements, destroy loop variables along with anything created within the loop at the end of each loop iteration, and destroy the cursor and range object when the loop terminates. Assisted-by: Gemini via Antigravity --------- Co-authored-by: Geoff Romer --- common/array_stack.h | 18 + common/array_stack_test.cpp | 118 ++++ toolchain/check/control_flow.cpp | 64 +- toolchain/check/control_flow.h | 60 +- toolchain/check/function.cpp | 3 + toolchain/check/handle_codeblock.cpp | 4 +- toolchain/check/handle_function.cpp | 2 +- toolchain/check/handle_if_expr.cpp | 4 + toolchain/check/handle_loop_statement.cpp | 55 +- toolchain/check/return.cpp | 8 +- toolchain/check/scope_stack.cpp | 64 +- toolchain/check/scope_stack.h | 90 ++- .../basics/duplicate_name_same_line.carbon | 12 +- .../check/testdata/class/destroy_calls.carbon | 4 +- toolchain/check/testdata/for/basic.carbon | 16 +- toolchain/check/testdata/for/pattern.carbon | 52 +- .../testdata/interop/cpp/range_for.carbon | 168 +++-- .../check/testdata/namespace/shadow.carbon | 6 +- .../return/fail_returned_var_shadow.carbon | 16 +- .../return/import_convert_function.carbon | 52 +- .../testdata/return/returned_var_scope.carbon | 24 +- .../testdata/var/destroy_control_flow.carbon | 620 ++++++++++++++++++ toolchain/check/testdata/var/shadowing.carbon | 4 +- toolchain/lower/testdata/array/iterate.carbon | 2 + toolchain/lower/testdata/for/bindings.carbon | 6 +- .../lower/testdata/for/break_continue.carbon | 3 + toolchain/lower/testdata/for/for.carbon | 4 +- .../testdata/var/destroy_control_flow.carbon | 588 +++++++++++++++++ toolchain/lower/testdata/var/nested.carbon | 2 +- 29 files changed, 1803 insertions(+), 266 deletions(-) create mode 100644 toolchain/check/testdata/var/destroy_control_flow.carbon create mode 100644 toolchain/lower/testdata/var/destroy_control_flow.carbon diff --git a/common/array_stack.h b/common/array_stack.h index 2c672ef9698b..d0e7bc0e244e 100644 --- a/common/array_stack.h +++ b/common/array_stack.h @@ -86,6 +86,24 @@ class ArrayStack { llvm::append_range(values_, values); } + // Truncates the top array on the stack to the given size. + auto TruncateTopArray(size_t size) -> void { + CARBON_CHECK(!array_offsets_.empty()); + values_.truncate(array_offsets_.back() + size); + } + + // Merges the top array into the the array two before it, leaving the top + // array empty. + auto MergeTopArrayIntoGrandparent() -> void { + CARBON_CHECK(array_offsets_.size() >= 3); + auto new_mid = + std::rotate(values_.begin() + array_offsets_[array_offsets_.size() - 2], + values_.begin() + array_offsets_[array_offsets_.size() - 1], + values_.end()); + array_offsets_[array_offsets_.size() - 2] = new_mid - values_.begin(); + array_offsets_[array_offsets_.size() - 1] = values_.size(); + } + // Returns the current number of values in all arrays. auto all_values_size() const -> size_t { return values_.size(); } diff --git a/common/array_stack_test.cpp b/common/array_stack_test.cpp index fc85a053b7d3..db1a3cf56de5 100644 --- a/common/array_stack_test.cpp +++ b/common/array_stack_test.cpp @@ -103,5 +103,123 @@ TEST(ArrayStack, PeekArrayAt) { EXPECT_THAT(stack.PeekArrayAt(2), ElementsAre(3)); } +TEST(ArrayStack, MergeTopArrayIntoGrandparent) { + ArrayStack stack; + + // Basic case with 3 arrays. + stack.PushArray(); + stack.AppendToTop(1); + stack.AppendToTop(2); + + stack.PushArray(); + stack.AppendToTop(3); + stack.AppendToTop(4); + stack.AppendToTop(5); + + stack.PushArray(); + stack.AppendToTop(6); + stack.AppendToTop(7); + + stack.MergeTopArrayIntoGrandparent(); + + EXPECT_THAT(stack.PeekArrayAt(0), ElementsAre(1, 2, 6, 7)); + EXPECT_THAT(stack.PeekArrayAt(1), ElementsAre(3, 4, 5)); + EXPECT_THAT(stack.PeekArrayAt(2), IsEmpty()); + EXPECT_THAT(stack.PeekArray(), IsEmpty()); + EXPECT_THAT(stack.PeekAllValues(), ElementsAre(1, 2, 6, 7, 3, 4, 5)); + + // Appending to the now-empty top array and popping works as expected. + stack.AppendToTop(8); + EXPECT_THAT(stack.PeekArray(), ElementsAre(8)); + EXPECT_THAT(stack.PeekAllValues(), ElementsAre(1, 2, 6, 7, 3, 4, 5, 8)); + + stack.PopArray(); + EXPECT_THAT(stack.PeekArray(), ElementsAre(3, 4, 5)); + EXPECT_THAT(stack.PeekAllValues(), ElementsAre(1, 2, 6, 7, 3, 4, 5)); + + stack.PopArray(); + EXPECT_THAT(stack.PeekArray(), ElementsAre(1, 2, 6, 7)); + EXPECT_THAT(stack.PeekAllValues(), ElementsAre(1, 2, 6, 7)); +} + +TEST(ArrayStack, MergeTopArrayIntoGrandparentDeeperStack) { + ArrayStack stack; + + // Verify behavior when there are more than 3 arrays on the stack. + stack.PushArray(); + stack.AppendToTop(10); + + stack.PushArray(); + stack.AppendToTop(20); + + stack.PushArray(); + stack.AppendToTop(30); + + stack.PushArray(); + stack.AppendToTop(40); + stack.AppendToTop(50); + + stack.MergeTopArrayIntoGrandparent(); + + EXPECT_THAT(stack.PeekArrayAt(0), ElementsAre(10)); + EXPECT_THAT(stack.PeekArrayAt(1), ElementsAre(20, 40, 50)); + EXPECT_THAT(stack.PeekArrayAt(2), ElementsAre(30)); + EXPECT_THAT(stack.PeekArrayAt(3), IsEmpty()); + EXPECT_THAT(stack.PeekAllValues(), ElementsAre(10, 20, 40, 50, 30)); +} + +TEST(ArrayStack, MergeTopArrayIntoGrandparentEmptyArrays) { + // Test when the parent array is initially empty. + { + ArrayStack stack; + stack.PushArray(); + stack.AppendToTop(1); + stack.PushArray(); + stack.PushArray(); + stack.AppendToTop(2); + stack.AppendToTop(3); + + stack.MergeTopArrayIntoGrandparent(); + EXPECT_THAT(stack.PeekArrayAt(0), ElementsAre(1, 2, 3)); + EXPECT_THAT(stack.PeekArrayAt(1), IsEmpty()); + EXPECT_THAT(stack.PeekArrayAt(2), IsEmpty()); + EXPECT_THAT(stack.PeekAllValues(), ElementsAre(1, 2, 3)); + } + + // Test when the top array is initially empty. + { + ArrayStack stack; + stack.PushArray(); + stack.AppendToTop(1); + stack.PushArray(); + stack.AppendToTop(2); + stack.PushArray(); + + stack.MergeTopArrayIntoGrandparent(); + EXPECT_THAT(stack.PeekArrayAt(0), ElementsAre(1)); + EXPECT_THAT(stack.PeekArrayAt(1), ElementsAre(2)); + EXPECT_THAT(stack.PeekArrayAt(2), IsEmpty()); + EXPECT_THAT(stack.PeekAllValues(), ElementsAre(1, 2)); + } + + // Test when the grandparent array is initially empty. + { + ArrayStack stack; + stack.PushArray(); + stack.PushArray(); + stack.AppendToTop(1); + stack.AppendToTop(2); + stack.PushArray(); + stack.AppendToTop(3); + stack.AppendToTop(4); + + stack.MergeTopArrayIntoGrandparent(); + EXPECT_THAT(stack.PeekArrayAt(0), ElementsAre(3, 4)); + EXPECT_THAT(stack.PeekArrayAt(1), ElementsAre(1, 2)); + EXPECT_THAT(stack.PeekArrayAt(2), IsEmpty()); + EXPECT_THAT(stack.PeekAllValues(), ElementsAre(3, 4, 1, 2)); + } +} + } // namespace } // namespace Carbon::Testing diff --git a/toolchain/check/control_flow.cpp b/toolchain/check/control_flow.cpp index 38b6b853f42a..b83dbb532865 100644 --- a/toolchain/check/control_flow.cpp +++ b/toolchain/check/control_flow.cpp @@ -11,6 +11,7 @@ #include "toolchain/check/inst.h" #include "toolchain/check/name_lookup.h" #include "toolchain/check/operator.h" +#include "toolchain/check/scope_stack.h" #include "toolchain/sem_ir/ids.h" #include "toolchain/sem_ir/typed_insts.h" @@ -139,25 +140,18 @@ auto IsCurrentPositionReachable(Context& context) -> bool { } auto MaybeAddCleanupForInst(Context& context, SemIR::InstId inst_id) -> void { - if (!context.scope_stack().IsInFunctionScope()) { - // Cleanup can only occur in function scopes. + if (!context.scope_stack().IsCleanupScope()) { return; } context.scope_stack().destroy_id_stack().AppendToTop(inst_id); } -// 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)) { +// Adds cleanups for variables added after `depth`. +static auto AddCleanups(Context& context, ScopeStack::CleanupScopeDepth depth) + -> void { + for (auto destroy_id : + llvm::reverse(context.scope_stack().GetCleanupsSince(depth))) { // TODO: This does the `Destroy` lookup and call at every cleanup block. // Control flow can lead to the same variable being destroyed by multiple // cleanup blocks, so we'll want to avoid this in the future. @@ -167,9 +161,47 @@ static auto AddCleanupBlock(Context& context) -> void { } } -auto AddReturnCleanupBlock(Context& context, - SemIR::LocIdAndInst loc_id_and_inst) -> void { - AddCleanupBlock(context); +auto AddAndDiscardCleanups(Context& context) -> void { + auto depth = context.scope_stack().enclosing_cleanup_scope_depth(); + AddCleanups(context, depth); + context.scope_stack().DiscardCleanupsSince(depth); +} + +// TODO: When we have multiple branches or returns in the same function, share +// cleanup instructions rather than duplicating them for each branch or return. +// We would do this by creating cleanup blocks that hold the relevant cleanups, +// and chaining between cleanup blocks to allow cleanup instruction reuse. We +// could either share cleanup blocks only if they end in the same instruction, +// or generate branches out of the cleanups to the chosen destination. +// +// 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. +// } +auto AddBranchWithCleanups(Context& context, SemIR::LocId loc_id, + SemIR::InstBlockId target_id, + ScopeStack::CleanupScopeDepth depth) -> void { + AddCleanups(context, depth); + AddInst(context, loc_id, {.target_id = target_id}); +} + +auto AddReturnInstWithCleanups(Context& context, + SemIR::LocIdAndInst loc_id_and_inst) -> void { + AddCleanups(context, context.scope_stack().function_cleanup_scope_depth()); AddInst(context, loc_id_and_inst); } diff --git a/toolchain/check/control_flow.h b/toolchain/check/control_flow.h index 574c69947359..76241d610465 100644 --- a/toolchain/check/control_flow.h +++ b/toolchain/check/control_flow.h @@ -7,6 +7,8 @@ #include "toolchain/check/context.h" #include "toolchain/check/inst.h" +#include "toolchain/check/scope_stack.h" +#include "toolchain/parse/node_ids.h" #include "toolchain/parse/typed_nodes.h" #include "toolchain/sem_ir/ids.h" #include "toolchain/sem_ir/inst.h" @@ -90,46 +92,32 @@ auto AddInstWithCleanupInNoBlock(Context& context, LocT loc, InstT inst) return inst_id; } -// Adds a return cleanup block, including the returning instruction. -// -// 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 `break;` and `continue;`. -// TODO: Add reuse (described above but not done). -auto AddReturnCleanupBlock(Context& context, - SemIR::LocIdAndInst loc_id_and_inst) -> void; +// Adds any cleanups for variables and temporaries within the current statement +// scope and discards them from cleanup tracking. +auto AddAndDiscardCleanups(Context& context) -> void; + +// Adds a branch to the given target block, which should be in the current scope +// or an enclosing scope, along with cleanups for all variables and temporaries +// created since the given depth, which should be the cleanup depth of the +// branch target. +auto AddBranchWithCleanups(Context& context, SemIR::LocId loc_id, + SemIR::InstBlockId target_id, + ScopeStack::CleanupScopeDepth depth) -> void; + +// Adds a return instruction, along with cleanups for all live variables or +// temporaries in the current function. If no instruction is passed, it +// defaults to `SemIR::Return{}`. +auto AddReturnInstWithCleanups(Context& context, + SemIR::LocIdAndInst loc_id_and_inst) -> void; template -auto AddReturnCleanupBlock(Context& context, LocT loc) -> void { - AddReturnCleanupBlock(context, SemIR::LocIdAndInst(loc, SemIR::Return{})); +auto AddReturnInstWithCleanups(Context& context, LocT loc) -> void { + AddReturnInstWithCleanups(context, SemIR::LocIdAndInst(loc, SemIR::Return{})); } template -auto AddReturnCleanupBlockWithExpr(Context& context, LocT loc, - SemIR::ReturnExpr inst) -> void { - AddReturnCleanupBlock(context, SemIR::LocIdAndInst(loc, inst)); +auto AddReturnInstWithCleanups(Context& context, LocT loc, + SemIR::ReturnExpr inst) -> void { + AddReturnInstWithCleanups(context, SemIR::LocIdAndInst(loc, inst)); } } // namespace Carbon::Check diff --git a/toolchain/check/function.cpp b/toolchain/check/function.cpp index a6a10612e362..e5cd6438206e 100644 --- a/toolchain/check/function.cpp +++ b/toolchain/check/function.cpp @@ -469,6 +469,9 @@ auto StartFunctionDefinition(Context& context, SemIR::InstId decl_id, auto FinishFunctionDefinition(Context& context, SemIR::FunctionId function_id) -> void { context.inst_block_stack().Pop(); + // Any cleanups for a function will have been handled when emitting `return`s. + context.scope_stack().DiscardCleanupsSince( + context.scope_stack().function_cleanup_scope_depth()); context.scope_stack().Pop(/*check_unused=*/true); auto observe_block_id = diff --git a/toolchain/check/handle_codeblock.cpp b/toolchain/check/handle_codeblock.cpp index 639e2dd033bd..915f46a847c1 100644 --- a/toolchain/check/handle_codeblock.cpp +++ b/toolchain/check/handle_codeblock.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/handle.h" #include "toolchain/check/unused.h" @@ -11,11 +12,12 @@ namespace Carbon::Check { auto HandleParseNode(Context& context, Parse::CodeBlockStartId node_id) -> bool { context.node_stack().Push(node_id); - context.scope_stack().PushForSameRegion(); + context.scope_stack().PushForSameRegion(ScopeStack::CleanupScopeKind::Owned); return true; } auto HandleParseNode(Context& context, Parse::CodeBlockId /*node_id*/) -> bool { + AddAndDiscardCleanups(context); context.scope_stack().Pop(/*check_unused=*/true); context.node_stack() .PopAndDiscardSoloNodeId(); diff --git a/toolchain/check/handle_function.cpp b/toolchain/check/handle_function.cpp index cad51842b547..89817a395c67 100644 --- a/toolchain/check/handle_function.cpp +++ b/toolchain/check/handle_function.cpp @@ -748,7 +748,7 @@ auto HandleParseNode(Context& context, Parse::FunctionDefinitionId node_id) context.emitter().Emit(LocIdForDiagnostics::TokenOnly(node_id), MissingReturnStatement); } else { - AddReturnCleanupBlock(context, node_id); + AddReturnInstWithCleanups(context, node_id); } } diff --git a/toolchain/check/handle_if_expr.cpp b/toolchain/check/handle_if_expr.cpp index b82613836447..fe71179072f5 100644 --- a/toolchain/check/handle_if_expr.cpp +++ b/toolchain/check/handle_if_expr.cpp @@ -24,6 +24,10 @@ auto HandleParseNode(Context& context, Parse::IfExprIfId node_id) -> bool { auto else_block_id = AddDominatedBlockAndBranch(context, if_node); // Start emitting the `then` block. + // TODO: Track the condition on the scope stack so that it can be associated + // with any cleanups that are registered within this arm of the `if`. We want + // to conditionalize the cleanups so they run in the same cases where the + // corresponding `if` arm was taken. context.inst_block_stack().Pop(); context.inst_block_stack().Push(then_block_id); context.region_stack().AddToRegion(then_block_id, node_id); diff --git a/toolchain/check/handle_loop_statement.cpp b/toolchain/check/handle_loop_statement.cpp index 4e679cd0c1e8..f370c075d72b 100644 --- a/toolchain/check/handle_loop_statement.cpp +++ b/toolchain/check/handle_loop_statement.cpp @@ -56,9 +56,15 @@ static auto BranchAndStartLoopBody(Context& context, Parse::NodeId node_id, context.inst_block_stack().Push(loop_body_id); context.region_stack().AddToRegion(loop_body_id, node_id); - // Allow `break` and `continue` in this scope. + // Allow `break` and `continue` in this scope. `continue` will destroy + // temporaries in the loop header, `break` will not, as the loop exit block + // also destroys those temporaries. context.break_continue_stack().push_back( - {.break_target = loop_exit_id, .continue_target = loop_header_id}); + {.break_target = loop_exit_id, + .break_depth = context.scope_stack().cleanup_scope_depth(), + .continue_target = loop_header_id, + .continue_depth = + context.scope_stack().enclosing_cleanup_scope_depth()}); } // Finishes emitting the body for a `while`-like loop. Adds a back-edge to the @@ -67,13 +73,18 @@ static auto FinishLoopBody(Context& context, Parse::NodeId node_id) -> void { auto blocks = context.break_continue_stack().pop_back_val(); // Add the loop backedge. - AddInst(context, node_id, - {.target_id = blocks.continue_target}); + AddBranchWithCleanups(context, node_id, blocks.continue_target, + blocks.continue_depth); context.inst_block_stack().Pop(); // Start emitting the loop exit block. + context.scope_stack().DiscardCleanupsSince(blocks.break_depth); context.inst_block_stack().Push(blocks.break_target); context.region_stack().AddToRegion(blocks.break_target, node_id); + + // Clean up anything created in the loop header and pop the loop scope. + AddAndDiscardCleanups(context); + context.scope_stack().Pop(/*check_unused=*/true); } // `while` @@ -81,6 +92,7 @@ static auto FinishLoopBody(Context& context, Parse::NodeId node_id) -> void { auto HandleParseNode(Context& context, Parse::WhileConditionStartId node_id) -> bool { + context.scope_stack().PushForSameRegion(ScopeStack::CleanupScopeKind::Owned); context.node_stack().Push(node_id, StartLoopHeader(context, node_id)); return true; } @@ -108,10 +120,12 @@ auto HandleParseNode(Context& context, Parse::WhileStatementId node_id) auto HandleParseNode(Context& context, Parse::ForHeaderStartId node_id) -> bool { - // Create a nested scope to hold the cursor variable. This is also the lexical - // scope that names in the pattern are added to, although they get rebound on - // each loop iteration. - context.scope_stack().PushForSameRegion(); + // Create a scope that will eventually hold the range and cursor of the for + // loop. + context.scope_stack().PushForSameRegion(ScopeStack::CleanupScopeKind::Owned); + + // Create a scope for any variables introduced in the pattern. + context.scope_stack().PushForSameRegion(ScopeStack::CleanupScopeKind::Owned); // Begin an implicit let declaration context for the pattern. context.decl_introducer_state_stack().Push(); @@ -130,6 +144,11 @@ auto HandleParseNode(Context& context, Parse::ForInId node_id) -> bool { {.pattern_block_id = pattern_block_id}); context.decl_introducer_state_stack().Pop(); context.full_pattern_stack().StartPatternInitializer(); + + // Create a temporary scope to hold the range expression and the cursor. This + // comes before the pattern in control flow order, but we'll reorder temporary + // destruction later. + context.scope_stack().PushForSameRegion(ScopeStack::CleanupScopeKind::Owned); return true; } @@ -182,6 +201,14 @@ auto HandleParseNode(Context& context, Parse::ForHeaderId node_id) -> bool { {.lhs_id = init_result.storage_id, .rhs_id = init_result.init_id}); cursor_var_id = init_result.storage_id; + // Now we're finished with the loop initialization, merge the scope containing + // the range expression into its grandparent scope. The parent scope currently + // contains the loop variables, whereas the current scope contains the range, + // and that's backwards from a control flow and destruction order perspective. + // We created the grandparent scope for this purpose when handling the + // ForHeaderStart node. + context.scope_stack().MergeTopScopeIntoGrandparentAndPop(); + // Start emitting the loop header block. auto loop_header_id = StartLoopHeader(context, start_node_id); @@ -221,6 +248,10 @@ auto HandleParseNode(Context& context, Parse::ForHeaderId node_id) -> bool { auto HandleParseNode(Context& context, Parse::ForStatementId node_id) -> bool { FinishLoopBody(context, node_id); + + // Pop the scope that the range and cursor live in. + AddAndDiscardCleanups(context); + context.scope_stack().Pop(/*check_unused=*/true); return true; } @@ -235,8 +266,8 @@ auto HandleParseNode(Context& context, Parse::BreakStatementStartId node_id) "`break` can only be used in a loop"); context.emitter().Emit(node_id, BreakOutsideLoop); } else { - AddInst(context, node_id, - {.target_id = stack.back().break_target}); + AddBranchWithCleanups(context, node_id, stack.back().break_target, + stack.back().break_depth); } context.inst_block_stack().Pop(); @@ -260,8 +291,8 @@ auto HandleParseNode(Context& context, Parse::ContinueStatementStartId node_id) "`continue` can only be used in a loop"); context.emitter().Emit(node_id, ContinueOutsideLoop); } else { - AddInst(context, node_id, - {.target_id = stack.back().continue_target}); + AddBranchWithCleanups(context, node_id, stack.back().continue_target, + stack.back().continue_depth); } context.inst_block_stack().Pop(); diff --git a/toolchain/check/return.cpp b/toolchain/check/return.cpp index 01d8cd1ec0e9..fcd10a9e3c25 100644 --- a/toolchain/check/return.cpp +++ b/toolchain/check/return.cpp @@ -139,7 +139,7 @@ auto BuildReturnWithNoExpr(Context& context, SemIR::LocId loc_id) -> void { diag.Emit(); } - AddReturnCleanupBlock(context, loc_id); + AddReturnInstWithCleanups(context, loc_id); } auto BuildReturnWithExpr(Context& context, SemIR::LocId loc_id, @@ -238,8 +238,8 @@ auto BuildReturnWithExpr(Context& context, SemIR::LocId loc_id, CARBON_FATAL("Unexpected inst kind: {0}", return_form); } } - AddReturnCleanupBlockWithExpr(context, loc_id, - {.expr_id = expr_id, .dest_id = out_param_id}); + AddReturnInstWithCleanups(context, loc_id, + {.expr_id = expr_id, .dest_id = out_param_id}); } auto BuildReturnVar(Context& context, Parse::ReturnStatementId node_id) @@ -260,7 +260,7 @@ auto BuildReturnVar(Context& context, Parse::ReturnStatementId node_id) // to indicate that this was a `return var`, not a reference return. returned_var_id = ConvertToValueExpr(context, returned_var_id); - AddReturnCleanupBlockWithExpr( + AddReturnInstWithCleanups( context, node_id, {.expr_id = returned_var_id, .dest_id = return_param_id}); } diff --git a/toolchain/check/scope_stack.cpp b/toolchain/check/scope_stack.cpp index f6d4399950ca..d6c410519d0d 100644 --- a/toolchain/check/scope_stack.cpp +++ b/toolchain/check/scope_stack.cpp @@ -9,6 +9,7 @@ #include "common/check.h" #include "common/find.h" #include "toolchain/check/context.h" +#include "toolchain/check/control_flow.h" #include "toolchain/check/unused.h" #include "toolchain/sem_ir/ids.h" @@ -51,6 +52,7 @@ auto ScopeStack::VerifyNextCompileTimeBindIndex(llvm::StringLiteral label, auto ScopeStack::Push(SemIR::InstId scope_inst_id, SemIR::NameScopeId scope_id, SemIR::SpecificId specific_id, + CleanupScopeKind cleanup_scope_kind, bool lexical_lookup_has_load_error) -> void { // If this scope doesn't have a specific of its own, it lives in the enclosing // scope's specific, if any. @@ -68,7 +70,8 @@ auto ScopeStack::Push(SemIR::InstId scope_inst_id, SemIR::NameScopeId scope_id, .next_compile_time_bind_index = SemIR::CompileTimeBindIndex( compile_time_binding_stack_.all_values_size()), .lexical_lookup_has_load_error = - LexicalLookupHasLoadError() || lexical_lookup_has_load_error}); + LexicalLookupHasLoadError() || lexical_lookup_has_load_error, + .cleanup_scope_kind = cleanup_scope_kind}); 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 @@ -93,11 +96,15 @@ auto ScopeStack::Push(SemIR::InstId scope_inst_id, SemIR::NameScopeId scope_id, ++next_scope_index_.index; VerifyNextCompileTimeBindIndex("Push", scope_stack_.back()); + + if (cleanup_scope_kind == CleanupScopeKind::Owned) { + destroy_id_stack_.PushArray(); + } } auto ScopeStack::PushForDeclName() -> void { Push(SemIR::InstId::None, SemIR::NameScopeId::None, SemIR::SpecificId::None, - /*lexical_lookup_has_load_error=*/false); + CleanupScopeKind::None, /*lexical_lookup_has_load_error=*/false); MarkNestingIfInReturnScope(); } @@ -107,13 +114,20 @@ auto ScopeStack::PushForEntity(SemIR::InstId scope_inst_id, bool lexical_lookup_has_load_error) -> void { CARBON_CHECK(scope_inst_id.has_value()); CARBON_DCHECK(!sem_ir().insts().Is(scope_inst_id)); - Push(scope_inst_id, scope_id, specific_id, lexical_lookup_has_load_error); + Push(scope_inst_id, scope_id, specific_id, CleanupScopeKind::None, + lexical_lookup_has_load_error); MarkNestingIfInReturnScope(); } -auto ScopeStack::PushForSameRegion() -> void { +auto ScopeStack::PushForSameRegion(CleanupScopeKind cleanup_scope_kind) + -> void { + if (cleanup_scope_kind == CleanupScopeKind::Inherited && + Peek().cleanup_scope_kind == CleanupScopeKind::None) { + cleanup_scope_kind = CleanupScopeKind::None; + } + Push(SemIR::InstId::None, SemIR::NameScopeId::None, SemIR::SpecificId::None, - /*lexical_lookup_has_load_error=*/false); + cleanup_scope_kind, /*lexical_lookup_has_load_error=*/false); } auto ScopeStack::PushForFunctionBody(SemIR::InstId scope_inst_id) -> void { @@ -122,10 +136,10 @@ auto ScopeStack::PushForFunctionBody(SemIR::InstId scope_inst_id) -> void { const auto& function = sem_ir().functions().Get(function_decl.function_id); auto self_specific = sem_ir().generics().GetSelfSpecific(function.generic_id); Push(scope_inst_id, SemIR::NameScopeId::None, self_specific, - /*lexical_lookup_has_load_error=*/false); + CleanupScopeKind::Owned, /*lexical_lookup_has_load_error=*/false); - return_scope_stack_.push_back({.decl_id = scope_inst_id}); - destroy_id_stack_.PushArray(); + return_scope_stack_.push_back( + {.decl_id = scope_inst_id, .cleanup_scope_depth = cleanup_scope_depth()}); } auto ScopeStack::Pop(bool check_unused) -> void { @@ -148,6 +162,12 @@ auto ScopeStack::Pop(bool check_unused) -> void { non_lexical_scope_stack_.pop_back(); } + if (scope.cleanup_scope_kind == CleanupScopeKind::Owned) { + CARBON_CHECK(destroy_id_stack_.PeekArray().empty(), + "Popping scope with cleanups"); + destroy_id_stack_.PopArray(); + } + if (!return_scope_stack_.empty()) { if (scope.has_returned_var) { CARBON_CHECK(return_scope_stack_.back().returned_var.has_value()); @@ -157,13 +177,12 @@ auto ScopeStack::Pop(bool check_unused) -> void { if (return_scope_stack_.back().decl_id == scope.scope_inst_id) { // Leaving the function scope. return_scope_stack_.pop_back(); - destroy_id_stack_.PopArray(); - } else if (return_scope_stack_.back().nested_scope_index == scope.index) { - // Returned to a function scope from a non-function nested entity scope. - return_scope_stack_.back().nested_scope_index = ScopeIndex::None; + } else { + if (return_scope_stack_.back().nested_scope_index == scope.index) { + // Returned to a function scope from a non-function nested entity scope. + return_scope_stack_.back().nested_scope_index = ScopeIndex::None; + } } - } else { - CARBON_CHECK(!scope.has_returned_var); } VerifyNextCompileTimeBindIndex("Pop", scope); @@ -179,6 +198,21 @@ auto ScopeStack::PopTo(ScopeIndex index, bool check_unused) -> void { PeekIndex()); } +auto ScopeStack::MergeTopScopeIntoGrandparentAndPop() -> void { + CARBON_CHECK(scope_stack_.size() >= 3); + auto& grandparent = scope_stack_[scope_stack_.size() - 3]; + auto& parent = scope_stack_[scope_stack_.size() - 2]; + auto& current = scope_stack_[scope_stack_.size() - 1]; + + CARBON_CHECK(current.num_names == 0); + CARBON_CHECK(grandparent.cleanup_scope_kind != CleanupScopeKind::None && + parent.cleanup_scope_kind == CleanupScopeKind::Owned && + current.cleanup_scope_kind == CleanupScopeKind::Owned); + destroy_id_stack_.MergeTopArrayIntoGrandparent(); + + Pop(); +} + auto ScopeStack::MarkUsed(SemIR::NameId name_id, SemIR::LocId loc_id, bool is_reachable) -> void { auto& lexical_results = lexical_lookup_.Get(name_id); @@ -361,6 +395,8 @@ auto ScopeStack::Suspend() -> SuspendedScope { compile_time_binding_stack_.PopArray(); // This would be easy to support if we had a need, but currently we do not. + CARBON_CHECK(result.entry.cleanup_scope_kind == CleanupScopeKind::None, + "Should not suspend a function definition scope."); CARBON_CHECK(!result.entry.has_returned_var, "Should not suspend a scope with a returned var."); return result; diff --git a/toolchain/check/scope_stack.h b/toolchain/check/scope_stack.h index 5a9241d5e368..39346b1881b0 100644 --- a/toolchain/check/scope_stack.h +++ b/toolchain/check/scope_stack.h @@ -25,10 +25,37 @@ class ScopeStack { public: explicit ScopeStack(Context& context); + // The kind of cleanup scope that is associated with a scope. A cleanup scope + // contains the destructor calls that are necessary to perform when leaving a + // scope. + enum class CleanupScopeKind : uint8_t { + // This scope is not used for runtime expression evaluation, so should only + // contain constants. Any cleanups introduced here can be discarded. + // TODO: Is this the behavior we want for such scopes? Should we check that + // the cleanups themselves are constant? + None, + // This scope is associated with a subexpression, not a full-expression, so + // its cleanup scope is inherited from the parent scope. + Inherited, + // This scope is associated with a statement, so it owns a cleanup scope, + // and its cleanups must be run or explicitly discarded when exiting this + // scope. + Owned, + }; + + // An index for the distance to the bottom of the cleanup stack. + struct CleanupScopeDepth : public IndexBase { + static constexpr llvm::StringLiteral Label = "cleanup_scope_depth"; + + using IndexBase::IndexBase; + }; + // A scope in which `break` and `continue` can be used. struct BreakContinueScope { SemIR::InstBlockId break_target; + CleanupScopeDepth break_depth; SemIR::InstBlockId continue_target; + CleanupScopeDepth continue_depth; }; // A non-lexical scope in which unqualified lookup may be required. @@ -59,7 +86,8 @@ class ScopeStack { // Pushes a scope which should be in the same region as the current scope. // These can be in a function without breaking `return` scoping. For example, // this is used by struct literals and code blocks. - auto PushForSameRegion() -> void; + auto PushForSameRegion(CleanupScopeKind cleanup_scope_kind = + CleanupScopeKind::Inherited) -> void; // Pushes a function scope. auto PushForFunctionBody(SemIR::InstId scope_inst_id) -> void; @@ -109,6 +137,19 @@ class ScopeStack { !return_scope_stack_.back().nested_scope_index.has_value(); } + // Merges the innermost scope into its grandparent scope, and pops the + // now-empty scope. This is used when handling a `for` statement. Given: + // + // for (var a: i32 in MakeTempRange()) { + // + // we have an outer scope containing `var a: i32` and an inner scope + // containing `MakeTempRange()`, and we want them the other way around, so we + // create an extra enclosing scope in advance and merge the inner scope into + // it. + // + // Requires that no names were introduced in the innermost scope. + auto MergeTopScopeIntoGrandparentAndPop() -> void; + // Returns the current scope, if it is of the specified kind. Otherwise, // returns nullopt. template @@ -198,6 +239,43 @@ class ScopeStack { // Runs verification that the processing cleanly finished. auto VerifyOnFinish() const -> void; + // Returns whether this is a scope in which cleanups are tracked. + auto IsCleanupScope() const -> bool { + return Peek().cleanup_scope_kind != CleanupScopeKind::None; + } + + // Returns all values on `destroy_id_stack_` added since `depth`. + auto GetCleanupsSince(CleanupScopeDepth depth) const + -> llvm::ArrayRef { + return destroy_id_stack_.PeekAllValues().slice(depth.index); + } + + // Discards cleanups after the given depth, which must be within the current + // scope. + auto DiscardCleanupsSince(CleanupScopeDepth depth) -> void { + auto enclosing = enclosing_cleanup_scope_depth(); + CARBON_CHECK(depth >= enclosing); + // TODO: Consider switching `destroy_id_stack_` from `ArrayStack` to + // `SmallVector` - `ArrayStack` gives us nothing here. + destroy_id_stack_.TruncateTopArray(depth.index - enclosing.index); + } + + // Returns the current depth of the cleanup stack. + auto cleanup_scope_depth() const -> CleanupScopeDepth { + return CleanupScopeDepth(destroy_id_stack_.all_values_size()); + } + + // Returns the depth of the cleanup stack enclosing the current scope. + auto enclosing_cleanup_scope_depth() const -> CleanupScopeDepth { + return CleanupScopeDepth(destroy_id_stack_.all_values_size() - + destroy_id_stack_.PeekArray().size()); + } + + // Returns the depth of the cleanup stack enclosing this function scope. + auto function_cleanup_scope_depth() const -> CleanupScopeDepth { + return return_scope_stack_.back().cleanup_scope_depth; + } + auto break_continue_stack() -> llvm::SmallVector& { return break_continue_stack_; } @@ -249,6 +327,9 @@ class ScopeStack { // unregistered when the scope ends. bool has_returned_var = false; + // The kind of cleanup scope that is associated with this scope. + CleanupScopeKind cleanup_scope_kind = CleanupScopeKind::None; + // Whether there are any ids in the `names` set. int num_names = 0; @@ -266,6 +347,9 @@ class ScopeStack { // set and unset as `returned var`s are declared and go out of scope. SemIR::InstId returned_var = SemIR::InstId::None; + // The cleanup stack depth when entering the function body. + CleanupScopeDepth cleanup_scope_depth; + // When a nested scope interrupts a return scope, this is the index of the // outermost interrupting scope (the one closest to the function scope). // This can then be used to determine whether we're actually inside the most @@ -283,8 +367,8 @@ class ScopeStack { // lexical_lookup_has_load_error is used to limit diagnostics when a given // namespace may contain a mix of both successful and failed name imports. auto Push(SemIR::InstId scope_inst_id, SemIR::NameScopeId scope_id, - SemIR::SpecificId specific_id, bool lexical_lookup_has_load_error) - -> void; + SemIR::SpecificId specific_id, CleanupScopeKind cleanup_scope_kind, + bool lexical_lookup_has_load_error) -> void; auto Peek(int drop = 0) const -> const ScopeStackEntry& { CARBON_DCHECK(drop < static_cast(scope_stack_.size())); diff --git a/toolchain/check/testdata/basics/duplicate_name_same_line.carbon b/toolchain/check/testdata/basics/duplicate_name_same_line.carbon index 582ca85649d9..f7fb245d892e 100644 --- a/toolchain/check/testdata/basics/duplicate_name_same_line.carbon +++ b/toolchain/check/testdata/basics/duplicate_name_same_line.carbon @@ -28,10 +28,10 @@ fn A() { // CHECK:STDOUT: %pattern_type.cb1: type = pattern_type %empty_tuple.type [concrete] // CHECK:STDOUT: %n.patt.785a0d.1: %pattern_type.cb1 = ref_binding_pattern n [concrete] // CHECK:STDOUT: %n.var_patt.961036.1: %pattern_type.cb1 = var_pattern %n.patt.785a0d.1 [concrete] -// CHECK:STDOUT: %n.patt.785a0d.2: %pattern_type.cb1 = ref_binding_pattern n [concrete] -// CHECK:STDOUT: %n.var_patt.961036.2: %pattern_type.cb1 = var_pattern %n.patt.785a0d.2 [concrete] // CHECK:STDOUT: %Destroy.Op.type: type = fn_type @Destroy.Op [concrete] // CHECK:STDOUT: %Destroy.Op: %Destroy.Op.type = struct_value () [concrete] +// CHECK:STDOUT: %n.patt.785a0d.2: %pattern_type.cb1 = ref_binding_pattern n [concrete] +// CHECK:STDOUT: %n.var_patt.961036.2: %pattern_type.cb1 = var_pattern %n.patt.785a0d.2 [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @A() { @@ -53,6 +53,8 @@ fn A() { // CHECK:STDOUT: %n.patt.loc18_17: %pattern_type.cb1 = ref_binding_pattern n [concrete = constants.%n.patt.785a0d.1] // CHECK:STDOUT: %n.var_patt.loc18_5: %pattern_type.cb1 = var_pattern %n.patt.loc18_17 [concrete = constants.%n.var_patt.961036.1] // CHECK:STDOUT: } +// CHECK:STDOUT: %Destroy.Op.bound.loc18_5: = bound_method %n.var.loc18_5, constants.%Destroy.Op +// CHECK:STDOUT: %Destroy.Op.call.loc18_5: init %empty_tuple.type = call %Destroy.Op.bound.loc18_5(%n.var.loc18_5) // CHECK:STDOUT: // CHECK:STDOUT: // CHECK:STDOUT: !if.else: @@ -70,13 +72,11 @@ fn A() { // CHECK:STDOUT: %n.patt.loc18_49: %pattern_type.cb1 = ref_binding_pattern n [concrete = constants.%n.patt.785a0d.2] // CHECK:STDOUT: %n.var_patt.loc18_37: %pattern_type.cb1 = var_pattern %n.patt.loc18_49 [concrete = constants.%n.var_patt.961036.2] // CHECK:STDOUT: } +// CHECK:STDOUT: %Destroy.Op.bound.loc18_37: = bound_method %n.var.loc18_37, constants.%Destroy.Op +// CHECK:STDOUT: %Destroy.Op.call.loc18_37: init %empty_tuple.type = call %Destroy.Op.bound.loc18_37(%n.var.loc18_37) // CHECK:STDOUT: // CHECK:STDOUT: // CHECK:STDOUT: !if.done: -// CHECK:STDOUT: %Destroy.Op.bound.loc18_37: = bound_method %n.var.loc18_37, constants.%Destroy.Op -// CHECK:STDOUT: %Destroy.Op.call.loc18_37: init %empty_tuple.type = call %Destroy.Op.bound.loc18_37(%n.var.loc18_37) -// CHECK:STDOUT: %Destroy.Op.bound.loc18_5: = bound_method %n.var.loc18_5, constants.%Destroy.Op -// CHECK:STDOUT: %Destroy.Op.call.loc18_5: init %empty_tuple.type = call %Destroy.Op.bound.loc18_5(%n.var.loc18_5) // CHECK:STDOUT: // CHECK:STDOUT: } // CHECK:STDOUT: diff --git a/toolchain/check/testdata/class/destroy_calls.carbon b/toolchain/check/testdata/class/destroy_calls.carbon index b101dd549698..76494ab22078 100644 --- a/toolchain/check/testdata/class/destroy_calls.carbon +++ b/toolchain/check/testdata/class/destroy_calls.carbon @@ -260,11 +260,11 @@ fn G() { F({}); } // CHECK:STDOUT: %c.patt: %pattern_type.98b = ref_binding_pattern c [concrete = constants.%c.patt] // CHECK:STDOUT: %c.var_patt: %pattern_type.98b = var_pattern %c.patt [concrete = constants.%c.var_patt] // CHECK:STDOUT: } +// CHECK:STDOUT: %Destroy.Op.bound.loc13: = bound_method %c.var, constants.%Destroy.Op.1a2547.2 +// CHECK:STDOUT: %Destroy.Op.call.loc13: init %empty_tuple.type = call %Destroy.Op.bound.loc13(%c.var) // CHECK:STDOUT: br !if.else // CHECK:STDOUT: // CHECK:STDOUT: !if.else: -// CHECK:STDOUT: %Destroy.Op.bound.loc13: = bound_method %c.var, constants.%Destroy.Op.1a2547.2 -// CHECK:STDOUT: %Destroy.Op.call.loc13: init %empty_tuple.type = call %Destroy.Op.bound.loc13(%c.var) // CHECK:STDOUT: %Destroy.Op.bound.loc11: = bound_method %b.var, constants.%Destroy.Op.1a2547.3 // CHECK:STDOUT: %Destroy.Op.call.loc11: init %empty_tuple.type = call %Destroy.Op.bound.loc11(%b.var) // CHECK:STDOUT: %Destroy.Op.bound.loc10: = bound_method %a.var, constants.%Destroy.Op.1a2547.4 diff --git a/toolchain/check/testdata/for/basic.carbon b/toolchain/check/testdata/for/basic.carbon index 204378741639..df95db88a10f 100644 --- a/toolchain/check/testdata/for/basic.carbon +++ b/toolchain/check/testdata/for/basic.carbon @@ -180,18 +180,20 @@ fn Run() { // CHECK:STDOUT: %_: %empty_tuple.type = wrapper_binding _, %.loc18_35.11 // CHECK:STDOUT: %Body.ref: %Body.type = name_ref Body, file.%Body.decl [concrete = constants.%Body] // CHECK:STDOUT: %Body.call: init %empty_tuple.type = call %Body.ref() -// CHECK:STDOUT: br !for.next -// CHECK:STDOUT: -// CHECK:STDOUT: !for.done: -// CHECK:STDOUT: %AfterLoop.ref: %AfterLoop.type = name_ref AfterLoop, file.%AfterLoop.decl [concrete = constants.%AfterLoop] -// CHECK:STDOUT: %AfterLoop.call: init %empty_tuple.type = call %AfterLoop.ref() // CHECK:STDOUT: %Destroy.Op.bound.loc18_35.1: = bound_method %.loc18_35.10, constants.%Destroy.Op.1a2547.1 // CHECK:STDOUT: %Destroy.Op.call.loc18_35.1: init %empty_tuple.type = call %Destroy.Op.bound.loc18_35.1(%.loc18_35.10) // CHECK:STDOUT: %Destroy.Op.bound.loc18_35.2: = bound_method %.loc18_35.2, constants.%Destroy.Op.1a2547.4 // CHECK:STDOUT: %Destroy.Op.call.loc18_35.2: init %empty_tuple.type = call %Destroy.Op.bound.loc18_35.2(%.loc18_35.2) -// CHECK:STDOUT: %Destroy.Op.bound.loc18_35.3: = bound_method %var, constants.%Destroy.Op.1a2547.1 -// CHECK:STDOUT: %Destroy.Op.call.loc18_35.3: init %empty_tuple.type = call %Destroy.Op.bound.loc18_35.3(%var) +// CHECK:STDOUT: br !for.next +// CHECK:STDOUT: +// CHECK:STDOUT: !for.done: +// CHECK:STDOUT: %Destroy.Op.bound.loc18_35.3: = bound_method %.loc18_35.2, constants.%Destroy.Op.1a2547.4 +// CHECK:STDOUT: %Destroy.Op.call.loc18_35.3: init %empty_tuple.type = call %Destroy.Op.bound.loc18_35.3(%.loc18_35.2) +// CHECK:STDOUT: %Destroy.Op.bound.loc18_35.4: = bound_method %var, constants.%Destroy.Op.1a2547.1 +// CHECK:STDOUT: %Destroy.Op.call.loc18_35.4: init %empty_tuple.type = call %Destroy.Op.bound.loc18_35.4(%var) // CHECK:STDOUT: %Destroy.Op.call.loc18_20: init %empty_tuple.type = call constants.%Destroy.Op.bound(constants.%.50e) +// CHECK:STDOUT: %AfterLoop.ref: %AfterLoop.type = name_ref AfterLoop, file.%AfterLoop.decl [concrete = constants.%AfterLoop] +// CHECK:STDOUT: %AfterLoop.call: init %empty_tuple.type = call %AfterLoop.ref() // CHECK:STDOUT: // CHECK:STDOUT: } // CHECK:STDOUT: diff --git a/toolchain/check/testdata/for/pattern.carbon b/toolchain/check/testdata/for/pattern.carbon index 23bc7907b24e..4d217f82222d 100644 --- a/toolchain/check/testdata/for/pattern.carbon +++ b/toolchain/check/testdata/for/pattern.carbon @@ -260,15 +260,17 @@ fn Run() { // CHECK:STDOUT: %Body.ref: %Body.type = name_ref Body, file.%Body.decl [concrete = constants.%Body] // CHECK:STDOUT: %c.ref: %C = name_ref c, %c // CHECK:STDOUT: %Body.call: init %empty_tuple.type = call %Body.ref(%c.ref) -// CHECK:STDOUT: br !for.next -// CHECK:STDOUT: -// CHECK:STDOUT: !for.done: // CHECK:STDOUT: %Destroy.Op.bound.loc10_36.1: = bound_method %.loc10_36.10, constants.%Destroy.Op.1a2547.2 // CHECK:STDOUT: %Destroy.Op.call.loc10_36.1: init %empty_tuple.type = call %Destroy.Op.bound.loc10_36.1(%.loc10_36.10) // CHECK:STDOUT: %Destroy.Op.bound.loc10_36.2: = bound_method %.loc10_36.2, constants.%Destroy.Op.1a2547.5 // CHECK:STDOUT: %Destroy.Op.call.loc10_36.2: init %empty_tuple.type = call %Destroy.Op.bound.loc10_36.2(%.loc10_36.2) -// CHECK:STDOUT: %Destroy.Op.bound.loc10_36.3: = bound_method %var, constants.%Destroy.Op.1a2547.1 -// CHECK:STDOUT: %Destroy.Op.call.loc10_36.3: init %empty_tuple.type = call %Destroy.Op.bound.loc10_36.3(%var) +// CHECK:STDOUT: br !for.next +// CHECK:STDOUT: +// CHECK:STDOUT: !for.done: +// CHECK:STDOUT: %Destroy.Op.bound.loc10_36.3: = bound_method %.loc10_36.2, constants.%Destroy.Op.1a2547.5 +// CHECK:STDOUT: %Destroy.Op.call.loc10_36.3: init %empty_tuple.type = call %Destroy.Op.bound.loc10_36.3(%.loc10_36.2) +// CHECK:STDOUT: %Destroy.Op.bound.loc10_36.4: = bound_method %var, constants.%Destroy.Op.1a2547.1 +// CHECK:STDOUT: %Destroy.Op.call.loc10_36.4: init %empty_tuple.type = call %Destroy.Op.bound.loc10_36.4(%var) // CHECK:STDOUT: %Destroy.Op.bound.loc10_35: = bound_method %.loc10_35.2, constants.%Destroy.Op.1a2547.6 // CHECK:STDOUT: %Destroy.Op.call.loc10_35: init %empty_tuple.type = call %Destroy.Op.bound.loc10_35(%.loc10_35.2) // CHECK:STDOUT: @@ -444,17 +446,21 @@ fn Run() { // CHECK:STDOUT: %c.ref: ref %C = name_ref c, %c // CHECK:STDOUT: %addr.loc11: %ptr.6b6 = addr_of %c.ref // CHECK:STDOUT: %Body.call: init %empty_tuple.type = call %Body.ref(%addr.loc11) +// CHECK:STDOUT: %Destroy.Op.bound.loc10_40.1: = bound_method %.loc10_40.2, constants.%Destroy.Op.1a2547.5 +// CHECK:STDOUT: %Destroy.Op.call.loc10_40.1: init %empty_tuple.type = call %Destroy.Op.bound.loc10_40.1(%.loc10_40.2) +// CHECK:STDOUT: %Destroy.Op.bound.loc10_8.1: = bound_method %c.var, constants.%Destroy.Op.1a2547.3 +// CHECK:STDOUT: %Destroy.Op.call.loc10_8.1: init %empty_tuple.type = call %Destroy.Op.bound.loc10_8.1(%c.var) // CHECK:STDOUT: br !for.next // CHECK:STDOUT: // CHECK:STDOUT: !for.done: -// CHECK:STDOUT: %Destroy.Op.bound.loc10_40.1: = bound_method %.loc10_40.2, constants.%Destroy.Op.1a2547.5 -// CHECK:STDOUT: %Destroy.Op.call.loc10_40.1: init %empty_tuple.type = call %Destroy.Op.bound.loc10_40.1(%.loc10_40.2) -// CHECK:STDOUT: %Destroy.Op.bound.loc10_40.2: = bound_method %var, constants.%Destroy.Op.1a2547.2 -// CHECK:STDOUT: %Destroy.Op.call.loc10_40.2: init %empty_tuple.type = call %Destroy.Op.bound.loc10_40.2(%var) +// CHECK:STDOUT: %Destroy.Op.bound.loc10_40.2: = bound_method %.loc10_40.2, constants.%Destroy.Op.1a2547.5 +// CHECK:STDOUT: %Destroy.Op.call.loc10_40.2: init %empty_tuple.type = call %Destroy.Op.bound.loc10_40.2(%.loc10_40.2) +// CHECK:STDOUT: %Destroy.Op.bound.loc10_8.2: = bound_method %c.var, constants.%Destroy.Op.1a2547.3 +// CHECK:STDOUT: %Destroy.Op.call.loc10_8.2: init %empty_tuple.type = call %Destroy.Op.bound.loc10_8.2(%c.var) +// CHECK:STDOUT: %Destroy.Op.bound.loc10_40.3: = bound_method %var, constants.%Destroy.Op.1a2547.2 +// CHECK:STDOUT: %Destroy.Op.call.loc10_40.3: init %empty_tuple.type = call %Destroy.Op.bound.loc10_40.3(%var) // CHECK:STDOUT: %Destroy.Op.bound.loc10_39: = bound_method %.loc10_39.2, constants.%Destroy.Op.1a2547.6 // CHECK:STDOUT: %Destroy.Op.call.loc10_39: init %empty_tuple.type = call %Destroy.Op.bound.loc10_39(%.loc10_39.2) -// CHECK:STDOUT: %Destroy.Op.bound.loc10_8: = bound_method %c.var, constants.%Destroy.Op.1a2547.3 -// CHECK:STDOUT: %Destroy.Op.call.loc10_8: init %empty_tuple.type = call %Destroy.Op.bound.loc10_8(%c.var) // CHECK:STDOUT: // CHECK:STDOUT: } // CHECK:STDOUT: @@ -645,15 +651,17 @@ fn Run() { // CHECK:STDOUT: %a.ref: bool = name_ref a, %a // CHECK:STDOUT: %b.ref: bool = name_ref b, %b // CHECK:STDOUT: %Body.call: init %empty_tuple.type = call %Body.ref(%a.ref, %b.ref) -// CHECK:STDOUT: br !for.next -// CHECK:STDOUT: -// CHECK:STDOUT: !for.done: // CHECK:STDOUT: %Destroy.Op.bound.loc10_61.1: = bound_method %.loc10_61.10, constants.%Destroy.Op.1a2547.2 // CHECK:STDOUT: %Destroy.Op.call.loc10_61.1: init %empty_tuple.type = call %Destroy.Op.bound.loc10_61.1(%.loc10_61.10) // CHECK:STDOUT: %Destroy.Op.bound.loc10_61.2: = bound_method %.loc10_61.2, constants.%Destroy.Op.1a2547.4 // CHECK:STDOUT: %Destroy.Op.call.loc10_61.2: init %empty_tuple.type = call %Destroy.Op.bound.loc10_61.2(%.loc10_61.2) -// CHECK:STDOUT: %Destroy.Op.bound.loc10_61.3: = bound_method %var, constants.%Destroy.Op.1a2547.5 -// CHECK:STDOUT: %Destroy.Op.call.loc10_61.3: init %empty_tuple.type = call %Destroy.Op.bound.loc10_61.3(%var) +// CHECK:STDOUT: br !for.next +// CHECK:STDOUT: +// CHECK:STDOUT: !for.done: +// CHECK:STDOUT: %Destroy.Op.bound.loc10_61.3: = bound_method %.loc10_61.2, constants.%Destroy.Op.1a2547.4 +// CHECK:STDOUT: %Destroy.Op.call.loc10_61.3: init %empty_tuple.type = call %Destroy.Op.bound.loc10_61.3(%.loc10_61.2) +// CHECK:STDOUT: %Destroy.Op.bound.loc10_61.4: = bound_method %var, constants.%Destroy.Op.1a2547.5 +// CHECK:STDOUT: %Destroy.Op.call.loc10_61.4: init %empty_tuple.type = call %Destroy.Op.bound.loc10_61.4(%var) // CHECK:STDOUT: %Destroy.Op.bound.loc10_60: = bound_method %.loc10_60.2, constants.%Destroy.Op.1a2547.6 // CHECK:STDOUT: %Destroy.Op.call.loc10_60: init %empty_tuple.type = call %Destroy.Op.bound.loc10_60(%.loc10_60.2) // CHECK:STDOUT: @@ -848,15 +856,17 @@ fn Run() { // CHECK:STDOUT: %a.ref: %C = name_ref a, %a // CHECK:STDOUT: %b.ref: %C = name_ref b, %b // CHECK:STDOUT: %Body.call: init %empty_tuple.type = call %Body.ref(%a.ref, %b.ref) -// CHECK:STDOUT: br !for.next -// CHECK:STDOUT: -// CHECK:STDOUT: !for.done: // CHECK:STDOUT: %Destroy.Op.bound.loc10_49.1: = bound_method %.loc10_49.10, constants.%Destroy.Op.1a2547.3 // CHECK:STDOUT: %Destroy.Op.call.loc10_49.1: init %empty_tuple.type = call %Destroy.Op.bound.loc10_49.1(%.loc10_49.10) // CHECK:STDOUT: %Destroy.Op.bound.loc10_49.2: = bound_method %.loc10_49.2, constants.%Destroy.Op.1a2547.6 // CHECK:STDOUT: %Destroy.Op.call.loc10_49.2: init %empty_tuple.type = call %Destroy.Op.bound.loc10_49.2(%.loc10_49.2) -// CHECK:STDOUT: %Destroy.Op.bound.loc10_49.3: = bound_method %var, constants.%Destroy.Op.1a2547.1 -// CHECK:STDOUT: %Destroy.Op.call.loc10_49.3: init %empty_tuple.type = call %Destroy.Op.bound.loc10_49.3(%var) +// CHECK:STDOUT: br !for.next +// CHECK:STDOUT: +// CHECK:STDOUT: !for.done: +// CHECK:STDOUT: %Destroy.Op.bound.loc10_49.3: = bound_method %.loc10_49.2, constants.%Destroy.Op.1a2547.6 +// CHECK:STDOUT: %Destroy.Op.call.loc10_49.3: init %empty_tuple.type = call %Destroy.Op.bound.loc10_49.3(%.loc10_49.2) +// CHECK:STDOUT: %Destroy.Op.bound.loc10_49.4: = bound_method %var, constants.%Destroy.Op.1a2547.1 +// CHECK:STDOUT: %Destroy.Op.call.loc10_49.4: init %empty_tuple.type = call %Destroy.Op.bound.loc10_49.4(%var) // CHECK:STDOUT: %Destroy.Op.bound.loc10_48: = bound_method %.loc10_48.2, constants.%Destroy.Op.1a2547.7 // CHECK:STDOUT: %Destroy.Op.call.loc10_48: init %empty_tuple.type = call %Destroy.Op.bound.loc10_48(%.loc10_48.2) // CHECK:STDOUT: diff --git a/toolchain/check/testdata/interop/cpp/range_for.carbon b/toolchain/check/testdata/interop/cpp/range_for.carbon index c2a7174a57b2..8039e519ebff 100644 --- a/toolchain/check/testdata/interop/cpp/range_for.carbon +++ b/toolchain/check/testdata/interop/cpp/range_for.carbon @@ -937,6 +937,8 @@ fn TestDriver(var no_begin_end: Cpp.NoBeginEnd, // CHECK:STDOUT: %ptr.45c: type = ptr_type %tuple.type.508 [concrete] // CHECK:STDOUT: %.037: type = fn_type_with_self_type %Iterate.WithSelf.Next.type.4ed, %Iterate.facet.3e3 [concrete] // CHECK:STDOUT: %R.as_type.as.Iterate.impl.Next.specific_fn.50d: = specific_function %R.as_type.as.Iterate.impl.Next.693, @R.as_type.as.Iterate.impl.Next(%facet_value.cd6) [concrete] +// CHECK:STDOUT: %Destroy.WithSelf.Op.type.c6c: type = fn_type @Destroy.WithSelf.Op, @Destroy.WithSelf(%Destroy.facet.30f) [concrete] +// CHECK:STDOUT: %.0a4: type = fn_type_with_self_type %Destroy.WithSelf.Op.type.c6c, %Destroy.facet.30f [concrete] // CHECK:STDOUT: %Iterate.WithSelf.NewCursor.type.005: type = fn_type @Iterate.WithSelf.NewCursor, @Iterate.WithSelf(%Iterate.facet.da6) [concrete] // CHECK:STDOUT: %Iterate.WithSelf.Next.type.212: type = fn_type @Iterate.WithSelf.Next, @Iterate.WithSelf(%Iterate.facet.da6) [concrete] // CHECK:STDOUT: %.bb7: type = fn_type_with_self_type %Iterate.WithSelf.NewCursor.type.005, %Iterate.facet.da6 [concrete] @@ -946,8 +948,6 @@ fn TestDriver(var no_begin_end: Cpp.NoBeginEnd, // CHECK:STDOUT: %R.as_type.as.Iterate.impl.Next.specific_fn.9ce: = specific_function %R.as_type.as.Iterate.impl.Next.119, @R.as_type.as.Iterate.impl.Next(%facet_value.e88) [concrete] // CHECK:STDOUT: %Destroy.WithSelf.Op.type.7ec: type = fn_type @Destroy.WithSelf.Op, @Destroy.WithSelf(%Destroy.facet.e4c) [concrete] // CHECK:STDOUT: %.0e7: type = fn_type_with_self_type %Destroy.WithSelf.Op.type.7ec, %Destroy.facet.e4c [concrete] -// CHECK:STDOUT: %Destroy.WithSelf.Op.type.c6c: type = fn_type @Destroy.WithSelf.Op, @Destroy.WithSelf(%Destroy.facet.30f) [concrete] -// CHECK:STDOUT: %.0a4: type = fn_type_with_self_type %Destroy.WithSelf.Op.type.c6c, %Destroy.facet.30f [concrete] // CHECK:STDOUT: %complete_type.c28: = complete_type_witness %tuple.type.508 [concrete] // CHECK:STDOUT: %complete_type.8d5: = complete_type_witness %tuple.type.69f [concrete] // CHECK:STDOUT: } @@ -1072,11 +1072,13 @@ fn TestDriver(var no_begin_end: Cpp.NoBeginEnd, // CHECK:STDOUT: %Int.as.AddAssignWith.impl.Op.specific_fn: = specific_function %Op.ref, @Int.as.AddAssignWith.impl.Op.2(constants.%int_32, constants.%ImplicitAs.facet.640) [concrete = constants.%Int.as.AddAssignWith.impl.Op.specific_fn.80c75c.2] // CHECK:STDOUT: %bound_method.loc35_9.3: = bound_method %sum.ref, %Int.as.AddAssignWith.impl.Op.specific_fn // CHECK:STDOUT: %Int.as.AddAssignWith.impl.Op.call: init %empty_tuple.type = call %bound_method.loc35_9.3(%sum.ref, %i.ref) +// CHECK:STDOUT: %Destroy.Op.bound.loc34_19.1: = bound_method %.loc34_19.8, constants.%Destroy.Op.1a2547.9 +// CHECK:STDOUT: %Destroy.Op.call.loc34_19.1: init %empty_tuple.type = call %Destroy.Op.bound.loc34_19.1(%.loc34_19.8) // CHECK:STDOUT: br !for.next // CHECK:STDOUT: // CHECK:STDOUT: !for.done: -// CHECK:STDOUT: %Destroy.Op.bound.loc34: = bound_method %.loc34_19.8, constants.%Destroy.Op.1a2547.9 -// CHECK:STDOUT: %Destroy.Op.call.loc34: init %empty_tuple.type = call %Destroy.Op.bound.loc34(%.loc34_19.8) +// CHECK:STDOUT: %Destroy.Op.bound.loc34_19.2: = bound_method %.loc34_19.8, constants.%Destroy.Op.1a2547.9 +// CHECK:STDOUT: %Destroy.Op.call.loc34_19.2: init %empty_tuple.type = call %Destroy.Op.bound.loc34_19.2(%.loc34_19.8) // CHECK:STDOUT: %impl.elem0.loc34_19.1: @TestIterate.%.loc34_19.21 (%.fa0) = impl_witness_access constants.%Destroy.lookup_impl_witness.d26, element0 [symbolic = %impl.elem0.loc34_19.2 (constants.%impl.elem0.544)] // CHECK:STDOUT: %bound_method.loc34_19.7: = bound_method %var, %impl.elem0.loc34_19.1 // CHECK:STDOUT: %.loc34_19.17: %Destroy.type = converted constants.%as_type.c8a, constants.%impl.elem1.295 [symbolic = %impl.elem1 (constants.%impl.elem1.295)] @@ -1185,9 +1187,15 @@ fn TestDriver(var no_begin_end: Cpp.NoBeginEnd, // CHECK:STDOUT: %Int.as.AddAssignWith.impl.Op.specific_fn.loc50: = specific_function %Op.ref.loc50, @Int.as.AddAssignWith.impl.Op.2(constants.%int_32, constants.%ImplicitAs.facet.640) [concrete = constants.%Int.as.AddAssignWith.impl.Op.specific_fn.80c75c.2] // CHECK:STDOUT: %bound_method.loc50_9.3: = bound_method %sum.ref.loc50, %Int.as.AddAssignWith.impl.Op.specific_fn.loc50 // CHECK:STDOUT: %Int.as.AddAssignWith.impl.Op.call.loc50: init %empty_tuple.type = call %bound_method.loc50_9.3(%sum.ref.loc50, %i.ref.loc50) +// CHECK:STDOUT: %Destroy.Op.bound.loc49_19.1: = bound_method %.loc49_19.6, constants.%Destroy.Op.1a2547.9 +// CHECK:STDOUT: %Destroy.Op.call.loc49_19.1: init %empty_tuple.type = call %Destroy.Op.bound.loc49_19.1(%.loc49_19.6) // CHECK:STDOUT: br !for.next.loc49 // CHECK:STDOUT: // CHECK:STDOUT: !for.done.loc49: +// CHECK:STDOUT: %Destroy.Op.bound.loc49_19.2: = bound_method %.loc49_19.6, constants.%Destroy.Op.1a2547.9 +// CHECK:STDOUT: %Destroy.Op.call.loc49_19.2: init %empty_tuple.type = call %Destroy.Op.bound.loc49_19.2(%.loc49_19.6) +// CHECK:STDOUT: %Destroy.Op.bound.loc49_19.3: = bound_method %var.loc49, constants.%Destroy.Op.1a2547.10 +// CHECK:STDOUT: %Destroy.Op.call.loc49_19.3: init %empty_tuple.type = call %Destroy.Op.bound.loc49_19.3(%var.loc49) // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %i.patt.loc55: %pattern_type.6b6 = value_binding_pattern i [concrete = constants.%i.patt] // CHECK:STDOUT: } @@ -1253,17 +1261,15 @@ fn TestDriver(var no_begin_end: Cpp.NoBeginEnd, // CHECK:STDOUT: %Int.as.AddAssignWith.impl.Op.specific_fn.loc56: = specific_function %Op.ref.loc56, @Int.as.AddAssignWith.impl.Op.2(constants.%int_32, constants.%ImplicitAs.facet.640) [concrete = constants.%Int.as.AddAssignWith.impl.Op.specific_fn.80c75c.2] // CHECK:STDOUT: %bound_method.loc56_9.3: = bound_method %sum.ref.loc56, %Int.as.AddAssignWith.impl.Op.specific_fn.loc56 // CHECK:STDOUT: %Int.as.AddAssignWith.impl.Op.call.loc56: init %empty_tuple.type = call %bound_method.loc56_9.3(%sum.ref.loc56, %i.ref.loc56) +// CHECK:STDOUT: %Destroy.Op.bound.loc55_19.1: = bound_method %.loc55_19.6, constants.%Destroy.Op.1a2547.9 +// CHECK:STDOUT: %Destroy.Op.call.loc55_19.1: init %empty_tuple.type = call %Destroy.Op.bound.loc55_19.1(%.loc55_19.6) // CHECK:STDOUT: br !for.next.loc55 // CHECK:STDOUT: // CHECK:STDOUT: !for.done.loc55: -// CHECK:STDOUT: %Destroy.Op.bound.loc55_19.1: = bound_method %.loc55_19.6, constants.%Destroy.Op.1a2547.9 -// CHECK:STDOUT: %Destroy.Op.call.loc55_19.1: init %empty_tuple.type = call %Destroy.Op.bound.loc55_19.1(%.loc55_19.6) -// CHECK:STDOUT: %Destroy.Op.bound.loc55_19.2: = bound_method %var.loc55, constants.%Destroy.Op.1a2547.11 -// CHECK:STDOUT: %Destroy.Op.call.loc55_19.2: init %empty_tuple.type = call %Destroy.Op.bound.loc55_19.2(%var.loc55) -// CHECK:STDOUT: %Destroy.Op.bound.loc49_19.1: = bound_method %.loc49_19.6, constants.%Destroy.Op.1a2547.9 -// CHECK:STDOUT: %Destroy.Op.call.loc49_19.1: init %empty_tuple.type = call %Destroy.Op.bound.loc49_19.1(%.loc49_19.6) -// CHECK:STDOUT: %Destroy.Op.bound.loc49_19.2: = bound_method %var.loc49, constants.%Destroy.Op.1a2547.10 -// CHECK:STDOUT: %Destroy.Op.call.loc49_19.2: init %empty_tuple.type = call %Destroy.Op.bound.loc49_19.2(%var.loc49) +// CHECK:STDOUT: %Destroy.Op.bound.loc55_19.2: = bound_method %.loc55_19.6, constants.%Destroy.Op.1a2547.9 +// CHECK:STDOUT: %Destroy.Op.call.loc55_19.2: init %empty_tuple.type = call %Destroy.Op.bound.loc55_19.2(%.loc55_19.6) +// CHECK:STDOUT: %Destroy.Op.bound.loc55_19.3: = bound_method %var.loc55, constants.%Destroy.Op.1a2547.11 +// CHECK:STDOUT: %Destroy.Op.call.loc55_19.3: init %empty_tuple.type = call %Destroy.Op.bound.loc55_19.3(%var.loc55) // CHECK:STDOUT: // CHECK:STDOUT: } // CHECK:STDOUT: @@ -1550,6 +1556,8 @@ fn TestDriver(var no_begin_end: Cpp.NoBeginEnd, // CHECK:STDOUT: %ptr.d6b: type = ptr_type %tuple.type.829 [concrete] // CHECK:STDOUT: %.121: type = fn_type_with_self_type %Iterate.WithSelf.Next.type.810, %Iterate.facet.ac1 [concrete] // CHECK:STDOUT: %R.as_type.as.Iterate.impl.Next.specific_fn.c86: = specific_function %R.as_type.as.Iterate.impl.Next.de6, @R.as_type.as.Iterate.impl.Next(%facet_value.55a) [concrete] +// CHECK:STDOUT: %Destroy.WithSelf.Op.type.c89: type = fn_type @Destroy.WithSelf.Op, @Destroy.WithSelf(%Destroy.facet.305) [concrete] +// CHECK:STDOUT: %.e8a: type = fn_type_with_self_type %Destroy.WithSelf.Op.type.c89, %Destroy.facet.305 [concrete] // CHECK:STDOUT: %Iterate.WithSelf.NewCursor.type.3b7: type = fn_type @Iterate.WithSelf.NewCursor, @Iterate.WithSelf(%Iterate.facet.02a) [concrete] // CHECK:STDOUT: %Iterate.WithSelf.Next.type.c48: type = fn_type @Iterate.WithSelf.Next, @Iterate.WithSelf(%Iterate.facet.02a) [concrete] // CHECK:STDOUT: %.71c: type = fn_type_with_self_type %Iterate.WithSelf.NewCursor.type.3b7, %Iterate.facet.02a [concrete] @@ -1559,8 +1567,6 @@ fn TestDriver(var no_begin_end: Cpp.NoBeginEnd, // CHECK:STDOUT: %R.as_type.as.Iterate.impl.Next.specific_fn.e3d: = specific_function %R.as_type.as.Iterate.impl.Next.31e, @R.as_type.as.Iterate.impl.Next(%facet_value.445) [concrete] // CHECK:STDOUT: %Destroy.WithSelf.Op.type.e30: type = fn_type @Destroy.WithSelf.Op, @Destroy.WithSelf(%Destroy.facet.e08) [concrete] // CHECK:STDOUT: %.987: type = fn_type_with_self_type %Destroy.WithSelf.Op.type.e30, %Destroy.facet.e08 [concrete] -// CHECK:STDOUT: %Destroy.WithSelf.Op.type.c89: type = fn_type @Destroy.WithSelf.Op, @Destroy.WithSelf(%Destroy.facet.305) [concrete] -// CHECK:STDOUT: %.e8a: type = fn_type_with_self_type %Destroy.WithSelf.Op.type.c89, %Destroy.facet.305 [concrete] // CHECK:STDOUT: %complete_type.94f: = complete_type_witness %tuple.type.829 [concrete] // CHECK:STDOUT: %complete_type.dea: = complete_type_witness %tuple.type.99f [concrete] // CHECK:STDOUT: } @@ -1695,15 +1701,17 @@ fn TestDriver(var no_begin_end: Cpp.NoBeginEnd, // CHECK:STDOUT: %.loc45_9.1: %ptr.6bf = as_compatible %addr.loc45 // CHECK:STDOUT: %.loc45_9.2: %ptr.6bf = converted %addr.loc45, %.loc45_9.1 // CHECK:STDOUT: %operator_PlusEqual__carbon_thunk.call: ref %ValueType = call imports.%operator_PlusEqual__carbon_thunk.decl(%sum.ref, %.loc45_9.2) -// CHECK:STDOUT: br !for.next -// CHECK:STDOUT: -// CHECK:STDOUT: !for.done: // CHECK:STDOUT: %ValueType.Op.bound.loc44: = bound_method %.loc44_29.16, constants.%ValueType.Op.4dca31.1 // CHECK:STDOUT: %Op.ref.loc44: %ValueType.cpp_destructor.type = name_ref Op, imports.%ValueType.cpp_destructor.decl [concrete = constants.%ValueType.cpp_destructor] // CHECK:STDOUT: %ValueType.cpp_destructor.bound.loc44: = bound_method %.loc44_29.16, %Op.ref.loc44 // CHECK:STDOUT: %ValueType.cpp_destructor.call.loc44: init %empty_tuple.type = call %ValueType.cpp_destructor.bound.loc44(%.loc44_29.16) -// CHECK:STDOUT: %Destroy.Op.bound: = bound_method %.loc44_29.8, constants.%Destroy.Op.1a2547.7 -// CHECK:STDOUT: %Destroy.Op.call: init %empty_tuple.type = call %Destroy.Op.bound(%.loc44_29.8) +// CHECK:STDOUT: %Destroy.Op.bound.loc44_29.1: = bound_method %.loc44_29.8, constants.%Destroy.Op.1a2547.7 +// CHECK:STDOUT: %Destroy.Op.call.loc44_29.1: init %empty_tuple.type = call %Destroy.Op.bound.loc44_29.1(%.loc44_29.8) +// CHECK:STDOUT: br !for.next +// CHECK:STDOUT: +// CHECK:STDOUT: !for.done: +// CHECK:STDOUT: %Destroy.Op.bound.loc44_29.2: = bound_method %.loc44_29.8, constants.%Destroy.Op.1a2547.7 +// CHECK:STDOUT: %Destroy.Op.call.loc44_29.2: init %empty_tuple.type = call %Destroy.Op.bound.loc44_29.2(%.loc44_29.8) // CHECK:STDOUT: %impl.elem0.loc44_29.1: @TestIterate.%.loc44_29.22 (%.98a) = impl_witness_access constants.%Destroy.lookup_impl_witness.b29, element0 [symbolic = %impl.elem0.loc44_29.2 (constants.%impl.elem0.81b)] // CHECK:STDOUT: %bound_method.loc44_29.7: = bound_method %var, %impl.elem0.loc44_29.1 // CHECK:STDOUT: %.loc44_29.18: %Destroy.type = converted constants.%as_type.dbb, constants.%impl.elem1.3cf [symbolic = %impl.elem1 (constants.%impl.elem1.3cf)] @@ -1811,9 +1819,19 @@ fn TestDriver(var no_begin_end: Cpp.NoBeginEnd, // CHECK:STDOUT: %.loc60_9.1: %ptr.6bf = as_compatible %addr.loc60 // CHECK:STDOUT: %.loc60_9.2: %ptr.6bf = converted %addr.loc60, %.loc60_9.1 // CHECK:STDOUT: %operator_PlusEqual__carbon_thunk.call.loc60: ref %ValueType = call imports.%operator_PlusEqual__carbon_thunk.decl(%sum.ref.loc60, %.loc60_9.2) +// CHECK:STDOUT: %ValueType.Op.bound.loc59: = bound_method %.loc59_29.14, constants.%ValueType.Op.4dca31.1 +// CHECK:STDOUT: %Op.ref.loc59: %ValueType.cpp_destructor.type = name_ref Op, imports.%ValueType.cpp_destructor.decl [concrete = constants.%ValueType.cpp_destructor] +// CHECK:STDOUT: %ValueType.cpp_destructor.bound.loc59: = bound_method %.loc59_29.14, %Op.ref.loc59 +// CHECK:STDOUT: %ValueType.cpp_destructor.call.loc59: init %empty_tuple.type = call %ValueType.cpp_destructor.bound.loc59(%.loc59_29.14) +// CHECK:STDOUT: %Destroy.Op.bound.loc59_29.1: = bound_method %.loc59_29.6, constants.%Destroy.Op.1a2547.7 +// CHECK:STDOUT: %Destroy.Op.call.loc59_29.1: init %empty_tuple.type = call %Destroy.Op.bound.loc59_29.1(%.loc59_29.6) // CHECK:STDOUT: br !for.next.loc59 // CHECK:STDOUT: // CHECK:STDOUT: !for.done.loc59: +// CHECK:STDOUT: %Destroy.Op.bound.loc59_29.2: = bound_method %.loc59_29.6, constants.%Destroy.Op.1a2547.7 +// CHECK:STDOUT: %Destroy.Op.call.loc59_29.2: init %empty_tuple.type = call %Destroy.Op.bound.loc59_29.2(%.loc59_29.6) +// CHECK:STDOUT: %Destroy.Op.bound.loc59_29.3: = bound_method %var.loc59, constants.%Destroy.Op.1a2547.8 +// CHECK:STDOUT: %Destroy.Op.call.loc59_29.3: init %empty_tuple.type = call %Destroy.Op.bound.loc59_29.3(%var.loc59) // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %i.patt.loc63: %pattern_type.e9f = value_binding_pattern i [concrete = constants.%i.patt] // CHECK:STDOUT: } @@ -1878,25 +1896,19 @@ fn TestDriver(var no_begin_end: Cpp.NoBeginEnd, // CHECK:STDOUT: %.loc64_9.1: %ptr.6bf = as_compatible %addr.loc64 // CHECK:STDOUT: %.loc64_9.2: %ptr.6bf = converted %addr.loc64, %.loc64_9.1 // CHECK:STDOUT: %operator_PlusEqual__carbon_thunk.call.loc64: ref %ValueType = call imports.%operator_PlusEqual__carbon_thunk.decl(%sum.ref.loc64, %.loc64_9.2) -// CHECK:STDOUT: br !for.next.loc63 -// CHECK:STDOUT: -// CHECK:STDOUT: !for.done.loc63: // CHECK:STDOUT: %ValueType.Op.bound.loc63: = bound_method %.loc63_29.14, constants.%ValueType.Op.4dca31.1 // CHECK:STDOUT: %Op.ref.loc63: %ValueType.cpp_destructor.type = name_ref Op, imports.%ValueType.cpp_destructor.decl [concrete = constants.%ValueType.cpp_destructor] // CHECK:STDOUT: %ValueType.cpp_destructor.bound.loc63: = bound_method %.loc63_29.14, %Op.ref.loc63 // CHECK:STDOUT: %ValueType.cpp_destructor.call.loc63: init %empty_tuple.type = call %ValueType.cpp_destructor.bound.loc63(%.loc63_29.14) // CHECK:STDOUT: %Destroy.Op.bound.loc63_29.1: = bound_method %.loc63_29.6, constants.%Destroy.Op.1a2547.7 // CHECK:STDOUT: %Destroy.Op.call.loc63_29.1: init %empty_tuple.type = call %Destroy.Op.bound.loc63_29.1(%.loc63_29.6) -// CHECK:STDOUT: %Destroy.Op.bound.loc63_29.2: = bound_method %var.loc63, constants.%Destroy.Op.1a2547.9 -// CHECK:STDOUT: %Destroy.Op.call.loc63_29.2: init %empty_tuple.type = call %Destroy.Op.bound.loc63_29.2(%var.loc63) -// CHECK:STDOUT: %ValueType.Op.bound.loc59: = bound_method %.loc59_29.14, constants.%ValueType.Op.4dca31.1 -// CHECK:STDOUT: %Op.ref.loc59: %ValueType.cpp_destructor.type = name_ref Op, imports.%ValueType.cpp_destructor.decl [concrete = constants.%ValueType.cpp_destructor] -// CHECK:STDOUT: %ValueType.cpp_destructor.bound.loc59: = bound_method %.loc59_29.14, %Op.ref.loc59 -// CHECK:STDOUT: %ValueType.cpp_destructor.call.loc59: init %empty_tuple.type = call %ValueType.cpp_destructor.bound.loc59(%.loc59_29.14) -// CHECK:STDOUT: %Destroy.Op.bound.loc59_29.1: = bound_method %.loc59_29.6, constants.%Destroy.Op.1a2547.7 -// CHECK:STDOUT: %Destroy.Op.call.loc59_29.1: init %empty_tuple.type = call %Destroy.Op.bound.loc59_29.1(%.loc59_29.6) -// CHECK:STDOUT: %Destroy.Op.bound.loc59_29.2: = bound_method %var.loc59, constants.%Destroy.Op.1a2547.8 -// CHECK:STDOUT: %Destroy.Op.call.loc59_29.2: init %empty_tuple.type = call %Destroy.Op.bound.loc59_29.2(%var.loc59) +// CHECK:STDOUT: br !for.next.loc63 +// CHECK:STDOUT: +// CHECK:STDOUT: !for.done.loc63: +// CHECK:STDOUT: %Destroy.Op.bound.loc63_29.2: = bound_method %.loc63_29.6, constants.%Destroy.Op.1a2547.7 +// CHECK:STDOUT: %Destroy.Op.call.loc63_29.2: init %empty_tuple.type = call %Destroy.Op.bound.loc63_29.2(%.loc63_29.6) +// CHECK:STDOUT: %Destroy.Op.bound.loc63_29.3: = bound_method %var.loc63, constants.%Destroy.Op.1a2547.9 +// CHECK:STDOUT: %Destroy.Op.call.loc63_29.3: init %empty_tuple.type = call %Destroy.Op.bound.loc63_29.3(%var.loc63) // CHECK:STDOUT: // CHECK:STDOUT: } // CHECK:STDOUT: @@ -2248,11 +2260,13 @@ fn TestDriver(var no_begin_end: Cpp.NoBeginEnd, // CHECK:STDOUT: %specific_fn.loc25: = specific_function %impl.elem0.loc25, @Float.as.AddAssignWith.impl.Op(constants.%int_64) [concrete = constants.%Float.as.AddAssignWith.impl.Op.specific_fn] // CHECK:STDOUT: %bound_method.loc25_9.2: = bound_method %sum.ref, %specific_fn.loc25 // CHECK:STDOUT: %Float.as.AddAssignWith.impl.Op.call: init %empty_tuple.type = call %bound_method.loc25_9.2(%sum.ref, %i.ref) +// CHECK:STDOUT: %Destroy.Op.bound.loc24_19.1: = bound_method %.loc24_19.8, constants.%Destroy.Op.1a2547.9 +// CHECK:STDOUT: %Destroy.Op.call.loc24_19.1: init %empty_tuple.type = call %Destroy.Op.bound.loc24_19.1(%.loc24_19.8) // CHECK:STDOUT: br !for.next // CHECK:STDOUT: // CHECK:STDOUT: !for.done: -// CHECK:STDOUT: %Destroy.Op.bound.loc24: = bound_method %.loc24_19.8, constants.%Destroy.Op.1a2547.9 -// CHECK:STDOUT: %Destroy.Op.call.loc24: init %empty_tuple.type = call %Destroy.Op.bound.loc24(%.loc24_19.8) +// CHECK:STDOUT: %Destroy.Op.bound.loc24_19.2: = bound_method %.loc24_19.8, constants.%Destroy.Op.1a2547.9 +// CHECK:STDOUT: %Destroy.Op.call.loc24_19.2: init %empty_tuple.type = call %Destroy.Op.bound.loc24_19.2(%.loc24_19.8) // CHECK:STDOUT: %impl.elem0.loc24_19.1: @TestIterate.%.loc24_19.21 (%.148) = impl_witness_access constants.%Destroy.lookup_impl_witness.def, element0 [symbolic = %impl.elem0.loc24_19.2 (constants.%impl.elem0.461)] // CHECK:STDOUT: %bound_method.loc24_19.7: = bound_method %var, %impl.elem0.loc24_19.1 // CHECK:STDOUT: %.loc24_19.17: %Destroy.type = converted constants.%as_type.8e4, constants.%impl.elem1.7a9 [symbolic = %impl.elem1 (constants.%impl.elem1.7a9)] @@ -2354,13 +2368,15 @@ fn TestDriver(var no_begin_end: Cpp.NoBeginEnd, // CHECK:STDOUT: %specific_fn.loc39: = specific_function %impl.elem0.loc39, @Float.as.AddAssignWith.impl.Op(constants.%int_64) [concrete = constants.%Float.as.AddAssignWith.impl.Op.specific_fn] // CHECK:STDOUT: %bound_method.loc39_9.2: = bound_method %sum.ref, %specific_fn.loc39 // CHECK:STDOUT: %Float.as.AddAssignWith.impl.Op.call: init %empty_tuple.type = call %bound_method.loc39_9.2(%sum.ref, %i.ref) +// CHECK:STDOUT: %Destroy.Op.bound.loc38_19.1: = bound_method %.loc38_19.6, constants.%Destroy.Op.1a2547.9 +// CHECK:STDOUT: %Destroy.Op.call.loc38_19.1: init %empty_tuple.type = call %Destroy.Op.bound.loc38_19.1(%.loc38_19.6) // CHECK:STDOUT: br !for.next // CHECK:STDOUT: // CHECK:STDOUT: !for.done: -// CHECK:STDOUT: %Destroy.Op.bound.loc38_19.1: = bound_method %.loc38_19.6, constants.%Destroy.Op.1a2547.9 -// CHECK:STDOUT: %Destroy.Op.call.loc38_19.1: init %empty_tuple.type = call %Destroy.Op.bound.loc38_19.1(%.loc38_19.6) -// CHECK:STDOUT: %Destroy.Op.bound.loc38_19.2: = bound_method %var, constants.%Destroy.Op.1a2547.10 -// CHECK:STDOUT: %Destroy.Op.call.loc38_19.2: init %empty_tuple.type = call %Destroy.Op.bound.loc38_19.2(%var) +// CHECK:STDOUT: %Destroy.Op.bound.loc38_19.2: = bound_method %.loc38_19.6, constants.%Destroy.Op.1a2547.9 +// CHECK:STDOUT: %Destroy.Op.call.loc38_19.2: init %empty_tuple.type = call %Destroy.Op.bound.loc38_19.2(%.loc38_19.6) +// CHECK:STDOUT: %Destroy.Op.bound.loc38_19.3: = bound_method %var, constants.%Destroy.Op.1a2547.10 +// CHECK:STDOUT: %Destroy.Op.call.loc38_19.3: init %empty_tuple.type = call %Destroy.Op.bound.loc38_19.3(%var) // CHECK:STDOUT: // CHECK:STDOUT: } // CHECK:STDOUT: @@ -2681,11 +2697,13 @@ fn TestDriver(var no_begin_end: Cpp.NoBeginEnd, // CHECK:STDOUT: %specific_fn.loc27: = specific_function %impl.elem0.loc27, @Float.as.AddAssignWith.impl.Op(constants.%int_64) [concrete = constants.%Float.as.AddAssignWith.impl.Op.specific_fn] // CHECK:STDOUT: %bound_method.loc27_9.2: = bound_method %sum.ref, %specific_fn.loc27 // CHECK:STDOUT: %Float.as.AddAssignWith.impl.Op.call: init %empty_tuple.type = call %bound_method.loc27_9.2(%sum.ref, %i.ref) +// CHECK:STDOUT: %Destroy.Op.bound.loc26_19.1: = bound_method %.loc26_19.8, constants.%Destroy.Op.1a2547.9 +// CHECK:STDOUT: %Destroy.Op.call.loc26_19.1: init %empty_tuple.type = call %Destroy.Op.bound.loc26_19.1(%.loc26_19.8) // CHECK:STDOUT: br !for.next // CHECK:STDOUT: // CHECK:STDOUT: !for.done: -// CHECK:STDOUT: %Destroy.Op.bound.loc26: = bound_method %.loc26_19.8, constants.%Destroy.Op.1a2547.9 -// CHECK:STDOUT: %Destroy.Op.call.loc26: init %empty_tuple.type = call %Destroy.Op.bound.loc26(%.loc26_19.8) +// CHECK:STDOUT: %Destroy.Op.bound.loc26_19.2: = bound_method %.loc26_19.8, constants.%Destroy.Op.1a2547.9 +// CHECK:STDOUT: %Destroy.Op.call.loc26_19.2: init %empty_tuple.type = call %Destroy.Op.bound.loc26_19.2(%.loc26_19.8) // CHECK:STDOUT: %impl.elem0.loc26_19.1: @TestIterate.%.loc26_19.21 (%.148) = impl_witness_access constants.%Destroy.lookup_impl_witness.def, element0 [symbolic = %impl.elem0.loc26_19.2 (constants.%impl.elem0.461)] // CHECK:STDOUT: %bound_method.loc26_19.7: = bound_method %var, %impl.elem0.loc26_19.1 // CHECK:STDOUT: %.loc26_19.17: %Destroy.type = converted constants.%as_type.8e4, constants.%impl.elem1.7a9 [symbolic = %impl.elem1 (constants.%impl.elem1.7a9)] @@ -2789,13 +2807,15 @@ fn TestDriver(var no_begin_end: Cpp.NoBeginEnd, // CHECK:STDOUT: %specific_fn.loc69: = specific_function %impl.elem0.loc69, @Float.as.AddAssignWith.impl.Op(constants.%int_64) [concrete = constants.%Float.as.AddAssignWith.impl.Op.specific_fn] // CHECK:STDOUT: %bound_method.loc69_9.2: = bound_method %sum.ref, %specific_fn.loc69 // CHECK:STDOUT: %Float.as.AddAssignWith.impl.Op.call: init %empty_tuple.type = call %bound_method.loc69_9.2(%sum.ref, %i.ref) +// CHECK:STDOUT: %Destroy.Op.bound.loc68_19.1: = bound_method %.loc68_19.6, constants.%Destroy.Op.1a2547.9 +// CHECK:STDOUT: %Destroy.Op.call.loc68_19.1: init %empty_tuple.type = call %Destroy.Op.bound.loc68_19.1(%.loc68_19.6) // CHECK:STDOUT: br !for.next // CHECK:STDOUT: // CHECK:STDOUT: !for.done: -// CHECK:STDOUT: %Destroy.Op.bound.loc68_19.1: = bound_method %.loc68_19.6, constants.%Destroy.Op.1a2547.9 -// CHECK:STDOUT: %Destroy.Op.call.loc68_19.1: init %empty_tuple.type = call %Destroy.Op.bound.loc68_19.1(%.loc68_19.6) -// CHECK:STDOUT: %Destroy.Op.bound.loc68_19.2: = bound_method %var, constants.%Destroy.Op.1a2547.10 -// CHECK:STDOUT: %Destroy.Op.call.loc68_19.2: init %empty_tuple.type = call %Destroy.Op.bound.loc68_19.2(%var) +// CHECK:STDOUT: %Destroy.Op.bound.loc68_19.2: = bound_method %.loc68_19.6, constants.%Destroy.Op.1a2547.9 +// CHECK:STDOUT: %Destroy.Op.call.loc68_19.2: init %empty_tuple.type = call %Destroy.Op.bound.loc68_19.2(%.loc68_19.6) +// CHECK:STDOUT: %Destroy.Op.bound.loc68_19.3: = bound_method %var, constants.%Destroy.Op.1a2547.10 +// CHECK:STDOUT: %Destroy.Op.call.loc68_19.3: init %empty_tuple.type = call %Destroy.Op.bound.loc68_19.3(%var) // CHECK:STDOUT: // CHECK:STDOUT: } // CHECK:STDOUT: @@ -3140,15 +3160,17 @@ fn TestDriver(var no_begin_end: Cpp.NoBeginEnd, // CHECK:STDOUT: %.loc32_9.1: %ptr.32e = as_compatible %addr.loc32 // CHECK:STDOUT: %.loc32_9.2: %ptr.32e = converted %addr.loc32, %.loc32_9.1 // CHECK:STDOUT: %operator_PlusEqual__carbon_thunk.call: ref %ValueType = call imports.%operator_PlusEqual__carbon_thunk.decl(%sum.ref, %.loc32_9.2) -// CHECK:STDOUT: br !for.next -// CHECK:STDOUT: -// CHECK:STDOUT: !for.done: // CHECK:STDOUT: %ValueType.Op.bound.loc31: = bound_method %.loc31_31.16, constants.%ValueType.Op.4c94b9.1 // CHECK:STDOUT: %Op.ref.loc31: %ValueType.cpp_destructor.type = name_ref Op, imports.%ValueType.cpp_destructor.decl [concrete = constants.%ValueType.cpp_destructor] // CHECK:STDOUT: %ValueType.cpp_destructor.bound.loc31: = bound_method %.loc31_31.16, %Op.ref.loc31 // CHECK:STDOUT: %ValueType.cpp_destructor.call.loc31: init %empty_tuple.type = call %ValueType.cpp_destructor.bound.loc31(%.loc31_31.16) -// CHECK:STDOUT: %Destroy.Op.bound: = bound_method %.loc31_31.8, constants.%Destroy.Op.1a2547.7 -// CHECK:STDOUT: %Destroy.Op.call: init %empty_tuple.type = call %Destroy.Op.bound(%.loc31_31.8) +// CHECK:STDOUT: %Destroy.Op.bound.loc31_31.1: = bound_method %.loc31_31.8, constants.%Destroy.Op.1a2547.7 +// CHECK:STDOUT: %Destroy.Op.call.loc31_31.1: init %empty_tuple.type = call %Destroy.Op.bound.loc31_31.1(%.loc31_31.8) +// CHECK:STDOUT: br !for.next +// CHECK:STDOUT: +// CHECK:STDOUT: !for.done: +// CHECK:STDOUT: %Destroy.Op.bound.loc31_31.2: = bound_method %.loc31_31.8, constants.%Destroy.Op.1a2547.7 +// CHECK:STDOUT: %Destroy.Op.call.loc31_31.2: init %empty_tuple.type = call %Destroy.Op.bound.loc31_31.2(%.loc31_31.8) // CHECK:STDOUT: %impl.elem0.loc31_31.1: @TestIterate.%.loc31_31.22 (%.57c) = impl_witness_access constants.%Destroy.lookup_impl_witness.198, element0 [symbolic = %impl.elem0.loc31_31.2 (constants.%impl.elem0.152)] // CHECK:STDOUT: %bound_method.loc31_31.7: = bound_method %var, %impl.elem0.loc31_31.1 // CHECK:STDOUT: %.loc31_31.18: %Destroy.type = converted constants.%as_type.e41, constants.%impl.elem1.834 [symbolic = %impl.elem1 (constants.%impl.elem1.834)] @@ -3255,17 +3277,19 @@ fn TestDriver(var no_begin_end: Cpp.NoBeginEnd, // CHECK:STDOUT: %.loc46_9.1: %ptr.32e = as_compatible %addr.loc46 // CHECK:STDOUT: %.loc46_9.2: %ptr.32e = converted %addr.loc46, %.loc46_9.1 // CHECK:STDOUT: %operator_PlusEqual__carbon_thunk.call: ref %ValueType = call imports.%operator_PlusEqual__carbon_thunk.decl(%sum.ref, %.loc46_9.2) -// CHECK:STDOUT: br !for.next -// CHECK:STDOUT: -// CHECK:STDOUT: !for.done: // CHECK:STDOUT: %ValueType.Op.bound.loc45: = bound_method %.loc45_31.14, constants.%ValueType.Op.4c94b9.1 // CHECK:STDOUT: %Op.ref.loc45: %ValueType.cpp_destructor.type = name_ref Op, imports.%ValueType.cpp_destructor.decl [concrete = constants.%ValueType.cpp_destructor] // CHECK:STDOUT: %ValueType.cpp_destructor.bound.loc45: = bound_method %.loc45_31.14, %Op.ref.loc45 // CHECK:STDOUT: %ValueType.cpp_destructor.call.loc45: init %empty_tuple.type = call %ValueType.cpp_destructor.bound.loc45(%.loc45_31.14) // CHECK:STDOUT: %Destroy.Op.bound.loc45_31.1: = bound_method %.loc45_31.6, constants.%Destroy.Op.1a2547.7 // CHECK:STDOUT: %Destroy.Op.call.loc45_31.1: init %empty_tuple.type = call %Destroy.Op.bound.loc45_31.1(%.loc45_31.6) -// CHECK:STDOUT: %Destroy.Op.bound.loc45_31.2: = bound_method %var, constants.%Destroy.Op.1a2547.8 -// CHECK:STDOUT: %Destroy.Op.call.loc45_31.2: init %empty_tuple.type = call %Destroy.Op.bound.loc45_31.2(%var) +// CHECK:STDOUT: br !for.next +// CHECK:STDOUT: +// CHECK:STDOUT: !for.done: +// CHECK:STDOUT: %Destroy.Op.bound.loc45_31.2: = bound_method %.loc45_31.6, constants.%Destroy.Op.1a2547.7 +// CHECK:STDOUT: %Destroy.Op.call.loc45_31.2: init %empty_tuple.type = call %Destroy.Op.bound.loc45_31.2(%.loc45_31.6) +// CHECK:STDOUT: %Destroy.Op.bound.loc45_31.3: = bound_method %var, constants.%Destroy.Op.1a2547.8 +// CHECK:STDOUT: %Destroy.Op.call.loc45_31.3: init %empty_tuple.type = call %Destroy.Op.bound.loc45_31.3(%var) // CHECK:STDOUT: // CHECK:STDOUT: } // CHECK:STDOUT: @@ -3519,15 +3543,17 @@ fn TestDriver(var no_begin_end: Cpp.NoBeginEnd, // CHECK:STDOUT: %.loc32_9.1: %ptr.32e = as_compatible %addr.loc32 // CHECK:STDOUT: %.loc32_9.2: %ptr.32e = converted %addr.loc32, %.loc32_9.1 // CHECK:STDOUT: %operator_PlusEqual__carbon_thunk.call: ref %ValueType = call imports.%operator_PlusEqual__carbon_thunk.decl(%sum.ref, %.loc32_9.2) -// CHECK:STDOUT: br !for.next -// CHECK:STDOUT: -// CHECK:STDOUT: !for.done: // CHECK:STDOUT: %ValueType.Op.bound.loc31: = bound_method %.loc31_31.16, constants.%ValueType.Op.4c94b9.1 // CHECK:STDOUT: %Op.ref.loc31: %ValueType.cpp_destructor.type = name_ref Op, imports.%ValueType.cpp_destructor.decl [concrete = constants.%ValueType.cpp_destructor] // CHECK:STDOUT: %ValueType.cpp_destructor.bound.loc31: = bound_method %.loc31_31.16, %Op.ref.loc31 // CHECK:STDOUT: %ValueType.cpp_destructor.call.loc31: init %empty_tuple.type = call %ValueType.cpp_destructor.bound.loc31(%.loc31_31.16) -// CHECK:STDOUT: %Destroy.Op.bound: = bound_method %.loc31_31.8, constants.%Destroy.Op.1a2547.7 -// CHECK:STDOUT: %Destroy.Op.call: init %empty_tuple.type = call %Destroy.Op.bound(%.loc31_31.8) +// CHECK:STDOUT: %Destroy.Op.bound.loc31_31.1: = bound_method %.loc31_31.8, constants.%Destroy.Op.1a2547.7 +// CHECK:STDOUT: %Destroy.Op.call.loc31_31.1: init %empty_tuple.type = call %Destroy.Op.bound.loc31_31.1(%.loc31_31.8) +// CHECK:STDOUT: br !for.next +// CHECK:STDOUT: +// CHECK:STDOUT: !for.done: +// CHECK:STDOUT: %Destroy.Op.bound.loc31_31.2: = bound_method %.loc31_31.8, constants.%Destroy.Op.1a2547.7 +// CHECK:STDOUT: %Destroy.Op.call.loc31_31.2: init %empty_tuple.type = call %Destroy.Op.bound.loc31_31.2(%.loc31_31.8) // CHECK:STDOUT: %impl.elem0.loc31_31.1: @TestIterate.%.loc31_31.22 (%.57c) = impl_witness_access constants.%Destroy.lookup_impl_witness.198, element0 [symbolic = %impl.elem0.loc31_31.2 (constants.%impl.elem0.152)] // CHECK:STDOUT: %bound_method.loc31_31.7: = bound_method %var, %impl.elem0.loc31_31.1 // CHECK:STDOUT: %.loc31_31.18: %Destroy.type = converted constants.%as_type.e41, constants.%impl.elem1.834 [symbolic = %impl.elem1 (constants.%impl.elem1.834)] @@ -3901,15 +3927,17 @@ fn TestDriver(var no_begin_end: Cpp.NoBeginEnd, // CHECK:STDOUT: %.loc39_9.1: %ptr.32e = as_compatible %addr.loc39 // CHECK:STDOUT: %.loc39_9.2: %ptr.32e = converted %addr.loc39, %.loc39_9.1 // CHECK:STDOUT: %operator_PlusEqual__carbon_thunk.call: ref %ValueType = call imports.%operator_PlusEqual__carbon_thunk.decl(%sum.ref, %.loc39_9.2) -// CHECK:STDOUT: br !for.next -// CHECK:STDOUT: -// CHECK:STDOUT: !for.done: // CHECK:STDOUT: %ValueType.Op.bound.loc38: = bound_method %.loc38_31.16, constants.%ValueType.Op.4c94b9.1 // CHECK:STDOUT: %Op.ref.loc38: %ValueType.cpp_destructor.type = name_ref Op, imports.%ValueType.cpp_destructor.decl [concrete = constants.%ValueType.cpp_destructor] // CHECK:STDOUT: %ValueType.cpp_destructor.bound.loc38: = bound_method %.loc38_31.16, %Op.ref.loc38 // CHECK:STDOUT: %ValueType.cpp_destructor.call.loc38: init %empty_tuple.type = call %ValueType.cpp_destructor.bound.loc38(%.loc38_31.16) -// CHECK:STDOUT: %Destroy.Op.bound: = bound_method %.loc38_31.8, constants.%Destroy.Op.1a2547.7 -// CHECK:STDOUT: %Destroy.Op.call: init %empty_tuple.type = call %Destroy.Op.bound(%.loc38_31.8) +// CHECK:STDOUT: %Destroy.Op.bound.loc38_31.1: = bound_method %.loc38_31.8, constants.%Destroy.Op.1a2547.7 +// CHECK:STDOUT: %Destroy.Op.call.loc38_31.1: init %empty_tuple.type = call %Destroy.Op.bound.loc38_31.1(%.loc38_31.8) +// CHECK:STDOUT: br !for.next +// CHECK:STDOUT: +// CHECK:STDOUT: !for.done: +// CHECK:STDOUT: %Destroy.Op.bound.loc38_31.2: = bound_method %.loc38_31.8, constants.%Destroy.Op.1a2547.7 +// CHECK:STDOUT: %Destroy.Op.call.loc38_31.2: init %empty_tuple.type = call %Destroy.Op.bound.loc38_31.2(%.loc38_31.8) // CHECK:STDOUT: %impl.elem0.loc38_31.1: @TestIterate.%.loc38_31.22 (%.57c) = impl_witness_access constants.%Destroy.lookup_impl_witness.198, element0 [symbolic = %impl.elem0.loc38_31.2 (constants.%impl.elem0.152)] // CHECK:STDOUT: %bound_method.loc38_31.7: = bound_method %var, %impl.elem0.loc38_31.1 // CHECK:STDOUT: %.loc38_31.18: %Destroy.type = converted constants.%as_type.e41, constants.%impl.elem1.834 [symbolic = %impl.elem1 (constants.%impl.elem1.834)] @@ -4018,17 +4046,19 @@ fn TestDriver(var no_begin_end: Cpp.NoBeginEnd, // CHECK:STDOUT: %.loc81_9.1: %ptr.32e = as_compatible %addr.loc81 // CHECK:STDOUT: %.loc81_9.2: %ptr.32e = converted %addr.loc81, %.loc81_9.1 // CHECK:STDOUT: %operator_PlusEqual__carbon_thunk.call: ref %ValueType = call imports.%operator_PlusEqual__carbon_thunk.decl(%sum.ref, %.loc81_9.2) -// CHECK:STDOUT: br !for.next -// CHECK:STDOUT: -// CHECK:STDOUT: !for.done: // CHECK:STDOUT: %ValueType.Op.bound.loc80: = bound_method %.loc80_32.14, constants.%ValueType.Op.4c94b9.1 // CHECK:STDOUT: %Op.ref.loc80: %ValueType.cpp_destructor.type = name_ref Op, imports.%ValueType.cpp_destructor.decl [concrete = constants.%ValueType.cpp_destructor] // CHECK:STDOUT: %ValueType.cpp_destructor.bound.loc80: = bound_method %.loc80_32.14, %Op.ref.loc80 // CHECK:STDOUT: %ValueType.cpp_destructor.call.loc80: init %empty_tuple.type = call %ValueType.cpp_destructor.bound.loc80(%.loc80_32.14) // CHECK:STDOUT: %Destroy.Op.bound.loc80_32.1: = bound_method %.loc80_32.6, constants.%Destroy.Op.1a2547.7 // CHECK:STDOUT: %Destroy.Op.call.loc80_32.1: init %empty_tuple.type = call %Destroy.Op.bound.loc80_32.1(%.loc80_32.6) -// CHECK:STDOUT: %Destroy.Op.bound.loc80_32.2: = bound_method %var, constants.%Destroy.Op.1a2547.8 -// CHECK:STDOUT: %Destroy.Op.call.loc80_32.2: init %empty_tuple.type = call %Destroy.Op.bound.loc80_32.2(%var) +// CHECK:STDOUT: br !for.next +// CHECK:STDOUT: +// CHECK:STDOUT: !for.done: +// CHECK:STDOUT: %Destroy.Op.bound.loc80_32.2: = bound_method %.loc80_32.6, constants.%Destroy.Op.1a2547.7 +// CHECK:STDOUT: %Destroy.Op.call.loc80_32.2: init %empty_tuple.type = call %Destroy.Op.bound.loc80_32.2(%.loc80_32.6) +// CHECK:STDOUT: %Destroy.Op.bound.loc80_32.3: = bound_method %var, constants.%Destroy.Op.1a2547.8 +// CHECK:STDOUT: %Destroy.Op.call.loc80_32.3: init %empty_tuple.type = call %Destroy.Op.bound.loc80_32.3(%var) // CHECK:STDOUT: // CHECK:STDOUT: } // CHECK:STDOUT: diff --git a/toolchain/check/testdata/namespace/shadow.carbon b/toolchain/check/testdata/namespace/shadow.carbon index b24d583f18a4..d071940558d3 100644 --- a/toolchain/check/testdata/namespace/shadow.carbon +++ b/toolchain/check/testdata/namespace/shadow.carbon @@ -167,8 +167,8 @@ fn N.M.B() -> i32 { // CHECK:STDOUT: %specific_fn.loc29: = specific_function %impl.elem0.loc29, @Int.as.Copy.impl.Op(constants.%int_32) [concrete = constants.%Int.as.Copy.impl.Op.specific_fn] // CHECK:STDOUT: %bound_method.loc29_12.2: = bound_method %.loc29, %specific_fn.loc29 // CHECK:STDOUT: %Int.as.Copy.impl.Op.call: init %i32 = call %bound_method.loc29_12.2(%.loc29) -// CHECK:STDOUT: %Destroy.Op.bound.loc26_5.1: = bound_method %A.var, constants.%Destroy.Op.1a2547.2 -// CHECK:STDOUT: %Destroy.Op.call.loc26_5.1: init %empty_tuple.type = call %Destroy.Op.bound.loc26_5.1(%A.var) +// CHECK:STDOUT: %Destroy.Op.bound: = bound_method %A.var, constants.%Destroy.Op.1a2547.2 +// CHECK:STDOUT: %Destroy.Op.call: init %empty_tuple.type = call %Destroy.Op.bound(%A.var) // CHECK:STDOUT: return %Int.as.Copy.impl.Op.call // CHECK:STDOUT: // CHECK:STDOUT: !if.else: @@ -179,8 +179,6 @@ fn N.M.B() -> i32 { // CHECK:STDOUT: %bound_method.loc31_11.2: = bound_method %int_0.loc31, %specific_fn.loc31 [concrete = constants.%bound_method] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc31: init %i32 = call %bound_method.loc31_11.2(%int_0.loc31) [concrete = constants.%int_0.3c0] // CHECK:STDOUT: %.loc31: init %i32 = converted %int_0.loc31, %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc31 [concrete = constants.%int_0.3c0] -// CHECK:STDOUT: %Destroy.Op.bound.loc26_5.2: = bound_method %A.var, constants.%Destroy.Op.1a2547.2 -// CHECK:STDOUT: %Destroy.Op.call.loc26_5.2: init %empty_tuple.type = call %Destroy.Op.bound.loc26_5.2(%A.var) // CHECK:STDOUT: return %.loc31 // CHECK:STDOUT: } // CHECK:STDOUT: diff --git a/toolchain/check/testdata/return/fail_returned_var_shadow.carbon b/toolchain/check/testdata/return/fail_returned_var_shadow.carbon index ae97394e3e75..9b3c5deb1981 100644 --- a/toolchain/check/testdata/return/fail_returned_var_shadow.carbon +++ b/toolchain/check/testdata/return/fail_returned_var_shadow.carbon @@ -172,6 +172,10 @@ fn DifferentScopes() -> i32 { // CHECK:STDOUT: %w.patt: %pattern_type.6b6 = ref_binding_pattern w [concrete = constants.%w.patt.859340.1] // CHECK:STDOUT: %w.var_patt: %pattern_type.6b6 = var_pattern %w.patt [concrete = constants.%w.var_patt.82cc74.1] // CHECK:STDOUT: } +// CHECK:STDOUT: %Destroy.Op.bound.loc25: = bound_method %w.var, constants.%Destroy.Op.1a2547.2 +// CHECK:STDOUT: %Destroy.Op.call.loc25: init %empty_tuple.type = call %Destroy.Op.bound.loc25(%w.var) +// CHECK:STDOUT: %Destroy.Op.bound.loc17: = bound_method %v.var, constants.%Destroy.Op.1a2547.2 +// CHECK:STDOUT: %Destroy.Op.call.loc17: init %empty_tuple.type = call %Destroy.Op.bound.loc17(%v.var) // CHECK:STDOUT: br !if.else // CHECK:STDOUT: // CHECK:STDOUT: !if.else: @@ -182,10 +186,6 @@ fn DifferentScopes() -> i32 { // CHECK:STDOUT: %bound_method.loc27_11.2: = bound_method %int_0.loc27, %specific_fn.loc27 [concrete = constants.%bound_method.6e0] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc27: init %i32 = call %bound_method.loc27_11.2(%int_0.loc27) [concrete = constants.%int_0.3c0] // CHECK:STDOUT: %.loc27: init %i32 = converted %int_0.loc27, %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc27 [concrete = constants.%int_0.3c0] -// CHECK:STDOUT: %Destroy.Op.bound.loc25: = bound_method %w.var, constants.%Destroy.Op.1a2547.2 -// CHECK:STDOUT: %Destroy.Op.call.loc25: init %empty_tuple.type = call %Destroy.Op.bound.loc25(%w.var) -// CHECK:STDOUT: %Destroy.Op.bound.loc17: = bound_method %v.var, constants.%Destroy.Op.1a2547.2 -// CHECK:STDOUT: %Destroy.Op.call.loc17: init %empty_tuple.type = call %Destroy.Op.bound.loc17(%v.var) // CHECK:STDOUT: return %.loc27 // CHECK:STDOUT: } // CHECK:STDOUT: @@ -236,9 +236,13 @@ fn DifferentScopes() -> i32 { // CHECK:STDOUT: %w.patt: %pattern_type.6b6 = ref_binding_pattern w [concrete = constants.%w.patt.859340.2] // CHECK:STDOUT: %w.var_patt: %pattern_type.6b6 = var_pattern %w.patt [concrete = constants.%w.var_patt.82cc74.2] // CHECK:STDOUT: } +// CHECK:STDOUT: %Destroy.Op.bound.loc41: = bound_method %w.var, constants.%Destroy.Op.1a2547.2 +// CHECK:STDOUT: %Destroy.Op.call.loc41: init %empty_tuple.type = call %Destroy.Op.bound.loc41(%w.var) // CHECK:STDOUT: br !if.else.loc33 // CHECK:STDOUT: // CHECK:STDOUT: !if.else.loc33: +// CHECK:STDOUT: %Destroy.Op.bound.loc32: = bound_method %v.var, constants.%Destroy.Op.1a2547.2 +// CHECK:STDOUT: %Destroy.Op.call.loc32: init %empty_tuple.type = call %Destroy.Op.bound.loc32(%v.var) // CHECK:STDOUT: br !if.else.loc31 // CHECK:STDOUT: // CHECK:STDOUT: !if.else.loc31: @@ -249,10 +253,6 @@ fn DifferentScopes() -> i32 { // CHECK:STDOUT: %bound_method.loc44_11.2: = bound_method %int_0.loc44, %specific_fn.loc44 [concrete = constants.%bound_method.6e0] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc44: init %i32 = call %bound_method.loc44_11.2(%int_0.loc44) [concrete = constants.%int_0.3c0] // CHECK:STDOUT: %.loc44: init %i32 = converted %int_0.loc44, %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc44 [concrete = constants.%int_0.3c0] -// CHECK:STDOUT: %Destroy.Op.bound.loc41: = bound_method %w.var, constants.%Destroy.Op.1a2547.2 -// CHECK:STDOUT: %Destroy.Op.call.loc41: init %empty_tuple.type = call %Destroy.Op.bound.loc41(%w.var) -// CHECK:STDOUT: %Destroy.Op.bound.loc32: = bound_method %v.var, constants.%Destroy.Op.1a2547.2 -// CHECK:STDOUT: %Destroy.Op.call.loc32: init %empty_tuple.type = call %Destroy.Op.bound.loc32(%v.var) // CHECK:STDOUT: return %.loc44 // CHECK:STDOUT: } // CHECK:STDOUT: diff --git a/toolchain/check/testdata/return/import_convert_function.carbon b/toolchain/check/testdata/return/import_convert_function.carbon index 279a6f1d1eeb..9de7b2d66065 100644 --- a/toolchain/check/testdata/return/import_convert_function.carbon +++ b/toolchain/check/testdata/return/import_convert_function.carbon @@ -1108,7 +1108,7 @@ fn F0(unused n: i32) -> P.D { // CHECK:STDOUT: %.loc7_26.3: %C.771 = acquire_value %.loc7_26.2 [concrete = constants.%C.val.d9b] // CHECK:STDOUT: %C.as.ImplicitAs.impl.Convert.call.loc7: init %D to %.loc6_26.1 = call %bound_method.loc7_35(%.loc7_26.3) // CHECK:STDOUT: %.loc7_35: init %D = converted %.loc7_26.1, %C.as.ImplicitAs.impl.Convert.call.loc7 -// CHECK:STDOUT: %Destroy.Op.call.loc7_26.1: init %empty_tuple.type = call constants.%Destroy.Op.bound.234(constants.%.de3) +// CHECK:STDOUT: %Destroy.Op.call.loc7: init %empty_tuple.type = call constants.%Destroy.Op.bound.234(constants.%.de3) // CHECK:STDOUT: return %.loc7_35 to %return.param // CHECK:STDOUT: // CHECK:STDOUT: !if.else.loc7: @@ -1138,8 +1138,7 @@ fn F0(unused n: i32) -> P.D { // CHECK:STDOUT: %.loc8_26.3: %C.83b = acquire_value %.loc8_26.2 [concrete = constants.%C.val.399] // CHECK:STDOUT: %C.as.ImplicitAs.impl.Convert.call.loc8: init %D to %.loc6_26.2 = call %bound_method.loc8_35(%.loc8_26.3) // CHECK:STDOUT: %.loc8_35: init %D = converted %.loc8_26.1, %C.as.ImplicitAs.impl.Convert.call.loc8 -// CHECK:STDOUT: %Destroy.Op.call.loc8_26.1: init %empty_tuple.type = call constants.%Destroy.Op.bound.d6e(constants.%.027) -// CHECK:STDOUT: %Destroy.Op.call.loc7_26.2: init %empty_tuple.type = call constants.%Destroy.Op.bound.234(constants.%.de3) +// CHECK:STDOUT: %Destroy.Op.call.loc8: init %empty_tuple.type = call constants.%Destroy.Op.bound.d6e(constants.%.027) // CHECK:STDOUT: return %.loc8_35 to %return.param // CHECK:STDOUT: // CHECK:STDOUT: !if.else.loc8: @@ -1169,9 +1168,7 @@ fn F0(unused n: i32) -> P.D { // CHECK:STDOUT: %.loc9_26.3: %C.5ce = acquire_value %.loc9_26.2 [concrete = constants.%C.val.040] // CHECK:STDOUT: %C.as.ImplicitAs.impl.Convert.call.loc9: init %D to %.loc6_26.3 = call %bound_method.loc9_35(%.loc9_26.3) // CHECK:STDOUT: %.loc9_35: init %D = converted %.loc9_26.1, %C.as.ImplicitAs.impl.Convert.call.loc9 -// CHECK:STDOUT: %Destroy.Op.call.loc9_26.1: init %empty_tuple.type = call constants.%Destroy.Op.bound.458(constants.%.ed7) -// CHECK:STDOUT: %Destroy.Op.call.loc8_26.2: init %empty_tuple.type = call constants.%Destroy.Op.bound.d6e(constants.%.027) -// CHECK:STDOUT: %Destroy.Op.call.loc7_26.3: init %empty_tuple.type = call constants.%Destroy.Op.bound.234(constants.%.de3) +// CHECK:STDOUT: %Destroy.Op.call.loc9: init %empty_tuple.type = call constants.%Destroy.Op.bound.458(constants.%.ed7) // CHECK:STDOUT: return %.loc9_35 to %return.param // CHECK:STDOUT: // CHECK:STDOUT: !if.else.loc9: @@ -1201,10 +1198,7 @@ fn F0(unused n: i32) -> P.D { // CHECK:STDOUT: %.loc10_26.3: %C.b3e = acquire_value %.loc10_26.2 [concrete = constants.%C.val.cf1] // CHECK:STDOUT: %C.as.ImplicitAs.impl.Convert.call.loc10: init %D to %.loc6_26.4 = call %bound_method.loc10_35(%.loc10_26.3) // CHECK:STDOUT: %.loc10_35: init %D = converted %.loc10_26.1, %C.as.ImplicitAs.impl.Convert.call.loc10 -// CHECK:STDOUT: %Destroy.Op.call.loc10_26.1: init %empty_tuple.type = call constants.%Destroy.Op.bound.5e3(constants.%.dc8) -// CHECK:STDOUT: %Destroy.Op.call.loc9_26.2: init %empty_tuple.type = call constants.%Destroy.Op.bound.458(constants.%.ed7) -// CHECK:STDOUT: %Destroy.Op.call.loc8_26.3: init %empty_tuple.type = call constants.%Destroy.Op.bound.d6e(constants.%.027) -// CHECK:STDOUT: %Destroy.Op.call.loc7_26.4: init %empty_tuple.type = call constants.%Destroy.Op.bound.234(constants.%.de3) +// CHECK:STDOUT: %Destroy.Op.call.loc10: init %empty_tuple.type = call constants.%Destroy.Op.bound.5e3(constants.%.dc8) // CHECK:STDOUT: return %.loc10_35 to %return.param // CHECK:STDOUT: // CHECK:STDOUT: !if.else.loc10: @@ -1234,11 +1228,7 @@ fn F0(unused n: i32) -> P.D { // CHECK:STDOUT: %.loc11_26.3: %C.8ee = acquire_value %.loc11_26.2 [concrete = constants.%C.val.19e] // CHECK:STDOUT: %C.as.ImplicitAs.impl.Convert.call.loc11: init %D to %.loc6_26.5 = call %bound_method.loc11_35(%.loc11_26.3) // CHECK:STDOUT: %.loc11_35: init %D = converted %.loc11_26.1, %C.as.ImplicitAs.impl.Convert.call.loc11 -// CHECK:STDOUT: %Destroy.Op.call.loc11_26.1: init %empty_tuple.type = call constants.%Destroy.Op.bound.670(constants.%.054) -// CHECK:STDOUT: %Destroy.Op.call.loc10_26.2: init %empty_tuple.type = call constants.%Destroy.Op.bound.5e3(constants.%.dc8) -// CHECK:STDOUT: %Destroy.Op.call.loc9_26.3: init %empty_tuple.type = call constants.%Destroy.Op.bound.458(constants.%.ed7) -// CHECK:STDOUT: %Destroy.Op.call.loc8_26.4: init %empty_tuple.type = call constants.%Destroy.Op.bound.d6e(constants.%.027) -// CHECK:STDOUT: %Destroy.Op.call.loc7_26.5: init %empty_tuple.type = call constants.%Destroy.Op.bound.234(constants.%.de3) +// CHECK:STDOUT: %Destroy.Op.call.loc11: init %empty_tuple.type = call constants.%Destroy.Op.bound.670(constants.%.054) // CHECK:STDOUT: return %.loc11_35 to %return.param // CHECK:STDOUT: // CHECK:STDOUT: !if.else.loc11: @@ -1268,12 +1258,7 @@ fn F0(unused n: i32) -> P.D { // CHECK:STDOUT: %.loc12_26.3: %C.5c1 = acquire_value %.loc12_26.2 [concrete = constants.%C.val.6ca] // CHECK:STDOUT: %C.as.ImplicitAs.impl.Convert.call.loc12: init %D to %.loc6_26.6 = call %bound_method.loc12_35(%.loc12_26.3) // CHECK:STDOUT: %.loc12_35: init %D = converted %.loc12_26.1, %C.as.ImplicitAs.impl.Convert.call.loc12 -// CHECK:STDOUT: %Destroy.Op.call.loc12_26.1: init %empty_tuple.type = call constants.%Destroy.Op.bound.443(constants.%.6f6) -// CHECK:STDOUT: %Destroy.Op.call.loc11_26.2: init %empty_tuple.type = call constants.%Destroy.Op.bound.670(constants.%.054) -// CHECK:STDOUT: %Destroy.Op.call.loc10_26.3: init %empty_tuple.type = call constants.%Destroy.Op.bound.5e3(constants.%.dc8) -// CHECK:STDOUT: %Destroy.Op.call.loc9_26.4: init %empty_tuple.type = call constants.%Destroy.Op.bound.458(constants.%.ed7) -// CHECK:STDOUT: %Destroy.Op.call.loc8_26.5: init %empty_tuple.type = call constants.%Destroy.Op.bound.d6e(constants.%.027) -// CHECK:STDOUT: %Destroy.Op.call.loc7_26.6: init %empty_tuple.type = call constants.%Destroy.Op.bound.234(constants.%.de3) +// CHECK:STDOUT: %Destroy.Op.call.loc12: init %empty_tuple.type = call constants.%Destroy.Op.bound.443(constants.%.6f6) // CHECK:STDOUT: return %.loc12_35 to %return.param // CHECK:STDOUT: // CHECK:STDOUT: !if.else.loc12: @@ -1303,13 +1288,7 @@ fn F0(unused n: i32) -> P.D { // CHECK:STDOUT: %.loc13_26.3: %C.57b = acquire_value %.loc13_26.2 [concrete = constants.%C.val.225] // CHECK:STDOUT: %C.as.ImplicitAs.impl.Convert.call.loc13: init %D to %.loc6_26.7 = call %bound_method.loc13_35(%.loc13_26.3) // CHECK:STDOUT: %.loc13_35: init %D = converted %.loc13_26.1, %C.as.ImplicitAs.impl.Convert.call.loc13 -// CHECK:STDOUT: %Destroy.Op.call.loc13_26.1: init %empty_tuple.type = call constants.%Destroy.Op.bound.742(constants.%.fa8) -// CHECK:STDOUT: %Destroy.Op.call.loc12_26.2: init %empty_tuple.type = call constants.%Destroy.Op.bound.443(constants.%.6f6) -// CHECK:STDOUT: %Destroy.Op.call.loc11_26.3: init %empty_tuple.type = call constants.%Destroy.Op.bound.670(constants.%.054) -// CHECK:STDOUT: %Destroy.Op.call.loc10_26.4: init %empty_tuple.type = call constants.%Destroy.Op.bound.5e3(constants.%.dc8) -// CHECK:STDOUT: %Destroy.Op.call.loc9_26.5: init %empty_tuple.type = call constants.%Destroy.Op.bound.458(constants.%.ed7) -// CHECK:STDOUT: %Destroy.Op.call.loc8_26.6: init %empty_tuple.type = call constants.%Destroy.Op.bound.d6e(constants.%.027) -// CHECK:STDOUT: %Destroy.Op.call.loc7_26.7: init %empty_tuple.type = call constants.%Destroy.Op.bound.234(constants.%.de3) +// CHECK:STDOUT: %Destroy.Op.call.loc13: init %empty_tuple.type = call constants.%Destroy.Op.bound.742(constants.%.fa8) // CHECK:STDOUT: return %.loc13_35 to %return.param // CHECK:STDOUT: // CHECK:STDOUT: !if.else.loc13: @@ -1339,14 +1318,7 @@ fn F0(unused n: i32) -> P.D { // CHECK:STDOUT: %.loc14_26.3: %C.b0a = acquire_value %.loc14_26.2 [concrete = constants.%C.val.79c] // CHECK:STDOUT: %C.as.ImplicitAs.impl.Convert.call.loc14: init %D to %.loc6_26.8 = call %bound_method.loc14_35(%.loc14_26.3) // CHECK:STDOUT: %.loc14_35: init %D = converted %.loc14_26.1, %C.as.ImplicitAs.impl.Convert.call.loc14 -// CHECK:STDOUT: %Destroy.Op.call.loc14_26.1: init %empty_tuple.type = call constants.%Destroy.Op.bound.e4a(constants.%.a0a) -// CHECK:STDOUT: %Destroy.Op.call.loc13_26.2: init %empty_tuple.type = call constants.%Destroy.Op.bound.742(constants.%.fa8) -// CHECK:STDOUT: %Destroy.Op.call.loc12_26.3: init %empty_tuple.type = call constants.%Destroy.Op.bound.443(constants.%.6f6) -// CHECK:STDOUT: %Destroy.Op.call.loc11_26.4: init %empty_tuple.type = call constants.%Destroy.Op.bound.670(constants.%.054) -// CHECK:STDOUT: %Destroy.Op.call.loc10_26.5: init %empty_tuple.type = call constants.%Destroy.Op.bound.5e3(constants.%.dc8) -// CHECK:STDOUT: %Destroy.Op.call.loc9_26.6: init %empty_tuple.type = call constants.%Destroy.Op.bound.458(constants.%.ed7) -// CHECK:STDOUT: %Destroy.Op.call.loc8_26.7: init %empty_tuple.type = call constants.%Destroy.Op.bound.d6e(constants.%.027) -// CHECK:STDOUT: %Destroy.Op.call.loc7_26.8: init %empty_tuple.type = call constants.%Destroy.Op.bound.234(constants.%.de3) +// CHECK:STDOUT: %Destroy.Op.call.loc14: init %empty_tuple.type = call constants.%Destroy.Op.bound.e4a(constants.%.a0a) // CHECK:STDOUT: return %.loc14_35 to %return.param // CHECK:STDOUT: // CHECK:STDOUT: !if.else.loc14: @@ -1354,14 +1326,6 @@ fn F0(unused n: i32) -> P.D { // CHECK:STDOUT: %Make.ref: %Make.type = name_ref Make, imports.%P.Make [concrete = constants.%Make] // CHECK:STDOUT: %.loc6_26.9: ref %D = splice_block %return.param {} // CHECK:STDOUT: %Make.call: init %D to %.loc6_26.9 = call %Make.ref() -// CHECK:STDOUT: %Destroy.Op.call.loc14_26.2: init %empty_tuple.type = call constants.%Destroy.Op.bound.e4a(constants.%.a0a) -// CHECK:STDOUT: %Destroy.Op.call.loc13_26.3: init %empty_tuple.type = call constants.%Destroy.Op.bound.742(constants.%.fa8) -// CHECK:STDOUT: %Destroy.Op.call.loc12_26.4: init %empty_tuple.type = call constants.%Destroy.Op.bound.443(constants.%.6f6) -// CHECK:STDOUT: %Destroy.Op.call.loc11_26.5: init %empty_tuple.type = call constants.%Destroy.Op.bound.670(constants.%.054) -// CHECK:STDOUT: %Destroy.Op.call.loc10_26.6: init %empty_tuple.type = call constants.%Destroy.Op.bound.5e3(constants.%.dc8) -// CHECK:STDOUT: %Destroy.Op.call.loc9_26.7: init %empty_tuple.type = call constants.%Destroy.Op.bound.458(constants.%.ed7) -// CHECK:STDOUT: %Destroy.Op.call.loc8_26.8: init %empty_tuple.type = call constants.%Destroy.Op.bound.d6e(constants.%.027) -// CHECK:STDOUT: %Destroy.Op.call.loc7_26.9: init %empty_tuple.type = call constants.%Destroy.Op.bound.234(constants.%.de3) // CHECK:STDOUT: return %Make.call to %return.param // CHECK:STDOUT: } // CHECK:STDOUT: diff --git a/toolchain/check/testdata/return/returned_var_scope.carbon b/toolchain/check/testdata/return/returned_var_scope.carbon index 59b7df6f4470..1e96fc6738d2 100644 --- a/toolchain/check/testdata/return/returned_var_scope.carbon +++ b/toolchain/check/testdata/return/returned_var_scope.carbon @@ -66,15 +66,15 @@ fn EnclosingButAfter(b: bool) -> i32 { // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn: = specific_function %Core.IntLiteral.as.ImplicitAs.impl.Convert.e39, @Core.IntLiteral.as.ImplicitAs.impl.Convert(%int_32) [concrete] // CHECK:STDOUT: %bound_method.6e0: = bound_method %int_0.5c6, %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn [concrete] // CHECK:STDOUT: %int_0.3c0: %i32 = int_value 0 [concrete] +// CHECK:STDOUT: %Destroy.type: type = facet_type <@Destroy> [concrete] +// CHECK:STDOUT: %Destroy.Op.type.1d8f74.2: type = fn_type @Destroy.Op.loc17_14.2 [concrete] +// CHECK:STDOUT: %Destroy.Op.1a2547.2: %Destroy.Op.type.1d8f74.2 = struct_value () [concrete] // CHECK:STDOUT: %w.patt: %pattern_type.6b6 = ref_binding_pattern w [concrete] // CHECK:STDOUT: %w.var_patt: %pattern_type.6b6 = var_pattern %w.patt [concrete] // CHECK:STDOUT: %int_1.5b8: Core.IntLiteral = int_value 1 [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.094: = bound_method %int_1.5b8, %Core.IntLiteral.as.ImplicitAs.impl.Convert.e39 [concrete] // CHECK:STDOUT: %bound_method.953: = bound_method %int_1.5b8, %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn [concrete] // CHECK:STDOUT: %int_1.0c6: %i32 = int_value 1 [concrete] -// CHECK:STDOUT: %Destroy.type: type = facet_type <@Destroy> [concrete] -// CHECK:STDOUT: %Destroy.Op.type.1d8f74.2: type = fn_type @Destroy.Op.loc20_14.2 [concrete] -// CHECK:STDOUT: %Destroy.Op.1a2547.2: %Destroy.Op.type.1d8f74.2 = struct_value () [concrete] // CHECK:STDOUT: %pattern_type.831: type = pattern_type bool [concrete] // CHECK:STDOUT: %b.param_patt: %pattern_type.831 = value_param_pattern [concrete] // CHECK:STDOUT: %b.patt: %pattern_type.831 = at_binding_pattern b, %b.param_patt [concrete] @@ -152,6 +152,8 @@ fn EnclosingButAfter(b: bool) -> i32 { // CHECK:STDOUT: %v.patt: %pattern_type.6b6 = ref_binding_pattern v [concrete = constants.%v.patt] // CHECK:STDOUT: %v.var_patt: %pattern_type.6b6 = var_pattern %v.patt [concrete = constants.%v.var_patt] // CHECK:STDOUT: } +// CHECK:STDOUT: %Destroy.Op.bound.loc17: = bound_method %v.var, constants.%Destroy.Op.1a2547.2 +// CHECK:STDOUT: %Destroy.Op.call.loc17: init %empty_tuple.type = call %Destroy.Op.bound.loc17(%v.var) // CHECK:STDOUT: br !if.else.loc16 // CHECK:STDOUT: // CHECK:STDOUT: !if.else.loc16: @@ -174,6 +176,8 @@ fn EnclosingButAfter(b: bool) -> i32 { // CHECK:STDOUT: %w.patt: %pattern_type.6b6 = ref_binding_pattern w [concrete = constants.%w.patt] // CHECK:STDOUT: %w.var_patt: %pattern_type.6b6 = var_pattern %w.patt [concrete = constants.%w.var_patt] // CHECK:STDOUT: } +// CHECK:STDOUT: %Destroy.Op.bound.loc20: = bound_method %w.var, constants.%Destroy.Op.1a2547.2 +// CHECK:STDOUT: %Destroy.Op.call.loc20: init %empty_tuple.type = call %Destroy.Op.bound.loc20(%w.var) // CHECK:STDOUT: br !if.else.loc19 // CHECK:STDOUT: // CHECK:STDOUT: !if.else.loc19: @@ -184,16 +188,12 @@ fn EnclosingButAfter(b: bool) -> i32 { // CHECK:STDOUT: %bound_method.loc22_11.2: = bound_method %int_0.loc22, %specific_fn.loc22 [concrete = constants.%bound_method.6e0] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc22: init %i32 = call %bound_method.loc22_11.2(%int_0.loc22) [concrete = constants.%int_0.3c0] // CHECK:STDOUT: %.loc22: init %i32 = converted %int_0.loc22, %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc22 [concrete = constants.%int_0.3c0] -// CHECK:STDOUT: %Destroy.Op.bound.loc20: = bound_method %w.var, constants.%Destroy.Op.1a2547.2 -// CHECK:STDOUT: %Destroy.Op.call.loc20: init %empty_tuple.type = call %Destroy.Op.bound.loc20(%w.var) -// CHECK:STDOUT: %Destroy.Op.bound.loc17: = bound_method %v.var, constants.%Destroy.Op.1a2547.2 -// CHECK:STDOUT: %Destroy.Op.call.loc17: init %empty_tuple.type = call %Destroy.Op.bound.loc17(%v.var) // CHECK:STDOUT: return %.loc22 // CHECK:STDOUT: } // CHECK:STDOUT: -// CHECK:STDOUT: fn @Destroy.Op.loc20_14.1(%self.param: ref %i32.builtin) = "no_op"; +// CHECK:STDOUT: fn @Destroy.Op.loc17_14.1(%self.param: ref %i32.builtin) = "no_op"; // CHECK:STDOUT: -// CHECK:STDOUT: fn @Destroy.Op.loc20_14.2(%self.param: ref %i32) { +// CHECK:STDOUT: fn @Destroy.Op.loc17_14.2(%self.param: ref %i32) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: return // CHECK:STDOUT: } @@ -220,8 +220,8 @@ fn EnclosingButAfter(b: bool) -> i32 { // CHECK:STDOUT: %v.var_patt: %pattern_type.6b6 = var_pattern %v.patt [concrete = constants.%v.var_patt] // CHECK:STDOUT: } // CHECK:STDOUT: %.loc27_19: %i32 = acquire_value %v -// CHECK:STDOUT: %Destroy.Op.bound.loc27_14.1: = bound_method %v.var, constants.%Destroy.Op.1a2547.2 -// CHECK:STDOUT: %Destroy.Op.call.loc27_14.1: init %empty_tuple.type = call %Destroy.Op.bound.loc27_14.1(%v.var) +// CHECK:STDOUT: %Destroy.Op.bound.loc27: = bound_method %v.var, constants.%Destroy.Op.1a2547.2 +// CHECK:STDOUT: %Destroy.Op.call.loc27: init %empty_tuple.type = call %Destroy.Op.bound.loc27(%v.var) // CHECK:STDOUT: return %.loc27_19 // CHECK:STDOUT: // CHECK:STDOUT: !if.else: @@ -243,8 +243,6 @@ fn EnclosingButAfter(b: bool) -> i32 { // CHECK:STDOUT: %.loc30_17: %i32 = acquire_value %w // CHECK:STDOUT: %Destroy.Op.bound.loc30: = bound_method %w.var, constants.%Destroy.Op.1a2547.2 // CHECK:STDOUT: %Destroy.Op.call.loc30: init %empty_tuple.type = call %Destroy.Op.bound.loc30(%w.var) -// CHECK:STDOUT: %Destroy.Op.bound.loc27_14.2: = bound_method %v.var, constants.%Destroy.Op.1a2547.2 -// CHECK:STDOUT: %Destroy.Op.call.loc27_14.2: init %empty_tuple.type = call %Destroy.Op.bound.loc27_14.2(%v.var) // CHECK:STDOUT: return %.loc30_17 // CHECK:STDOUT: } // CHECK:STDOUT: diff --git a/toolchain/check/testdata/var/destroy_control_flow.carbon b/toolchain/check/testdata/var/destroy_control_flow.carbon new file mode 100644 index 000000000000..5104fc8cec37 --- /dev/null +++ b/toolchain/check/testdata/var/destroy_control_flow.carbon @@ -0,0 +1,620 @@ +// 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 +// +// INCLUDE-FILE: toolchain/testing/testdata/min_prelude/primitives.carbon +// +// AUTOUPDATE +// TIP: To test this file alone, run: +// TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/var/destroy_control_flow.carbon +// TIP: To dump output, run: +// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/var/destroy_control_flow.carbon + +// --- destructible.carbon + +library "[[@TEST_NAME]]"; + +class Destructible { + // TODO: Implement Core.Destructor instead. + impl as Core.Destroy { + fn Op(self); + } +}; + +fn Use(ref d: Destructible); + +// --- if_then.carbon + +library "[[@TEST_NAME]]"; +import library "destructible"; + +fn IfThen(cond: bool) { + //@dump-sem-ir-begin + var a: Destructible = {}; + Use(ref a); + if (cond) { + var b: Destructible = {}; + Use(ref b); + } + Use(ref a); + //@dump-sem-ir-end +} + +// --- if_else.carbon + +library "[[@TEST_NAME]]"; +import library "destructible"; + +fn IfElse(cond: bool) { + //@dump-sem-ir-begin + var a: Destructible = {}; + Use(ref a); + if (cond) { + var b: Destructible = {}; + Use(ref b); + } else { + var c: Destructible = {}; + Use(ref c); + } + Use(ref a); + //@dump-sem-ir-end +} + +// --- if_return.carbon + +library "[[@TEST_NAME]]"; +import library "destructible"; + +fn IfReturn(cond: bool) { + //@dump-sem-ir-begin + var a: Destructible = {}; + Use(ref a); + if (cond) { + var b: Destructible = {}; + Use(ref b); + return; + } + //@dump-sem-ir-end +} + +// --- while_break.carbon + +library "[[@TEST_NAME]]"; +import library "destructible"; + +fn WhileBreak(cond: bool) { + //@dump-sem-ir-begin + var a: Destructible = {}; + Use(ref a); + while (cond) { + Use(ref a); + var b: Destructible = {}; + Use(ref b); + if (cond) { + var c: Destructible = {}; + Use(ref c); + break; + } + } + Use(ref a); + //@dump-sem-ir-end +} + +// --- while_continue.carbon + +library "[[@TEST_NAME]]"; +import library "destructible"; + +fn WhileContinue(cond: bool) { + //@dump-sem-ir-begin + var a: Destructible = {}; + Use(ref a); + while (cond) { + Use(ref a); + var b: Destructible = {}; + Use(ref b); + if (cond) { + var c: Destructible = {}; + Use(ref c); + continue; + } + } + Use(ref a); + //@dump-sem-ir-end +} + +// CHECK:STDOUT: --- if_then.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] +// CHECK:STDOUT: %Destructible: type = class_type @Destructible [concrete] +// CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] +// CHECK:STDOUT: %pattern_type.397: type = pattern_type %Destructible [concrete] +// CHECK:STDOUT: %a.patt: %pattern_type.397 = ref_binding_pattern a [concrete] +// CHECK:STDOUT: %a.var_patt: %pattern_type.397 = var_pattern %a.patt [concrete] +// CHECK:STDOUT: %empty_struct: %empty_struct_type = struct_value () [concrete] +// CHECK:STDOUT: %Destructible.val: %Destructible = struct_value () [concrete] +// CHECK:STDOUT: %Use.type: type = fn_type @Use [concrete] +// CHECK:STDOUT: %Use: %Use.type = struct_value () [concrete] +// CHECK:STDOUT: %b.patt: %pattern_type.397 = ref_binding_pattern b [concrete] +// CHECK:STDOUT: %b.var_patt: %pattern_type.397 = var_pattern %b.patt [concrete] +// CHECK:STDOUT: %Destroy.Op.type.1d8f74.2: type = fn_type @Destroy.Op.loc10_5.2 [concrete] +// CHECK:STDOUT: %Destroy.Op.1a2547.2: %Destroy.Op.type.1d8f74.2 = struct_value () [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: imports { +// CHECK:STDOUT: %Main.Destructible: type = import_ref Main//destructible, Destructible, loaded [concrete = constants.%Destructible] +// CHECK:STDOUT: %Main.Use: %Use.type = import_ref Main//destructible, Use, loaded [concrete = constants.%Use] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @IfThen(%cond.param: bool) { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: %a.var: ref %Destructible = var_storage %a.var_patt +// CHECK:STDOUT: %.loc7_26.1: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct] +// CHECK:STDOUT: %.loc7_26.2: init %Destructible to %a.var = class_init () [concrete = constants.%Destructible.val] +// CHECK:STDOUT: %.loc7_3: init %Destructible = converted %.loc7_26.1, %.loc7_26.2 [concrete = constants.%Destructible.val] +// CHECK:STDOUT: assign %a.var, %.loc7_3 +// CHECK:STDOUT: %Destructible.ref.loc7: type = name_ref Destructible, imports.%Main.Destructible [concrete = constants.%Destructible] +// CHECK:STDOUT: %a: ref %Destructible = wrapper_binding a, %a.var +// CHECK:STDOUT: name_binding_decl { +// CHECK:STDOUT: %a.patt: %pattern_type.397 = ref_binding_pattern a [concrete = constants.%a.patt] +// CHECK:STDOUT: %a.var_patt: %pattern_type.397 = var_pattern %a.patt [concrete = constants.%a.var_patt] +// CHECK:STDOUT: } +// CHECK:STDOUT: %Use.ref.loc8: %Use.type = name_ref Use, imports.%Main.Use [concrete = constants.%Use] +// CHECK:STDOUT: %a.ref.loc8: ref %Destructible = name_ref a, %a +// CHECK:STDOUT: %.loc8: %Destructible = ref_tag %a.ref.loc8 +// CHECK:STDOUT: %Use.call.loc8: init %empty_tuple.type = call %Use.ref.loc8(%a.ref.loc8) +// CHECK:STDOUT: %cond.ref: bool = name_ref cond, %cond +// CHECK:STDOUT: if %cond.ref br !if.then else br !if.else +// CHECK:STDOUT: +// CHECK:STDOUT: !if.then: +// CHECK:STDOUT: %b.var: ref %Destructible = var_storage %b.var_patt +// CHECK:STDOUT: %.loc10_28.1: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct] +// CHECK:STDOUT: %.loc10_28.2: init %Destructible to %b.var = class_init () [concrete = constants.%Destructible.val] +// CHECK:STDOUT: %.loc10_5: init %Destructible = converted %.loc10_28.1, %.loc10_28.2 [concrete = constants.%Destructible.val] +// CHECK:STDOUT: assign %b.var, %.loc10_5 +// CHECK:STDOUT: %Destructible.ref.loc10: type = name_ref Destructible, imports.%Main.Destructible [concrete = constants.%Destructible] +// CHECK:STDOUT: %b: ref %Destructible = wrapper_binding b, %b.var +// CHECK:STDOUT: name_binding_decl { +// CHECK:STDOUT: %b.patt: %pattern_type.397 = ref_binding_pattern b [concrete = constants.%b.patt] +// CHECK:STDOUT: %b.var_patt: %pattern_type.397 = var_pattern %b.patt [concrete = constants.%b.var_patt] +// CHECK:STDOUT: } +// CHECK:STDOUT: %Use.ref.loc11: %Use.type = name_ref Use, imports.%Main.Use [concrete = constants.%Use] +// CHECK:STDOUT: %b.ref: ref %Destructible = name_ref b, %b +// CHECK:STDOUT: %.loc11: %Destructible = ref_tag %b.ref +// CHECK:STDOUT: %Use.call.loc11: init %empty_tuple.type = call %Use.ref.loc11(%b.ref) +// CHECK:STDOUT: %Destroy.Op.bound.loc10: = bound_method %b.var, constants.%Destroy.Op.1a2547.2 +// CHECK:STDOUT: %Destroy.Op.call.loc10: init %empty_tuple.type = call %Destroy.Op.bound.loc10(%b.var) +// CHECK:STDOUT: br !if.else +// CHECK:STDOUT: +// CHECK:STDOUT: !if.else: +// CHECK:STDOUT: %Use.ref.loc13: %Use.type = name_ref Use, imports.%Main.Use [concrete = constants.%Use] +// CHECK:STDOUT: %a.ref.loc13: ref %Destructible = name_ref a, %a +// CHECK:STDOUT: %.loc13: %Destructible = ref_tag %a.ref.loc13 +// CHECK:STDOUT: %Use.call.loc13: init %empty_tuple.type = call %Use.ref.loc13(%a.ref.loc13) +// CHECK:STDOUT: %Destroy.Op.bound.loc7: = bound_method %a.var, constants.%Destroy.Op.1a2547.2 +// CHECK:STDOUT: %Destroy.Op.call.loc7: init %empty_tuple.type = call %Destroy.Op.bound.loc7(%a.var) +// CHECK:STDOUT: +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @Destroy.Op.loc10_5.1(%self.param: ref %empty_struct_type) = "no_op"; +// CHECK:STDOUT: +// CHECK:STDOUT: fn @Destroy.Op.loc10_5.2(%self.param: ref %Destructible) { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: return +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: --- if_else.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] +// CHECK:STDOUT: %Destructible: type = class_type @Destructible [concrete] +// CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] +// CHECK:STDOUT: %pattern_type.397: type = pattern_type %Destructible [concrete] +// CHECK:STDOUT: %a.patt: %pattern_type.397 = ref_binding_pattern a [concrete] +// CHECK:STDOUT: %a.var_patt: %pattern_type.397 = var_pattern %a.patt [concrete] +// CHECK:STDOUT: %empty_struct: %empty_struct_type = struct_value () [concrete] +// CHECK:STDOUT: %Destructible.val: %Destructible = struct_value () [concrete] +// CHECK:STDOUT: %Use.type: type = fn_type @Use [concrete] +// CHECK:STDOUT: %Use: %Use.type = struct_value () [concrete] +// CHECK:STDOUT: %b.patt: %pattern_type.397 = ref_binding_pattern b [concrete] +// CHECK:STDOUT: %b.var_patt: %pattern_type.397 = var_pattern %b.patt [concrete] +// CHECK:STDOUT: %Destroy.Op.type.1d8f74.2: type = fn_type @Destroy.Op.loc10_5.2 [concrete] +// CHECK:STDOUT: %Destroy.Op.1a2547.2: %Destroy.Op.type.1d8f74.2 = struct_value () [concrete] +// CHECK:STDOUT: %c.patt: %pattern_type.397 = ref_binding_pattern c [concrete] +// CHECK:STDOUT: %c.var_patt: %pattern_type.397 = var_pattern %c.patt [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: imports { +// CHECK:STDOUT: %Main.Destructible: type = import_ref Main//destructible, Destructible, loaded [concrete = constants.%Destructible] +// CHECK:STDOUT: %Main.Use: %Use.type = import_ref Main//destructible, Use, loaded [concrete = constants.%Use] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @IfElse(%cond.param: bool) { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: %a.var: ref %Destructible = var_storage %a.var_patt +// CHECK:STDOUT: %.loc7_26.1: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct] +// CHECK:STDOUT: %.loc7_26.2: init %Destructible to %a.var = class_init () [concrete = constants.%Destructible.val] +// CHECK:STDOUT: %.loc7_3: init %Destructible = converted %.loc7_26.1, %.loc7_26.2 [concrete = constants.%Destructible.val] +// CHECK:STDOUT: assign %a.var, %.loc7_3 +// CHECK:STDOUT: %Destructible.ref.loc7: type = name_ref Destructible, imports.%Main.Destructible [concrete = constants.%Destructible] +// CHECK:STDOUT: %a: ref %Destructible = wrapper_binding a, %a.var +// CHECK:STDOUT: name_binding_decl { +// CHECK:STDOUT: %a.patt: %pattern_type.397 = ref_binding_pattern a [concrete = constants.%a.patt] +// CHECK:STDOUT: %a.var_patt: %pattern_type.397 = var_pattern %a.patt [concrete = constants.%a.var_patt] +// CHECK:STDOUT: } +// CHECK:STDOUT: %Use.ref.loc8: %Use.type = name_ref Use, imports.%Main.Use [concrete = constants.%Use] +// CHECK:STDOUT: %a.ref.loc8: ref %Destructible = name_ref a, %a +// CHECK:STDOUT: %.loc8: %Destructible = ref_tag %a.ref.loc8 +// CHECK:STDOUT: %Use.call.loc8: init %empty_tuple.type = call %Use.ref.loc8(%a.ref.loc8) +// CHECK:STDOUT: %cond.ref: bool = name_ref cond, %cond +// CHECK:STDOUT: if %cond.ref br !if.then else br !if.else +// CHECK:STDOUT: +// CHECK:STDOUT: !if.then: +// CHECK:STDOUT: %b.var: ref %Destructible = var_storage %b.var_patt +// CHECK:STDOUT: %.loc10_28.1: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct] +// CHECK:STDOUT: %.loc10_28.2: init %Destructible to %b.var = class_init () [concrete = constants.%Destructible.val] +// CHECK:STDOUT: %.loc10_5: init %Destructible = converted %.loc10_28.1, %.loc10_28.2 [concrete = constants.%Destructible.val] +// CHECK:STDOUT: assign %b.var, %.loc10_5 +// CHECK:STDOUT: %Destructible.ref.loc10: type = name_ref Destructible, imports.%Main.Destructible [concrete = constants.%Destructible] +// CHECK:STDOUT: %b: ref %Destructible = wrapper_binding b, %b.var +// CHECK:STDOUT: name_binding_decl { +// CHECK:STDOUT: %b.patt: %pattern_type.397 = ref_binding_pattern b [concrete = constants.%b.patt] +// CHECK:STDOUT: %b.var_patt: %pattern_type.397 = var_pattern %b.patt [concrete = constants.%b.var_patt] +// CHECK:STDOUT: } +// CHECK:STDOUT: %Use.ref.loc11: %Use.type = name_ref Use, imports.%Main.Use [concrete = constants.%Use] +// CHECK:STDOUT: %b.ref: ref %Destructible = name_ref b, %b +// CHECK:STDOUT: %.loc11: %Destructible = ref_tag %b.ref +// CHECK:STDOUT: %Use.call.loc11: init %empty_tuple.type = call %Use.ref.loc11(%b.ref) +// CHECK:STDOUT: %Destroy.Op.bound.loc10: = bound_method %b.var, constants.%Destroy.Op.1a2547.2 +// CHECK:STDOUT: %Destroy.Op.call.loc10: init %empty_tuple.type = call %Destroy.Op.bound.loc10(%b.var) +// CHECK:STDOUT: br !if.done +// CHECK:STDOUT: +// CHECK:STDOUT: !if.else: +// CHECK:STDOUT: %c.var: ref %Destructible = var_storage %c.var_patt +// CHECK:STDOUT: %.loc13_28.1: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct] +// CHECK:STDOUT: %.loc13_28.2: init %Destructible to %c.var = class_init () [concrete = constants.%Destructible.val] +// CHECK:STDOUT: %.loc13_5: init %Destructible = converted %.loc13_28.1, %.loc13_28.2 [concrete = constants.%Destructible.val] +// CHECK:STDOUT: assign %c.var, %.loc13_5 +// CHECK:STDOUT: %Destructible.ref.loc13: type = name_ref Destructible, imports.%Main.Destructible [concrete = constants.%Destructible] +// CHECK:STDOUT: %c: ref %Destructible = wrapper_binding c, %c.var +// CHECK:STDOUT: name_binding_decl { +// CHECK:STDOUT: %c.patt: %pattern_type.397 = ref_binding_pattern c [concrete = constants.%c.patt] +// CHECK:STDOUT: %c.var_patt: %pattern_type.397 = var_pattern %c.patt [concrete = constants.%c.var_patt] +// CHECK:STDOUT: } +// CHECK:STDOUT: %Use.ref.loc14: %Use.type = name_ref Use, imports.%Main.Use [concrete = constants.%Use] +// CHECK:STDOUT: %c.ref: ref %Destructible = name_ref c, %c +// CHECK:STDOUT: %.loc14: %Destructible = ref_tag %c.ref +// CHECK:STDOUT: %Use.call.loc14: init %empty_tuple.type = call %Use.ref.loc14(%c.ref) +// CHECK:STDOUT: %Destroy.Op.bound.loc13: = bound_method %c.var, constants.%Destroy.Op.1a2547.2 +// CHECK:STDOUT: %Destroy.Op.call.loc13: init %empty_tuple.type = call %Destroy.Op.bound.loc13(%c.var) +// CHECK:STDOUT: br !if.done +// CHECK:STDOUT: +// CHECK:STDOUT: !if.done: +// CHECK:STDOUT: %Use.ref.loc16: %Use.type = name_ref Use, imports.%Main.Use [concrete = constants.%Use] +// CHECK:STDOUT: %a.ref.loc16: ref %Destructible = name_ref a, %a +// CHECK:STDOUT: %.loc16: %Destructible = ref_tag %a.ref.loc16 +// CHECK:STDOUT: %Use.call.loc16: init %empty_tuple.type = call %Use.ref.loc16(%a.ref.loc16) +// CHECK:STDOUT: %Destroy.Op.bound.loc7: = bound_method %a.var, constants.%Destroy.Op.1a2547.2 +// CHECK:STDOUT: %Destroy.Op.call.loc7: init %empty_tuple.type = call %Destroy.Op.bound.loc7(%a.var) +// CHECK:STDOUT: +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @Destroy.Op.loc10_5.1(%self.param: ref %empty_struct_type) = "no_op"; +// CHECK:STDOUT: +// CHECK:STDOUT: fn @Destroy.Op.loc10_5.2(%self.param: ref %Destructible) { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: return +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: --- if_return.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] +// CHECK:STDOUT: %Destructible: type = class_type @Destructible [concrete] +// CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] +// CHECK:STDOUT: %pattern_type.397: type = pattern_type %Destructible [concrete] +// CHECK:STDOUT: %a.patt: %pattern_type.397 = ref_binding_pattern a [concrete] +// CHECK:STDOUT: %a.var_patt: %pattern_type.397 = var_pattern %a.patt [concrete] +// CHECK:STDOUT: %empty_struct: %empty_struct_type = struct_value () [concrete] +// CHECK:STDOUT: %Destructible.val: %Destructible = struct_value () [concrete] +// CHECK:STDOUT: %Use.type: type = fn_type @Use [concrete] +// CHECK:STDOUT: %Use: %Use.type = struct_value () [concrete] +// CHECK:STDOUT: %b.patt: %pattern_type.397 = ref_binding_pattern b [concrete] +// CHECK:STDOUT: %b.var_patt: %pattern_type.397 = var_pattern %b.patt [concrete] +// CHECK:STDOUT: %Destroy.Op.type.1d8f74.2: type = fn_type @Destroy.Op.loc10_5.2 [concrete] +// CHECK:STDOUT: %Destroy.Op.1a2547.2: %Destroy.Op.type.1d8f74.2 = struct_value () [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: imports { +// CHECK:STDOUT: %Main.Destructible: type = import_ref Main//destructible, Destructible, loaded [concrete = constants.%Destructible] +// CHECK:STDOUT: %Main.Use: %Use.type = import_ref Main//destructible, Use, loaded [concrete = constants.%Use] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @IfReturn(%cond.param: bool) { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: %a.var: ref %Destructible = var_storage %a.var_patt +// CHECK:STDOUT: %.loc7_26.1: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct] +// CHECK:STDOUT: %.loc7_26.2: init %Destructible to %a.var = class_init () [concrete = constants.%Destructible.val] +// CHECK:STDOUT: %.loc7_3: init %Destructible = converted %.loc7_26.1, %.loc7_26.2 [concrete = constants.%Destructible.val] +// CHECK:STDOUT: assign %a.var, %.loc7_3 +// CHECK:STDOUT: %Destructible.ref.loc7: type = name_ref Destructible, imports.%Main.Destructible [concrete = constants.%Destructible] +// CHECK:STDOUT: %a: ref %Destructible = wrapper_binding a, %a.var +// CHECK:STDOUT: name_binding_decl { +// CHECK:STDOUT: %a.patt: %pattern_type.397 = ref_binding_pattern a [concrete = constants.%a.patt] +// CHECK:STDOUT: %a.var_patt: %pattern_type.397 = var_pattern %a.patt [concrete = constants.%a.var_patt] +// CHECK:STDOUT: } +// CHECK:STDOUT: %Use.ref.loc8: %Use.type = name_ref Use, imports.%Main.Use [concrete = constants.%Use] +// CHECK:STDOUT: %a.ref: ref %Destructible = name_ref a, %a +// CHECK:STDOUT: %.loc8: %Destructible = ref_tag %a.ref +// CHECK:STDOUT: %Use.call.loc8: init %empty_tuple.type = call %Use.ref.loc8(%a.ref) +// CHECK:STDOUT: %cond.ref: bool = name_ref cond, %cond +// CHECK:STDOUT: if %cond.ref br !if.then else br !if.else +// CHECK:STDOUT: +// CHECK:STDOUT: !if.then: +// CHECK:STDOUT: %b.var: ref %Destructible = var_storage %b.var_patt +// CHECK:STDOUT: %.loc10_28.1: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct] +// CHECK:STDOUT: %.loc10_28.2: init %Destructible to %b.var = class_init () [concrete = constants.%Destructible.val] +// CHECK:STDOUT: %.loc10_5: init %Destructible = converted %.loc10_28.1, %.loc10_28.2 [concrete = constants.%Destructible.val] +// CHECK:STDOUT: assign %b.var, %.loc10_5 +// CHECK:STDOUT: %Destructible.ref.loc10: type = name_ref Destructible, imports.%Main.Destructible [concrete = constants.%Destructible] +// CHECK:STDOUT: %b: ref %Destructible = wrapper_binding b, %b.var +// CHECK:STDOUT: name_binding_decl { +// CHECK:STDOUT: %b.patt: %pattern_type.397 = ref_binding_pattern b [concrete = constants.%b.patt] +// CHECK:STDOUT: %b.var_patt: %pattern_type.397 = var_pattern %b.patt [concrete = constants.%b.var_patt] +// CHECK:STDOUT: } +// CHECK:STDOUT: %Use.ref.loc11: %Use.type = name_ref Use, imports.%Main.Use [concrete = constants.%Use] +// CHECK:STDOUT: %b.ref: ref %Destructible = name_ref b, %b +// CHECK:STDOUT: %.loc11: %Destructible = ref_tag %b.ref +// CHECK:STDOUT: %Use.call.loc11: init %empty_tuple.type = call %Use.ref.loc11(%b.ref) +// CHECK:STDOUT: %Destroy.Op.bound.loc10: = bound_method %b.var, constants.%Destroy.Op.1a2547.2 +// CHECK:STDOUT: %Destroy.Op.call.loc10: init %empty_tuple.type = call %Destroy.Op.bound.loc10(%b.var) +// CHECK:STDOUT: %Destroy.Op.bound.loc7_3.1: = bound_method %a.var, constants.%Destroy.Op.1a2547.2 +// CHECK:STDOUT: %Destroy.Op.call.loc7_3.1: init %empty_tuple.type = call %Destroy.Op.bound.loc7_3.1(%a.var) +// CHECK:STDOUT: return +// CHECK:STDOUT: +// CHECK:STDOUT: !if.else: +// CHECK:STDOUT: %Destroy.Op.bound.loc7_3.2: = bound_method %a.var, constants.%Destroy.Op.1a2547.2 +// CHECK:STDOUT: %Destroy.Op.call.loc7_3.2: init %empty_tuple.type = call %Destroy.Op.bound.loc7_3.2(%a.var) +// CHECK:STDOUT: +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @Destroy.Op.loc10_5.1(%self.param: ref %empty_struct_type) = "no_op"; +// CHECK:STDOUT: +// CHECK:STDOUT: fn @Destroy.Op.loc10_5.2(%self.param: ref %Destructible) { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: return +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: --- while_break.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] +// CHECK:STDOUT: %Destructible: type = class_type @Destructible [concrete] +// CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] +// CHECK:STDOUT: %pattern_type.397: type = pattern_type %Destructible [concrete] +// CHECK:STDOUT: %a.patt: %pattern_type.397 = ref_binding_pattern a [concrete] +// CHECK:STDOUT: %a.var_patt: %pattern_type.397 = var_pattern %a.patt [concrete] +// CHECK:STDOUT: %empty_struct: %empty_struct_type = struct_value () [concrete] +// CHECK:STDOUT: %Destructible.val: %Destructible = struct_value () [concrete] +// CHECK:STDOUT: %Use.type: type = fn_type @Use [concrete] +// CHECK:STDOUT: %Use: %Use.type = struct_value () [concrete] +// CHECK:STDOUT: %b.patt: %pattern_type.397 = ref_binding_pattern b [concrete] +// CHECK:STDOUT: %b.var_patt: %pattern_type.397 = var_pattern %b.patt [concrete] +// CHECK:STDOUT: %c.patt: %pattern_type.397 = ref_binding_pattern c [concrete] +// CHECK:STDOUT: %c.var_patt: %pattern_type.397 = var_pattern %c.patt [concrete] +// CHECK:STDOUT: %Destroy.Op.type.1d8f74.2: type = fn_type @Destroy.Op.loc14_7.2 [concrete] +// CHECK:STDOUT: %Destroy.Op.1a2547.2: %Destroy.Op.type.1d8f74.2 = struct_value () [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: imports { +// CHECK:STDOUT: %Main.Destructible: type = import_ref Main//destructible, Destructible, loaded [concrete = constants.%Destructible] +// CHECK:STDOUT: %Main.Use: %Use.type = import_ref Main//destructible, Use, loaded [concrete = constants.%Use] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @WhileBreak(%cond.param: bool) { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: %a.var: ref %Destructible = var_storage %a.var_patt +// CHECK:STDOUT: %.loc7_26.1: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct] +// CHECK:STDOUT: %.loc7_26.2: init %Destructible to %a.var = class_init () [concrete = constants.%Destructible.val] +// CHECK:STDOUT: %.loc7_3: init %Destructible = converted %.loc7_26.1, %.loc7_26.2 [concrete = constants.%Destructible.val] +// CHECK:STDOUT: assign %a.var, %.loc7_3 +// CHECK:STDOUT: %Destructible.ref.loc7: type = name_ref Destructible, imports.%Main.Destructible [concrete = constants.%Destructible] +// CHECK:STDOUT: %a: ref %Destructible = wrapper_binding a, %a.var +// CHECK:STDOUT: name_binding_decl { +// CHECK:STDOUT: %a.patt: %pattern_type.397 = ref_binding_pattern a [concrete = constants.%a.patt] +// CHECK:STDOUT: %a.var_patt: %pattern_type.397 = var_pattern %a.patt [concrete = constants.%a.var_patt] +// CHECK:STDOUT: } +// CHECK:STDOUT: %Use.ref.loc8: %Use.type = name_ref Use, imports.%Main.Use [concrete = constants.%Use] +// CHECK:STDOUT: %a.ref.loc8: ref %Destructible = name_ref a, %a +// CHECK:STDOUT: %.loc8: %Destructible = ref_tag %a.ref.loc8 +// CHECK:STDOUT: %Use.call.loc8: init %empty_tuple.type = call %Use.ref.loc8(%a.ref.loc8) +// CHECK:STDOUT: br !while.cond +// CHECK:STDOUT: +// CHECK:STDOUT: !while.cond: +// CHECK:STDOUT: %cond.ref.loc9: bool = name_ref cond, %cond +// CHECK:STDOUT: if %cond.ref.loc9 br !while.body else br !while.done +// CHECK:STDOUT: +// CHECK:STDOUT: !while.body: +// CHECK:STDOUT: %Use.ref.loc10: %Use.type = name_ref Use, imports.%Main.Use [concrete = constants.%Use] +// CHECK:STDOUT: %a.ref.loc10: ref %Destructible = name_ref a, %a +// CHECK:STDOUT: %.loc10: %Destructible = ref_tag %a.ref.loc10 +// CHECK:STDOUT: %Use.call.loc10: init %empty_tuple.type = call %Use.ref.loc10(%a.ref.loc10) +// CHECK:STDOUT: %b.var: ref %Destructible = var_storage %b.var_patt +// CHECK:STDOUT: %.loc11_28.1: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct] +// CHECK:STDOUT: %.loc11_28.2: init %Destructible to %b.var = class_init () [concrete = constants.%Destructible.val] +// CHECK:STDOUT: %.loc11_5: init %Destructible = converted %.loc11_28.1, %.loc11_28.2 [concrete = constants.%Destructible.val] +// CHECK:STDOUT: assign %b.var, %.loc11_5 +// CHECK:STDOUT: %Destructible.ref.loc11: type = name_ref Destructible, imports.%Main.Destructible [concrete = constants.%Destructible] +// CHECK:STDOUT: %b: ref %Destructible = wrapper_binding b, %b.var +// CHECK:STDOUT: name_binding_decl { +// CHECK:STDOUT: %b.patt: %pattern_type.397 = ref_binding_pattern b [concrete = constants.%b.patt] +// CHECK:STDOUT: %b.var_patt: %pattern_type.397 = var_pattern %b.patt [concrete = constants.%b.var_patt] +// CHECK:STDOUT: } +// CHECK:STDOUT: %Use.ref.loc12: %Use.type = name_ref Use, imports.%Main.Use [concrete = constants.%Use] +// CHECK:STDOUT: %b.ref: ref %Destructible = name_ref b, %b +// CHECK:STDOUT: %.loc12: %Destructible = ref_tag %b.ref +// CHECK:STDOUT: %Use.call.loc12: init %empty_tuple.type = call %Use.ref.loc12(%b.ref) +// CHECK:STDOUT: %cond.ref.loc13: bool = name_ref cond, %cond +// CHECK:STDOUT: if %cond.ref.loc13 br !if.then else br !if.else +// CHECK:STDOUT: +// CHECK:STDOUT: !if.then: +// CHECK:STDOUT: %c.var: ref %Destructible = var_storage %c.var_patt +// CHECK:STDOUT: %.loc14_30.1: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct] +// CHECK:STDOUT: %.loc14_30.2: init %Destructible to %c.var = class_init () [concrete = constants.%Destructible.val] +// CHECK:STDOUT: %.loc14_7: init %Destructible = converted %.loc14_30.1, %.loc14_30.2 [concrete = constants.%Destructible.val] +// CHECK:STDOUT: assign %c.var, %.loc14_7 +// CHECK:STDOUT: %Destructible.ref.loc14: type = name_ref Destructible, imports.%Main.Destructible [concrete = constants.%Destructible] +// CHECK:STDOUT: %c: ref %Destructible = wrapper_binding c, %c.var +// CHECK:STDOUT: name_binding_decl { +// CHECK:STDOUT: %c.patt: %pattern_type.397 = ref_binding_pattern c [concrete = constants.%c.patt] +// CHECK:STDOUT: %c.var_patt: %pattern_type.397 = var_pattern %c.patt [concrete = constants.%c.var_patt] +// CHECK:STDOUT: } +// CHECK:STDOUT: %Use.ref.loc15: %Use.type = name_ref Use, imports.%Main.Use [concrete = constants.%Use] +// CHECK:STDOUT: %c.ref: ref %Destructible = name_ref c, %c +// CHECK:STDOUT: %.loc15: %Destructible = ref_tag %c.ref +// CHECK:STDOUT: %Use.call.loc15: init %empty_tuple.type = call %Use.ref.loc15(%c.ref) +// CHECK:STDOUT: %Destroy.Op.bound.loc14: = bound_method %c.var, constants.%Destroy.Op.1a2547.2 +// CHECK:STDOUT: %Destroy.Op.call.loc14: init %empty_tuple.type = call %Destroy.Op.bound.loc14(%c.var) +// CHECK:STDOUT: %Destroy.Op.bound.loc11_5.1: = bound_method %b.var, constants.%Destroy.Op.1a2547.2 +// CHECK:STDOUT: %Destroy.Op.call.loc11_5.1: init %empty_tuple.type = call %Destroy.Op.bound.loc11_5.1(%b.var) +// CHECK:STDOUT: br !while.done +// CHECK:STDOUT: +// CHECK:STDOUT: !if.else: +// CHECK:STDOUT: %Destroy.Op.bound.loc11_5.2: = bound_method %b.var, constants.%Destroy.Op.1a2547.2 +// CHECK:STDOUT: %Destroy.Op.call.loc11_5.2: init %empty_tuple.type = call %Destroy.Op.bound.loc11_5.2(%b.var) +// CHECK:STDOUT: br !while.cond +// CHECK:STDOUT: +// CHECK:STDOUT: !while.done: +// CHECK:STDOUT: %Use.ref.loc19: %Use.type = name_ref Use, imports.%Main.Use [concrete = constants.%Use] +// CHECK:STDOUT: %a.ref.loc19: ref %Destructible = name_ref a, %a +// CHECK:STDOUT: %.loc19: %Destructible = ref_tag %a.ref.loc19 +// CHECK:STDOUT: %Use.call.loc19: init %empty_tuple.type = call %Use.ref.loc19(%a.ref.loc19) +// CHECK:STDOUT: %Destroy.Op.bound.loc7: = bound_method %a.var, constants.%Destroy.Op.1a2547.2 +// CHECK:STDOUT: %Destroy.Op.call.loc7: init %empty_tuple.type = call %Destroy.Op.bound.loc7(%a.var) +// CHECK:STDOUT: +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @Destroy.Op.loc14_7.1(%self.param: ref %empty_struct_type) = "no_op"; +// CHECK:STDOUT: +// CHECK:STDOUT: fn @Destroy.Op.loc14_7.2(%self.param: ref %Destructible) { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: return +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: --- while_continue.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] +// CHECK:STDOUT: %Destructible: type = class_type @Destructible [concrete] +// CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] +// CHECK:STDOUT: %pattern_type.397: type = pattern_type %Destructible [concrete] +// CHECK:STDOUT: %a.patt: %pattern_type.397 = ref_binding_pattern a [concrete] +// CHECK:STDOUT: %a.var_patt: %pattern_type.397 = var_pattern %a.patt [concrete] +// CHECK:STDOUT: %empty_struct: %empty_struct_type = struct_value () [concrete] +// CHECK:STDOUT: %Destructible.val: %Destructible = struct_value () [concrete] +// CHECK:STDOUT: %Use.type: type = fn_type @Use [concrete] +// CHECK:STDOUT: %Use: %Use.type = struct_value () [concrete] +// CHECK:STDOUT: %b.patt: %pattern_type.397 = ref_binding_pattern b [concrete] +// CHECK:STDOUT: %b.var_patt: %pattern_type.397 = var_pattern %b.patt [concrete] +// CHECK:STDOUT: %c.patt: %pattern_type.397 = ref_binding_pattern c [concrete] +// CHECK:STDOUT: %c.var_patt: %pattern_type.397 = var_pattern %c.patt [concrete] +// CHECK:STDOUT: %Destroy.Op.type.1d8f74.2: type = fn_type @Destroy.Op.loc14_7.2 [concrete] +// CHECK:STDOUT: %Destroy.Op.1a2547.2: %Destroy.Op.type.1d8f74.2 = struct_value () [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: imports { +// CHECK:STDOUT: %Main.Destructible: type = import_ref Main//destructible, Destructible, loaded [concrete = constants.%Destructible] +// CHECK:STDOUT: %Main.Use: %Use.type = import_ref Main//destructible, Use, loaded [concrete = constants.%Use] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @WhileContinue(%cond.param: bool) { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: %a.var: ref %Destructible = var_storage %a.var_patt +// CHECK:STDOUT: %.loc7_26.1: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct] +// CHECK:STDOUT: %.loc7_26.2: init %Destructible to %a.var = class_init () [concrete = constants.%Destructible.val] +// CHECK:STDOUT: %.loc7_3: init %Destructible = converted %.loc7_26.1, %.loc7_26.2 [concrete = constants.%Destructible.val] +// CHECK:STDOUT: assign %a.var, %.loc7_3 +// CHECK:STDOUT: %Destructible.ref.loc7: type = name_ref Destructible, imports.%Main.Destructible [concrete = constants.%Destructible] +// CHECK:STDOUT: %a: ref %Destructible = wrapper_binding a, %a.var +// CHECK:STDOUT: name_binding_decl { +// CHECK:STDOUT: %a.patt: %pattern_type.397 = ref_binding_pattern a [concrete = constants.%a.patt] +// CHECK:STDOUT: %a.var_patt: %pattern_type.397 = var_pattern %a.patt [concrete = constants.%a.var_patt] +// CHECK:STDOUT: } +// CHECK:STDOUT: %Use.ref.loc8: %Use.type = name_ref Use, imports.%Main.Use [concrete = constants.%Use] +// CHECK:STDOUT: %a.ref.loc8: ref %Destructible = name_ref a, %a +// CHECK:STDOUT: %.loc8: %Destructible = ref_tag %a.ref.loc8 +// CHECK:STDOUT: %Use.call.loc8: init %empty_tuple.type = call %Use.ref.loc8(%a.ref.loc8) +// CHECK:STDOUT: br !while.cond +// CHECK:STDOUT: +// CHECK:STDOUT: !while.cond: +// CHECK:STDOUT: %cond.ref.loc9: bool = name_ref cond, %cond +// CHECK:STDOUT: if %cond.ref.loc9 br !while.body else br !while.done +// CHECK:STDOUT: +// CHECK:STDOUT: !while.body: +// CHECK:STDOUT: %Use.ref.loc10: %Use.type = name_ref Use, imports.%Main.Use [concrete = constants.%Use] +// CHECK:STDOUT: %a.ref.loc10: ref %Destructible = name_ref a, %a +// CHECK:STDOUT: %.loc10: %Destructible = ref_tag %a.ref.loc10 +// CHECK:STDOUT: %Use.call.loc10: init %empty_tuple.type = call %Use.ref.loc10(%a.ref.loc10) +// CHECK:STDOUT: %b.var: ref %Destructible = var_storage %b.var_patt +// CHECK:STDOUT: %.loc11_28.1: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct] +// CHECK:STDOUT: %.loc11_28.2: init %Destructible to %b.var = class_init () [concrete = constants.%Destructible.val] +// CHECK:STDOUT: %.loc11_5: init %Destructible = converted %.loc11_28.1, %.loc11_28.2 [concrete = constants.%Destructible.val] +// CHECK:STDOUT: assign %b.var, %.loc11_5 +// CHECK:STDOUT: %Destructible.ref.loc11: type = name_ref Destructible, imports.%Main.Destructible [concrete = constants.%Destructible] +// CHECK:STDOUT: %b: ref %Destructible = wrapper_binding b, %b.var +// CHECK:STDOUT: name_binding_decl { +// CHECK:STDOUT: %b.patt: %pattern_type.397 = ref_binding_pattern b [concrete = constants.%b.patt] +// CHECK:STDOUT: %b.var_patt: %pattern_type.397 = var_pattern %b.patt [concrete = constants.%b.var_patt] +// CHECK:STDOUT: } +// CHECK:STDOUT: %Use.ref.loc12: %Use.type = name_ref Use, imports.%Main.Use [concrete = constants.%Use] +// CHECK:STDOUT: %b.ref: ref %Destructible = name_ref b, %b +// CHECK:STDOUT: %.loc12: %Destructible = ref_tag %b.ref +// CHECK:STDOUT: %Use.call.loc12: init %empty_tuple.type = call %Use.ref.loc12(%b.ref) +// CHECK:STDOUT: %cond.ref.loc13: bool = name_ref cond, %cond +// CHECK:STDOUT: if %cond.ref.loc13 br !if.then else br !if.else +// CHECK:STDOUT: +// CHECK:STDOUT: !if.then: +// CHECK:STDOUT: %c.var: ref %Destructible = var_storage %c.var_patt +// CHECK:STDOUT: %.loc14_30.1: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct] +// CHECK:STDOUT: %.loc14_30.2: init %Destructible to %c.var = class_init () [concrete = constants.%Destructible.val] +// CHECK:STDOUT: %.loc14_7: init %Destructible = converted %.loc14_30.1, %.loc14_30.2 [concrete = constants.%Destructible.val] +// CHECK:STDOUT: assign %c.var, %.loc14_7 +// CHECK:STDOUT: %Destructible.ref.loc14: type = name_ref Destructible, imports.%Main.Destructible [concrete = constants.%Destructible] +// CHECK:STDOUT: %c: ref %Destructible = wrapper_binding c, %c.var +// CHECK:STDOUT: name_binding_decl { +// CHECK:STDOUT: %c.patt: %pattern_type.397 = ref_binding_pattern c [concrete = constants.%c.patt] +// CHECK:STDOUT: %c.var_patt: %pattern_type.397 = var_pattern %c.patt [concrete = constants.%c.var_patt] +// CHECK:STDOUT: } +// CHECK:STDOUT: %Use.ref.loc15: %Use.type = name_ref Use, imports.%Main.Use [concrete = constants.%Use] +// CHECK:STDOUT: %c.ref: ref %Destructible = name_ref c, %c +// CHECK:STDOUT: %.loc15: %Destructible = ref_tag %c.ref +// CHECK:STDOUT: %Use.call.loc15: init %empty_tuple.type = call %Use.ref.loc15(%c.ref) +// CHECK:STDOUT: %Destroy.Op.bound.loc14: = bound_method %c.var, constants.%Destroy.Op.1a2547.2 +// CHECK:STDOUT: %Destroy.Op.call.loc14: init %empty_tuple.type = call %Destroy.Op.bound.loc14(%c.var) +// CHECK:STDOUT: %Destroy.Op.bound.loc11_5.1: = bound_method %b.var, constants.%Destroy.Op.1a2547.2 +// CHECK:STDOUT: %Destroy.Op.call.loc11_5.1: init %empty_tuple.type = call %Destroy.Op.bound.loc11_5.1(%b.var) +// CHECK:STDOUT: br !while.cond +// CHECK:STDOUT: +// CHECK:STDOUT: !if.else: +// CHECK:STDOUT: %Destroy.Op.bound.loc11_5.2: = bound_method %b.var, constants.%Destroy.Op.1a2547.2 +// CHECK:STDOUT: %Destroy.Op.call.loc11_5.2: init %empty_tuple.type = call %Destroy.Op.bound.loc11_5.2(%b.var) +// CHECK:STDOUT: br !while.cond +// CHECK:STDOUT: +// CHECK:STDOUT: !while.done: +// CHECK:STDOUT: %Use.ref.loc19: %Use.type = name_ref Use, imports.%Main.Use [concrete = constants.%Use] +// CHECK:STDOUT: %a.ref.loc19: ref %Destructible = name_ref a, %a +// CHECK:STDOUT: %.loc19: %Destructible = ref_tag %a.ref.loc19 +// CHECK:STDOUT: %Use.call.loc19: init %empty_tuple.type = call %Use.ref.loc19(%a.ref.loc19) +// CHECK:STDOUT: %Destroy.Op.bound.loc7: = bound_method %a.var, constants.%Destroy.Op.1a2547.2 +// CHECK:STDOUT: %Destroy.Op.call.loc7: init %empty_tuple.type = call %Destroy.Op.bound.loc7(%a.var) +// CHECK:STDOUT: +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @Destroy.Op.loc14_7.1(%self.param: ref %empty_struct_type) = "no_op"; +// CHECK:STDOUT: +// CHECK:STDOUT: fn @Destroy.Op.loc14_7.2(%self.param: ref %Destructible) { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: return +// CHECK:STDOUT: } +// CHECK:STDOUT: diff --git a/toolchain/check/testdata/var/shadowing.carbon b/toolchain/check/testdata/var/shadowing.carbon index 86e506fc7184..a63f5994e3e3 100644 --- a/toolchain/check/testdata/var/shadowing.carbon +++ b/toolchain/check/testdata/var/shadowing.carbon @@ -127,11 +127,11 @@ fn Main() { // CHECK:STDOUT: %.loc13_10.2: init %empty_tuple.type = tuple_init () [concrete = constants.%empty_tuple] // CHECK:STDOUT: %.loc13_7: init %empty_tuple.type = converted %.loc13_10.1, %.loc13_10.2 [concrete = constants.%empty_tuple] // CHECK:STDOUT: assign %x.ref, %.loc13_7 +// CHECK:STDOUT: %Destroy.Op.bound.loc10: = bound_method %x.var.loc10, constants.%Destroy.Op +// CHECK:STDOUT: %Destroy.Op.call.loc10: init %empty_tuple.type = call %Destroy.Op.bound.loc10(%x.var.loc10) // CHECK:STDOUT: br !if.else // CHECK:STDOUT: // CHECK:STDOUT: !if.else: -// CHECK:STDOUT: %Destroy.Op.bound.loc10: = bound_method %x.var.loc10, constants.%Destroy.Op -// CHECK:STDOUT: %Destroy.Op.call.loc10: init %empty_tuple.type = call %Destroy.Op.bound.loc10(%x.var.loc10) // CHECK:STDOUT: %Destroy.Op.bound.loc8: = bound_method %x.var.loc8, constants.%Destroy.Op // CHECK:STDOUT: %Destroy.Op.call.loc8: init %empty_tuple.type = call %Destroy.Op.bound.loc8(%x.var.loc8) // CHECK:STDOUT: %Destroy.Op.bound.loc5: = bound_method %NS.var, constants.%Destroy.Op diff --git a/toolchain/lower/testdata/array/iterate.carbon b/toolchain/lower/testdata/array/iterate.carbon index ea094b702302..f755f35df35d 100644 --- a/toolchain/lower/testdata/array/iterate.carbon +++ b/toolchain/lower/testdata/array/iterate.carbon @@ -55,6 +55,7 @@ fn F() { // CHECK:STDOUT: for.body: ; preds = %for.next // CHECK:STDOUT: %Optional.Get.call = call i32 @_CGet.Optional.Core.8698ccdc411f2307(ptr %.loc17_19.5.temp), !dbg !8 // CHECK:STDOUT: call void @_CG.Main(i32 %Optional.Get.call), !dbg !9 +// CHECK:STDOUT: call void @"_COp.9619ac5c2f73b9b5:core.Destroy.Core"(ptr %.loc17_19.5.temp), !dbg !8 // CHECK:STDOUT: br label %for.next, !dbg !10 // CHECK:STDOUT: // CHECK:STDOUT: for.done: ; preds = %for.next @@ -218,6 +219,7 @@ fn F() { // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: ; uselistorder directives +// CHECK:STDOUT: uselistorder ptr @"_COp.9619ac5c2f73b9b5:core.Destroy.Core", { 1, 0 } // CHECK:STDOUT: uselistorder ptr @llvm.lifetime.start.p0, { 2, 1, 0 } // CHECK:STDOUT: // CHECK:STDOUT: attributes #0 = { nounwind } diff --git a/toolchain/lower/testdata/for/bindings.carbon b/toolchain/lower/testdata/for/bindings.carbon index 5178d90ca8e4..cfb1a44afce8 100644 --- a/toolchain/lower/testdata/for/bindings.carbon +++ b/toolchain/lower/testdata/for/bindings.carbon @@ -71,10 +71,12 @@ fn For() { // CHECK:STDOUT: %.loc29_33.12 = load i32, ptr %tuple.elem1.tuple.elem, align 4, !dbg !9 // CHECK:STDOUT: store i32 %.loc29_33.12, ptr %n.var, align 4, !dbg !8 // CHECK:STDOUT: call void @_CF.Main(i32 %.loc29_33.11, ptr %n.var), !dbg !10 +// CHECK:STDOUT: call void @"_COp.eff30dc40f4a53d2:core.Destroy.Core"(ptr %.loc29_33.8.temp), !dbg !9 +// CHECK:STDOUT: call void @"_COp.a79511dff093d1a6:core.Destroy.Core"(ptr %.loc29_33.1.temp), !dbg !9 +// CHECK:STDOUT: call void @"_COp.5e27612b9dd31a14:core.Destroy.Core"(ptr %n.var), !dbg !8 // CHECK:STDOUT: br label %for.next, !dbg !11 // CHECK:STDOUT: // CHECK:STDOUT: for.done: ; preds = %for.next -// CHECK:STDOUT: call void @"_COp.eff30dc40f4a53d2:core.Destroy.Core"(ptr %.loc29_33.8.temp), !dbg !9 // CHECK:STDOUT: call void @"_COp.a79511dff093d1a6:core.Destroy.Core"(ptr %.loc29_33.1.temp), !dbg !9 // CHECK:STDOUT: call void @"_COp.5e27612b9dd31a14:core.Destroy.Core"(ptr %n.var), !dbg !8 // CHECK:STDOUT: call void @"_COp.88c97397fb274b67:core.Destroy.Core"(ptr %r.var), !dbg !7 @@ -202,6 +204,8 @@ fn For() { // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: ; uselistorder directives +// CHECK:STDOUT: uselistorder ptr @"_COp.5e27612b9dd31a14:core.Destroy.Core", { 1, 0 } +// CHECK:STDOUT: uselistorder ptr @"_COp.a79511dff093d1a6:core.Destroy.Core", { 1, 0 } // CHECK:STDOUT: uselistorder ptr @llvm.lifetime.start.p0, { 4, 3, 2, 1, 0 } // CHECK:STDOUT: // CHECK:STDOUT: attributes #0 = { nounwind } diff --git a/toolchain/lower/testdata/for/break_continue.carbon b/toolchain/lower/testdata/for/break_continue.carbon index a99d1011d8ca..0a622893b574 100644 --- a/toolchain/lower/testdata/for/break_continue.carbon +++ b/toolchain/lower/testdata/for/break_continue.carbon @@ -66,10 +66,12 @@ fn For() { // CHECK:STDOUT: br i1 %G.call, label %if.then.loc22, label %if.else.loc22, !dbg !13 // CHECK:STDOUT: // CHECK:STDOUT: if.then.loc22: ; preds = %if.else.loc21 +// CHECK:STDOUT: call void @"_COp.f9d363db90f1ff8b:core.Destroy.Core"(ptr %.loc20_33.1.temp), !dbg !8 // CHECK:STDOUT: br label %for.next, !dbg !14 // CHECK:STDOUT: // CHECK:STDOUT: if.else.loc22: ; preds = %if.else.loc21 // CHECK:STDOUT: call void @_CH.Main(), !dbg !15 +// CHECK:STDOUT: call void @"_COp.f9d363db90f1ff8b:core.Destroy.Core"(ptr %.loc20_33.1.temp), !dbg !8 // CHECK:STDOUT: br label %for.next, !dbg !16 // CHECK:STDOUT: // CHECK:STDOUT: for.done: ; preds = %if.then.loc21, %for.next @@ -252,6 +254,7 @@ fn For() { // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: ; uselistorder directives +// CHECK:STDOUT: uselistorder ptr @"_COp.f9d363db90f1ff8b:core.Destroy.Core", { 2, 1, 0 } // CHECK:STDOUT: uselistorder ptr @llvm.lifetime.start.p0, { 0, 3, 2, 1 } // CHECK:STDOUT: // CHECK:STDOUT: attributes #0 = { nounwind } diff --git a/toolchain/lower/testdata/for/for.carbon b/toolchain/lower/testdata/for/for.carbon index 98de4f352774..c8a8dd6048a2 100644 --- a/toolchain/lower/testdata/for/for.carbon +++ b/toolchain/lower/testdata/for/for.carbon @@ -57,13 +57,14 @@ fn For() { // CHECK:STDOUT: for.body: ; preds = %for.next // CHECK:STDOUT: %Optional.Get.call = call i32 @_CGet.Optional.Core.329ac87584bc77f7(ptr %.loc21_33.1.temp), !dbg !8 // CHECK:STDOUT: call void @_CG.Main(), !dbg !10 +// CHECK:STDOUT: call void @"_COp.f9d363db90f1ff8b:core.Destroy.Core"(ptr %.loc21_33.1.temp), !dbg !8 // CHECK:STDOUT: br label %for.next, !dbg !11 // CHECK:STDOUT: // CHECK:STDOUT: for.done: ; preds = %for.next -// CHECK:STDOUT: call void @_CH.Main(), !dbg !12 // CHECK:STDOUT: call void @"_COp.f9d363db90f1ff8b:core.Destroy.Core"(ptr %.loc21_33.1.temp), !dbg !8 // CHECK:STDOUT: call void @"_COp.5e27612b9dd31a14:core.Destroy.Core"(ptr %var), !dbg !8 // CHECK:STDOUT: call void @"_COp.0d5da2659ce22e5f:core.Destroy.Core"(ptr %.loc21_32.1.temp), !dbg !7 +// CHECK:STDOUT: call void @_CH.Main(), !dbg !12 // CHECK:STDOUT: ret void, !dbg !13 // CHECK:STDOUT: } // CHECK:STDOUT: @@ -240,6 +241,7 @@ fn For() { // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: ; uselistorder directives +// CHECK:STDOUT: uselistorder ptr @"_COp.f9d363db90f1ff8b:core.Destroy.Core", { 1, 0 } // CHECK:STDOUT: uselistorder ptr @llvm.lifetime.start.p0, { 0, 3, 2, 1 } // CHECK:STDOUT: // CHECK:STDOUT: attributes #0 = { nounwind } diff --git a/toolchain/lower/testdata/var/destroy_control_flow.carbon b/toolchain/lower/testdata/var/destroy_control_flow.carbon new file mode 100644 index 000000000000..84def1754bfe --- /dev/null +++ b/toolchain/lower/testdata/var/destroy_control_flow.carbon @@ -0,0 +1,588 @@ +// 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 +// +// INCLUDE-FILE: toolchain/testing/testdata/min_prelude/primitives.carbon +// +// AUTOUPDATE +// TIP: To test this file alone, run: +// TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/lower/testdata/var/destroy_control_flow.carbon +// TIP: To dump output, run: +// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/lower/testdata/var/destroy_control_flow.carbon + +// --- destructible.carbon + +library "[[@TEST_NAME]]"; + +class Destructible { + // TODO: Implement Core.Destructor instead. + impl as Core.Destroy { + fn Op(self); + } +}; + +fn Use(ref d: Destructible); + +// --- if_then.carbon + +library "[[@TEST_NAME]]"; +import library "destructible"; + +fn IfThen(cond: bool) { + var a: Destructible = {}; + Use(ref a); + if (cond) { + var b: Destructible = {}; + Use(ref b); + } + Use(ref a); +} + +// --- if_else.carbon + +library "[[@TEST_NAME]]"; +import library "destructible"; + +fn IfElse(cond: bool) { + var a: Destructible = {}; + Use(ref a); + if (cond) { + var b: Destructible = {}; + Use(ref b); + } else { + var c: Destructible = {}; + Use(ref c); + } + Use(ref a); +} + +// --- if_return.carbon + +library "[[@TEST_NAME]]"; +import library "destructible"; + +fn IfReturn(cond: bool) { + var a: Destructible = {}; + Use(ref a); + if (cond) { + var b: Destructible = {}; + Use(ref b); + return; + } +} + +// --- while_break.carbon + +library "[[@TEST_NAME]]"; +import library "destructible"; + +fn WhileBreak(cond: bool) { + var a: Destructible = {}; + Use(ref a); + while (cond) { + Use(ref a); + var b: Destructible = {}; + Use(ref b); + if (cond) { + var c: Destructible = {}; + Use(ref c); + break; + } + } + Use(ref a); +} + +// --- while_continue.carbon + +library "[[@TEST_NAME]]"; +import library "destructible"; + +fn WhileContinue(cond: bool) { + var a: Destructible = {}; + Use(ref a); + while (cond) { + Use(ref a); + var b: Destructible = {}; + Use(ref b); + if (cond) { + var c: Destructible = {}; + Use(ref c); + continue; + } + } + Use(ref a); +} + +// CHECK:STDOUT: ; --- +// CHECK:STDOUT: ; ModuleID = 'destructible.carbon' +// CHECK:STDOUT: source_filename = "destructible.carbon" +// CHECK:STDOUT: +// CHECK:STDOUT: declare void @"_COp.Destructible.Main:Destroy.Core"(ptr) +// CHECK:STDOUT: +// CHECK:STDOUT: ; Function Attrs: alwaysinline nounwind +// CHECK:STDOUT: define void @"_COp:thunk:Destroy.25364816f7046881.Core:Destructible.Main:Destroy.Core"(ptr %self) #0 !dbg !4 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: call void @"_COp.Destructible.Main:Destroy.Core"(ptr %self), !dbg !10 +// CHECK:STDOUT: ret void, !dbg !10 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: declare void @_CUse.Main(ptr) +// CHECK:STDOUT: +// CHECK:STDOUT: attributes #0 = { alwaysinline nounwind } +// CHECK:STDOUT: +// CHECK:STDOUT: !llvm.dbg.cu = !{!0} +// CHECK:STDOUT: !llvm.module.flags = !{!2, !3} +// CHECK:STDOUT: +// CHECK:STDOUT: !0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus, file: !1, producer: "carbon", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug) +// CHECK:STDOUT: !1 = !DIFile(filename: "destructible.carbon", directory: "") +// CHECK:STDOUT: !2 = !{i32 7, !"Dwarf Version", i32 5} +// CHECK:STDOUT: !3 = !{i32 2, !"Debug Info Version", i32 3} +// CHECK:STDOUT: !4 = distinct !DISubprogram(name: "Op", linkageName: "_COp:thunk:Destroy.25364816f7046881.Core:Destructible.Main:Destroy.Core", scope: null, file: !1, line: 7, type: !5, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !8) +// CHECK:STDOUT: !5 = !DISubroutineType(types: !6) +// CHECK:STDOUT: !6 = !{null, !7} +// CHECK:STDOUT: !7 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: null, size: 64) +// CHECK:STDOUT: !8 = !{!9} +// CHECK:STDOUT: !9 = !DILocalVariable(arg: 1, scope: !4, type: !7) +// CHECK:STDOUT: !10 = !DILocation(line: 7, column: 5, scope: !4) +// CHECK:STDOUT: +// CHECK:STDOUT: ; --- +// CHECK:STDOUT: ; ModuleID = 'if_then.carbon' +// CHECK:STDOUT: source_filename = "if_then.carbon" +// CHECK:STDOUT: +// CHECK:STDOUT: @Destructible.val.loc6_3 = internal constant {} zeroinitializer +// CHECK:STDOUT: +// CHECK:STDOUT: ; Function Attrs: nounwind +// CHECK:STDOUT: define void @_CIfThen.Main(i1 %cond) #0 !dbg !4 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: %a.var = alloca {}, align 1, !dbg !10 +// CHECK:STDOUT: %b.var = alloca {}, align 1, !dbg !11 +// CHECK:STDOUT: call void @llvm.lifetime.start.p0(ptr %a.var), !dbg !10 +// CHECK:STDOUT: call void @llvm.memcpy.p0.p0.i64(ptr align 1 %a.var, ptr align 1 @Destructible.val.loc6_3, i64 0, i1 false), !dbg !10 +// CHECK:STDOUT: call void @_CUse.Main(ptr %a.var), !dbg !12 +// CHECK:STDOUT: br i1 %cond, label %if.then, label %if.else, !dbg !13 +// CHECK:STDOUT: +// CHECK:STDOUT: if.then: ; preds = %entry +// CHECK:STDOUT: call void @llvm.lifetime.start.p0(ptr %b.var), !dbg !11 +// CHECK:STDOUT: call void @llvm.memcpy.p0.p0.i64(ptr align 1 %b.var, ptr align 1 @Destructible.val.loc6_3, i64 0, i1 false), !dbg !11 +// CHECK:STDOUT: call void @_CUse.Main(ptr %b.var), !dbg !14 +// CHECK:STDOUT: call void @"_COp.eef1c8b64ca90051:core.Destroy.Core"(ptr %b.var), !dbg !11 +// CHECK:STDOUT: br label %if.else, !dbg !15 +// CHECK:STDOUT: +// CHECK:STDOUT: if.else: ; preds = %if.then, %entry +// CHECK:STDOUT: call void @_CUse.Main(ptr %a.var), !dbg !16 +// CHECK:STDOUT: call void @"_COp.eef1c8b64ca90051:core.Destroy.Core"(ptr %a.var), !dbg !10 +// CHECK:STDOUT: ret void, !dbg !17 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: declare void @_CUse.Main(ptr) +// CHECK:STDOUT: +// CHECK:STDOUT: ; Function Attrs: nounwind +// CHECK:STDOUT: define weak_odr void @"_COp.eef1c8b64ca90051:core.Destroy.Core"(ptr %self) #0 !dbg !18 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: ret void, !dbg !21 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) +// CHECK:STDOUT: declare void @llvm.lifetime.start.p0(ptr captures(none)) #1 +// CHECK:STDOUT: +// CHECK:STDOUT: ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) +// CHECK:STDOUT: declare void @llvm.memcpy.p0.p0.i64(ptr noalias writeonly captures(none), ptr noalias readonly captures(none), i64, i1 immarg) #1 +// CHECK:STDOUT: +// CHECK:STDOUT: ; uselistorder directives +// CHECK:STDOUT: uselistorder ptr @_CUse.Main, { 2, 1, 0 } +// CHECK:STDOUT: uselistorder ptr @"_COp.eef1c8b64ca90051:core.Destroy.Core", { 1, 0 } +// CHECK:STDOUT: uselistorder ptr @llvm.lifetime.start.p0, { 1, 0 } +// CHECK:STDOUT: uselistorder ptr @llvm.memcpy.p0.p0.i64, { 1, 0 } +// CHECK:STDOUT: +// CHECK:STDOUT: attributes #0 = { nounwind } +// CHECK:STDOUT: attributes #1 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } +// CHECK:STDOUT: +// CHECK:STDOUT: !llvm.dbg.cu = !{!0} +// CHECK:STDOUT: !llvm.module.flags = !{!2, !3} +// CHECK:STDOUT: +// CHECK:STDOUT: !0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus, file: !1, producer: "carbon", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug) +// CHECK:STDOUT: !1 = !DIFile(filename: "if_then.carbon", directory: "") +// CHECK:STDOUT: !2 = !{i32 7, !"Dwarf Version", i32 5} +// CHECK:STDOUT: !3 = !{i32 2, !"Debug Info Version", i32 3} +// CHECK:STDOUT: !4 = distinct !DISubprogram(name: "IfThen", linkageName: "_CIfThen.Main", scope: null, file: !1, line: 5, type: !5, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !8) +// CHECK:STDOUT: !5 = !DISubroutineType(types: !6) +// CHECK:STDOUT: !6 = !{null, !7} +// CHECK:STDOUT: !7 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: null, size: 64) +// CHECK:STDOUT: !8 = !{!9} +// CHECK:STDOUT: !9 = !DILocalVariable(arg: 1, scope: !4, type: !7) +// CHECK:STDOUT: !10 = !DILocation(line: 6, column: 3, scope: !4) +// CHECK:STDOUT: !11 = !DILocation(line: 9, column: 5, scope: !4) +// CHECK:STDOUT: !12 = !DILocation(line: 7, column: 3, scope: !4) +// CHECK:STDOUT: !13 = !DILocation(line: 8, column: 6, scope: !4) +// CHECK:STDOUT: !14 = !DILocation(line: 10, column: 5, scope: !4) +// CHECK:STDOUT: !15 = !DILocation(line: 8, column: 3, scope: !4) +// CHECK:STDOUT: !16 = !DILocation(line: 12, column: 3, scope: !4) +// CHECK:STDOUT: !17 = !DILocation(line: 5, column: 1, scope: !4) +// CHECK:STDOUT: !18 = distinct !DISubprogram(name: "Op", linkageName: "_COp.eef1c8b64ca90051:core.Destroy.Core", scope: null, file: !1, line: 9, type: !5, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !19) +// CHECK:STDOUT: !19 = !{!20} +// CHECK:STDOUT: !20 = !DILocalVariable(arg: 1, scope: !18, type: !7) +// CHECK:STDOUT: !21 = !DILocation(line: 9, column: 5, scope: !18) +// CHECK:STDOUT: +// CHECK:STDOUT: ; --- +// CHECK:STDOUT: ; ModuleID = 'if_else.carbon' +// CHECK:STDOUT: source_filename = "if_else.carbon" +// CHECK:STDOUT: +// CHECK:STDOUT: @Destructible.val.loc6_3 = internal constant {} zeroinitializer +// CHECK:STDOUT: +// CHECK:STDOUT: ; Function Attrs: nounwind +// CHECK:STDOUT: define void @_CIfElse.Main(i1 %cond) #0 !dbg !4 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: %a.var = alloca {}, align 1, !dbg !10 +// CHECK:STDOUT: %b.var = alloca {}, align 1, !dbg !11 +// CHECK:STDOUT: %c.var = alloca {}, align 1, !dbg !12 +// CHECK:STDOUT: call void @llvm.lifetime.start.p0(ptr %a.var), !dbg !10 +// CHECK:STDOUT: call void @llvm.memcpy.p0.p0.i64(ptr align 1 %a.var, ptr align 1 @Destructible.val.loc6_3, i64 0, i1 false), !dbg !10 +// CHECK:STDOUT: call void @_CUse.Main(ptr %a.var), !dbg !13 +// CHECK:STDOUT: br i1 %cond, label %if.then, label %if.else, !dbg !14 +// CHECK:STDOUT: +// CHECK:STDOUT: if.then: ; preds = %entry +// CHECK:STDOUT: call void @llvm.lifetime.start.p0(ptr %b.var), !dbg !11 +// CHECK:STDOUT: call void @llvm.memcpy.p0.p0.i64(ptr align 1 %b.var, ptr align 1 @Destructible.val.loc6_3, i64 0, i1 false), !dbg !11 +// CHECK:STDOUT: call void @_CUse.Main(ptr %b.var), !dbg !15 +// CHECK:STDOUT: call void @"_COp.eef1c8b64ca90051:core.Destroy.Core"(ptr %b.var), !dbg !11 +// CHECK:STDOUT: br label %if.done, !dbg !16 +// CHECK:STDOUT: +// CHECK:STDOUT: if.else: ; preds = %entry +// CHECK:STDOUT: call void @llvm.lifetime.start.p0(ptr %c.var), !dbg !12 +// CHECK:STDOUT: call void @llvm.memcpy.p0.p0.i64(ptr align 1 %c.var, ptr align 1 @Destructible.val.loc6_3, i64 0, i1 false), !dbg !12 +// CHECK:STDOUT: call void @_CUse.Main(ptr %c.var), !dbg !17 +// CHECK:STDOUT: call void @"_COp.eef1c8b64ca90051:core.Destroy.Core"(ptr %c.var), !dbg !12 +// CHECK:STDOUT: br label %if.done, !dbg !16 +// CHECK:STDOUT: +// CHECK:STDOUT: if.done: ; preds = %if.else, %if.then +// CHECK:STDOUT: call void @_CUse.Main(ptr %a.var), !dbg !18 +// CHECK:STDOUT: call void @"_COp.eef1c8b64ca90051:core.Destroy.Core"(ptr %a.var), !dbg !10 +// CHECK:STDOUT: ret void, !dbg !19 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: declare void @_CUse.Main(ptr) +// CHECK:STDOUT: +// CHECK:STDOUT: ; Function Attrs: nounwind +// CHECK:STDOUT: define weak_odr void @"_COp.eef1c8b64ca90051:core.Destroy.Core"(ptr %self) #0 !dbg !20 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: ret void, !dbg !23 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) +// CHECK:STDOUT: declare void @llvm.lifetime.start.p0(ptr captures(none)) #1 +// CHECK:STDOUT: +// CHECK:STDOUT: ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) +// CHECK:STDOUT: declare void @llvm.memcpy.p0.p0.i64(ptr noalias writeonly captures(none), ptr noalias readonly captures(none), i64, i1 immarg) #1 +// CHECK:STDOUT: +// CHECK:STDOUT: ; uselistorder directives +// CHECK:STDOUT: uselistorder ptr @_CUse.Main, { 3, 2, 1, 0 } +// CHECK:STDOUT: uselistorder ptr @"_COp.eef1c8b64ca90051:core.Destroy.Core", { 2, 1, 0 } +// CHECK:STDOUT: uselistorder ptr @llvm.lifetime.start.p0, { 2, 1, 0 } +// CHECK:STDOUT: uselistorder ptr @llvm.memcpy.p0.p0.i64, { 2, 1, 0 } +// CHECK:STDOUT: +// CHECK:STDOUT: attributes #0 = { nounwind } +// CHECK:STDOUT: attributes #1 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } +// CHECK:STDOUT: +// CHECK:STDOUT: !llvm.dbg.cu = !{!0} +// CHECK:STDOUT: !llvm.module.flags = !{!2, !3} +// CHECK:STDOUT: +// CHECK:STDOUT: !0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus, file: !1, producer: "carbon", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug) +// CHECK:STDOUT: !1 = !DIFile(filename: "if_else.carbon", directory: "") +// CHECK:STDOUT: !2 = !{i32 7, !"Dwarf Version", i32 5} +// CHECK:STDOUT: !3 = !{i32 2, !"Debug Info Version", i32 3} +// CHECK:STDOUT: !4 = distinct !DISubprogram(name: "IfElse", linkageName: "_CIfElse.Main", scope: null, file: !1, line: 5, type: !5, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !8) +// CHECK:STDOUT: !5 = !DISubroutineType(types: !6) +// CHECK:STDOUT: !6 = !{null, !7} +// CHECK:STDOUT: !7 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: null, size: 64) +// CHECK:STDOUT: !8 = !{!9} +// CHECK:STDOUT: !9 = !DILocalVariable(arg: 1, scope: !4, type: !7) +// CHECK:STDOUT: !10 = !DILocation(line: 6, column: 3, scope: !4) +// CHECK:STDOUT: !11 = !DILocation(line: 9, column: 5, scope: !4) +// CHECK:STDOUT: !12 = !DILocation(line: 12, column: 5, scope: !4) +// CHECK:STDOUT: !13 = !DILocation(line: 7, column: 3, scope: !4) +// CHECK:STDOUT: !14 = !DILocation(line: 8, column: 6, scope: !4) +// CHECK:STDOUT: !15 = !DILocation(line: 10, column: 5, scope: !4) +// CHECK:STDOUT: !16 = !DILocation(line: 8, column: 3, scope: !4) +// CHECK:STDOUT: !17 = !DILocation(line: 13, column: 5, scope: !4) +// CHECK:STDOUT: !18 = !DILocation(line: 15, column: 3, scope: !4) +// CHECK:STDOUT: !19 = !DILocation(line: 5, column: 1, scope: !4) +// CHECK:STDOUT: !20 = distinct !DISubprogram(name: "Op", linkageName: "_COp.eef1c8b64ca90051:core.Destroy.Core", scope: null, file: !1, line: 9, type: !5, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !21) +// CHECK:STDOUT: !21 = !{!22} +// CHECK:STDOUT: !22 = !DILocalVariable(arg: 1, scope: !20, type: !7) +// CHECK:STDOUT: !23 = !DILocation(line: 9, column: 5, scope: !20) +// CHECK:STDOUT: +// CHECK:STDOUT: ; --- +// CHECK:STDOUT: ; ModuleID = 'if_return.carbon' +// CHECK:STDOUT: source_filename = "if_return.carbon" +// CHECK:STDOUT: +// CHECK:STDOUT: @Destructible.val.loc6_3 = internal constant {} zeroinitializer +// CHECK:STDOUT: +// CHECK:STDOUT: ; Function Attrs: nounwind +// CHECK:STDOUT: define void @_CIfReturn.Main(i1 %cond) #0 !dbg !4 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: %a.var = alloca {}, align 1, !dbg !10 +// CHECK:STDOUT: %b.var = alloca {}, align 1, !dbg !11 +// CHECK:STDOUT: call void @llvm.lifetime.start.p0(ptr %a.var), !dbg !10 +// CHECK:STDOUT: call void @llvm.memcpy.p0.p0.i64(ptr align 1 %a.var, ptr align 1 @Destructible.val.loc6_3, i64 0, i1 false), !dbg !10 +// CHECK:STDOUT: call void @_CUse.Main(ptr %a.var), !dbg !12 +// CHECK:STDOUT: br i1 %cond, label %if.then, label %if.else, !dbg !13 +// CHECK:STDOUT: +// CHECK:STDOUT: if.then: ; preds = %entry +// CHECK:STDOUT: call void @llvm.lifetime.start.p0(ptr %b.var), !dbg !11 +// CHECK:STDOUT: call void @llvm.memcpy.p0.p0.i64(ptr align 1 %b.var, ptr align 1 @Destructible.val.loc6_3, i64 0, i1 false), !dbg !11 +// CHECK:STDOUT: call void @_CUse.Main(ptr %b.var), !dbg !14 +// CHECK:STDOUT: call void @"_COp.eef1c8b64ca90051:core.Destroy.Core"(ptr %b.var), !dbg !11 +// CHECK:STDOUT: call void @"_COp.eef1c8b64ca90051:core.Destroy.Core"(ptr %a.var), !dbg !10 +// CHECK:STDOUT: ret void, !dbg !15 +// CHECK:STDOUT: +// CHECK:STDOUT: if.else: ; preds = %entry +// CHECK:STDOUT: call void @"_COp.eef1c8b64ca90051:core.Destroy.Core"(ptr %a.var), !dbg !10 +// CHECK:STDOUT: ret void, !dbg !16 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: declare void @_CUse.Main(ptr) +// CHECK:STDOUT: +// CHECK:STDOUT: ; Function Attrs: nounwind +// CHECK:STDOUT: define weak_odr void @"_COp.eef1c8b64ca90051:core.Destroy.Core"(ptr %self) #0 !dbg !17 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: ret void, !dbg !20 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) +// CHECK:STDOUT: declare void @llvm.lifetime.start.p0(ptr captures(none)) #1 +// CHECK:STDOUT: +// CHECK:STDOUT: ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) +// CHECK:STDOUT: declare void @llvm.memcpy.p0.p0.i64(ptr noalias writeonly captures(none), ptr noalias readonly captures(none), i64, i1 immarg) #1 +// CHECK:STDOUT: +// CHECK:STDOUT: ; uselistorder directives +// CHECK:STDOUT: uselistorder ptr @_CUse.Main, { 1, 0 } +// CHECK:STDOUT: uselistorder ptr @"_COp.eef1c8b64ca90051:core.Destroy.Core", { 2, 1, 0 } +// CHECK:STDOUT: uselistorder ptr @llvm.lifetime.start.p0, { 1, 0 } +// CHECK:STDOUT: uselistorder ptr @llvm.memcpy.p0.p0.i64, { 1, 0 } +// CHECK:STDOUT: +// CHECK:STDOUT: attributes #0 = { nounwind } +// CHECK:STDOUT: attributes #1 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } +// CHECK:STDOUT: +// CHECK:STDOUT: !llvm.dbg.cu = !{!0} +// CHECK:STDOUT: !llvm.module.flags = !{!2, !3} +// CHECK:STDOUT: +// CHECK:STDOUT: !0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus, file: !1, producer: "carbon", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug) +// CHECK:STDOUT: !1 = !DIFile(filename: "if_return.carbon", directory: "") +// CHECK:STDOUT: !2 = !{i32 7, !"Dwarf Version", i32 5} +// CHECK:STDOUT: !3 = !{i32 2, !"Debug Info Version", i32 3} +// CHECK:STDOUT: !4 = distinct !DISubprogram(name: "IfReturn", linkageName: "_CIfReturn.Main", scope: null, file: !1, line: 5, type: !5, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !8) +// CHECK:STDOUT: !5 = !DISubroutineType(types: !6) +// CHECK:STDOUT: !6 = !{null, !7} +// CHECK:STDOUT: !7 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: null, size: 64) +// CHECK:STDOUT: !8 = !{!9} +// CHECK:STDOUT: !9 = !DILocalVariable(arg: 1, scope: !4, type: !7) +// CHECK:STDOUT: !10 = !DILocation(line: 6, column: 3, scope: !4) +// CHECK:STDOUT: !11 = !DILocation(line: 9, column: 5, scope: !4) +// CHECK:STDOUT: !12 = !DILocation(line: 7, column: 3, scope: !4) +// CHECK:STDOUT: !13 = !DILocation(line: 8, column: 6, scope: !4) +// CHECK:STDOUT: !14 = !DILocation(line: 10, column: 5, scope: !4) +// CHECK:STDOUT: !15 = !DILocation(line: 11, column: 5, scope: !4) +// CHECK:STDOUT: !16 = !DILocation(line: 5, column: 1, scope: !4) +// CHECK:STDOUT: !17 = distinct !DISubprogram(name: "Op", linkageName: "_COp.eef1c8b64ca90051:core.Destroy.Core", scope: null, file: !1, line: 9, type: !5, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !18) +// CHECK:STDOUT: !18 = !{!19} +// CHECK:STDOUT: !19 = !DILocalVariable(arg: 1, scope: !17, type: !7) +// CHECK:STDOUT: !20 = !DILocation(line: 9, column: 5, scope: !17) +// CHECK:STDOUT: +// CHECK:STDOUT: ; --- +// CHECK:STDOUT: ; ModuleID = 'while_break.carbon' +// CHECK:STDOUT: source_filename = "while_break.carbon" +// CHECK:STDOUT: +// CHECK:STDOUT: @Destructible.val.loc6_3 = internal constant {} zeroinitializer +// CHECK:STDOUT: +// CHECK:STDOUT: ; Function Attrs: nounwind +// CHECK:STDOUT: define void @_CWhileBreak.Main(i1 %cond) #0 !dbg !4 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: %a.var = alloca {}, align 1, !dbg !10 +// CHECK:STDOUT: %b.var = alloca {}, align 1, !dbg !11 +// CHECK:STDOUT: %c.var = alloca {}, align 1, !dbg !12 +// CHECK:STDOUT: call void @llvm.lifetime.start.p0(ptr %a.var), !dbg !10 +// CHECK:STDOUT: call void @llvm.memcpy.p0.p0.i64(ptr align 1 %a.var, ptr align 1 @Destructible.val.loc6_3, i64 0, i1 false), !dbg !10 +// CHECK:STDOUT: call void @_CUse.Main(ptr %a.var), !dbg !13 +// CHECK:STDOUT: br label %while.cond, !dbg !14 +// CHECK:STDOUT: +// CHECK:STDOUT: while.cond: ; preds = %if.else, %entry +// CHECK:STDOUT: br i1 %cond, label %while.body, label %while.done, !dbg !14 +// CHECK:STDOUT: +// CHECK:STDOUT: while.body: ; preds = %while.cond +// CHECK:STDOUT: call void @_CUse.Main(ptr %a.var), !dbg !15 +// CHECK:STDOUT: call void @llvm.lifetime.start.p0(ptr %b.var), !dbg !11 +// CHECK:STDOUT: call void @llvm.memcpy.p0.p0.i64(ptr align 1 %b.var, ptr align 1 @Destructible.val.loc6_3, i64 0, i1 false), !dbg !11 +// CHECK:STDOUT: call void @_CUse.Main(ptr %b.var), !dbg !16 +// CHECK:STDOUT: br i1 %cond, label %if.then, label %if.else, !dbg !17 +// CHECK:STDOUT: +// CHECK:STDOUT: if.then: ; preds = %while.body +// CHECK:STDOUT: call void @llvm.lifetime.start.p0(ptr %c.var), !dbg !12 +// CHECK:STDOUT: call void @llvm.memcpy.p0.p0.i64(ptr align 1 %c.var, ptr align 1 @Destructible.val.loc6_3, i64 0, i1 false), !dbg !12 +// CHECK:STDOUT: call void @_CUse.Main(ptr %c.var), !dbg !18 +// CHECK:STDOUT: call void @"_COp.eef1c8b64ca90051:core.Destroy.Core"(ptr %c.var), !dbg !12 +// CHECK:STDOUT: call void @"_COp.eef1c8b64ca90051:core.Destroy.Core"(ptr %b.var), !dbg !11 +// CHECK:STDOUT: br label %while.done, !dbg !19 +// CHECK:STDOUT: +// CHECK:STDOUT: if.else: ; preds = %while.body +// CHECK:STDOUT: call void @"_COp.eef1c8b64ca90051:core.Destroy.Core"(ptr %b.var), !dbg !11 +// CHECK:STDOUT: br label %while.cond, !dbg !20 +// CHECK:STDOUT: +// CHECK:STDOUT: while.done: ; preds = %if.then, %while.cond +// CHECK:STDOUT: call void @_CUse.Main(ptr %a.var), !dbg !21 +// CHECK:STDOUT: call void @"_COp.eef1c8b64ca90051:core.Destroy.Core"(ptr %a.var), !dbg !10 +// CHECK:STDOUT: ret void, !dbg !22 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: declare void @_CUse.Main(ptr) +// CHECK:STDOUT: +// CHECK:STDOUT: ; Function Attrs: nounwind +// CHECK:STDOUT: define weak_odr void @"_COp.eef1c8b64ca90051:core.Destroy.Core"(ptr %self) #0 !dbg !23 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: ret void, !dbg !26 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) +// CHECK:STDOUT: declare void @llvm.lifetime.start.p0(ptr captures(none)) #1 +// CHECK:STDOUT: +// CHECK:STDOUT: ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) +// CHECK:STDOUT: declare void @llvm.memcpy.p0.p0.i64(ptr noalias writeonly captures(none), ptr noalias readonly captures(none), i64, i1 immarg) #1 +// CHECK:STDOUT: +// CHECK:STDOUT: ; uselistorder directives +// CHECK:STDOUT: uselistorder ptr @_CUse.Main, { 4, 3, 2, 1, 0 } +// CHECK:STDOUT: uselistorder ptr @"_COp.eef1c8b64ca90051:core.Destroy.Core", { 3, 2, 1, 0 } +// CHECK:STDOUT: uselistorder ptr @llvm.lifetime.start.p0, { 2, 1, 0 } +// CHECK:STDOUT: uselistorder ptr @llvm.memcpy.p0.p0.i64, { 2, 1, 0 } +// CHECK:STDOUT: +// CHECK:STDOUT: attributes #0 = { nounwind } +// CHECK:STDOUT: attributes #1 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } +// CHECK:STDOUT: +// CHECK:STDOUT: !llvm.dbg.cu = !{!0} +// CHECK:STDOUT: !llvm.module.flags = !{!2, !3} +// CHECK:STDOUT: +// CHECK:STDOUT: !0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus, file: !1, producer: "carbon", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug) +// CHECK:STDOUT: !1 = !DIFile(filename: "while_break.carbon", directory: "") +// CHECK:STDOUT: !2 = !{i32 7, !"Dwarf Version", i32 5} +// CHECK:STDOUT: !3 = !{i32 2, !"Debug Info Version", i32 3} +// CHECK:STDOUT: !4 = distinct !DISubprogram(name: "WhileBreak", linkageName: "_CWhileBreak.Main", scope: null, file: !1, line: 5, type: !5, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !8) +// CHECK:STDOUT: !5 = !DISubroutineType(types: !6) +// CHECK:STDOUT: !6 = !{null, !7} +// CHECK:STDOUT: !7 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: null, size: 64) +// CHECK:STDOUT: !8 = !{!9} +// CHECK:STDOUT: !9 = !DILocalVariable(arg: 1, scope: !4, type: !7) +// CHECK:STDOUT: !10 = !DILocation(line: 6, column: 3, scope: !4) +// CHECK:STDOUT: !11 = !DILocation(line: 10, column: 5, scope: !4) +// CHECK:STDOUT: !12 = !DILocation(line: 13, column: 7, scope: !4) +// CHECK:STDOUT: !13 = !DILocation(line: 7, column: 3, scope: !4) +// CHECK:STDOUT: !14 = !DILocation(line: 8, column: 9, scope: !4) +// CHECK:STDOUT: !15 = !DILocation(line: 9, column: 5, scope: !4) +// CHECK:STDOUT: !16 = !DILocation(line: 11, column: 5, scope: !4) +// CHECK:STDOUT: !17 = !DILocation(line: 12, column: 8, scope: !4) +// CHECK:STDOUT: !18 = !DILocation(line: 14, column: 7, scope: !4) +// CHECK:STDOUT: !19 = !DILocation(line: 15, column: 7, scope: !4) +// CHECK:STDOUT: !20 = !DILocation(line: 8, column: 3, scope: !4) +// CHECK:STDOUT: !21 = !DILocation(line: 18, column: 3, scope: !4) +// CHECK:STDOUT: !22 = !DILocation(line: 5, column: 1, scope: !4) +// CHECK:STDOUT: !23 = distinct !DISubprogram(name: "Op", linkageName: "_COp.eef1c8b64ca90051:core.Destroy.Core", scope: null, file: !1, line: 13, type: !5, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !24) +// CHECK:STDOUT: !24 = !{!25} +// CHECK:STDOUT: !25 = !DILocalVariable(arg: 1, scope: !23, type: !7) +// CHECK:STDOUT: !26 = !DILocation(line: 13, column: 7, scope: !23) +// CHECK:STDOUT: +// CHECK:STDOUT: ; --- +// CHECK:STDOUT: ; ModuleID = 'while_continue.carbon' +// CHECK:STDOUT: source_filename = "while_continue.carbon" +// CHECK:STDOUT: +// CHECK:STDOUT: @Destructible.val.loc6_3 = internal constant {} zeroinitializer +// CHECK:STDOUT: +// CHECK:STDOUT: ; Function Attrs: nounwind +// CHECK:STDOUT: define void @_CWhileContinue.Main(i1 %cond) #0 !dbg !4 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: %a.var = alloca {}, align 1, !dbg !10 +// CHECK:STDOUT: %b.var = alloca {}, align 1, !dbg !11 +// CHECK:STDOUT: %c.var = alloca {}, align 1, !dbg !12 +// CHECK:STDOUT: call void @llvm.lifetime.start.p0(ptr %a.var), !dbg !10 +// CHECK:STDOUT: call void @llvm.memcpy.p0.p0.i64(ptr align 1 %a.var, ptr align 1 @Destructible.val.loc6_3, i64 0, i1 false), !dbg !10 +// CHECK:STDOUT: call void @_CUse.Main(ptr %a.var), !dbg !13 +// CHECK:STDOUT: br label %while.cond, !dbg !14 +// CHECK:STDOUT: +// CHECK:STDOUT: while.cond: ; preds = %if.else, %if.then, %entry +// CHECK:STDOUT: br i1 %cond, label %while.body, label %while.done, !dbg !14 +// CHECK:STDOUT: +// CHECK:STDOUT: while.body: ; preds = %while.cond +// CHECK:STDOUT: call void @_CUse.Main(ptr %a.var), !dbg !15 +// CHECK:STDOUT: call void @llvm.lifetime.start.p0(ptr %b.var), !dbg !11 +// CHECK:STDOUT: call void @llvm.memcpy.p0.p0.i64(ptr align 1 %b.var, ptr align 1 @Destructible.val.loc6_3, i64 0, i1 false), !dbg !11 +// CHECK:STDOUT: call void @_CUse.Main(ptr %b.var), !dbg !16 +// CHECK:STDOUT: br i1 %cond, label %if.then, label %if.else, !dbg !17 +// CHECK:STDOUT: +// CHECK:STDOUT: if.then: ; preds = %while.body +// CHECK:STDOUT: call void @llvm.lifetime.start.p0(ptr %c.var), !dbg !12 +// CHECK:STDOUT: call void @llvm.memcpy.p0.p0.i64(ptr align 1 %c.var, ptr align 1 @Destructible.val.loc6_3, i64 0, i1 false), !dbg !12 +// CHECK:STDOUT: call void @_CUse.Main(ptr %c.var), !dbg !18 +// CHECK:STDOUT: call void @"_COp.eef1c8b64ca90051:core.Destroy.Core"(ptr %c.var), !dbg !12 +// CHECK:STDOUT: call void @"_COp.eef1c8b64ca90051:core.Destroy.Core"(ptr %b.var), !dbg !11 +// CHECK:STDOUT: br label %while.cond, !dbg !19 +// CHECK:STDOUT: +// CHECK:STDOUT: if.else: ; preds = %while.body +// CHECK:STDOUT: call void @"_COp.eef1c8b64ca90051:core.Destroy.Core"(ptr %b.var), !dbg !11 +// CHECK:STDOUT: br label %while.cond, !dbg !20 +// CHECK:STDOUT: +// CHECK:STDOUT: while.done: ; preds = %while.cond +// CHECK:STDOUT: call void @_CUse.Main(ptr %a.var), !dbg !21 +// CHECK:STDOUT: call void @"_COp.eef1c8b64ca90051:core.Destroy.Core"(ptr %a.var), !dbg !10 +// CHECK:STDOUT: ret void, !dbg !22 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: declare void @_CUse.Main(ptr) +// CHECK:STDOUT: +// CHECK:STDOUT: ; Function Attrs: nounwind +// CHECK:STDOUT: define weak_odr void @"_COp.eef1c8b64ca90051:core.Destroy.Core"(ptr %self) #0 !dbg !23 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: ret void, !dbg !26 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) +// CHECK:STDOUT: declare void @llvm.lifetime.start.p0(ptr captures(none)) #1 +// CHECK:STDOUT: +// CHECK:STDOUT: ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) +// CHECK:STDOUT: declare void @llvm.memcpy.p0.p0.i64(ptr noalias writeonly captures(none), ptr noalias readonly captures(none), i64, i1 immarg) #1 +// CHECK:STDOUT: +// CHECK:STDOUT: ; uselistorder directives +// CHECK:STDOUT: uselistorder ptr @_CUse.Main, { 4, 3, 2, 1, 0 } +// CHECK:STDOUT: uselistorder ptr @"_COp.eef1c8b64ca90051:core.Destroy.Core", { 3, 2, 1, 0 } +// CHECK:STDOUT: uselistorder ptr @llvm.lifetime.start.p0, { 2, 1, 0 } +// CHECK:STDOUT: uselistorder ptr @llvm.memcpy.p0.p0.i64, { 2, 1, 0 } +// CHECK:STDOUT: +// CHECK:STDOUT: attributes #0 = { nounwind } +// CHECK:STDOUT: attributes #1 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } +// CHECK:STDOUT: +// CHECK:STDOUT: !llvm.dbg.cu = !{!0} +// CHECK:STDOUT: !llvm.module.flags = !{!2, !3} +// CHECK:STDOUT: +// CHECK:STDOUT: !0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus, file: !1, producer: "carbon", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug) +// CHECK:STDOUT: !1 = !DIFile(filename: "while_continue.carbon", directory: "") +// CHECK:STDOUT: !2 = !{i32 7, !"Dwarf Version", i32 5} +// CHECK:STDOUT: !3 = !{i32 2, !"Debug Info Version", i32 3} +// CHECK:STDOUT: !4 = distinct !DISubprogram(name: "WhileContinue", linkageName: "_CWhileContinue.Main", scope: null, file: !1, line: 5, type: !5, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !8) +// CHECK:STDOUT: !5 = !DISubroutineType(types: !6) +// CHECK:STDOUT: !6 = !{null, !7} +// CHECK:STDOUT: !7 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: null, size: 64) +// CHECK:STDOUT: !8 = !{!9} +// CHECK:STDOUT: !9 = !DILocalVariable(arg: 1, scope: !4, type: !7) +// CHECK:STDOUT: !10 = !DILocation(line: 6, column: 3, scope: !4) +// CHECK:STDOUT: !11 = !DILocation(line: 10, column: 5, scope: !4) +// CHECK:STDOUT: !12 = !DILocation(line: 13, column: 7, scope: !4) +// CHECK:STDOUT: !13 = !DILocation(line: 7, column: 3, scope: !4) +// CHECK:STDOUT: !14 = !DILocation(line: 8, column: 9, scope: !4) +// CHECK:STDOUT: !15 = !DILocation(line: 9, column: 5, scope: !4) +// CHECK:STDOUT: !16 = !DILocation(line: 11, column: 5, scope: !4) +// CHECK:STDOUT: !17 = !DILocation(line: 12, column: 8, scope: !4) +// CHECK:STDOUT: !18 = !DILocation(line: 14, column: 7, scope: !4) +// CHECK:STDOUT: !19 = !DILocation(line: 15, column: 7, scope: !4) +// CHECK:STDOUT: !20 = !DILocation(line: 8, column: 3, scope: !4) +// CHECK:STDOUT: !21 = !DILocation(line: 18, column: 3, scope: !4) +// CHECK:STDOUT: !22 = !DILocation(line: 5, column: 1, scope: !4) +// CHECK:STDOUT: !23 = distinct !DISubprogram(name: "Op", linkageName: "_COp.eef1c8b64ca90051:core.Destroy.Core", scope: null, file: !1, line: 13, type: !5, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !24) +// CHECK:STDOUT: !24 = !{!25} +// CHECK:STDOUT: !25 = !DILocalVariable(arg: 1, scope: !23, type: !7) +// CHECK:STDOUT: !26 = !DILocation(line: 13, column: 7, scope: !23) +// CHECK:STDOUT: diff --git a/toolchain/lower/testdata/var/nested.carbon b/toolchain/lower/testdata/var/nested.carbon index 95c045dec88d..59cb0a8f9c81 100644 --- a/toolchain/lower/testdata/var/nested.carbon +++ b/toolchain/lower/testdata/var/nested.carbon @@ -41,11 +41,11 @@ fn Run() -> i32 { // CHECK:STDOUT: store i32 %.loc16, ptr %b.var, align 4, !dbg !9 // CHECK:STDOUT: %.loc17 = load i32, ptr %b.var, align 4, !dbg !12 // CHECK:STDOUT: store i32 %.loc17, ptr %a.var, align 4, !dbg !13 +// CHECK:STDOUT: call void @"_COp.5e27612b9dd31a14:core.Destroy.Core"(ptr %b.var), !dbg !9 // CHECK:STDOUT: br label %while.cond, !dbg !14 // CHECK:STDOUT: // CHECK:STDOUT: while.done: ; preds = %while.cond // CHECK:STDOUT: %.loc19 = load i32, ptr %a.var, align 4, !dbg !15 -// CHECK:STDOUT: call void @"_COp.5e27612b9dd31a14:core.Destroy.Core"(ptr %b.var), !dbg !9 // CHECK:STDOUT: call void @"_COp.5e27612b9dd31a14:core.Destroy.Core"(ptr %a.var), !dbg !8 // CHECK:STDOUT: ret i32 %.loc19, !dbg !16 // CHECK:STDOUT: }