diff --git a/core/prelude.carbon b/core/prelude.carbon index a6a9e2a9d7a2..5f4012aa30d5 100644 --- a/core/prelude.carbon +++ b/core/prelude.carbon @@ -6,5 +6,6 @@ package Core library "prelude"; +export import library "prelude/iterate"; export import library "prelude/operators"; export import library "prelude/types"; diff --git a/core/prelude/iterate.carbon b/core/prelude/iterate.carbon new file mode 100644 index 000000000000..115286a2c419 --- /dev/null +++ b/core/prelude/iterate.carbon @@ -0,0 +1,14 @@ +// 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 + +package Core library "prelude/iterate"; + +export import library "prelude/types/optional"; + +interface Iterate { + let ElementType:! type; + let CursorType:! type; + fn NewCursor[self: Self]() -> CursorType; + fn Next[self: Self](cursor: CursorType*) -> Optional(ElementType); +} diff --git a/core/prelude/types.carbon b/core/prelude/types.carbon index 9ca9edba894b..c55062bfcb6b 100644 --- a/core/prelude/types.carbon +++ b/core/prelude/types.carbon @@ -7,6 +7,7 @@ package Core library "prelude/types"; export import library "prelude/types/bool"; export import library "prelude/types/int_literal"; export import library "prelude/types/int"; +export import library "prelude/types/optional"; export import library "prelude/types/uint"; fn Float(size: IntLiteral()) -> type = "float.make_type"; diff --git a/core/prelude/types/optional.carbon b/core/prelude/types/optional.carbon new file mode 100644 index 000000000000..bdbb7d0d52a5 --- /dev/null +++ b/core/prelude/types/optional.carbon @@ -0,0 +1,33 @@ +// 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 + +package Core library "prelude/types/optional"; + +import library "prelude/types/bool"; + +// For now, an `Optional(T)` is stored as a pair of a `bool` and a `T`, with +// the `T` left uninitialized if the `bool` is `false`. This isn't a viable +// approach in the longer term, but is the best we can do for now. +// +// TODO: Revisit this once we have choice types implemented in the toolchain. +// +// TODO: We don't have an approved design for an `Optional` type yet, but it's +// used by the design for `Iterate`. The API here is a placeholder. +class Optional(T:! type) { + fn None() -> Self { + returned var me: Self; + me.has_value = false; + return var; + } + + fn Some(value: T) -> Self { + return {.has_value = true, .value = value}; + } + + fn HasValue[self: Self]() -> bool { return self.has_value; } + fn Get[self: Self]() -> T { return self.value; } + + private var has_value: bool; + private var value: T; +} diff --git a/toolchain/check/BUILD b/toolchain/check/BUILD index 7533885009e2..8010240cbf7f 100644 --- a/toolchain/check/BUILD +++ b/toolchain/check/BUILD @@ -179,6 +179,7 @@ cc_library( ":context", ":diagnostic_emitter", ":dump", + ":scope_stack", "//common:check", "//common:error", "//common:find", @@ -197,6 +198,7 @@ cc_library( "//toolchain/lex:tokenized_buffer", "//toolchain/parse:node_kind", "//toolchain/parse:tree", + "//toolchain/sem_ir:absolute_node_id", "//toolchain/sem_ir:entry_point", "//toolchain/sem_ir:expr_info", "//toolchain/sem_ir:file", diff --git a/toolchain/check/eval_inst.cpp b/toolchain/check/eval_inst.cpp index 44b5e5729b48..c717d7b776e0 100644 --- a/toolchain/check/eval_inst.cpp +++ b/toolchain/check/eval_inst.cpp @@ -580,6 +580,12 @@ auto EvalConstantInst(Context& context, SemIR::ValueOfInitializer inst) auto EvalConstantInst(Context& context, SemIR::InstId inst_id, SemIR::VarStorage inst) -> ConstantEvalResult { + if (!inst.pattern_id.has_value()) { + // This variable was not created from a `var` pattern, so isn't a global + // variable. + return ConstantEvalResult::NotConstant; + } + // A variable is constant if it's global. auto entity_name_id = SemIR::GetFirstBindingNameFromPatternId( context.sem_ir(), inst.pattern_id); diff --git a/toolchain/check/handle_let_and_var.cpp b/toolchain/check/handle_let_and_var.cpp index 34db30700684..54b7f3cdb429 100644 --- a/toolchain/check/handle_let_and_var.cpp +++ b/toolchain/check/handle_let_and_var.cpp @@ -5,7 +5,6 @@ #include #include "toolchain/check/context.h" -#include "toolchain/check/control_flow.h" #include "toolchain/check/convert.h" #include "toolchain/check/decl_introducer_state.h" #include "toolchain/check/generic.h" @@ -16,7 +15,6 @@ #include "toolchain/check/modifiers.h" #include "toolchain/check/pattern.h" #include "toolchain/check/pattern_match.h" -#include "toolchain/check/return.h" #include "toolchain/diagnostics/diagnostic_emitter.h" #include "toolchain/diagnostics/format_providers.h" #include "toolchain/lex/token_kind.h" @@ -116,29 +114,6 @@ auto HandleParseNode(Context& context, Parse::FieldIntroducerId node_id) return true; } -// Returns a VarStorage inst for the given `var` pattern. If the pattern -// is the body of a returned var, this reuses the return slot, and otherwise it -// adds a new inst. -static auto GetOrAddStorage(Context& context, SemIR::InstId var_pattern_id) - -> SemIR::InstId { - if (context.decl_introducer_state_stack().innermost().modifier_set.HasAnyOf( - KeywordModifierSet::Returned)) { - auto& function = GetCurrentFunctionForReturn(context); - auto return_info = - SemIR::ReturnTypeInfo::ForFunction(context.sem_ir(), function); - if (return_info.has_return_slot()) { - return GetCurrentReturnSlot(context); - } - } - auto pattern = context.insts().GetWithLocId(var_pattern_id); - - return AddInstWithCleanup( - context, pattern.loc_id, - SemIR::VarStorage{.type_id = ExtractScrutineeType(context.sem_ir(), - pattern.inst.type_id()), - .pattern_id = var_pattern_id}); -} - auto HandleParseNode(Context& context, Parse::VariablePatternId node_id) -> bool { auto subpattern_id = context.node_stack().PopPattern(); @@ -183,17 +158,11 @@ static auto EndFullPattern(Context& context) -> void { AddInst(context, context.node_stack().PeekNodeId(), {.pattern_block_id = pattern_block_id}); - // We need to emit the VarStorage insts early, because they may be output - // arguments for the initializer. However, we can't emit them when we emit - // the corresponding `VarPattern`s because they're part of the pattern match, - // not part of the pattern. - // TODO: find a way to do this without walking the whole pattern block. - for (auto inst_id : context.inst_blocks().Get(pattern_block_id)) { - if (context.insts().Is(inst_id)) { - context.var_storage_map().Insert(inst_id, - GetOrAddStorage(context, inst_id)); - } - } + // Emit storage for any `var`s in the pattern now. + bool returned = + context.decl_introducer_state_stack().innermost().modifier_set.HasAnyOf( + KeywordModifierSet::Returned); + AddPatternVarStorage(context, pattern_block_id, returned); } static auto HandleInitializer(Context& context, Parse::NodeId node_id) -> bool { diff --git a/toolchain/check/handle_loop_statement.cpp b/toolchain/check/handle_loop_statement.cpp index 48a24219ac47..5eacab4658c5 100644 --- a/toolchain/check/handle_loop_statement.cpp +++ b/toolchain/check/handle_loop_statement.cpp @@ -2,19 +2,27 @@ // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +#include "toolchain/check/call.h" #include "toolchain/check/context.h" #include "toolchain/check/control_flow.h" #include "toolchain/check/convert.h" +#include "toolchain/check/full_pattern_stack.h" #include "toolchain/check/handle.h" #include "toolchain/check/inst.h" +#include "toolchain/check/member_access.h" +#include "toolchain/check/operator.h" +#include "toolchain/check/pattern.h" +#include "toolchain/check/pattern_match.h" +#include "toolchain/check/type.h" +#include "toolchain/sem_ir/absolute_node_id.h" +#include "toolchain/sem_ir/ids.h" namespace Carbon::Check { -// `while` -// ------- - -auto HandleParseNode(Context& context, Parse::WhileConditionStartId node_id) - -> bool { +// Starts emitting the loop header for a `while`-like looping construct. Returns +// the loop header block ID. +static auto StartLoopHeader(Context& context, Parse::NodeId node_id) + -> SemIR::InstBlockId { // Branch to the loop header block. Note that we create a new block here even // if the current block is empty; this ensures that the loop always has a // preheader block. @@ -25,15 +33,15 @@ auto HandleParseNode(Context& context, Parse::WhileConditionStartId node_id) context.inst_block_stack().Push(loop_header_id); context.region_stack().AddToRegion(loop_header_id, node_id); - context.node_stack().Push(node_id, loop_header_id); - return true; + return loop_header_id; } -auto HandleParseNode(Context& context, Parse::WhileConditionId node_id) - -> bool { - auto cond_value_id = context.node_stack().PopExpr(); - auto loop_header_id = - context.node_stack().Peek(); +// Starts emitting the loop body for a `while`-like looping construct. Converts +// `cond_value_id` to bool and branches to the loop body if it is `true` and to +// the loop exit if it is `false`. +static auto BranchAndStartLoopBody(Context& context, Parse::NodeId node_id, + SemIR::InstBlockId loop_header_id, + SemIR::InstId cond_value_id) -> void { cond_value_id = ConvertToBoolValue(context, node_id, cond_value_id); // Branch to either the loop body or the loop exit block. @@ -45,28 +53,51 @@ auto HandleParseNode(Context& context, Parse::WhileConditionId node_id) // Start emitting the loop body. context.inst_block_stack().Push(loop_body_id); context.region_stack().AddToRegion(loop_body_id, node_id); + + // Allow `break` and `continue` in this scope. context.break_continue_stack().push_back( {.break_target = loop_exit_id, .continue_target = loop_header_id}); +} - context.node_stack().Push(node_id, loop_exit_id); +// Finishes emitting the body for a `while`-like loop. Adds a back-edge to the +// loop header, and starts emitting in the loop exit block. +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}); + context.inst_block_stack().Pop(); + + // Start emitting the loop exit block. + context.inst_block_stack().Push(blocks.break_target); + context.region_stack().AddToRegion(blocks.break_target, node_id); +} + +// `while` +// ------- + +auto HandleParseNode(Context& context, Parse::WhileConditionStartId node_id) + -> bool { + context.node_stack().Push(node_id, StartLoopHeader(context, node_id)); + return true; +} + +auto HandleParseNode(Context& context, Parse::WhileConditionId node_id) + -> bool { + auto cond_value_id = context.node_stack().PopExpr(); + auto loop_header_id = + context.node_stack().Pop(); + + // Branch to either the loop body or the loop exit block, and start emitting + // the loop body. + BranchAndStartLoopBody(context, node_id, loop_header_id, cond_value_id); return true; } auto HandleParseNode(Context& context, Parse::WhileStatementId node_id) -> bool { - auto loop_exit_id = - context.node_stack().Pop(); - auto loop_header_id = - context.node_stack().Pop(); - context.break_continue_stack().pop_back(); - - // Add the loop backedge. - AddInst(context, node_id, {.target_id = loop_header_id}); - context.inst_block_stack().Pop(); - - // Start emitting the loop exit block. - context.inst_block_stack().Push(loop_exit_id); - context.region_stack().AddToRegion(loop_exit_id, node_id); + FinishLoopBody(context, node_id); return true; } @@ -75,20 +106,112 @@ auto HandleParseNode(Context& context, Parse::WhileStatementId node_id) auto HandleParseNode(Context& context, Parse::ForHeaderStartId node_id) -> bool { - return context.TODO(node_id, "HandleForHeaderStart"); + // 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(); + + // Begin an implicit let declaration context for the pattern. + context.decl_introducer_state_stack().Push(); + context.pattern_block_stack().Push(); + context.full_pattern_stack().PushFullPattern( + FullPatternStack::Kind::NameBindingDecl); + BeginSubpattern(context); + + context.node_stack().Push(node_id); + return true; } auto HandleParseNode(Context& context, Parse::ForInId node_id) -> bool { - context.decl_introducer_state_stack().Pop(); - return context.TODO(node_id, "HandleForIn"); + auto pattern_block_id = context.pattern_block_stack().Pop(); + AddInst(context, node_id, + {.pattern_block_id = pattern_block_id}); + context.decl_introducer_state_stack().Pop(); + context.full_pattern_stack().StartPatternInitializer(); + context.node_stack().Push(node_id, pattern_block_id); + return true; +} + +// For a value or reference of type `Optional(T)`, call the given accessor. +static auto CallOptionalAccessor(Context& context, Parse::NodeId node_id, + SemIR::InstId optional_id, + llvm::StringLiteral accessor_name) + -> SemIR::InstId { + auto accessor_name_id = + SemIR::NameId::ForIdentifier(context.identifiers().Add(accessor_name)); + auto accessor_id = + PerformMemberAccess(context, node_id, optional_id, accessor_name_id); + return PerformCall(context, node_id, accessor_id, {}); } auto HandleParseNode(Context& context, Parse::ForHeaderId node_id) -> bool { - return context.TODO(node_id, "HandleForHeader"); + auto range_id = context.node_stack().PopExpr(); + auto pattern_block_id = context.node_stack().Pop(); + auto pattern_id = context.node_stack().PopPattern(); + auto start_node_id = + context.node_stack().PopForSoloNodeId(); + + // Convert the range expression to a value or reference so that we can use it + // multiple times. + // TODO: If this produces a temporary, its lifetime should presumably be + // extended to cover the loop body. + range_id = ConvertToValueOrRefExpr(context, range_id); + + // Create the cursor variable. + // TODO: Produce a custom diagnostic if the range operand can't be used as a + // range. + auto cursor_id = BuildUnaryOperator( + context, node_id, {.interface_name = "Iterate", .op_name = "NewCursor"}, + range_id); + auto cursor_type_id = context.insts().Get(cursor_id).type_id(); + auto cursor_var_id = AddInstWithCleanup( + context, node_id, + {.type_id = cursor_type_id, .pattern_id = SemIR::AbsoluteInstId::None}); + auto init_id = Initialize(context, node_id, cursor_var_id, cursor_id); + AddInst(context, node_id, + {.lhs_id = cursor_var_id, .rhs_id = init_id}); + + // Start emitting the loop header block. + auto loop_header_id = StartLoopHeader(context, start_node_id); + + // Call `.(Iterate.Next)(&cursor)`. + auto cursor_type_inst_id = context.types().GetInstId(cursor_type_id); + auto cursor_addr_id = AddInst( + context, node_id, + {.type_id = GetPointerType(context, cursor_type_inst_id), + .lvalue_id = cursor_var_id}); + auto element_id = BuildBinaryOperator( + context, node_id, {.interface_name = "Iterate", .op_name = "Next"}, + range_id, cursor_addr_id); + // We need to convert away from an initializing expression in order to call + // `HasValue` and then separately pattern-match against the element. + // TODO: Instead, form a `.Some(pattern_id)` pattern and pattern-match against + // that. + element_id = ConvertToValueOrRefExpr(context, element_id); + + // Branch to the loop body if the optional element has a value. + auto cond_value_id = + CallOptionalAccessor(context, node_id, element_id, "HasValue"); + BranchAndStartLoopBody(context, node_id, loop_header_id, cond_value_id); + + // The loop pattern's initializer is now complete, and any bindings in it + // should be in scope. + context.full_pattern_stack().EndPatternInitializer(); + context.full_pattern_stack().PopFullPattern(); + + // Create storage for var patterns now. + AddPatternVarStorage(context, pattern_block_id, /*is_returned_var=*/false); + + // Initialize the pattern from `.Get()`. + auto element_value_id = + CallOptionalAccessor(context, node_id, element_id, "Get"); + LocalPatternMatch(context, pattern_id, element_value_id); + return true; } auto HandleParseNode(Context& context, Parse::ForStatementId node_id) -> bool { - return context.TODO(node_id, "HandleForStatement"); + FinishLoopBody(context, node_id); + return true; } // `break` diff --git a/toolchain/check/node_stack.h b/toolchain/check/node_stack.h index d98cebf20498..20ecdf3f88ba 100644 --- a/toolchain/check/node_stack.h +++ b/toolchain/check/node_stack.h @@ -419,10 +419,10 @@ class NodeStack { case Parse::NodeKind::WhereOperand: return Id::KindFor(); case Parse::NodeKind::ExplicitParamList: + case Parse::NodeKind::ForIn: case Parse::NodeKind::IfCondition: case Parse::NodeKind::IfExprIf: case Parse::NodeKind::ImplicitParamList: - case Parse::NodeKind::WhileCondition: case Parse::NodeKind::WhileConditionStart: return Id::KindFor(); case Parse::NodeKind::FunctionDefinitionStart: @@ -449,6 +449,7 @@ class NodeStack { case Parse::NodeKind::ExplicitParamListStart: case Parse::NodeKind::FieldInitializer: case Parse::NodeKind::FieldIntroducer: + case Parse::NodeKind::ForHeaderStart: case Parse::NodeKind::FunctionIntroducer: case Parse::NodeKind::IfStatementElse: case Parse::NodeKind::ImplicitParamListStart: @@ -483,10 +484,8 @@ class NodeStack { case Parse::NodeKind::ExportIntroducer: case Parse::NodeKind::FileEnd: case Parse::NodeKind::FileStart: - case Parse::NodeKind::Forall: case Parse::NodeKind::ForHeader: - case Parse::NodeKind::ForHeaderStart: - case Parse::NodeKind::ForIn: + case Parse::NodeKind::Forall: case Parse::NodeKind::IdentifierNameQualifierWithParams: case Parse::NodeKind::IdentifierNameQualifierWithoutParams: case Parse::NodeKind::IdentifierPackageName: @@ -528,6 +527,7 @@ class NodeStack { case Parse::NodeKind::StructFieldDesignator: case Parse::NodeKind::StructTypeLiteralComma: case Parse::NodeKind::TupleLiteralComma: + case Parse::NodeKind::WhileCondition: return Id::Kind::Invalid; default: // In this case, the kind must be determinable from the category, or we diff --git a/toolchain/check/pattern.cpp b/toolchain/check/pattern.cpp index a8f851931cd1..c225b2238da3 100644 --- a/toolchain/check/pattern.cpp +++ b/toolchain/check/pattern.cpp @@ -4,7 +4,9 @@ #include "toolchain/check/pattern.h" +#include "toolchain/check/control_flow.h" #include "toolchain/check/inst.h" +#include "toolchain/check/return.h" #include "toolchain/check/type.h" namespace Carbon::Check { @@ -94,4 +96,41 @@ auto AddBindingPattern(Context& context, SemIR::LocId name_loc, return {.pattern_id = binding_pattern_id, .bind_id = bind_id}; } +// Returns a VarStorage inst for the given `var` pattern. If the pattern +// is the body of a returned var, this reuses the return slot, and otherwise it +// adds a new inst. +static auto GetOrAddVarStorage(Context& context, SemIR::InstId var_pattern_id, + bool is_returned_var) -> SemIR::InstId { + if (is_returned_var) { + auto& function = GetCurrentFunctionForReturn(context); + auto return_info = + SemIR::ReturnTypeInfo::ForFunction(context.sem_ir(), function); + if (return_info.has_return_slot()) { + return GetCurrentReturnSlot(context); + } + } + auto pattern = context.insts().GetWithLocId(var_pattern_id); + + return AddInstWithCleanup( + context, pattern.loc_id, + SemIR::VarStorage{.type_id = ExtractScrutineeType(context.sem_ir(), + pattern.inst.type_id()), + .pattern_id = var_pattern_id}); +} + +auto AddPatternVarStorage(Context& context, SemIR::InstBlockId pattern_block_id, + bool is_returned_var) -> void { + // We need to emit the VarStorage insts early, because they may be output + // arguments for the initializer. However, we can't emit them when we emit + // the corresponding `VarPattern`s because they're part of the pattern match, + // not part of the pattern. + // TODO: Find a way to do this without walking the whole pattern block. + for (auto inst_id : context.inst_blocks().Get(pattern_block_id)) { + if (context.insts().Is(inst_id)) { + context.var_storage_map().Insert( + inst_id, GetOrAddVarStorage(context, inst_id, is_returned_var)); + } + } +} + } // namespace Carbon::Check diff --git a/toolchain/check/pattern.h b/toolchain/check/pattern.h index efb3c634e291..6a0413ca4988 100644 --- a/toolchain/check/pattern.h +++ b/toolchain/check/pattern.h @@ -42,6 +42,12 @@ auto AddBindingPattern(Context& context, SemIR::LocId name_loc, SemIR::ExprRegionId type_region_id, bool is_generic, bool is_template) -> BindingPatternInfo; +// Creates storage for `var` patterns nested within the given pattern at the +// current location in the output SemIR. For a `returned var`, this +// reuses the function's return slot when present. +auto AddPatternVarStorage(Context& context, SemIR::InstBlockId pattern_block_id, + bool is_returned_var) -> void; + } // namespace Carbon::Check #endif // CARBON_TOOLCHAIN_CHECK_PATTERN_H_ diff --git a/toolchain/check/testdata/for/basic.carbon b/toolchain/check/testdata/for/basic.carbon new file mode 100644 index 000000000000..569938ea1cf0 --- /dev/null +++ b/toolchain/check/testdata/for/basic.carbon @@ -0,0 +1,168 @@ +// 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/for.carbon +// +// AUTOUPDATE +// TIP: To test this file alone, run: +// TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/for/basic.carbon +// TIP: To dump output, run: +// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/for/basic.carbon + +// --- fail_not_range.carbon + +library "[[@TEST_NAME]]"; + +fn Run() { + // TODO: These diagnostics could be better. If nothing else, we should only diagnose once. + // CHECK:STDERR: fail_not_range.carbon:[[@LINE+8]]:7: error: cannot access member of interface `Core.Iterate` in type `{}` that does not implement that interface [MissingImplInMemberAccess] + // CHECK:STDERR: for (c: {} in {}) { + // CHECK:STDERR: ^~~~~~~~~~~~~ + // CHECK:STDERR: + // CHECK:STDERR: fail_not_range.carbon:[[@LINE+4]]:7: error: cannot access member of interface `Core.Iterate` in type `{}` that does not implement that interface [MissingImplInMemberAccess] + // CHECK:STDERR: for (c: {} in {}) { + // CHECK:STDERR: ^~~~~~~~~~~~~ + // CHECK:STDERR: + for (c: {} in {}) { + } +} + +// --- trivial.carbon + +library "[[@TEST_NAME]]"; + +class TrivialRange { + impl as Core.Iterate where .CursorType = () and .ElementType = () { + fn NewCursor[self: Self]() {} + fn Next[self: Self](cursor: ()*) -> Core.Optional(()) { + return Core.Optional(()).None(); + } + } +} + +fn Body(); +fn AfterLoop(); + +fn Run() { + //@dump-sem-ir-begin + for (_: () in {} as TrivialRange) { + Body(); + } + AfterLoop(); + //@dump-sem-ir-end +} + +// CHECK:STDOUT: --- trivial.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %TrivialRange: type = class_type @TrivialRange [concrete] +// CHECK:STDOUT: %Iterate.type: type = facet_type <@Iterate> [concrete] +// CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] +// CHECK:STDOUT: %NewCursor.type.427: type = fn_type @NewCursor.1 [concrete] +// CHECK:STDOUT: %Next.type.941: type = fn_type @Next.1 [concrete] +// CHECK:STDOUT: %T: type = bind_symbolic_name T, 0 [symbolic] +// CHECK:STDOUT: %HasValue.type.a15: type = fn_type @HasValue, @Optional(%T) [symbolic] +// CHECK:STDOUT: %HasValue.73f: %HasValue.type.a15 = struct_value () [symbolic] +// CHECK:STDOUT: %Get.type.e03: type = fn_type @Get, @Optional(%T) [symbolic] +// CHECK:STDOUT: %Get.971: %Get.type.e03 = struct_value () [symbolic] +// CHECK:STDOUT: %Iterate.impl_witness: = impl_witness @TrivialRange.%Iterate.impl_witness_table [concrete] +// CHECK:STDOUT: %NewCursor.type.bd2331.1: type = fn_type @NewCursor.2 [concrete] +// CHECK:STDOUT: %NewCursor.c20a1c.1: %NewCursor.type.bd2331.1 = struct_value () [concrete] +// CHECK:STDOUT: %ptr.843: type = ptr_type %empty_tuple.type [concrete] +// CHECK:STDOUT: %Optional.f9a: type = class_type @Optional, @Optional(%empty_tuple.type) [concrete] +// CHECK:STDOUT: %Next.type.9f0: type = fn_type @Next.2 [concrete] +// CHECK:STDOUT: %Next.029: %Next.type.9f0 = struct_value () [concrete] +// CHECK:STDOUT: %Iterate.facet.d50: %Iterate.type = facet_value %TrivialRange, (%Iterate.impl_witness) [concrete] +// CHECK:STDOUT: %pattern_type.cb1: type = pattern_type %empty_tuple.type [concrete] +// CHECK:STDOUT: %NewCursor.type.bd2331.2: type = fn_type @NewCursor.3 [concrete] +// CHECK:STDOUT: %NewCursor.c20a1c.2: %NewCursor.type.bd2331.2 = struct_value () [concrete] +// CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] +// CHECK:STDOUT: %HasValue.type.b7a: type = fn_type @HasValue, @Optional(%empty_tuple.type) [concrete] +// CHECK:STDOUT: %HasValue.ada: %HasValue.type.b7a = struct_value () [concrete] +// CHECK:STDOUT: %Get.type.130: type = fn_type @Get, @Optional(%empty_tuple.type) [concrete] +// CHECK:STDOUT: %Get.6e8: %Get.type.130 = struct_value () [concrete] +// CHECK:STDOUT: %empty_tuple: %empty_tuple.type = tuple_value () [concrete] +// CHECK:STDOUT: %Body.type: type = fn_type @Body [concrete] +// CHECK:STDOUT: %Body: %Body.type = struct_value () [concrete] +// CHECK:STDOUT: %AfterLoop.type: type = fn_type @AfterLoop [concrete] +// CHECK:STDOUT: %AfterLoop: %AfterLoop.type = struct_value () [concrete] +// CHECK:STDOUT: %TrivialRange.val: %TrivialRange = struct_value () [concrete] +// CHECK:STDOUT: %.510: type = fn_type_with_self_type %NewCursor.type.427, %Iterate.facet.d50 [concrete] +// CHECK:STDOUT: %.708: type = fn_type_with_self_type %Next.type.941, %Iterate.facet.d50 [concrete] +// CHECK:STDOUT: %HasValue.specific_fn: = specific_function %HasValue.ada, @HasValue(%empty_tuple.type) [concrete] +// CHECK:STDOUT: %Get.specific_fn: = specific_function %Get.6e8, @Get(%empty_tuple.type) [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: imports { +// CHECK:STDOUT: %Core.import_ref.cd6: @Optional.%HasValue.type (%HasValue.type.a15) = import_ref Core//prelude/parts/iterate, inst98 [indirect], loaded [symbolic = @Optional.%HasValue (constants.%HasValue.73f)] +// CHECK:STDOUT: %Core.import_ref.4fd: @Optional.%Get.type (%Get.type.e03) = import_ref Core//prelude/parts/iterate, inst99 [indirect], loaded [symbolic = @Optional.%Get (constants.%Get.971)] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @Run() { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: name_binding_decl { +// CHECK:STDOUT: %_.patt: %pattern_type.cb1 = binding_pattern _ [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: %.loc18_18.1: %empty_struct_type = struct_literal () +// CHECK:STDOUT: %TrivialRange.ref: type = name_ref TrivialRange, file.%TrivialRange.decl [concrete = constants.%TrivialRange] +// CHECK:STDOUT: %.loc18_18.2: ref %TrivialRange = temporary_storage +// CHECK:STDOUT: %.loc18_18.3: init %TrivialRange = class_init (), %.loc18_18.2 [concrete = constants.%TrivialRange.val] +// CHECK:STDOUT: %.loc18_18.4: ref %TrivialRange = temporary %.loc18_18.2, %.loc18_18.3 +// CHECK:STDOUT: %.loc18_20.1: ref %TrivialRange = converted %.loc18_18.1, %.loc18_18.4 +// CHECK:STDOUT: %impl.elem2: %.510 = impl_witness_access constants.%Iterate.impl_witness, element2 [concrete = constants.%NewCursor.c20a1c.2] +// CHECK:STDOUT: %bound_method.loc18_35.1: = bound_method %.loc18_20.1, %impl.elem2 +// CHECK:STDOUT: %.loc18_20.2: %TrivialRange = bind_value %.loc18_20.1 +// CHECK:STDOUT: %NewCursor.ref: %NewCursor.type.bd2331.1 = name_ref NewCursor, @impl.%NewCursor.decl.loc6_32.1 [concrete = constants.%NewCursor.c20a1c.1] +// CHECK:STDOUT: %NewCursor.bound: = bound_method %.loc18_20.2, %NewCursor.ref +// CHECK:STDOUT: %NewCursor.call: init %empty_tuple.type = call %NewCursor.bound(%.loc18_20.2) +// CHECK:STDOUT: %var: ref %empty_tuple.type = var invalid +// CHECK:STDOUT: assign %var, %NewCursor.call +// CHECK:STDOUT: br !for.next +// CHECK:STDOUT: +// CHECK:STDOUT: !for.next: +// CHECK:STDOUT: %addr: %ptr.843 = addr_of %var +// CHECK:STDOUT: %impl.elem3: %.708 = impl_witness_access constants.%Iterate.impl_witness, element3 [concrete = constants.%Next.029] +// CHECK:STDOUT: %bound_method.loc18_35.2: = bound_method %.loc18_20.1, %impl.elem3 +// CHECK:STDOUT: %.loc18_35.1: ref %Optional.f9a = temporary_storage +// CHECK:STDOUT: %.loc18_20.3: %TrivialRange = bind_value %.loc18_20.1 +// CHECK:STDOUT: %Next.call: init %Optional.f9a = call %bound_method.loc18_35.2(%.loc18_20.3, %addr) to %.loc18_35.1 +// CHECK:STDOUT: %.loc18_35.2: ref %Optional.f9a = temporary %.loc18_35.1, %Next.call +// CHECK:STDOUT: %.loc18_35.3: %HasValue.type.b7a = specific_constant imports.%Core.import_ref.cd6, @Optional(constants.%empty_tuple.type) [concrete = constants.%HasValue.ada] +// CHECK:STDOUT: %HasValue.ref: %HasValue.type.b7a = name_ref HasValue, %.loc18_35.3 [concrete = constants.%HasValue.ada] +// CHECK:STDOUT: %HasValue.bound: = bound_method %.loc18_35.2, %HasValue.ref +// CHECK:STDOUT: %HasValue.specific_fn: = specific_function %HasValue.ref, @HasValue(constants.%empty_tuple.type) [concrete = constants.%HasValue.specific_fn] +// CHECK:STDOUT: %bound_method.loc18_35.3: = bound_method %.loc18_35.2, %HasValue.specific_fn +// CHECK:STDOUT: %.loc18_35.4: %Optional.f9a = bind_value %.loc18_35.2 +// CHECK:STDOUT: %HasValue.call: init bool = call %bound_method.loc18_35.3(%.loc18_35.4) +// CHECK:STDOUT: %.loc18_35.5: bool = value_of_initializer %HasValue.call +// CHECK:STDOUT: %.loc18_35.6: bool = converted %HasValue.call, %.loc18_35.5 +// CHECK:STDOUT: if %.loc18_35.6 br !for.body else br !for.done +// CHECK:STDOUT: +// CHECK:STDOUT: !for.body: +// CHECK:STDOUT: %.loc18_35.7: %Get.type.130 = specific_constant imports.%Core.import_ref.4fd, @Optional(constants.%empty_tuple.type) [concrete = constants.%Get.6e8] +// CHECK:STDOUT: %Get.ref: %Get.type.130 = name_ref Get, %.loc18_35.7 [concrete = constants.%Get.6e8] +// CHECK:STDOUT: %Get.bound: = bound_method %.loc18_35.2, %Get.ref +// CHECK:STDOUT: %Get.specific_fn: = specific_function %Get.ref, @Get(constants.%empty_tuple.type) [concrete = constants.%Get.specific_fn] +// CHECK:STDOUT: %bound_method.loc18_35.4: = bound_method %.loc18_35.2, %Get.specific_fn +// CHECK:STDOUT: %.loc18_35.8: %Optional.f9a = bind_value %.loc18_35.2 +// CHECK:STDOUT: %Get.call: init %empty_tuple.type = call %bound_method.loc18_35.4(%.loc18_35.8) +// CHECK:STDOUT: %.loc18_12.1: type = splice_block %.loc18_12.3 [concrete = constants.%empty_tuple.type] { +// CHECK:STDOUT: %.loc18_12.2: %empty_tuple.type = tuple_literal () +// CHECK:STDOUT: %.loc18_12.3: type = converted %.loc18_12.2, constants.%empty_tuple.type [concrete = constants.%empty_tuple.type] +// CHECK:STDOUT: } +// CHECK:STDOUT: %.loc18_35.9: ref %empty_tuple.type = temporary_storage +// CHECK:STDOUT: %.loc18_35.10: ref %empty_tuple.type = temporary %.loc18_35.9, %Get.call +// CHECK:STDOUT: %tuple: %empty_tuple.type = tuple_value () [concrete = constants.%empty_tuple] +// CHECK:STDOUT: %.loc18_35.11: %empty_tuple.type = converted %Get.call, %tuple [concrete = constants.%empty_tuple] +// CHECK:STDOUT: %_: %empty_tuple.type = bind_name _, %.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: +// CHECK:STDOUT: } +// CHECK:STDOUT: diff --git a/toolchain/check/testdata/for/pattern.carbon b/toolchain/check/testdata/for/pattern.carbon new file mode 100644 index 000000000000..f2d0754e3c0b --- /dev/null +++ b/toolchain/check/testdata/for/pattern.carbon @@ -0,0 +1,676 @@ +// 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/for.carbon +// +// AUTOUPDATE +// TIP: To test this file alone, run: +// TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/for/pattern.carbon +// TIP: To dump output, run: +// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/for/pattern.carbon + +// --- empty_range.carbon + +library "[[@TEST_NAME]]"; + +class EmptyRange(T:! type) { + fn Make() -> Self { return {}; } + + impl as Core.Iterate where .CursorType = {} and .ElementType = T { + fn NewCursor[self: Self]() -> {} { + return {}; + } + fn Next[self: Self](cursor: {}*) -> Core.Optional(T) { + return Core.Optional(T).None(); + } + } +} + +// --- value.carbon + +library "[[@TEST_NAME]]"; + +import library "empty_range"; + +class C {} + +fn Body(c: C); + +fn Run() { + //@dump-sem-ir-begin + for (c: C in EmptyRange(C).Make()) { + Body(c); + } + //@dump-sem-ir-end +} + +// --- var.carbon + +library "[[@TEST_NAME]]"; + +import library "empty_range"; + +class C {} + +fn Body(c: C*); + +fn Run() { + //@dump-sem-ir-begin + for (var c: C in EmptyRange(C).Make()) { + Body(&c); + } + //@dump-sem-ir-end +} + +// --- tuple.carbon + +library "[[@TEST_NAME]]"; + +import library "empty_range"; + +fn Body(a: bool, b: bool); + +fn Run() { + //@dump-sem-ir-begin + for ((a: bool, b: bool) in EmptyRange((bool, bool)).Make()) { + Body(a, b); + } + //@dump-sem-ir-end +} + +// --- tuple_class.carbon + +library "[[@TEST_NAME]]"; + +import library "empty_range"; + +class C {} + +fn Body(a: C, b: C); + +fn Run() { + //@dump-sem-ir-begin + for ((a: C, b: C) in EmptyRange((C, C)).Make()) { + Body(a, b); + } + //@dump-sem-ir-end +} + +// --- fail_bad_pattern.carbon + +library "[[@TEST_NAME]]"; + +import library "empty_range"; + +class X {} +class Y {} + +fn Run() { + // CHECK:STDERR: fail_bad_pattern.carbon:[[@LINE+7]]:7: error: cannot implicitly convert expression of type `Y` to `X` [ConversionFailure] + // CHECK:STDERR: for (x: X in EmptyRange(Y).Make()) { + // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + // CHECK:STDERR: fail_bad_pattern.carbon:[[@LINE+4]]:7: note: type `Y` does not implement interface `Core.ImplicitAs(X)` [MissingImplInMemberAccessNote] + // CHECK:STDERR: for (x: X in EmptyRange(Y).Make()) { + // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + // CHECK:STDERR: + for (x: X in EmptyRange(Y).Make()) { + } +} + +// CHECK:STDOUT: --- value.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %C: type = class_type @C [concrete] +// CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] +// CHECK:STDOUT: %pattern_type.c48: type = pattern_type %C [concrete] +// CHECK:STDOUT: %Body.type: type = fn_type @Body [concrete] +// CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] +// CHECK:STDOUT: %Body: %Body.type = struct_value () [concrete] +// CHECK:STDOUT: %ptr.c28: type = ptr_type %empty_struct_type [concrete] +// CHECK:STDOUT: %EmptyRange.type: type = generic_class_type @EmptyRange [concrete] +// CHECK:STDOUT: %EmptyRange.generic: %EmptyRange.type = struct_value () [concrete] +// CHECK:STDOUT: %T: type = bind_symbolic_name T, 0 [symbolic] +// CHECK:STDOUT: %Make.type.bd2: type = fn_type @Make, @EmptyRange(%T) [symbolic] +// CHECK:STDOUT: %Make.ed1: %Make.type.bd2 = struct_value () [symbolic] +// CHECK:STDOUT: %EmptyRange.cc8: type = class_type @EmptyRange, @EmptyRange(%C) [concrete] +// CHECK:STDOUT: %Make.type.557: type = fn_type @Make, @EmptyRange(%C) [concrete] +// CHECK:STDOUT: %Make.74d: %Make.type.557 = struct_value () [concrete] +// CHECK:STDOUT: %Make.specific_fn: = specific_function %Make.74d, @Make(%C) [concrete] +// CHECK:STDOUT: %Iterate.type: type = facet_type <@Iterate> [concrete] +// CHECK:STDOUT: %NewCursor.type.427: type = fn_type @NewCursor.1 [concrete] +// CHECK:STDOUT: %NewCursor.type.f5f: type = fn_type @NewCursor.2, @impl(%T) [symbolic] +// CHECK:STDOUT: %NewCursor.ec1: %NewCursor.type.f5f = struct_value () [symbolic] +// CHECK:STDOUT: %Next.type.264: type = fn_type @Next.1, @impl(%T) [symbolic] +// CHECK:STDOUT: %Next.08e: %Next.type.264 = struct_value () [symbolic] +// CHECK:STDOUT: %HasValue.type.f81: type = fn_type @HasValue, @Optional(%T) [symbolic] +// CHECK:STDOUT: %HasValue.6fd: %HasValue.type.f81 = struct_value () [symbolic] +// CHECK:STDOUT: %Get.type.b8f: type = fn_type @Get, @Optional(%T) [symbolic] +// CHECK:STDOUT: %Get.9c8: %Get.type.b8f = struct_value () [symbolic] +// CHECK:STDOUT: %Iterate.impl_witness.52a: = impl_witness imports.%Iterate.impl_witness_table, @impl(%C) [concrete] +// CHECK:STDOUT: %NewCursor.type.951: type = fn_type @NewCursor.2, @impl(%C) [concrete] +// CHECK:STDOUT: %NewCursor.535: %NewCursor.type.951 = struct_value () [concrete] +// CHECK:STDOUT: %Next.type.63f: type = fn_type @Next.1, @impl(%C) [concrete] +// CHECK:STDOUT: %Next.899: %Next.type.63f = struct_value () [concrete] +// CHECK:STDOUT: %Iterate.facet: %Iterate.type = facet_value %EmptyRange.cc8, (%Iterate.impl_witness.52a) [concrete] +// CHECK:STDOUT: %.2f6: type = fn_type_with_self_type %NewCursor.type.427, %Iterate.facet [concrete] +// CHECK:STDOUT: %NewCursor.specific_fn: = specific_function %NewCursor.535, @NewCursor.2(%C) [concrete] +// CHECK:STDOUT: %Next.type.941: type = fn_type @Next.2 [concrete] +// CHECK:STDOUT: %.5d5: type = fn_type_with_self_type %Next.type.941, %Iterate.facet [concrete] +// CHECK:STDOUT: %Optional.cf0: type = class_type @Optional, @Optional(%C) [concrete] +// CHECK:STDOUT: %Next.specific_fn: = specific_function %Next.899, @Next.1(%C) [concrete] +// CHECK:STDOUT: %HasValue.type.71d: type = fn_type @HasValue, @Optional(%C) [concrete] +// CHECK:STDOUT: %HasValue.513: %HasValue.type.71d = struct_value () [concrete] +// CHECK:STDOUT: %Get.type.115: type = fn_type @Get, @Optional(%C) [concrete] +// CHECK:STDOUT: %Get.9c1: %Get.type.115 = struct_value () [concrete] +// CHECK:STDOUT: %HasValue.specific_fn: = specific_function %HasValue.513, @HasValue(%C) [concrete] +// CHECK:STDOUT: %Get.specific_fn: = specific_function %Get.9c1, @Get(%C) [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: imports { +// CHECK:STDOUT: %Main.EmptyRange: %EmptyRange.type = import_ref Main//empty_range, EmptyRange, loaded [concrete = constants.%EmptyRange.generic] +// CHECK:STDOUT: %Main.import_ref.6ea: @EmptyRange.%Make.type (%Make.type.bd2) = import_ref Main//empty_range, loc5_21, loaded [symbolic = @EmptyRange.%Make (constants.%Make.ed1)] +// CHECK:STDOUT: %Main.import_ref.6ce = import_ref Main//empty_range, loc7_68, unloaded +// CHECK:STDOUT: %Main.import_ref.999 = import_ref Main//empty_range, loc7_68, unloaded +// CHECK:STDOUT: %Main.import_ref.57b: @impl.%NewCursor.type (%NewCursor.type.f5f) = import_ref Main//empty_range, loc8_38, loaded [symbolic = @impl.%NewCursor (constants.%NewCursor.ec1)] +// CHECK:STDOUT: %Main.import_ref.170: @impl.%Next.type (%Next.type.264) = import_ref Main//empty_range, loc11_58, loaded [symbolic = @impl.%Next (constants.%Next.08e)] +// CHECK:STDOUT: %Iterate.impl_witness_table = impl_witness_table (%Main.import_ref.6ce, %Main.import_ref.999, %Main.import_ref.57b, %Main.import_ref.170), @impl [concrete] +// CHECK:STDOUT: %Main.import_ref.7f9: @Optional.%HasValue.type (%HasValue.type.f81) = import_ref Main//empty_range, inst136 [indirect], loaded [symbolic = @Optional.%HasValue (constants.%HasValue.6fd)] +// CHECK:STDOUT: %Main.import_ref.d10: @Optional.%Get.type (%Get.type.b8f) = import_ref Main//empty_range, inst137 [indirect], loaded [symbolic = @Optional.%Get (constants.%Get.9c8)] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @Run() { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: name_binding_decl { +// CHECK:STDOUT: %c.patt: %pattern_type.c48 = binding_pattern c [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: %EmptyRange.ref: %EmptyRange.type = name_ref EmptyRange, imports.%Main.EmptyRange [concrete = constants.%EmptyRange.generic] +// CHECK:STDOUT: %C.ref.loc12_27: type = name_ref C, file.%C.decl [concrete = constants.%C] +// CHECK:STDOUT: %EmptyRange: type = class_type @EmptyRange, @EmptyRange(constants.%C) [concrete = constants.%EmptyRange.cc8] +// CHECK:STDOUT: %.loc12_29: %Make.type.557 = specific_constant imports.%Main.import_ref.6ea, @EmptyRange(constants.%C) [concrete = constants.%Make.74d] +// CHECK:STDOUT: %Make.ref: %Make.type.557 = name_ref Make, %.loc12_29 [concrete = constants.%Make.74d] +// CHECK:STDOUT: %Make.specific_fn: = specific_function %Make.ref, @Make(constants.%C) [concrete = constants.%Make.specific_fn] +// CHECK:STDOUT: %.loc12_35.1: ref %EmptyRange.cc8 = temporary_storage +// CHECK:STDOUT: %Make.call: init %EmptyRange.cc8 = call %Make.specific_fn() to %.loc12_35.1 +// CHECK:STDOUT: %.loc12_35.2: ref %EmptyRange.cc8 = temporary %.loc12_35.1, %Make.call +// CHECK:STDOUT: %impl.elem2: %.2f6 = impl_witness_access constants.%Iterate.impl_witness.52a, element2 [concrete = constants.%NewCursor.535] +// CHECK:STDOUT: %bound_method.loc12_36.1: = bound_method %.loc12_35.2, %impl.elem2 +// CHECK:STDOUT: %specific_fn.loc12_36.1: = specific_function %impl.elem2, @NewCursor.2(constants.%C) [concrete = constants.%NewCursor.specific_fn] +// CHECK:STDOUT: %bound_method.loc12_36.2: = bound_method %.loc12_35.2, %specific_fn.loc12_36.1 +// CHECK:STDOUT: %.loc12_35.3: %EmptyRange.cc8 = bind_value %.loc12_35.2 +// CHECK:STDOUT: %NewCursor.call: init %empty_struct_type = call %bound_method.loc12_36.2(%.loc12_35.3) +// CHECK:STDOUT: %var: ref %empty_struct_type = var invalid +// CHECK:STDOUT: assign %var, %NewCursor.call +// CHECK:STDOUT: br !for.next +// CHECK:STDOUT: +// CHECK:STDOUT: !for.next: +// CHECK:STDOUT: %addr: %ptr.c28 = addr_of %var +// CHECK:STDOUT: %impl.elem3: %.5d5 = impl_witness_access constants.%Iterate.impl_witness.52a, element3 [concrete = constants.%Next.899] +// CHECK:STDOUT: %bound_method.loc12_36.3: = bound_method %.loc12_35.2, %impl.elem3 +// CHECK:STDOUT: %specific_fn.loc12_36.2: = specific_function %impl.elem3, @Next.1(constants.%C) [concrete = constants.%Next.specific_fn] +// CHECK:STDOUT: %bound_method.loc12_36.4: = bound_method %.loc12_35.2, %specific_fn.loc12_36.2 +// CHECK:STDOUT: %.loc12_36.1: ref %Optional.cf0 = temporary_storage +// CHECK:STDOUT: %.loc12_35.4: %EmptyRange.cc8 = bind_value %.loc12_35.2 +// CHECK:STDOUT: %Next.call: init %Optional.cf0 = call %bound_method.loc12_36.4(%.loc12_35.4, %addr) to %.loc12_36.1 +// CHECK:STDOUT: %.loc12_36.2: ref %Optional.cf0 = temporary %.loc12_36.1, %Next.call +// CHECK:STDOUT: %.loc12_36.3: %HasValue.type.71d = specific_constant imports.%Main.import_ref.7f9, @Optional(constants.%C) [concrete = constants.%HasValue.513] +// CHECK:STDOUT: %HasValue.ref: %HasValue.type.71d = name_ref HasValue, %.loc12_36.3 [concrete = constants.%HasValue.513] +// CHECK:STDOUT: %HasValue.bound: = bound_method %.loc12_36.2, %HasValue.ref +// CHECK:STDOUT: %HasValue.specific_fn: = specific_function %HasValue.ref, @HasValue(constants.%C) [concrete = constants.%HasValue.specific_fn] +// CHECK:STDOUT: %bound_method.loc12_36.5: = bound_method %.loc12_36.2, %HasValue.specific_fn +// CHECK:STDOUT: %.loc12_36.4: %Optional.cf0 = bind_value %.loc12_36.2 +// CHECK:STDOUT: %HasValue.call: init bool = call %bound_method.loc12_36.5(%.loc12_36.4) +// CHECK:STDOUT: %.loc12_36.5: bool = value_of_initializer %HasValue.call +// CHECK:STDOUT: %.loc12_36.6: bool = converted %HasValue.call, %.loc12_36.5 +// CHECK:STDOUT: if %.loc12_36.6 br !for.body else br !for.done +// CHECK:STDOUT: +// CHECK:STDOUT: !for.body: +// CHECK:STDOUT: %.loc12_36.7: %Get.type.115 = specific_constant imports.%Main.import_ref.d10, @Optional(constants.%C) [concrete = constants.%Get.9c1] +// CHECK:STDOUT: %Get.ref: %Get.type.115 = name_ref Get, %.loc12_36.7 [concrete = constants.%Get.9c1] +// CHECK:STDOUT: %Get.bound: = bound_method %.loc12_36.2, %Get.ref +// CHECK:STDOUT: %Get.specific_fn: = specific_function %Get.ref, @Get(constants.%C) [concrete = constants.%Get.specific_fn] +// CHECK:STDOUT: %bound_method.loc12_36.6: = bound_method %.loc12_36.2, %Get.specific_fn +// CHECK:STDOUT: %.loc12_36.8: ref %C = temporary_storage +// CHECK:STDOUT: %.loc12_36.9: %Optional.cf0 = bind_value %.loc12_36.2 +// CHECK:STDOUT: %Get.call: init %C = call %bound_method.loc12_36.6(%.loc12_36.9) to %.loc12_36.8 +// CHECK:STDOUT: %C.ref.loc12_11: type = name_ref C, file.%C.decl [concrete = constants.%C] +// CHECK:STDOUT: %.loc12_36.10: ref %C = temporary %.loc12_36.8, %Get.call +// CHECK:STDOUT: %.loc12_36.11: %C = bind_value %.loc12_36.10 +// CHECK:STDOUT: %c: %C = bind_name c, %.loc12_36.11 +// 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: +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: --- var.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %C: type = class_type @C [concrete] +// CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] +// CHECK:STDOUT: %ptr.019: type = ptr_type %C [concrete] +// CHECK:STDOUT: %Body.type: type = fn_type @Body [concrete] +// CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] +// CHECK:STDOUT: %Body: %Body.type = struct_value () [concrete] +// CHECK:STDOUT: %ptr.c28: type = ptr_type %empty_struct_type [concrete] +// CHECK:STDOUT: %pattern_type.c48: type = pattern_type %C [concrete] +// CHECK:STDOUT: %EmptyRange.type: type = generic_class_type @EmptyRange [concrete] +// CHECK:STDOUT: %EmptyRange.generic: %EmptyRange.type = struct_value () [concrete] +// CHECK:STDOUT: %T: type = bind_symbolic_name T, 0 [symbolic] +// CHECK:STDOUT: %Make.type.bd2: type = fn_type @Make, @EmptyRange(%T) [symbolic] +// CHECK:STDOUT: %Make.ed1: %Make.type.bd2 = struct_value () [symbolic] +// CHECK:STDOUT: %EmptyRange.cc8: type = class_type @EmptyRange, @EmptyRange(%C) [concrete] +// CHECK:STDOUT: %Make.type.557: type = fn_type @Make, @EmptyRange(%C) [concrete] +// CHECK:STDOUT: %Make.74d: %Make.type.557 = struct_value () [concrete] +// CHECK:STDOUT: %Make.specific_fn: = specific_function %Make.74d, @Make(%C) [concrete] +// CHECK:STDOUT: %Iterate.type: type = facet_type <@Iterate> [concrete] +// CHECK:STDOUT: %NewCursor.type.427: type = fn_type @NewCursor.1 [concrete] +// CHECK:STDOUT: %NewCursor.type.f5f: type = fn_type @NewCursor.2, @impl(%T) [symbolic] +// CHECK:STDOUT: %NewCursor.ec1: %NewCursor.type.f5f = struct_value () [symbolic] +// CHECK:STDOUT: %Next.type.264: type = fn_type @Next.1, @impl(%T) [symbolic] +// CHECK:STDOUT: %Next.08e: %Next.type.264 = struct_value () [symbolic] +// CHECK:STDOUT: %HasValue.type.f81: type = fn_type @HasValue, @Optional(%T) [symbolic] +// CHECK:STDOUT: %HasValue.6fd: %HasValue.type.f81 = struct_value () [symbolic] +// CHECK:STDOUT: %Get.type.b8f: type = fn_type @Get, @Optional(%T) [symbolic] +// CHECK:STDOUT: %Get.9c8: %Get.type.b8f = struct_value () [symbolic] +// CHECK:STDOUT: %Iterate.impl_witness.52a: = impl_witness imports.%Iterate.impl_witness_table, @impl(%C) [concrete] +// CHECK:STDOUT: %NewCursor.type.951: type = fn_type @NewCursor.2, @impl(%C) [concrete] +// CHECK:STDOUT: %NewCursor.535: %NewCursor.type.951 = struct_value () [concrete] +// CHECK:STDOUT: %Next.type.63f: type = fn_type @Next.1, @impl(%C) [concrete] +// CHECK:STDOUT: %Next.899: %Next.type.63f = struct_value () [concrete] +// CHECK:STDOUT: %Iterate.facet: %Iterate.type = facet_value %EmptyRange.cc8, (%Iterate.impl_witness.52a) [concrete] +// CHECK:STDOUT: %.2f6: type = fn_type_with_self_type %NewCursor.type.427, %Iterate.facet [concrete] +// CHECK:STDOUT: %NewCursor.specific_fn: = specific_function %NewCursor.535, @NewCursor.2(%C) [concrete] +// CHECK:STDOUT: %Next.type.941: type = fn_type @Next.2 [concrete] +// CHECK:STDOUT: %.5d5: type = fn_type_with_self_type %Next.type.941, %Iterate.facet [concrete] +// CHECK:STDOUT: %Optional.cf0: type = class_type @Optional, @Optional(%C) [concrete] +// CHECK:STDOUT: %Next.specific_fn: = specific_function %Next.899, @Next.1(%C) [concrete] +// CHECK:STDOUT: %HasValue.type.71d: type = fn_type @HasValue, @Optional(%C) [concrete] +// CHECK:STDOUT: %HasValue.513: %HasValue.type.71d = struct_value () [concrete] +// CHECK:STDOUT: %Get.type.115: type = fn_type @Get, @Optional(%C) [concrete] +// CHECK:STDOUT: %Get.9c1: %Get.type.115 = struct_value () [concrete] +// CHECK:STDOUT: %HasValue.specific_fn: = specific_function %HasValue.513, @HasValue(%C) [concrete] +// CHECK:STDOUT: %Get.specific_fn: = specific_function %Get.9c1, @Get(%C) [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: imports { +// CHECK:STDOUT: %Main.EmptyRange: %EmptyRange.type = import_ref Main//empty_range, EmptyRange, loaded [concrete = constants.%EmptyRange.generic] +// CHECK:STDOUT: %Main.import_ref.6ea: @EmptyRange.%Make.type (%Make.type.bd2) = import_ref Main//empty_range, loc5_21, loaded [symbolic = @EmptyRange.%Make (constants.%Make.ed1)] +// CHECK:STDOUT: %Main.import_ref.6ce = import_ref Main//empty_range, loc7_68, unloaded +// CHECK:STDOUT: %Main.import_ref.999 = import_ref Main//empty_range, loc7_68, unloaded +// CHECK:STDOUT: %Main.import_ref.57b: @impl.%NewCursor.type (%NewCursor.type.f5f) = import_ref Main//empty_range, loc8_38, loaded [symbolic = @impl.%NewCursor (constants.%NewCursor.ec1)] +// CHECK:STDOUT: %Main.import_ref.170: @impl.%Next.type (%Next.type.264) = import_ref Main//empty_range, loc11_58, loaded [symbolic = @impl.%Next (constants.%Next.08e)] +// CHECK:STDOUT: %Iterate.impl_witness_table = impl_witness_table (%Main.import_ref.6ce, %Main.import_ref.999, %Main.import_ref.57b, %Main.import_ref.170), @impl [concrete] +// CHECK:STDOUT: %Main.import_ref.7f9: @Optional.%HasValue.type (%HasValue.type.f81) = import_ref Main//empty_range, inst136 [indirect], loaded [symbolic = @Optional.%HasValue (constants.%HasValue.6fd)] +// CHECK:STDOUT: %Main.import_ref.d10: @Optional.%Get.type (%Get.type.b8f) = import_ref Main//empty_range, inst137 [indirect], loaded [symbolic = @Optional.%Get (constants.%Get.9c8)] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @Run() { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: name_binding_decl { +// CHECK:STDOUT: %c.patt: %pattern_type.c48 = binding_pattern c [concrete] +// CHECK:STDOUT: %c.var_patt: %pattern_type.c48 = var_pattern %c.patt [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: %EmptyRange.ref: %EmptyRange.type = name_ref EmptyRange, imports.%Main.EmptyRange [concrete = constants.%EmptyRange.generic] +// CHECK:STDOUT: %C.ref.loc12_31: type = name_ref C, file.%C.decl [concrete = constants.%C] +// CHECK:STDOUT: %EmptyRange: type = class_type @EmptyRange, @EmptyRange(constants.%C) [concrete = constants.%EmptyRange.cc8] +// CHECK:STDOUT: %.loc12_33: %Make.type.557 = specific_constant imports.%Main.import_ref.6ea, @EmptyRange(constants.%C) [concrete = constants.%Make.74d] +// CHECK:STDOUT: %Make.ref: %Make.type.557 = name_ref Make, %.loc12_33 [concrete = constants.%Make.74d] +// CHECK:STDOUT: %Make.specific_fn: = specific_function %Make.ref, @Make(constants.%C) [concrete = constants.%Make.specific_fn] +// CHECK:STDOUT: %.loc12_39.1: ref %EmptyRange.cc8 = temporary_storage +// CHECK:STDOUT: %Make.call: init %EmptyRange.cc8 = call %Make.specific_fn() to %.loc12_39.1 +// CHECK:STDOUT: %.loc12_39.2: ref %EmptyRange.cc8 = temporary %.loc12_39.1, %Make.call +// CHECK:STDOUT: %impl.elem2: %.2f6 = impl_witness_access constants.%Iterate.impl_witness.52a, element2 [concrete = constants.%NewCursor.535] +// CHECK:STDOUT: %bound_method.loc12_40.1: = bound_method %.loc12_39.2, %impl.elem2 +// CHECK:STDOUT: %specific_fn.loc12_40.1: = specific_function %impl.elem2, @NewCursor.2(constants.%C) [concrete = constants.%NewCursor.specific_fn] +// CHECK:STDOUT: %bound_method.loc12_40.2: = bound_method %.loc12_39.2, %specific_fn.loc12_40.1 +// CHECK:STDOUT: %.loc12_39.3: %EmptyRange.cc8 = bind_value %.loc12_39.2 +// CHECK:STDOUT: %NewCursor.call: init %empty_struct_type = call %bound_method.loc12_40.2(%.loc12_39.3) +// CHECK:STDOUT: %var: ref %empty_struct_type = var invalid +// CHECK:STDOUT: assign %var, %NewCursor.call +// CHECK:STDOUT: br !for.next +// CHECK:STDOUT: +// CHECK:STDOUT: !for.next: +// CHECK:STDOUT: %addr.loc12: %ptr.c28 = addr_of %var +// CHECK:STDOUT: %impl.elem3: %.5d5 = impl_witness_access constants.%Iterate.impl_witness.52a, element3 [concrete = constants.%Next.899] +// CHECK:STDOUT: %bound_method.loc12_40.3: = bound_method %.loc12_39.2, %impl.elem3 +// CHECK:STDOUT: %specific_fn.loc12_40.2: = specific_function %impl.elem3, @Next.1(constants.%C) [concrete = constants.%Next.specific_fn] +// CHECK:STDOUT: %bound_method.loc12_40.4: = bound_method %.loc12_39.2, %specific_fn.loc12_40.2 +// CHECK:STDOUT: %.loc12_40.1: ref %Optional.cf0 = temporary_storage +// CHECK:STDOUT: %.loc12_39.4: %EmptyRange.cc8 = bind_value %.loc12_39.2 +// CHECK:STDOUT: %Next.call: init %Optional.cf0 = call %bound_method.loc12_40.4(%.loc12_39.4, %addr.loc12) to %.loc12_40.1 +// CHECK:STDOUT: %.loc12_40.2: ref %Optional.cf0 = temporary %.loc12_40.1, %Next.call +// CHECK:STDOUT: %.loc12_40.3: %HasValue.type.71d = specific_constant imports.%Main.import_ref.7f9, @Optional(constants.%C) [concrete = constants.%HasValue.513] +// CHECK:STDOUT: %HasValue.ref: %HasValue.type.71d = name_ref HasValue, %.loc12_40.3 [concrete = constants.%HasValue.513] +// CHECK:STDOUT: %HasValue.bound: = bound_method %.loc12_40.2, %HasValue.ref +// CHECK:STDOUT: %HasValue.specific_fn: = specific_function %HasValue.ref, @HasValue(constants.%C) [concrete = constants.%HasValue.specific_fn] +// CHECK:STDOUT: %bound_method.loc12_40.5: = bound_method %.loc12_40.2, %HasValue.specific_fn +// CHECK:STDOUT: %.loc12_40.4: %Optional.cf0 = bind_value %.loc12_40.2 +// CHECK:STDOUT: %HasValue.call: init bool = call %bound_method.loc12_40.5(%.loc12_40.4) +// CHECK:STDOUT: %.loc12_40.5: bool = value_of_initializer %HasValue.call +// CHECK:STDOUT: %.loc12_40.6: bool = converted %HasValue.call, %.loc12_40.5 +// CHECK:STDOUT: if %.loc12_40.6 br !for.body else br !for.done +// CHECK:STDOUT: +// CHECK:STDOUT: !for.body: +// CHECK:STDOUT: %c.var: ref %C = var %c.var_patt +// CHECK:STDOUT: %.loc12_40.7: %Get.type.115 = specific_constant imports.%Main.import_ref.d10, @Optional(constants.%C) [concrete = constants.%Get.9c1] +// CHECK:STDOUT: %Get.ref: %Get.type.115 = name_ref Get, %.loc12_40.7 [concrete = constants.%Get.9c1] +// CHECK:STDOUT: %Get.bound: = bound_method %.loc12_40.2, %Get.ref +// CHECK:STDOUT: %Get.specific_fn: = specific_function %Get.ref, @Get(constants.%C) [concrete = constants.%Get.specific_fn] +// CHECK:STDOUT: %bound_method.loc12_40.6: = bound_method %.loc12_40.2, %Get.specific_fn +// CHECK:STDOUT: %.loc12_8: ref %C = splice_block %c.var {} +// CHECK:STDOUT: %.loc12_40.8: %Optional.cf0 = bind_value %.loc12_40.2 +// CHECK:STDOUT: %Get.call: init %C = call %bound_method.loc12_40.6(%.loc12_40.8) to %.loc12_8 +// CHECK:STDOUT: assign %c.var, %Get.call +// CHECK:STDOUT: %C.ref.loc12_15: type = name_ref C, file.%C.decl [concrete = constants.%C] +// CHECK:STDOUT: %c: ref %C = bind_name c, %c.var +// CHECK:STDOUT: %Body.ref: %Body.type = name_ref Body, file.%Body.decl [concrete = constants.%Body] +// CHECK:STDOUT: %c.ref: ref %C = name_ref c, %c +// CHECK:STDOUT: %addr.loc13: %ptr.019 = addr_of %c.ref +// CHECK:STDOUT: %Body.call: init %empty_tuple.type = call %Body.ref(%addr.loc13) +// CHECK:STDOUT: br !for.next +// CHECK:STDOUT: +// CHECK:STDOUT: !for.done: +// CHECK:STDOUT: +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: --- tuple.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %Bool.type: type = fn_type @Bool [concrete] +// CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] +// CHECK:STDOUT: %Bool: %Bool.type = struct_value () [concrete] +// CHECK:STDOUT: %pattern_type.831: type = pattern_type bool [concrete] +// CHECK:STDOUT: %Body.type: type = fn_type @Body [concrete] +// CHECK:STDOUT: %Body: %Body.type = struct_value () [concrete] +// CHECK:STDOUT: %tuple.type.784: type = tuple_type (bool, bool) [concrete] +// CHECK:STDOUT: %pattern_type.860: type = pattern_type %tuple.type.784 [concrete] +// CHECK:STDOUT: %EmptyRange.type: type = generic_class_type @EmptyRange [concrete] +// CHECK:STDOUT: %EmptyRange.generic: %EmptyRange.type = struct_value () [concrete] +// CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] +// CHECK:STDOUT: %T: type = bind_symbolic_name T, 0 [symbolic] +// CHECK:STDOUT: %Make.type.bd2: type = fn_type @Make, @EmptyRange(%T) [symbolic] +// CHECK:STDOUT: %Make.ed1: %Make.type.bd2 = struct_value () [symbolic] +// CHECK:STDOUT: %tuple.type.24b: type = tuple_type (type, type) [concrete] +// CHECK:STDOUT: %EmptyRange.2f3: type = class_type @EmptyRange, @EmptyRange(%tuple.type.784) [concrete] +// CHECK:STDOUT: %Make.type.4fb: type = fn_type @Make, @EmptyRange(%tuple.type.784) [concrete] +// CHECK:STDOUT: %Make.743: %Make.type.4fb = struct_value () [concrete] +// CHECK:STDOUT: %ptr.c28: type = ptr_type %empty_struct_type [concrete] +// CHECK:STDOUT: %Make.specific_fn: = specific_function %Make.743, @Make(%tuple.type.784) [concrete] +// CHECK:STDOUT: %Iterate.type: type = facet_type <@Iterate> [concrete] +// CHECK:STDOUT: %NewCursor.type.427: type = fn_type @NewCursor.1 [concrete] +// CHECK:STDOUT: %NewCursor.type.f5f: type = fn_type @NewCursor.2, @impl(%T) [symbolic] +// CHECK:STDOUT: %NewCursor.ec1: %NewCursor.type.f5f = struct_value () [symbolic] +// CHECK:STDOUT: %Next.type.264: type = fn_type @Next.1, @impl(%T) [symbolic] +// CHECK:STDOUT: %Next.08e: %Next.type.264 = struct_value () [symbolic] +// CHECK:STDOUT: %HasValue.type.f81: type = fn_type @HasValue, @Optional(%T) [symbolic] +// CHECK:STDOUT: %HasValue.6fd: %HasValue.type.f81 = struct_value () [symbolic] +// CHECK:STDOUT: %Get.type.b8f: type = fn_type @Get, @Optional(%T) [symbolic] +// CHECK:STDOUT: %Get.9c8: %Get.type.b8f = struct_value () [symbolic] +// CHECK:STDOUT: %Iterate.impl_witness.6db: = impl_witness imports.%Iterate.impl_witness_table, @impl(%tuple.type.784) [concrete] +// CHECK:STDOUT: %NewCursor.type.d32: type = fn_type @NewCursor.2, @impl(%tuple.type.784) [concrete] +// CHECK:STDOUT: %NewCursor.763: %NewCursor.type.d32 = struct_value () [concrete] +// CHECK:STDOUT: %Next.type.482: type = fn_type @Next.1, @impl(%tuple.type.784) [concrete] +// CHECK:STDOUT: %Next.e60: %Next.type.482 = struct_value () [concrete] +// CHECK:STDOUT: %Iterate.facet: %Iterate.type = facet_value %EmptyRange.2f3, (%Iterate.impl_witness.6db) [concrete] +// CHECK:STDOUT: %.2bf: type = fn_type_with_self_type %NewCursor.type.427, %Iterate.facet [concrete] +// CHECK:STDOUT: %NewCursor.specific_fn: = specific_function %NewCursor.763, @NewCursor.2(%tuple.type.784) [concrete] +// CHECK:STDOUT: %Next.type.941: type = fn_type @Next.2 [concrete] +// CHECK:STDOUT: %.6aa: type = fn_type_with_self_type %Next.type.941, %Iterate.facet [concrete] +// CHECK:STDOUT: %Optional.79e: type = class_type @Optional, @Optional(%tuple.type.784) [concrete] +// CHECK:STDOUT: %Next.specific_fn: = specific_function %Next.e60, @Next.1(%tuple.type.784) [concrete] +// CHECK:STDOUT: %HasValue.type.414: type = fn_type @HasValue, @Optional(%tuple.type.784) [concrete] +// CHECK:STDOUT: %HasValue.4f9: %HasValue.type.414 = struct_value () [concrete] +// CHECK:STDOUT: %Get.type.ebd: type = fn_type @Get, @Optional(%tuple.type.784) [concrete] +// CHECK:STDOUT: %Get.d99: %Get.type.ebd = struct_value () [concrete] +// CHECK:STDOUT: %HasValue.specific_fn: = specific_function %HasValue.4f9, @HasValue(%tuple.type.784) [concrete] +// CHECK:STDOUT: %Get.specific_fn: = specific_function %Get.d99, @Get(%tuple.type.784) [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: imports { +// CHECK:STDOUT: %Main.EmptyRange: %EmptyRange.type = import_ref Main//empty_range, EmptyRange, loaded [concrete = constants.%EmptyRange.generic] +// CHECK:STDOUT: %Main.import_ref.6ea: @EmptyRange.%Make.type (%Make.type.bd2) = import_ref Main//empty_range, loc5_21, loaded [symbolic = @EmptyRange.%Make (constants.%Make.ed1)] +// CHECK:STDOUT: %Main.import_ref.6ce = import_ref Main//empty_range, loc7_68, unloaded +// CHECK:STDOUT: %Main.import_ref.999 = import_ref Main//empty_range, loc7_68, unloaded +// CHECK:STDOUT: %Main.import_ref.57b: @impl.%NewCursor.type (%NewCursor.type.f5f) = import_ref Main//empty_range, loc8_38, loaded [symbolic = @impl.%NewCursor (constants.%NewCursor.ec1)] +// CHECK:STDOUT: %Main.import_ref.170: @impl.%Next.type (%Next.type.264) = import_ref Main//empty_range, loc11_58, loaded [symbolic = @impl.%Next (constants.%Next.08e)] +// CHECK:STDOUT: %Iterate.impl_witness_table = impl_witness_table (%Main.import_ref.6ce, %Main.import_ref.999, %Main.import_ref.57b, %Main.import_ref.170), @impl [concrete] +// CHECK:STDOUT: %Main.import_ref.7f9: @Optional.%HasValue.type (%HasValue.type.f81) = import_ref Main//empty_range, inst136 [indirect], loaded [symbolic = @Optional.%HasValue (constants.%HasValue.6fd)] +// CHECK:STDOUT: %Main.import_ref.d10: @Optional.%Get.type (%Get.type.b8f) = import_ref Main//empty_range, inst137 [indirect], loaded [symbolic = @Optional.%Get (constants.%Get.9c8)] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @Run() { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: name_binding_decl { +// CHECK:STDOUT: %a.patt: %pattern_type.831 = binding_pattern a [concrete] +// CHECK:STDOUT: %b.patt: %pattern_type.831 = binding_pattern b [concrete] +// CHECK:STDOUT: %.loc10_25: %pattern_type.860 = tuple_pattern (%a.patt, %b.patt) [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: %EmptyRange.ref: %EmptyRange.type = name_ref EmptyRange, imports.%Main.EmptyRange [concrete = constants.%EmptyRange.generic] +// CHECK:STDOUT: %bool.make_type.loc10_42: init type = call constants.%Bool() [concrete = bool] +// CHECK:STDOUT: %bool.make_type.loc10_48: init type = call constants.%Bool() [concrete = bool] +// CHECK:STDOUT: %.loc10_52: %tuple.type.24b = tuple_literal (%bool.make_type.loc10_42, %bool.make_type.loc10_48) +// CHECK:STDOUT: %.loc10_53.1: type = value_of_initializer %bool.make_type.loc10_42 [concrete = bool] +// CHECK:STDOUT: %.loc10_53.2: type = converted %bool.make_type.loc10_42, %.loc10_53.1 [concrete = bool] +// CHECK:STDOUT: %.loc10_53.3: type = value_of_initializer %bool.make_type.loc10_48 [concrete = bool] +// CHECK:STDOUT: %.loc10_53.4: type = converted %bool.make_type.loc10_48, %.loc10_53.3 [concrete = bool] +// CHECK:STDOUT: %.loc10_53.5: type = converted %.loc10_52, constants.%tuple.type.784 [concrete = constants.%tuple.type.784] +// CHECK:STDOUT: %EmptyRange: type = class_type @EmptyRange, @EmptyRange(constants.%tuple.type.784) [concrete = constants.%EmptyRange.2f3] +// CHECK:STDOUT: %.loc10_54: %Make.type.4fb = specific_constant imports.%Main.import_ref.6ea, @EmptyRange(constants.%tuple.type.784) [concrete = constants.%Make.743] +// CHECK:STDOUT: %Make.ref: %Make.type.4fb = name_ref Make, %.loc10_54 [concrete = constants.%Make.743] +// CHECK:STDOUT: %Make.specific_fn: = specific_function %Make.ref, @Make(constants.%tuple.type.784) [concrete = constants.%Make.specific_fn] +// CHECK:STDOUT: %.loc10_60.1: ref %EmptyRange.2f3 = temporary_storage +// CHECK:STDOUT: %Make.call: init %EmptyRange.2f3 = call %Make.specific_fn() to %.loc10_60.1 +// CHECK:STDOUT: %.loc10_60.2: ref %EmptyRange.2f3 = temporary %.loc10_60.1, %Make.call +// CHECK:STDOUT: %impl.elem2: %.2bf = impl_witness_access constants.%Iterate.impl_witness.6db, element2 [concrete = constants.%NewCursor.763] +// CHECK:STDOUT: %bound_method.loc10_61.1: = bound_method %.loc10_60.2, %impl.elem2 +// CHECK:STDOUT: %specific_fn.loc10_61.1: = specific_function %impl.elem2, @NewCursor.2(constants.%tuple.type.784) [concrete = constants.%NewCursor.specific_fn] +// CHECK:STDOUT: %bound_method.loc10_61.2: = bound_method %.loc10_60.2, %specific_fn.loc10_61.1 +// CHECK:STDOUT: %.loc10_60.3: %EmptyRange.2f3 = bind_value %.loc10_60.2 +// CHECK:STDOUT: %NewCursor.call: init %empty_struct_type = call %bound_method.loc10_61.2(%.loc10_60.3) +// CHECK:STDOUT: %var: ref %empty_struct_type = var invalid +// CHECK:STDOUT: assign %var, %NewCursor.call +// CHECK:STDOUT: br !for.next +// CHECK:STDOUT: +// CHECK:STDOUT: !for.next: +// CHECK:STDOUT: %addr: %ptr.c28 = addr_of %var +// CHECK:STDOUT: %impl.elem3: %.6aa = impl_witness_access constants.%Iterate.impl_witness.6db, element3 [concrete = constants.%Next.e60] +// CHECK:STDOUT: %bound_method.loc10_61.3: = bound_method %.loc10_60.2, %impl.elem3 +// CHECK:STDOUT: %specific_fn.loc10_61.2: = specific_function %impl.elem3, @Next.1(constants.%tuple.type.784) [concrete = constants.%Next.specific_fn] +// CHECK:STDOUT: %bound_method.loc10_61.4: = bound_method %.loc10_60.2, %specific_fn.loc10_61.2 +// CHECK:STDOUT: %.loc10_61.1: ref %Optional.79e = temporary_storage +// CHECK:STDOUT: %.loc10_60.4: %EmptyRange.2f3 = bind_value %.loc10_60.2 +// CHECK:STDOUT: %Next.call: init %Optional.79e = call %bound_method.loc10_61.4(%.loc10_60.4, %addr) to %.loc10_61.1 +// CHECK:STDOUT: %.loc10_61.2: ref %Optional.79e = temporary %.loc10_61.1, %Next.call +// CHECK:STDOUT: %.loc10_61.3: %HasValue.type.414 = specific_constant imports.%Main.import_ref.7f9, @Optional(constants.%tuple.type.784) [concrete = constants.%HasValue.4f9] +// CHECK:STDOUT: %HasValue.ref: %HasValue.type.414 = name_ref HasValue, %.loc10_61.3 [concrete = constants.%HasValue.4f9] +// CHECK:STDOUT: %HasValue.bound: = bound_method %.loc10_61.2, %HasValue.ref +// CHECK:STDOUT: %HasValue.specific_fn: = specific_function %HasValue.ref, @HasValue(constants.%tuple.type.784) [concrete = constants.%HasValue.specific_fn] +// CHECK:STDOUT: %bound_method.loc10_61.5: = bound_method %.loc10_61.2, %HasValue.specific_fn +// CHECK:STDOUT: %.loc10_61.4: %Optional.79e = bind_value %.loc10_61.2 +// CHECK:STDOUT: %HasValue.call: init bool = call %bound_method.loc10_61.5(%.loc10_61.4) +// CHECK:STDOUT: %.loc10_61.5: bool = value_of_initializer %HasValue.call +// CHECK:STDOUT: %.loc10_61.6: bool = converted %HasValue.call, %.loc10_61.5 +// CHECK:STDOUT: if %.loc10_61.6 br !for.body else br !for.done +// CHECK:STDOUT: +// CHECK:STDOUT: !for.body: +// CHECK:STDOUT: %.loc10_61.7: %Get.type.ebd = specific_constant imports.%Main.import_ref.d10, @Optional(constants.%tuple.type.784) [concrete = constants.%Get.d99] +// CHECK:STDOUT: %Get.ref: %Get.type.ebd = name_ref Get, %.loc10_61.7 [concrete = constants.%Get.d99] +// CHECK:STDOUT: %Get.bound: = bound_method %.loc10_61.2, %Get.ref +// CHECK:STDOUT: %Get.specific_fn: = specific_function %Get.ref, @Get(constants.%tuple.type.784) [concrete = constants.%Get.specific_fn] +// CHECK:STDOUT: %bound_method.loc10_61.6: = bound_method %.loc10_61.2, %Get.specific_fn +// CHECK:STDOUT: %.loc10_61.8: ref %tuple.type.784 = temporary_storage +// CHECK:STDOUT: %.loc10_61.9: %Optional.79e = bind_value %.loc10_61.2 +// CHECK:STDOUT: %Get.call: init %tuple.type.784 = call %bound_method.loc10_61.6(%.loc10_61.9) to %.loc10_61.8 +// CHECK:STDOUT: %.loc10_61.10: ref %tuple.type.784 = temporary %.loc10_61.8, %Get.call +// CHECK:STDOUT: %tuple.elem0: ref bool = tuple_access %.loc10_61.10, element0 +// CHECK:STDOUT: %tuple.elem1: ref bool = tuple_access %.loc10_61.10, element1 +// CHECK:STDOUT: %.loc10_12.1: type = splice_block %.loc10_12.3 [concrete = bool] { +// CHECK:STDOUT: %bool.make_type.loc10_12: init type = call constants.%Bool() [concrete = bool] +// CHECK:STDOUT: %.loc10_12.2: type = value_of_initializer %bool.make_type.loc10_12 [concrete = bool] +// CHECK:STDOUT: %.loc10_12.3: type = converted %bool.make_type.loc10_12, %.loc10_12.2 [concrete = bool] +// CHECK:STDOUT: } +// CHECK:STDOUT: %.loc10_61.11: bool = bind_value %tuple.elem0 +// CHECK:STDOUT: %a: bool = bind_name a, %.loc10_61.11 +// CHECK:STDOUT: %.loc10_21.1: type = splice_block %.loc10_21.3 [concrete = bool] { +// CHECK:STDOUT: %bool.make_type.loc10_21: init type = call constants.%Bool() [concrete = bool] +// CHECK:STDOUT: %.loc10_21.2: type = value_of_initializer %bool.make_type.loc10_21 [concrete = bool] +// CHECK:STDOUT: %.loc10_21.3: type = converted %bool.make_type.loc10_21, %.loc10_21.2 [concrete = bool] +// CHECK:STDOUT: } +// CHECK:STDOUT: %.loc10_61.12: bool = bind_value %tuple.elem1 +// CHECK:STDOUT: %b: bool = bind_name b, %.loc10_61.12 +// CHECK:STDOUT: %Body.ref: %Body.type = name_ref Body, file.%Body.decl [concrete = constants.%Body] +// 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: +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: --- tuple_class.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %C: type = class_type @C [concrete] +// CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] +// CHECK:STDOUT: %pattern_type.c48: type = pattern_type %C [concrete] +// CHECK:STDOUT: %Body.type: type = fn_type @Body [concrete] +// CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] +// CHECK:STDOUT: %Body: %Body.type = struct_value () [concrete] +// CHECK:STDOUT: %ptr.c28: type = ptr_type %empty_struct_type [concrete] +// CHECK:STDOUT: %tuple.type.56b: type = tuple_type (%C, %C) [concrete] +// CHECK:STDOUT: %pattern_type.99e: type = pattern_type %tuple.type.56b [concrete] +// CHECK:STDOUT: %EmptyRange.type: type = generic_class_type @EmptyRange [concrete] +// CHECK:STDOUT: %EmptyRange.generic: %EmptyRange.type = struct_value () [concrete] +// CHECK:STDOUT: %T: type = bind_symbolic_name T, 0 [symbolic] +// CHECK:STDOUT: %Make.type.bd2: type = fn_type @Make, @EmptyRange(%T) [symbolic] +// CHECK:STDOUT: %Make.ed1: %Make.type.bd2 = struct_value () [symbolic] +// CHECK:STDOUT: %tuple.type.24b: type = tuple_type (type, type) [concrete] +// CHECK:STDOUT: %EmptyRange.90a: type = class_type @EmptyRange, @EmptyRange(%tuple.type.56b) [concrete] +// CHECK:STDOUT: %Make.type.829: type = fn_type @Make, @EmptyRange(%tuple.type.56b) [concrete] +// CHECK:STDOUT: %Make.d4f: %Make.type.829 = struct_value () [concrete] +// CHECK:STDOUT: %Make.specific_fn: = specific_function %Make.d4f, @Make(%tuple.type.56b) [concrete] +// CHECK:STDOUT: %Iterate.type: type = facet_type <@Iterate> [concrete] +// CHECK:STDOUT: %NewCursor.type.427: type = fn_type @NewCursor.1 [concrete] +// CHECK:STDOUT: %NewCursor.type.f5f: type = fn_type @NewCursor.2, @impl(%T) [symbolic] +// CHECK:STDOUT: %NewCursor.ec1: %NewCursor.type.f5f = struct_value () [symbolic] +// CHECK:STDOUT: %Next.type.264: type = fn_type @Next.1, @impl(%T) [symbolic] +// CHECK:STDOUT: %Next.08e: %Next.type.264 = struct_value () [symbolic] +// CHECK:STDOUT: %HasValue.type.f81: type = fn_type @HasValue, @Optional(%T) [symbolic] +// CHECK:STDOUT: %HasValue.6fd: %HasValue.type.f81 = struct_value () [symbolic] +// CHECK:STDOUT: %Get.type.b8f: type = fn_type @Get, @Optional(%T) [symbolic] +// CHECK:STDOUT: %Get.9c8: %Get.type.b8f = struct_value () [symbolic] +// CHECK:STDOUT: %Iterate.impl_witness.627: = impl_witness imports.%Iterate.impl_witness_table, @impl(%tuple.type.56b) [concrete] +// CHECK:STDOUT: %NewCursor.type.59c: type = fn_type @NewCursor.2, @impl(%tuple.type.56b) [concrete] +// CHECK:STDOUT: %NewCursor.263: %NewCursor.type.59c = struct_value () [concrete] +// CHECK:STDOUT: %Next.type.70f: type = fn_type @Next.1, @impl(%tuple.type.56b) [concrete] +// CHECK:STDOUT: %Next.852: %Next.type.70f = struct_value () [concrete] +// CHECK:STDOUT: %Iterate.facet: %Iterate.type = facet_value %EmptyRange.90a, (%Iterate.impl_witness.627) [concrete] +// CHECK:STDOUT: %.8bd: type = fn_type_with_self_type %NewCursor.type.427, %Iterate.facet [concrete] +// CHECK:STDOUT: %NewCursor.specific_fn: = specific_function %NewCursor.263, @NewCursor.2(%tuple.type.56b) [concrete] +// CHECK:STDOUT: %Next.type.941: type = fn_type @Next.2 [concrete] +// CHECK:STDOUT: %.8d0: type = fn_type_with_self_type %Next.type.941, %Iterate.facet [concrete] +// CHECK:STDOUT: %Optional.657: type = class_type @Optional, @Optional(%tuple.type.56b) [concrete] +// CHECK:STDOUT: %Next.specific_fn: = specific_function %Next.852, @Next.1(%tuple.type.56b) [concrete] +// CHECK:STDOUT: %HasValue.type.2f1: type = fn_type @HasValue, @Optional(%tuple.type.56b) [concrete] +// CHECK:STDOUT: %HasValue.bef: %HasValue.type.2f1 = struct_value () [concrete] +// CHECK:STDOUT: %Get.type.4a4: type = fn_type @Get, @Optional(%tuple.type.56b) [concrete] +// CHECK:STDOUT: %Get.8a7: %Get.type.4a4 = struct_value () [concrete] +// CHECK:STDOUT: %HasValue.specific_fn: = specific_function %HasValue.bef, @HasValue(%tuple.type.56b) [concrete] +// CHECK:STDOUT: %Get.specific_fn: = specific_function %Get.8a7, @Get(%tuple.type.56b) [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: imports { +// CHECK:STDOUT: %Main.EmptyRange: %EmptyRange.type = import_ref Main//empty_range, EmptyRange, loaded [concrete = constants.%EmptyRange.generic] +// CHECK:STDOUT: %Main.import_ref.6ea: @EmptyRange.%Make.type (%Make.type.bd2) = import_ref Main//empty_range, loc5_21, loaded [symbolic = @EmptyRange.%Make (constants.%Make.ed1)] +// CHECK:STDOUT: %Main.import_ref.6ce = import_ref Main//empty_range, loc7_68, unloaded +// CHECK:STDOUT: %Main.import_ref.999 = import_ref Main//empty_range, loc7_68, unloaded +// CHECK:STDOUT: %Main.import_ref.57b: @impl.%NewCursor.type (%NewCursor.type.f5f) = import_ref Main//empty_range, loc8_38, loaded [symbolic = @impl.%NewCursor (constants.%NewCursor.ec1)] +// CHECK:STDOUT: %Main.import_ref.170: @impl.%Next.type (%Next.type.264) = import_ref Main//empty_range, loc11_58, loaded [symbolic = @impl.%Next (constants.%Next.08e)] +// CHECK:STDOUT: %Iterate.impl_witness_table = impl_witness_table (%Main.import_ref.6ce, %Main.import_ref.999, %Main.import_ref.57b, %Main.import_ref.170), @impl [concrete] +// CHECK:STDOUT: %Main.import_ref.7f9: @Optional.%HasValue.type (%HasValue.type.f81) = import_ref Main//empty_range, inst136 [indirect], loaded [symbolic = @Optional.%HasValue (constants.%HasValue.6fd)] +// CHECK:STDOUT: %Main.import_ref.d10: @Optional.%Get.type (%Get.type.b8f) = import_ref Main//empty_range, inst137 [indirect], loaded [symbolic = @Optional.%Get (constants.%Get.9c8)] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @Run() { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: name_binding_decl { +// CHECK:STDOUT: %a.patt: %pattern_type.c48 = binding_pattern a [concrete] +// CHECK:STDOUT: %b.patt: %pattern_type.c48 = binding_pattern b [concrete] +// CHECK:STDOUT: %.loc12_19: %pattern_type.99e = tuple_pattern (%a.patt, %b.patt) [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: %EmptyRange.ref: %EmptyRange.type = name_ref EmptyRange, imports.%Main.EmptyRange [concrete = constants.%EmptyRange.generic] +// CHECK:STDOUT: %C.ref.loc12_36: type = name_ref C, file.%C.decl [concrete = constants.%C] +// CHECK:STDOUT: %C.ref.loc12_39: type = name_ref C, file.%C.decl [concrete = constants.%C] +// CHECK:STDOUT: %.loc12_40: %tuple.type.24b = tuple_literal (%C.ref.loc12_36, %C.ref.loc12_39) +// CHECK:STDOUT: %.loc12_41: type = converted %.loc12_40, constants.%tuple.type.56b [concrete = constants.%tuple.type.56b] +// CHECK:STDOUT: %EmptyRange: type = class_type @EmptyRange, @EmptyRange(constants.%tuple.type.56b) [concrete = constants.%EmptyRange.90a] +// CHECK:STDOUT: %.loc12_42: %Make.type.829 = specific_constant imports.%Main.import_ref.6ea, @EmptyRange(constants.%tuple.type.56b) [concrete = constants.%Make.d4f] +// CHECK:STDOUT: %Make.ref: %Make.type.829 = name_ref Make, %.loc12_42 [concrete = constants.%Make.d4f] +// CHECK:STDOUT: %Make.specific_fn: = specific_function %Make.ref, @Make(constants.%tuple.type.56b) [concrete = constants.%Make.specific_fn] +// CHECK:STDOUT: %.loc12_48.1: ref %EmptyRange.90a = temporary_storage +// CHECK:STDOUT: %Make.call: init %EmptyRange.90a = call %Make.specific_fn() to %.loc12_48.1 +// CHECK:STDOUT: %.loc12_48.2: ref %EmptyRange.90a = temporary %.loc12_48.1, %Make.call +// CHECK:STDOUT: %impl.elem2: %.8bd = impl_witness_access constants.%Iterate.impl_witness.627, element2 [concrete = constants.%NewCursor.263] +// CHECK:STDOUT: %bound_method.loc12_49.1: = bound_method %.loc12_48.2, %impl.elem2 +// CHECK:STDOUT: %specific_fn.loc12_49.1: = specific_function %impl.elem2, @NewCursor.2(constants.%tuple.type.56b) [concrete = constants.%NewCursor.specific_fn] +// CHECK:STDOUT: %bound_method.loc12_49.2: = bound_method %.loc12_48.2, %specific_fn.loc12_49.1 +// CHECK:STDOUT: %.loc12_48.3: %EmptyRange.90a = bind_value %.loc12_48.2 +// CHECK:STDOUT: %NewCursor.call: init %empty_struct_type = call %bound_method.loc12_49.2(%.loc12_48.3) +// CHECK:STDOUT: %var: ref %empty_struct_type = var invalid +// CHECK:STDOUT: assign %var, %NewCursor.call +// CHECK:STDOUT: br !for.next +// CHECK:STDOUT: +// CHECK:STDOUT: !for.next: +// CHECK:STDOUT: %addr: %ptr.c28 = addr_of %var +// CHECK:STDOUT: %impl.elem3: %.8d0 = impl_witness_access constants.%Iterate.impl_witness.627, element3 [concrete = constants.%Next.852] +// CHECK:STDOUT: %bound_method.loc12_49.3: = bound_method %.loc12_48.2, %impl.elem3 +// CHECK:STDOUT: %specific_fn.loc12_49.2: = specific_function %impl.elem3, @Next.1(constants.%tuple.type.56b) [concrete = constants.%Next.specific_fn] +// CHECK:STDOUT: %bound_method.loc12_49.4: = bound_method %.loc12_48.2, %specific_fn.loc12_49.2 +// CHECK:STDOUT: %.loc12_49.1: ref %Optional.657 = temporary_storage +// CHECK:STDOUT: %.loc12_48.4: %EmptyRange.90a = bind_value %.loc12_48.2 +// CHECK:STDOUT: %Next.call: init %Optional.657 = call %bound_method.loc12_49.4(%.loc12_48.4, %addr) to %.loc12_49.1 +// CHECK:STDOUT: %.loc12_49.2: ref %Optional.657 = temporary %.loc12_49.1, %Next.call +// CHECK:STDOUT: %.loc12_49.3: %HasValue.type.2f1 = specific_constant imports.%Main.import_ref.7f9, @Optional(constants.%tuple.type.56b) [concrete = constants.%HasValue.bef] +// CHECK:STDOUT: %HasValue.ref: %HasValue.type.2f1 = name_ref HasValue, %.loc12_49.3 [concrete = constants.%HasValue.bef] +// CHECK:STDOUT: %HasValue.bound: = bound_method %.loc12_49.2, %HasValue.ref +// CHECK:STDOUT: %HasValue.specific_fn: = specific_function %HasValue.ref, @HasValue(constants.%tuple.type.56b) [concrete = constants.%HasValue.specific_fn] +// CHECK:STDOUT: %bound_method.loc12_49.5: = bound_method %.loc12_49.2, %HasValue.specific_fn +// CHECK:STDOUT: %.loc12_49.4: %Optional.657 = bind_value %.loc12_49.2 +// CHECK:STDOUT: %HasValue.call: init bool = call %bound_method.loc12_49.5(%.loc12_49.4) +// CHECK:STDOUT: %.loc12_49.5: bool = value_of_initializer %HasValue.call +// CHECK:STDOUT: %.loc12_49.6: bool = converted %HasValue.call, %.loc12_49.5 +// CHECK:STDOUT: if %.loc12_49.6 br !for.body else br !for.done +// CHECK:STDOUT: +// CHECK:STDOUT: !for.body: +// CHECK:STDOUT: %.loc12_49.7: %Get.type.4a4 = specific_constant imports.%Main.import_ref.d10, @Optional(constants.%tuple.type.56b) [concrete = constants.%Get.8a7] +// CHECK:STDOUT: %Get.ref: %Get.type.4a4 = name_ref Get, %.loc12_49.7 [concrete = constants.%Get.8a7] +// CHECK:STDOUT: %Get.bound: = bound_method %.loc12_49.2, %Get.ref +// CHECK:STDOUT: %Get.specific_fn: = specific_function %Get.ref, @Get(constants.%tuple.type.56b) [concrete = constants.%Get.specific_fn] +// CHECK:STDOUT: %bound_method.loc12_49.6: = bound_method %.loc12_49.2, %Get.specific_fn +// CHECK:STDOUT: %.loc12_49.8: ref %tuple.type.56b = temporary_storage +// CHECK:STDOUT: %.loc12_49.9: %Optional.657 = bind_value %.loc12_49.2 +// CHECK:STDOUT: %Get.call: init %tuple.type.56b = call %bound_method.loc12_49.6(%.loc12_49.9) to %.loc12_49.8 +// CHECK:STDOUT: %.loc12_49.10: ref %tuple.type.56b = temporary %.loc12_49.8, %Get.call +// CHECK:STDOUT: %tuple.elem0: ref %C = tuple_access %.loc12_49.10, element0 +// CHECK:STDOUT: %tuple.elem1: ref %C = tuple_access %.loc12_49.10, element1 +// CHECK:STDOUT: %C.ref.loc12_12: type = name_ref C, file.%C.decl [concrete = constants.%C] +// CHECK:STDOUT: %.loc12_49.11: %C = bind_value %tuple.elem0 +// CHECK:STDOUT: %a: %C = bind_name a, %.loc12_49.11 +// CHECK:STDOUT: %C.ref.loc12_18: type = name_ref C, file.%C.decl [concrete = constants.%C] +// CHECK:STDOUT: %.loc12_49.12: %C = bind_value %tuple.elem1 +// CHECK:STDOUT: %b: %C = bind_name b, %.loc12_49.12 +// CHECK:STDOUT: %Body.ref: %Body.type = name_ref Body, file.%Body.decl [concrete = constants.%Body] +// 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: +// CHECK:STDOUT: } +// CHECK:STDOUT: diff --git a/toolchain/lower/handle_aggregates.cpp b/toolchain/lower/handle_aggregates.cpp index 17773b7f5356..0793ab422064 100644 --- a/toolchain/lower/handle_aggregates.cpp +++ b/toolchain/lower/handle_aggregates.cpp @@ -41,7 +41,9 @@ static auto GetAggregateElement(FunctionContext& context, case SemIR::ExprCategory::NotExpr: case SemIR::ExprCategory::Initializing: case SemIR::ExprCategory::Mixed: - CARBON_FATAL("Unexpected expression category for aggregate access"); + CARBON_FATAL( + "Unexpected expression category for aggregate access into {0}", + context.sem_ir().insts().Get(aggr_inst_id)); case SemIR::ExprCategory::Value: { auto aggr_type = context.GetTypeIdOfInst(aggr_inst_id); diff --git a/toolchain/lower/testdata/for/bindings.carbon b/toolchain/lower/testdata/for/bindings.carbon new file mode 100644 index 000000000000..1bc71b627b01 --- /dev/null +++ b/toolchain/lower/testdata/for/bindings.carbon @@ -0,0 +1,144 @@ +// Part of the Carbon Language project, under the Apache License v2.0 with LLVM +// Exceptions. See /LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// AUTOUPDATE +// TIP: To test this file alone, run: +// TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/lower/testdata/for/bindings.carbon +// TIP: To dump output, run: +// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/lower/testdata/for/bindings.carbon + +class EmptyRange(T:! type) { + impl as Core.Iterate where .CursorType = {} and .ElementType = T { + fn NewCursor[self: Self]() -> {} { + return {}; + } + fn Next[self: Self](cursor: {}*) -> Core.Optional(T) { + return Core.Optional(T).None(); + } + } +} + +fn F(m: i32, n: i32*); + +fn For() { + var r: EmptyRange((i32, i32)) = {}; + + for ((m: i32, var n: i32) in r) { + F(m, &n); + } +} + +// CHECK:STDOUT: ; ModuleID = 'bindings.carbon' +// CHECK:STDOUT: source_filename = "bindings.carbon" +// CHECK:STDOUT: +// CHECK:STDOUT: @EmptyRange.val.loc25_3 = internal constant {} zeroinitializer +// CHECK:STDOUT: +// CHECK:STDOUT: declare void @_CF.Main(i32, ptr) +// CHECK:STDOUT: +// CHECK:STDOUT: define void @_CFor.Main() !dbg !4 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: %r.var = alloca {}, align 8, !dbg !7 +// CHECK:STDOUT: %var = alloca {}, align 8, !dbg !8 +// CHECK:STDOUT: %.loc27_33.1.temp = alloca { i1, { i32, i32 } }, align 8, !dbg !8 +// CHECK:STDOUT: %n.var = alloca i32, align 4, !dbg !9 +// CHECK:STDOUT: %.loc27_33.8.temp = alloca { i32, i32 }, align 8, !dbg !8 +// CHECK:STDOUT: call void @llvm.lifetime.start.p0(i64 0, ptr %r.var), !dbg !7 +// CHECK:STDOUT: call void @llvm.memcpy.p0.p0.i64(ptr align 1 %r.var, ptr align 1 @EmptyRange.val.loc25_3, i64 0, i1 false), !dbg !7 +// CHECK:STDOUT: call void @"_CNewCursor.EmptyRange.Main:Iterate.Core.29d34654e802e24e"(ptr %r.var), !dbg !8 +// CHECK:STDOUT: call void @llvm.lifetime.start.p0(i64 0, ptr %var), !dbg !8 +// CHECK:STDOUT: br label %for.next, !dbg !8 +// CHECK:STDOUT: +// CHECK:STDOUT: for.next: ; preds = %for.body, %entry +// CHECK:STDOUT: call void @llvm.lifetime.start.p0(i64 12, ptr %.loc27_33.1.temp), !dbg !8 +// CHECK:STDOUT: call void @"_CNext.EmptyRange.Main:Iterate.Core.29d34654e802e24e"(ptr %.loc27_33.1.temp, ptr %r.var, ptr %var), !dbg !8 +// CHECK:STDOUT: %HasValue.call = call i1 @_CHasValue.Optional.Core.29d34654e802e24e(ptr %.loc27_33.1.temp), !dbg !8 +// CHECK:STDOUT: br i1 %HasValue.call, label %for.body, label %for.done, !dbg !8 +// CHECK:STDOUT: +// CHECK:STDOUT: for.body: ; preds = %for.next +// CHECK:STDOUT: call void @llvm.lifetime.start.p0(i64 4, ptr %n.var), !dbg !9 +// CHECK:STDOUT: call void @llvm.lifetime.start.p0(i64 8, ptr %.loc27_33.8.temp), !dbg !8 +// CHECK:STDOUT: call void @_CGet.Optional.Core.29d34654e802e24e(ptr %.loc27_33.8.temp, ptr %.loc27_33.1.temp), !dbg !8 +// CHECK:STDOUT: %tuple.elem0.tuple.elem = getelementptr inbounds nuw { i32, i32 }, ptr %.loc27_33.8.temp, i32 0, i32 0, !dbg !8 +// CHECK:STDOUT: %tuple.elem1.tuple.elem = getelementptr inbounds nuw { i32, i32 }, ptr %.loc27_33.8.temp, i32 0, i32 1, !dbg !8 +// CHECK:STDOUT: %.loc27_33.11 = load i32, ptr %tuple.elem0.tuple.elem, align 4, !dbg !8 +// CHECK:STDOUT: %.loc27_33.12 = load i32, ptr %tuple.elem1.tuple.elem, align 4, !dbg !8 +// CHECK:STDOUT: store i32 %.loc27_33.12, ptr %n.var, align 4, !dbg !9 +// CHECK:STDOUT: call void @_CF.Main(i32 %.loc27_33.11, ptr %n.var), !dbg !10 +// CHECK:STDOUT: br label %for.next, !dbg !11 +// CHECK:STDOUT: +// CHECK:STDOUT: for.done: ; preds = %for.next +// CHECK:STDOUT: ret void, !dbg !12 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) +// CHECK:STDOUT: declare void @llvm.lifetime.start.p0(i64 immarg, ptr captures(none)) #0 +// CHECK:STDOUT: +// CHECK:STDOUT: ; Function Attrs: nocallback nofree 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: define linkonce_odr void @"_CNewCursor.EmptyRange.Main:Iterate.Core.29d34654e802e24e"(ptr %self) !dbg !13 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: ret void, !dbg !14 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: define linkonce_odr void @"_CNext.EmptyRange.Main:Iterate.Core.29d34654e802e24e"(ptr sret({ i1, { i32, i32 } }) %return, ptr %self, ptr %cursor) !dbg !15 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: call void @_CNone.Optional.Core.29d34654e802e24e(ptr %return), !dbg !16 +// CHECK:STDOUT: ret void, !dbg !17 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: define linkonce_odr i1 @_CHasValue.Optional.Core.29d34654e802e24e(ptr %self) !dbg !18 { +// CHECK:STDOUT: %has_value = getelementptr inbounds nuw { i1, { i32, i32 } }, ptr %self, i32 0, i32 0, !dbg !20 +// CHECK:STDOUT: %1 = load i1, ptr %has_value, align 1, !dbg !20 +// CHECK:STDOUT: ret i1 %1, !dbg !21 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: define linkonce_odr void @_CGet.Optional.Core.29d34654e802e24e(ptr sret({ i32, i32 }) %return, ptr %self) !dbg !22 { +// CHECK:STDOUT: %value = getelementptr inbounds nuw { i1, { i32, i32 } }, ptr %self, i32 0, i32 1, !dbg !23 +// CHECK:STDOUT: ret void, !dbg !24 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: define linkonce_odr void @_CNone.Optional.Core.29d34654e802e24e(ptr sret({ i1, { i32, i32 } }) %return) !dbg !25 { +// CHECK:STDOUT: %has_value = getelementptr inbounds nuw { i1, { i32, i32 } }, ptr %return, i32 0, i32 0, !dbg !26 +// CHECK:STDOUT: store i1 false, ptr %has_value, align 1, !dbg !26 +// CHECK:STDOUT: ret void, !dbg !27 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: ; uselistorder directives +// CHECK:STDOUT: uselistorder ptr @llvm.lifetime.start.p0, { 4, 3, 2, 1, 0 } +// CHECK:STDOUT: +// CHECK:STDOUT: attributes #0 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } +// CHECK:STDOUT: attributes #1 = { nocallback nofree nounwind willreturn memory(argmem: readwrite) } +// CHECK:STDOUT: +// CHECK:STDOUT: !llvm.module.flags = !{!0, !1} +// CHECK:STDOUT: !llvm.dbg.cu = !{!2} +// CHECK:STDOUT: +// CHECK:STDOUT: !0 = !{i32 7, !"Dwarf Version", i32 5} +// CHECK:STDOUT: !1 = !{i32 2, !"Debug Info Version", i32 3} +// CHECK:STDOUT: !2 = distinct !DICompileUnit(language: DW_LANG_C, file: !3, producer: "carbon", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug) +// CHECK:STDOUT: !3 = !DIFile(filename: "bindings.carbon", directory: "") +// CHECK:STDOUT: !4 = distinct !DISubprogram(name: "For", linkageName: "_CFor.Main", scope: null, file: !3, line: 24, type: !5, spFlags: DISPFlagDefinition, unit: !2) +// CHECK:STDOUT: !5 = !DISubroutineType(types: !6) +// CHECK:STDOUT: !6 = !{} +// CHECK:STDOUT: !7 = !DILocation(line: 25, column: 3, scope: !4) +// CHECK:STDOUT: !8 = !DILocation(line: 27, column: 7, scope: !4) +// CHECK:STDOUT: !9 = !DILocation(line: 27, column: 17, scope: !4) +// CHECK:STDOUT: !10 = !DILocation(line: 28, column: 5, scope: !4) +// CHECK:STDOUT: !11 = !DILocation(line: 27, column: 3, scope: !4) +// CHECK:STDOUT: !12 = !DILocation(line: 24, column: 1, scope: !4) +// CHECK:STDOUT: !13 = distinct !DISubprogram(name: "NewCursor", linkageName: "_CNewCursor.EmptyRange.Main:Iterate.Core.29d34654e802e24e", scope: null, file: !3, line: 13, type: !5, spFlags: DISPFlagDefinition, unit: !2) +// CHECK:STDOUT: !14 = !DILocation(line: 14, column: 7, scope: !13) +// CHECK:STDOUT: !15 = distinct !DISubprogram(name: "Next", linkageName: "_CNext.EmptyRange.Main:Iterate.Core.29d34654e802e24e", scope: null, file: !3, line: 16, type: !5, spFlags: DISPFlagDefinition, unit: !2) +// CHECK:STDOUT: !16 = !DILocation(line: 17, column: 14, scope: !15) +// CHECK:STDOUT: !17 = !DILocation(line: 17, column: 7, scope: !15) +// CHECK:STDOUT: !18 = distinct !DISubprogram(name: "HasValue", linkageName: "_CHasValue.Optional.Core.29d34654e802e24e", scope: null, file: !19, line: 28, type: !5, spFlags: DISPFlagDefinition, unit: !2) +// CHECK:STDOUT: !19 = !DIFile(filename: "{{.*}}/prelude/types/optional.carbon", directory: "") +// CHECK:STDOUT: !20 = !DILocation(line: 28, column: 46, scope: !18) +// CHECK:STDOUT: !21 = !DILocation(line: 28, column: 39, scope: !18) +// CHECK:STDOUT: !22 = distinct !DISubprogram(name: "Get", linkageName: "_CGet.Optional.Core.29d34654e802e24e", scope: null, file: !19, line: 29, type: !5, spFlags: DISPFlagDefinition, unit: !2) +// CHECK:STDOUT: !23 = !DILocation(line: 29, column: 38, scope: !22) +// CHECK:STDOUT: !24 = !DILocation(line: 29, column: 31, scope: !22) +// CHECK:STDOUT: !25 = distinct !DISubprogram(name: "None", linkageName: "_CNone.Optional.Core.29d34654e802e24e", scope: null, file: !19, line: 18, type: !5, spFlags: DISPFlagDefinition, unit: !2) +// CHECK:STDOUT: !26 = !DILocation(line: 20, column: 5, scope: !25) +// CHECK:STDOUT: !27 = !DILocation(line: 21, column: 5, scope: !25) diff --git a/toolchain/lower/testdata/for/break_continue.carbon b/toolchain/lower/testdata/for/break_continue.carbon new file mode 100644 index 000000000000..e0a55547d095 --- /dev/null +++ b/toolchain/lower/testdata/for/break_continue.carbon @@ -0,0 +1,162 @@ +// Part of the Carbon Language project, under the Apache License v2.0 with LLVM +// Exceptions. See /LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// AUTOUPDATE +// TIP: To test this file alone, run: +// TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/lower/testdata/for/break_continue.carbon +// TIP: To dump output, run: +// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/lower/testdata/for/break_continue.carbon + +class EmptyRange(T:! type) { + impl as Core.Iterate where .CursorType = {} and .ElementType = T { + fn NewCursor[self: Self]() -> {} { + return {}; + } + fn Next[self: Self](cursor: {}*) -> Core.Optional(T) { + return Core.Optional(T).None(); + } + } +} + +fn F() -> bool; +fn G() -> bool; +fn H(); + +fn For() { + var r: EmptyRange(i32) = {}; + for (n: i32 in r) { + if (F()) { break; } + if (G()) { continue; } + H(); + } +} + +// CHECK:STDOUT: ; ModuleID = 'break_continue.carbon' +// CHECK:STDOUT: source_filename = "break_continue.carbon" +// CHECK:STDOUT: +// CHECK:STDOUT: @EmptyRange.val.loc27_3 = internal constant {} zeroinitializer +// CHECK:STDOUT: +// CHECK:STDOUT: declare i1 @_CF.Main() +// CHECK:STDOUT: +// CHECK:STDOUT: declare i1 @_CG.Main() +// CHECK:STDOUT: +// CHECK:STDOUT: declare void @_CH.Main() +// CHECK:STDOUT: +// CHECK:STDOUT: define void @_CFor.Main() !dbg !4 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: %r.var = alloca {}, align 8, !dbg !7 +// CHECK:STDOUT: %var = alloca {}, align 8, !dbg !8 +// CHECK:STDOUT: %.loc28_19.1.temp = alloca { i1, i32 }, align 8, !dbg !8 +// CHECK:STDOUT: call void @llvm.lifetime.start.p0(i64 0, ptr %r.var), !dbg !7 +// CHECK:STDOUT: call void @llvm.memcpy.p0.p0.i64(ptr align 1 %r.var, ptr align 1 @EmptyRange.val.loc27_3, i64 0, i1 false), !dbg !7 +// CHECK:STDOUT: call void @"_CNewCursor.EmptyRange.Main:Iterate.Core.b88d1103f417c6d4"(ptr %r.var), !dbg !8 +// CHECK:STDOUT: call void @llvm.lifetime.start.p0(i64 0, ptr %var), !dbg !8 +// CHECK:STDOUT: br label %for.next, !dbg !8 +// CHECK:STDOUT: +// CHECK:STDOUT: for.next: ; preds = %if.else.loc30, %if.then.loc30, %entry +// CHECK:STDOUT: call void @llvm.lifetime.start.p0(i64 8, ptr %.loc28_19.1.temp), !dbg !8 +// CHECK:STDOUT: call void @"_CNext.EmptyRange.Main:Iterate.Core.b88d1103f417c6d4"(ptr %.loc28_19.1.temp, ptr %r.var, ptr %var), !dbg !8 +// CHECK:STDOUT: %HasValue.call = call i1 @_CHasValue.Optional.Core.b88d1103f417c6d4(ptr %.loc28_19.1.temp), !dbg !8 +// CHECK:STDOUT: br i1 %HasValue.call, label %for.body, label %for.done, !dbg !8 +// CHECK:STDOUT: +// CHECK:STDOUT: for.body: ; preds = %for.next +// CHECK:STDOUT: %Get.call = call i32 @_CGet.Optional.Core.b88d1103f417c6d4(ptr %.loc28_19.1.temp), !dbg !8 +// CHECK:STDOUT: %F.call = call i1 @_CF.Main(), !dbg !9 +// CHECK:STDOUT: br i1 %F.call, label %if.then.loc29, label %if.else.loc29, !dbg !10 +// CHECK:STDOUT: +// CHECK:STDOUT: if.then.loc29: ; preds = %for.body +// CHECK:STDOUT: br label %for.done, !dbg !11 +// CHECK:STDOUT: +// CHECK:STDOUT: if.else.loc29: ; preds = %for.body +// CHECK:STDOUT: %G.call = call i1 @_CG.Main(), !dbg !12 +// CHECK:STDOUT: br i1 %G.call, label %if.then.loc30, label %if.else.loc30, !dbg !13 +// CHECK:STDOUT: +// CHECK:STDOUT: if.then.loc30: ; preds = %if.else.loc29 +// CHECK:STDOUT: br label %for.next, !dbg !14 +// CHECK:STDOUT: +// CHECK:STDOUT: if.else.loc30: ; preds = %if.else.loc29 +// CHECK:STDOUT: call void @_CH.Main(), !dbg !15 +// CHECK:STDOUT: br label %for.next, !dbg !16 +// CHECK:STDOUT: +// CHECK:STDOUT: for.done: ; preds = %if.then.loc29, %for.next +// CHECK:STDOUT: ret void, !dbg !17 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) +// CHECK:STDOUT: declare void @llvm.lifetime.start.p0(i64 immarg, ptr captures(none)) #0 +// CHECK:STDOUT: +// CHECK:STDOUT: ; Function Attrs: nocallback nofree 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: define linkonce_odr void @"_CNewCursor.EmptyRange.Main:Iterate.Core.b88d1103f417c6d4"(ptr %self) !dbg !18 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: ret void, !dbg !19 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: define linkonce_odr void @"_CNext.EmptyRange.Main:Iterate.Core.b88d1103f417c6d4"(ptr sret({ i1, i32 }) %return, ptr %self, ptr %cursor) !dbg !20 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: call void @_CNone.Optional.Core.b88d1103f417c6d4(ptr %return), !dbg !21 +// CHECK:STDOUT: ret void, !dbg !22 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: define linkonce_odr i1 @_CHasValue.Optional.Core.b88d1103f417c6d4(ptr %self) !dbg !23 { +// CHECK:STDOUT: %has_value = getelementptr inbounds nuw { i1, i32 }, ptr %self, i32 0, i32 0, !dbg !25 +// CHECK:STDOUT: %1 = load i1, ptr %has_value, align 1, !dbg !25 +// CHECK:STDOUT: ret i1 %1, !dbg !26 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: define linkonce_odr i32 @_CGet.Optional.Core.b88d1103f417c6d4(ptr %self) !dbg !27 { +// CHECK:STDOUT: %value = getelementptr inbounds nuw { i1, i32 }, ptr %self, i32 0, i32 1, !dbg !28 +// CHECK:STDOUT: %1 = load i32, ptr %value, align 4, !dbg !28 +// CHECK:STDOUT: ret i32 %1, !dbg !29 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: define linkonce_odr void @_CNone.Optional.Core.b88d1103f417c6d4(ptr sret({ i1, i32 }) %return) !dbg !30 { +// CHECK:STDOUT: %has_value = getelementptr inbounds nuw { i1, i32 }, ptr %return, i32 0, i32 0, !dbg !31 +// CHECK:STDOUT: store i1 false, ptr %has_value, align 1, !dbg !31 +// CHECK:STDOUT: ret void, !dbg !32 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: ; uselistorder directives +// CHECK:STDOUT: uselistorder ptr @llvm.lifetime.start.p0, { 2, 1, 0 } +// CHECK:STDOUT: +// CHECK:STDOUT: attributes #0 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } +// CHECK:STDOUT: attributes #1 = { nocallback nofree nounwind willreturn memory(argmem: readwrite) } +// CHECK:STDOUT: +// CHECK:STDOUT: !llvm.module.flags = !{!0, !1} +// CHECK:STDOUT: !llvm.dbg.cu = !{!2} +// CHECK:STDOUT: +// CHECK:STDOUT: !0 = !{i32 7, !"Dwarf Version", i32 5} +// CHECK:STDOUT: !1 = !{i32 2, !"Debug Info Version", i32 3} +// CHECK:STDOUT: !2 = distinct !DICompileUnit(language: DW_LANG_C, file: !3, producer: "carbon", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug) +// CHECK:STDOUT: !3 = !DIFile(filename: "break_continue.carbon", directory: "") +// CHECK:STDOUT: !4 = distinct !DISubprogram(name: "For", linkageName: "_CFor.Main", scope: null, file: !3, line: 26, type: !5, spFlags: DISPFlagDefinition, unit: !2) +// CHECK:STDOUT: !5 = !DISubroutineType(types: !6) +// CHECK:STDOUT: !6 = !{} +// CHECK:STDOUT: !7 = !DILocation(line: 27, column: 3, scope: !4) +// CHECK:STDOUT: !8 = !DILocation(line: 28, column: 7, scope: !4) +// CHECK:STDOUT: !9 = !DILocation(line: 29, column: 9, scope: !4) +// CHECK:STDOUT: !10 = !DILocation(line: 29, column: 8, scope: !4) +// CHECK:STDOUT: !11 = !DILocation(line: 29, column: 16, scope: !4) +// CHECK:STDOUT: !12 = !DILocation(line: 30, column: 9, scope: !4) +// CHECK:STDOUT: !13 = !DILocation(line: 30, column: 8, scope: !4) +// CHECK:STDOUT: !14 = !DILocation(line: 30, column: 16, scope: !4) +// CHECK:STDOUT: !15 = !DILocation(line: 31, column: 5, scope: !4) +// CHECK:STDOUT: !16 = !DILocation(line: 28, column: 3, scope: !4) +// CHECK:STDOUT: !17 = !DILocation(line: 26, column: 1, scope: !4) +// CHECK:STDOUT: !18 = distinct !DISubprogram(name: "NewCursor", linkageName: "_CNewCursor.EmptyRange.Main:Iterate.Core.b88d1103f417c6d4", scope: null, file: !3, line: 13, type: !5, spFlags: DISPFlagDefinition, unit: !2) +// CHECK:STDOUT: !19 = !DILocation(line: 14, column: 7, scope: !18) +// CHECK:STDOUT: !20 = distinct !DISubprogram(name: "Next", linkageName: "_CNext.EmptyRange.Main:Iterate.Core.b88d1103f417c6d4", scope: null, file: !3, line: 16, type: !5, spFlags: DISPFlagDefinition, unit: !2) +// CHECK:STDOUT: !21 = !DILocation(line: 17, column: 14, scope: !20) +// CHECK:STDOUT: !22 = !DILocation(line: 17, column: 7, scope: !20) +// CHECK:STDOUT: !23 = distinct !DISubprogram(name: "HasValue", linkageName: "_CHasValue.Optional.Core.b88d1103f417c6d4", scope: null, file: !24, line: 28, type: !5, spFlags: DISPFlagDefinition, unit: !2) +// CHECK:STDOUT: !24 = !DIFile(filename: "{{.*}}/prelude/types/optional.carbon", directory: "") +// CHECK:STDOUT: !25 = !DILocation(line: 28, column: 46, scope: !23) +// CHECK:STDOUT: !26 = !DILocation(line: 28, column: 39, scope: !23) +// CHECK:STDOUT: !27 = distinct !DISubprogram(name: "Get", linkageName: "_CGet.Optional.Core.b88d1103f417c6d4", scope: null, file: !24, line: 29, type: !5, spFlags: DISPFlagDefinition, unit: !2) +// CHECK:STDOUT: !28 = !DILocation(line: 29, column: 38, scope: !27) +// CHECK:STDOUT: !29 = !DILocation(line: 29, column: 31, scope: !27) +// CHECK:STDOUT: !30 = distinct !DISubprogram(name: "None", linkageName: "_CNone.Optional.Core.b88d1103f417c6d4", scope: null, file: !24, line: 18, type: !5, spFlags: DISPFlagDefinition, unit: !2) +// CHECK:STDOUT: !31 = !DILocation(line: 20, column: 5, scope: !30) +// CHECK:STDOUT: !32 = !DILocation(line: 21, column: 5, scope: !30) diff --git a/toolchain/lower/testdata/for/for.carbon b/toolchain/lower/testdata/for/for.carbon new file mode 100644 index 000000000000..a6bcd751c581 --- /dev/null +++ b/toolchain/lower/testdata/for/for.carbon @@ -0,0 +1,147 @@ +// Part of the Carbon Language project, under the Apache License v2.0 with LLVM +// Exceptions. See /LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// AUTOUPDATE +// TIP: To test this file alone, run: +// TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/lower/testdata/for/for.carbon +// TIP: To dump output, run: +// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/lower/testdata/for/for.carbon + +class EmptyRange(T:! type) { + impl as Core.Iterate where .CursorType = {} and .ElementType = T { + fn NewCursor[self: Self]() -> {} { + return {}; + } + fn Next[self: Self](cursor: {}*) -> Core.Optional(T) { + return Core.Optional(T).None(); + } + } +} + +fn F(); +fn G(); +fn H(); + +fn For() { + var r: EmptyRange(i32) = {}; + + F(); + for (n: i32 in r) { + G(); + } + H(); +} + +// CHECK:STDOUT: ; ModuleID = 'for.carbon' +// CHECK:STDOUT: source_filename = "for.carbon" +// CHECK:STDOUT: +// CHECK:STDOUT: @EmptyRange.val.loc27_3 = internal constant {} zeroinitializer +// CHECK:STDOUT: +// CHECK:STDOUT: declare void @_CF.Main() +// CHECK:STDOUT: +// CHECK:STDOUT: declare void @_CG.Main() +// CHECK:STDOUT: +// CHECK:STDOUT: declare void @_CH.Main() +// CHECK:STDOUT: +// CHECK:STDOUT: define void @_CFor.Main() !dbg !4 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: %r.var = alloca {}, align 8, !dbg !7 +// CHECK:STDOUT: %var = alloca {}, align 8, !dbg !8 +// CHECK:STDOUT: %.loc30_19.1.temp = alloca { i1, i32 }, align 8, !dbg !8 +// CHECK:STDOUT: call void @llvm.lifetime.start.p0(i64 0, ptr %r.var), !dbg !7 +// CHECK:STDOUT: call void @llvm.memcpy.p0.p0.i64(ptr align 1 %r.var, ptr align 1 @EmptyRange.val.loc27_3, i64 0, i1 false), !dbg !7 +// CHECK:STDOUT: call void @_CF.Main(), !dbg !9 +// CHECK:STDOUT: call void @"_CNewCursor.EmptyRange.Main:Iterate.Core.b88d1103f417c6d4"(ptr %r.var), !dbg !8 +// CHECK:STDOUT: call void @llvm.lifetime.start.p0(i64 0, ptr %var), !dbg !8 +// CHECK:STDOUT: br label %for.next, !dbg !8 +// CHECK:STDOUT: +// CHECK:STDOUT: for.next: ; preds = %for.body, %entry +// CHECK:STDOUT: call void @llvm.lifetime.start.p0(i64 8, ptr %.loc30_19.1.temp), !dbg !8 +// CHECK:STDOUT: call void @"_CNext.EmptyRange.Main:Iterate.Core.b88d1103f417c6d4"(ptr %.loc30_19.1.temp, ptr %r.var, ptr %var), !dbg !8 +// CHECK:STDOUT: %HasValue.call = call i1 @_CHasValue.Optional.Core.b88d1103f417c6d4(ptr %.loc30_19.1.temp), !dbg !8 +// CHECK:STDOUT: br i1 %HasValue.call, label %for.body, label %for.done, !dbg !8 +// CHECK:STDOUT: +// CHECK:STDOUT: for.body: ; preds = %for.next +// CHECK:STDOUT: %Get.call = call i32 @_CGet.Optional.Core.b88d1103f417c6d4(ptr %.loc30_19.1.temp), !dbg !8 +// CHECK:STDOUT: call void @_CG.Main(), !dbg !10 +// 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: ret void, !dbg !13 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) +// CHECK:STDOUT: declare void @llvm.lifetime.start.p0(i64 immarg, ptr captures(none)) #0 +// CHECK:STDOUT: +// CHECK:STDOUT: ; Function Attrs: nocallback nofree 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: define linkonce_odr void @"_CNewCursor.EmptyRange.Main:Iterate.Core.b88d1103f417c6d4"(ptr %self) !dbg !14 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: ret void, !dbg !15 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: define linkonce_odr void @"_CNext.EmptyRange.Main:Iterate.Core.b88d1103f417c6d4"(ptr sret({ i1, i32 }) %return, ptr %self, ptr %cursor) !dbg !16 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: call void @_CNone.Optional.Core.b88d1103f417c6d4(ptr %return), !dbg !17 +// CHECK:STDOUT: ret void, !dbg !18 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: define linkonce_odr i1 @_CHasValue.Optional.Core.b88d1103f417c6d4(ptr %self) !dbg !19 { +// CHECK:STDOUT: %has_value = getelementptr inbounds nuw { i1, i32 }, ptr %self, i32 0, i32 0, !dbg !21 +// CHECK:STDOUT: %1 = load i1, ptr %has_value, align 1, !dbg !21 +// CHECK:STDOUT: ret i1 %1, !dbg !22 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: define linkonce_odr i32 @_CGet.Optional.Core.b88d1103f417c6d4(ptr %self) !dbg !23 { +// CHECK:STDOUT: %value = getelementptr inbounds nuw { i1, i32 }, ptr %self, i32 0, i32 1, !dbg !24 +// CHECK:STDOUT: %1 = load i32, ptr %value, align 4, !dbg !24 +// CHECK:STDOUT: ret i32 %1, !dbg !25 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: define linkonce_odr void @_CNone.Optional.Core.b88d1103f417c6d4(ptr sret({ i1, i32 }) %return) !dbg !26 { +// CHECK:STDOUT: %has_value = getelementptr inbounds nuw { i1, i32 }, ptr %return, i32 0, i32 0, !dbg !27 +// CHECK:STDOUT: store i1 false, ptr %has_value, align 1, !dbg !27 +// CHECK:STDOUT: ret void, !dbg !28 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: ; uselistorder directives +// CHECK:STDOUT: uselistorder ptr @llvm.lifetime.start.p0, { 2, 1, 0 } +// CHECK:STDOUT: +// CHECK:STDOUT: attributes #0 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } +// CHECK:STDOUT: attributes #1 = { nocallback nofree nounwind willreturn memory(argmem: readwrite) } +// CHECK:STDOUT: +// CHECK:STDOUT: !llvm.module.flags = !{!0, !1} +// CHECK:STDOUT: !llvm.dbg.cu = !{!2} +// CHECK:STDOUT: +// CHECK:STDOUT: !0 = !{i32 7, !"Dwarf Version", i32 5} +// CHECK:STDOUT: !1 = !{i32 2, !"Debug Info Version", i32 3} +// CHECK:STDOUT: !2 = distinct !DICompileUnit(language: DW_LANG_C, file: !3, producer: "carbon", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug) +// CHECK:STDOUT: !3 = !DIFile(filename: "for.carbon", directory: "") +// CHECK:STDOUT: !4 = distinct !DISubprogram(name: "For", linkageName: "_CFor.Main", scope: null, file: !3, line: 26, type: !5, spFlags: DISPFlagDefinition, unit: !2) +// CHECK:STDOUT: !5 = !DISubroutineType(types: !6) +// CHECK:STDOUT: !6 = !{} +// CHECK:STDOUT: !7 = !DILocation(line: 27, column: 3, scope: !4) +// CHECK:STDOUT: !8 = !DILocation(line: 30, column: 7, scope: !4) +// CHECK:STDOUT: !9 = !DILocation(line: 29, column: 3, scope: !4) +// CHECK:STDOUT: !10 = !DILocation(line: 31, column: 5, scope: !4) +// CHECK:STDOUT: !11 = !DILocation(line: 30, column: 3, scope: !4) +// CHECK:STDOUT: !12 = !DILocation(line: 33, column: 3, scope: !4) +// CHECK:STDOUT: !13 = !DILocation(line: 26, column: 1, scope: !4) +// CHECK:STDOUT: !14 = distinct !DISubprogram(name: "NewCursor", linkageName: "_CNewCursor.EmptyRange.Main:Iterate.Core.b88d1103f417c6d4", scope: null, file: !3, line: 13, type: !5, spFlags: DISPFlagDefinition, unit: !2) +// CHECK:STDOUT: !15 = !DILocation(line: 14, column: 7, scope: !14) +// CHECK:STDOUT: !16 = distinct !DISubprogram(name: "Next", linkageName: "_CNext.EmptyRange.Main:Iterate.Core.b88d1103f417c6d4", scope: null, file: !3, line: 16, type: !5, spFlags: DISPFlagDefinition, unit: !2) +// CHECK:STDOUT: !17 = !DILocation(line: 17, column: 14, scope: !16) +// CHECK:STDOUT: !18 = !DILocation(line: 17, column: 7, scope: !16) +// CHECK:STDOUT: !19 = distinct !DISubprogram(name: "HasValue", linkageName: "_CHasValue.Optional.Core.b88d1103f417c6d4", scope: null, file: !20, line: 28, type: !5, spFlags: DISPFlagDefinition, unit: !2) +// CHECK:STDOUT: !20 = !DIFile(filename: "{{.*}}/prelude/types/optional.carbon", directory: "") +// CHECK:STDOUT: !21 = !DILocation(line: 28, column: 46, scope: !19) +// CHECK:STDOUT: !22 = !DILocation(line: 28, column: 39, scope: !19) +// CHECK:STDOUT: !23 = distinct !DISubprogram(name: "Get", linkageName: "_CGet.Optional.Core.b88d1103f417c6d4", scope: null, file: !20, line: 29, type: !5, spFlags: DISPFlagDefinition, unit: !2) +// CHECK:STDOUT: !24 = !DILocation(line: 29, column: 38, scope: !23) +// CHECK:STDOUT: !25 = !DILocation(line: 29, column: 31, scope: !23) +// CHECK:STDOUT: !26 = distinct !DISubprogram(name: "None", linkageName: "_CNone.Optional.Core.b88d1103f417c6d4", scope: null, file: !20, line: 18, type: !5, spFlags: DISPFlagDefinition, unit: !2) +// CHECK:STDOUT: !27 = !DILocation(line: 20, column: 5, scope: !26) +// CHECK:STDOUT: !28 = !DILocation(line: 21, column: 5, scope: !26) diff --git a/toolchain/parse/typed_nodes.h b/toolchain/parse/typed_nodes.h index 8b8c03e9c4e1..bce980e78548 100644 --- a/toolchain/parse/typed_nodes.h +++ b/toolchain/parse/typed_nodes.h @@ -683,7 +683,7 @@ struct ForIn { Lex::InTokenIndex token; }; -// The `for (... in ...)` portion of a `for` statement. +// The `(... in ...)` portion of a `for` statement. struct ForHeader { static constexpr auto Kind = NodeKind::ForHeader.Define({.bracketed_by = ForHeaderStart::Kind}); diff --git a/toolchain/sem_ir/inst_namer.cpp b/toolchain/sem_ir/inst_namer.cpp index ac63fcfbca3d..fdb212bf843e 100644 --- a/toolchain/sem_ir/inst_namer.cpp +++ b/toolchain/sem_ir/inst_namer.cpp @@ -409,6 +409,12 @@ struct BranchNames { // Returns names for a branching parse node, or nullopt if not a branch. static auto For(Parse::NodeKind node_kind) -> std::optional { switch (node_kind) { + case Parse::NodeKind::ForHeaderStart: + return {{.prefix = "for", .branch = "next"}}; + + case Parse::NodeKind::ForHeader: + return {{.prefix = "for", .branch_if = "body", .branch = "done"}}; + case Parse::NodeKind::IfExprIf: return {{.prefix = "if.expr", .branch_if = "then", diff --git a/toolchain/sem_ir/typed_insts.h b/toolchain/sem_ir/typed_insts.h index 5715bc10f34f..4adba43ea228 100644 --- a/toolchain/sem_ir/typed_insts.h +++ b/toolchain/sem_ir/typed_insts.h @@ -1745,7 +1745,8 @@ struct VarStorage { TypeId type_id; - // If this storage was created for a `var` pattern, the pattern. + // If this storage was created for a `var` pattern, the pattern. Otherwise, + // such as the implicit storage in `for`, this is `None`. AbsoluteInstId pattern_id; }; diff --git a/toolchain/testing/file_test.cpp b/toolchain/testing/file_test.cpp index 5ec3b9579ea4..fa542a2bd7b3 100644 --- a/toolchain/testing/file_test.cpp +++ b/toolchain/testing/file_test.cpp @@ -250,9 +250,10 @@ auto ToolchainFileTest::DoExtraCheckReplacements(std::string& check_line) const // The column happens to be right for FileStart, but the line is wrong. static RE2 file_token_re(R"((FileEnd.*column: |FileStart.*line: )( *\d+))"); RE2::Replace(&check_line, file_token_re, R"(\1{{ *\\d+}})"); - } else if (component_ == "check") { - // The path to the core package appears in some check diagnostics, and will - // differ between testing environments, so don't test it. + } else if (component_ == "check" || component_ == "lower") { + // The path to the core package appears in some check diagnostics and in + // debug information produced by lowering, and will differ between testing + // environments, so don't test it. // TODO: Consider adding a content keyword to name the core package, and // replace with that instead. Alternatively, consider adding the core // package to the VFS with a fixed name. diff --git a/toolchain/testing/testdata/min_prelude/for.carbon b/toolchain/testing/testdata/min_prelude/for.carbon new file mode 100644 index 000000000000..e6049d6470a2 --- /dev/null +++ b/toolchain/testing/testdata/min_prelude/for.carbon @@ -0,0 +1,19 @@ +// Part of the Carbon Language project, under the Apache License v2.0 with LLVM +// Exceptions. See /LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// EXTRA-ARGS: --custom-core --exclude-dump-file-prefix=min_prelude/ +// INCLUDE-FILE: toolchain/testing/testdata/min_prelude/parts/as.carbon +// INCLUDE-FILE: toolchain/testing/testdata/min_prelude/parts/bool.carbon +// INCLUDE-FILE: toolchain/testing/testdata/min_prelude/parts/iterate.carbon +// INCLUDE-FILE: toolchain/testing/testdata/min_prelude/parts/optional.carbon + +// --- min_prelude/bool.carbon + +// A minimal prelude for testing `for` loops. +package Core library "prelude"; + +export import library "prelude/parts/as"; +export import library "prelude/parts/bool"; +export import library "prelude/parts/iterate"; +export import library "prelude/parts/optional"; diff --git a/toolchain/testing/testdata/min_prelude/parts/iterate.carbon b/toolchain/testing/testdata/min_prelude/parts/iterate.carbon new file mode 100644 index 000000000000..bbd36ce723ae --- /dev/null +++ b/toolchain/testing/testdata/min_prelude/parts/iterate.carbon @@ -0,0 +1,16 @@ +// 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 + +// --- min_prelude/parts/iterate.carbon + +package Core library "prelude/parts/iterate"; + +import library "prelude/parts/optional"; + +interface Iterate { + let ElementType:! type; + let CursorType:! type; + fn NewCursor[self: Self]() -> CursorType; + fn Next[self: Self](cursor: CursorType*) -> Optional(ElementType); +} diff --git a/toolchain/testing/testdata/min_prelude/parts/optional.carbon b/toolchain/testing/testdata/min_prelude/parts/optional.carbon new file mode 100644 index 000000000000..07e9d68e23fa --- /dev/null +++ b/toolchain/testing/testdata/min_prelude/parts/optional.carbon @@ -0,0 +1,27 @@ +// 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 + +// --- min_prelude/parts/optional.carbon + +package Core library "prelude/parts/optional"; + +import library "prelude/parts/bool"; + +class Optional(T:! type) { + fn None() -> Self { + returned var me: Self; + me.has_value = false; + return var; + } + + fn Some(value: T) -> Self { + return {.has_value = true, .value = value}; + } + + fn HasValue[self: Self]() -> bool { return self.has_value; } + fn Get[self: Self]() -> T { return self.value; } + + var has_value: bool; + var value: T; +}