From 03e693873b07a41447b7992cfdfd8a4d8ac1b1b0 Mon Sep 17 00:00:00 2001 From: Jon Ross-Perkins Date: Wed, 23 Apr 2025 12:03:53 -0700 Subject: [PATCH] Detect control flow in entities nested inside functions (#5336) Right now, return_scope_stack is being used to determine whether logic is in a function scope. However, we need to handle nested entities inside function scopes. For example where this crashes right now: ``` base class C(B:! bool) {} fn F() { class B { extend base: C(true or false); } } ``` This is doing a few things to make this kind of code not crash: - Split `scope_stack().Push` into `PushForDeclName`, `PushForEntity`, `PushForExpr`, and `PushForFunction` so that better decisions can be made about behaviors. - Hide `return_scope_stack` in the API, instead using interfaces to get at the underlying data. - Also using `PushForFunction` to update it similar to the other stacks that `ScopeStack` manages. - Add `IsInFunctionScope` as the best way to determine presence in function scope. - Remove `PeekIsLexicalScope` since destruction really wants function scope information anyways. - Clean up `destroy_id_stack` handling to be for function scopes rather than lexical scopes. - Return after related `context.TODO`s in a couple more spots, so that code doesn't proceed to add control flow in spite of the lack of support. --------- Co-authored-by: Geoff Romer --- toolchain/check/check_unit.cpp | 6 +- toolchain/check/context.h | 5 +- toolchain/check/control_flow.cpp | 4 +- toolchain/check/decl_name_stack.cpp | 8 +- toolchain/check/handle_choice.cpp | 4 +- toolchain/check/handle_class.cpp | 2 +- toolchain/check/handle_codeblock.cpp | 2 +- toolchain/check/handle_function.cpp | 6 +- toolchain/check/handle_if_expr.cpp | 8 +- toolchain/check/handle_impl.cpp | 2 +- toolchain/check/handle_interface.cpp | 4 +- toolchain/check/handle_operator.cpp | 8 +- toolchain/check/handle_struct.cpp | 4 +- toolchain/check/handle_where.cpp | 2 +- toolchain/check/return.cpp | 13 +- toolchain/check/scope_stack.cpp | 66 ++++-- toolchain/check/scope_stack.h | 105 +++++++--- .../testdata/alias/fail_control_flow.carbon | 88 ++++++-- .../class/no_prelude/destroy_calls.carbon | 13 +- .../if_expr/fail_not_in_function.carbon | 187 ++++++++++------- .../fail_and_or_not_in_function.carbon | 194 ++++++++---------- .../var/fail_todo_control_flow_init.carbon | 136 ++++-------- 22 files changed, 476 insertions(+), 391 deletions(-) diff --git a/toolchain/check/check_unit.cpp b/toolchain/check/check_unit.cpp index 6f2908f28072..f0a761965a00 100644 --- a/toolchain/check/check_unit.cpp +++ b/toolchain/check/check_unit.cpp @@ -229,7 +229,9 @@ auto CheckUnit::ImportCurrentPackage(SemIR::InstId package_inst_id, unit_and_imports_->package_imports_map.Lookup(PackageNameId::None); if (!import_map_lookup) { // Push the scope; there are no names to add. - context_.scope_stack().Push(package_inst_id, SemIR::NameScopeId::Package); + context_.scope_stack().PushForEntity( + package_inst_id, SemIR::NameScopeId::Package, SemIR::SpecificId::None, + /*lexical_lookup_has_load_error=*/false); return; } PackageImports& self_import = @@ -244,7 +246,7 @@ auto CheckUnit::ImportCurrentPackage(SemIR::InstId package_inst_id, CollectTransitiveImports(self_import.import_decl_id, &self_import, /*api_imports=*/nullptr)); - context_.scope_stack().Push( + context_.scope_stack().PushForEntity( package_inst_id, SemIR::NameScopeId::Package, SemIR::SpecificId::None, context_.name_scopes().Get(SemIR::NameScopeId::Package).has_error()); } diff --git a/toolchain/check/context.h b/toolchain/check/context.h index 2c1434833ef8..135028ce1524 100644 --- a/toolchain/check/context.h +++ b/toolchain/check/context.h @@ -118,10 +118,7 @@ class Context { auto scope_stack() -> ScopeStack& { return scope_stack_; } - // Conveneicne functions for frequently-used `scope_stack` members. - auto return_scope_stack() -> llvm::SmallVector& { - return scope_stack().return_scope_stack(); - } + // Convenience functions for frequently-used `scope_stack` members. auto break_continue_stack() -> llvm::SmallVector& { return scope_stack().break_continue_stack(); diff --git a/toolchain/check/control_flow.cpp b/toolchain/check/control_flow.cpp index d92e67ad96ad..07141d2ad4a6 100644 --- a/toolchain/check/control_flow.cpp +++ b/toolchain/check/control_flow.cpp @@ -139,8 +139,8 @@ auto IsCurrentPositionReachable(Context& context) -> bool { auto MaybeAddCleanupForInst(Context& context, SemIR::TypeId type_id, SemIR::InstId inst_id) -> void { - if (!context.scope_stack().PeekIsLexicalScope()) { - // Cleanup can only occur in lexical scopes. + if (!context.scope_stack().IsInFunctionScope()) { + // Cleanup can only occur in function scopes. return; } diff --git a/toolchain/check/decl_name_stack.cpp b/toolchain/check/decl_name_stack.cpp index 824ee67c90c2..1647d0cae423 100644 --- a/toolchain/check/decl_name_stack.cpp +++ b/toolchain/check/decl_name_stack.cpp @@ -62,7 +62,7 @@ auto DeclNameStack::PushScopeAndStartName() -> void { decl_name_stack_.push_back(MakeEmptyNameContext()); // Create a scope for any parameters introduced in this name. - context_->scope_stack().Push(); + context_->scope_stack().PushForDeclName(); } auto DeclNameStack::FinishName(const NameComponent& name) -> NameContext { @@ -233,8 +233,8 @@ static auto PushNameQualifierScope(Context& context, SemIR::LocId loc_id, // providing the definition. StartGenericDecl(context); - context.scope_stack().Push(scope_inst_id, scope_id, self_specific_id, - has_error); + context.scope_stack().PushForEntity(scope_inst_id, scope_id, self_specific_id, + has_error); // An interface also introduces its 'Self' parameter into scope, despite it // not being redeclared as part of the qualifier. @@ -246,7 +246,7 @@ static auto PushNameQualifierScope(Context& context, SemIR::LocId loc_id, } // Enter a parameter scope in case the qualified name itself has parameters. - context.scope_stack().Push(); + context.scope_stack().PushForSameRegion(); } auto DeclNameStack::ApplyNameQualifier(const NameComponent& name) -> void { diff --git a/toolchain/check/handle_choice.cpp b/toolchain/check/handle_choice.cpp index 1b6a767c4d52..b799184d5159 100644 --- a/toolchain/check/handle_choice.cpp +++ b/toolchain/check/handle_choice.cpp @@ -108,8 +108,8 @@ auto HandleParseNode(Context& context, Parse::ChoiceDefinitionStartId node_id) mut_class.self_type_id = self_type_id; // Enter the choice scope. - context.scope_stack().Push(class_decl_id, class_info.scope_id, - self_specific_id); + context.scope_stack().PushForEntity(class_decl_id, class_info.scope_id, + self_specific_id); // Checking the binding pattern for an alternative requires a non-empty stack. // We reuse the Choice token even though we're now checking an alternative // inside the Choice, since there's no better token to use. diff --git a/toolchain/check/handle_class.cpp b/toolchain/check/handle_class.cpp index 09e443780238..0d9858e25085 100644 --- a/toolchain/check/handle_class.cpp +++ b/toolchain/check/handle_class.cpp @@ -278,7 +278,7 @@ auto HandleParseNode(Context& context, Parse::ClassDefinitionStartId node_id) StartClassDefinition(context, class_info, class_decl_id); // Enter the class scope. - context.scope_stack().Push( + context.scope_stack().PushForEntity( class_decl_id, class_info.scope_id, context.generics().GetSelfSpecific(class_info.generic_id)); StartGenericDefinition(context); diff --git a/toolchain/check/handle_codeblock.cpp b/toolchain/check/handle_codeblock.cpp index 0571d59c1cf6..99e94424af2f 100644 --- a/toolchain/check/handle_codeblock.cpp +++ b/toolchain/check/handle_codeblock.cpp @@ -10,7 +10,7 @@ namespace Carbon::Check { auto HandleParseNode(Context& context, Parse::CodeBlockStartId node_id) -> bool { context.node_stack().Push(node_id); - context.scope_stack().Push(); + context.scope_stack().PushForSameRegion(); return true; } diff --git a/toolchain/check/handle_function.cpp b/toolchain/check/handle_function.cpp index 47375d54334f..5dd8b00fcf4c 100644 --- a/toolchain/check/handle_function.cpp +++ b/toolchain/check/handle_function.cpp @@ -585,10 +585,9 @@ static auto HandleFunctionDefinitionAfterSignature( auto& function = context.functions().Get(function_id); // Create the function scope and the entry block. - context.return_scope_stack().push_back({.decl_id = decl_id}); + context.scope_stack().PushForFunctionBody(decl_id); context.inst_block_stack().Push(); context.region_stack().PushRegion(context.inst_block_stack().PeekOrAdd()); - context.scope_stack().Push(decl_id); StartGenericDefinition(context); CheckFunctionDefinitionSignature(context, function); @@ -645,9 +644,8 @@ auto HandleParseNode(Context& context, Parse::FunctionDefinitionId node_id) } } - context.scope_stack().Pop(); context.inst_block_stack().Pop(); - context.return_scope_stack().pop_back(); + context.scope_stack().Pop(); context.decl_name_stack().PopScope(); auto& function = context.functions().Get(function_id); diff --git a/toolchain/check/handle_if_expr.cpp b/toolchain/check/handle_if_expr.cpp index 22829386c6d0..ff9b06b8af4c 100644 --- a/toolchain/check/handle_if_expr.cpp +++ b/toolchain/check/handle_if_expr.cpp @@ -64,10 +64,10 @@ auto HandleParseNode(Context& context, Parse::IfExprThenId node_id) -> bool { } auto HandleParseNode(Context& context, Parse::IfExprElseId node_id) -> bool { - if (context.return_scope_stack().empty()) { - context.TODO(node_id, - "Control flow expressions are currently only supported inside " - "functions."); + if (!context.scope_stack().IsInFunctionScope()) { + return context.TODO(node_id, + "Control flow expressions are currently only supported " + "inside functions."); } // Alias node_id for if/then/else consistency. auto& else_node = node_id; diff --git a/toolchain/check/handle_impl.cpp b/toolchain/check/handle_impl.cpp index a5e4d4f56435..0010a03cf174 100644 --- a/toolchain/check/handle_impl.cpp +++ b/toolchain/check/handle_impl.cpp @@ -537,7 +537,7 @@ auto HandleParseNode(Context& context, Parse::ImplDefinitionStartId node_id) context.name_scopes().Add(impl_decl_id, SemIR::NameId::None, context.decl_name_stack().PeekParentScopeId()); - context.scope_stack().Push( + context.scope_stack().PushForEntity( impl_decl_id, impl_info.scope_id, context.generics().GetSelfSpecific(impl_info.generic_id)); StartGenericDefinition(context); diff --git a/toolchain/check/handle_interface.cpp b/toolchain/check/handle_interface.cpp index c1dcd7f4d185..72a3b39b7a7e 100644 --- a/toolchain/check/handle_interface.cpp +++ b/toolchain/check/handle_interface.cpp @@ -193,8 +193,8 @@ auto HandleParseNode(Context& context, interface_info.self_param_id); // Enter the interface scope. - context.scope_stack().Push(interface_decl_id, interface_info.scope_id, - self_specific_id); + context.scope_stack().PushForEntity( + interface_decl_id, interface_info.scope_id, self_specific_id); // TODO: Handle the case where there's control flow in the interface body. For // example: diff --git a/toolchain/check/handle_operator.cpp b/toolchain/check/handle_operator.cpp index 31974d004c15..e29d732364df 100644 --- a/toolchain/check/handle_operator.cpp +++ b/toolchain/check/handle_operator.cpp @@ -398,10 +398,10 @@ auto HandleParseNode(Context& context, Parse::ShortCircuitOperandOrId node_id) // occurs during operand handling. static auto HandleShortCircuitOperator(Context& context, Parse::NodeId node_id) -> bool { - if (context.return_scope_stack().empty()) { - context.TODO(node_id, - "Control flow expressions are currently only supported inside " - "functions."); + if (!context.scope_stack().IsInFunctionScope()) { + return context.TODO(node_id, + "Control flow expressions are currently only supported " + "inside functions."); } auto [rhs_node, rhs_id] = context.node_stack().PopExprWithNodeId(); auto short_circuit_result_id = context.node_stack().PopExpr(); diff --git a/toolchain/check/handle_struct.cpp b/toolchain/check/handle_struct.cpp index 1e3f3947f5e1..a2320c311f90 100644 --- a/toolchain/check/handle_struct.cpp +++ b/toolchain/check/handle_struct.cpp @@ -14,7 +14,7 @@ namespace Carbon::Check { auto HandleParseNode(Context& context, Parse::StructLiteralStartId node_id) -> bool { - context.scope_stack().Push(); + context.scope_stack().PushForSameRegion(); context.node_stack().Push(node_id); context.struct_type_fields_stack().PushArray(); context.param_and_arg_refs_stack().Push(); @@ -23,7 +23,7 @@ auto HandleParseNode(Context& context, Parse::StructLiteralStartId node_id) auto HandleParseNode(Context& context, Parse::StructTypeLiteralStartId node_id) -> bool { - context.scope_stack().Push(); + context.scope_stack().PushForSameRegion(); context.node_stack().Push(node_id); context.struct_type_fields_stack().PushArray(); return true; diff --git a/toolchain/check/handle_where.cpp b/toolchain/check/handle_where.cpp index 676f14c99591..ed353d9ee493 100644 --- a/toolchain/check/handle_where.cpp +++ b/toolchain/check/handle_where.cpp @@ -27,7 +27,7 @@ auto HandleParseNode(Context& context, Parse::WhereOperandId node_id) -> bool { // Introduce a name scope so that we can remove the `.Self` entry we are // adding to name lookup at the end of the `where` expression. - context.scope_stack().Push(); + context.scope_stack().PushForSameRegion(); // Introduce `.Self` as a symbolic binding. Its type is the value of the // expression to the left of `where`, so `MyInterface` in the example above. auto entity_name_id = context.entity_names().Add( diff --git a/toolchain/check/return.cpp b/toolchain/check/return.cpp index 128b9486f094..791b0faa1365 100644 --- a/toolchain/check/return.cpp +++ b/toolchain/check/return.cpp @@ -12,12 +12,11 @@ namespace Carbon::Check { // Gets the function that lexically encloses the current location. auto GetCurrentFunctionForReturn(Context& context) -> SemIR::Function& { - CARBON_CHECK(!context.return_scope_stack().empty(), + CARBON_CHECK(context.scope_stack().IsInFunctionScope(), "Handling return but not in a function"); - auto function_id = context.insts() - .GetAs( - context.return_scope_stack().back().decl_id) - .function_id; + auto decl_id = context.scope_stack().GetReturnScopeDeclId(); + auto function_id = + context.insts().GetAs(decl_id).function_id; return context.functions().Get(function_id); } @@ -33,9 +32,9 @@ auto GetCurrentReturnSlot(Context& context) -> SemIR::InstId { // Gets the currently in scope `returned var`, if any, that would be returned // by a `return var;`. static auto GetCurrentReturnedVar(Context& context) -> SemIR::InstId { - CARBON_CHECK(!context.return_scope_stack().empty(), + CARBON_CHECK(context.scope_stack().IsInFunctionScope(), "Handling return but not in a function"); - return context.return_scope_stack().back().returned_var; + return context.scope_stack().GetReturnedVar(); } // Produces a note that the given function has no explicit return type. diff --git a/toolchain/check/scope_stack.cpp b/toolchain/check/scope_stack.cpp index 3de37a86d52c..67a5e70110dc 100644 --- a/toolchain/check/scope_stack.cpp +++ b/toolchain/check/scope_stack.cpp @@ -63,8 +63,6 @@ auto ScopeStack::Push(SemIR::InstId scope_inst_id, SemIR::NameScopeId scope_id, // specifics, we'll need to somehow track them in lookup. CARBON_CHECK(!specific_id.has_value(), "Lexical scope should not have an associated specific."); - - destroy_id_stack_.PushArray(); } else { non_lexical_scope_stack_.push_back({.scope_index = next_scope_index_, .name_scope_id = scope_id, @@ -79,6 +77,36 @@ auto ScopeStack::Push(SemIR::InstId scope_inst_id, SemIR::NameScopeId scope_id, VerifyNextCompileTimeBindIndex("Push", scope_stack_.back()); } +auto ScopeStack::PushForDeclName() -> void { + Push(SemIR::InstId::None, SemIR::NameScopeId::None, SemIR::SpecificId::None, + /*lexical_lookup_has_load_error=*/false); + MarkNestingIfInReturnScope(); +} + +auto ScopeStack::PushForEntity(SemIR::InstId scope_inst_id, + SemIR::NameScopeId scope_id, + SemIR::SpecificId specific_id, + bool lexical_lookup_has_load_error) -> void { + CARBON_CHECK(scope_inst_id.has_value()); + CARBON_DCHECK(!sem_ir_->insts().Is(scope_inst_id)); + Push(scope_inst_id, scope_id, specific_id, lexical_lookup_has_load_error); + MarkNestingIfInReturnScope(); +} + +auto ScopeStack::PushForSameRegion() -> void { + Push(SemIR::InstId::None, SemIR::NameScopeId::None, SemIR::SpecificId::None, + /*lexical_lookup_has_load_error=*/false); +} + +auto ScopeStack::PushForFunctionBody(SemIR::InstId scope_inst_id) -> void { + CARBON_DCHECK(sem_ir_->insts().Is(scope_inst_id)); + Push(scope_inst_id, SemIR::NameScopeId::None, SemIR::SpecificId::None, + /*lexical_lookup_has_load_error=*/false); + + return_scope_stack_.push_back({.decl_id = scope_inst_id}); + destroy_id_stack_.PushArray(); +} + auto ScopeStack::Pop() -> void { auto scope = scope_stack_.pop_back_val(); @@ -89,17 +117,27 @@ auto ScopeStack::Pop() -> void { lexical_results.pop_back(); }); - if (scope.is_lexical_scope()) { - destroy_id_stack_.PopArray(); - } else { + if (!scope.is_lexical_scope()) { CARBON_CHECK(non_lexical_scope_stack_.back().scope_index == scope.index); non_lexical_scope_stack_.pop_back(); } - if (scope.has_returned_var) { - CARBON_CHECK(!return_scope_stack_.empty()); - CARBON_CHECK(return_scope_stack_.back().returned_var.has_value()); - return_scope_stack_.back().returned_var = SemIR::InstId::None; + if (!return_scope_stack_.empty()) { + if (scope.has_returned_var) { + CARBON_CHECK(return_scope_stack_.back().returned_var.has_value()); + return_scope_stack_.back().returned_var = SemIR::InstId::None; + } + + if (return_scope_stack_.back().decl_id == scope.scope_inst_id) { + // Leaving the function scope. + return_scope_stack_.pop_back(); + destroy_id_stack_.PopArray(); + } else if (return_scope_stack_.back().nested_scope_index == scope.index) { + // Returned to a function scope from a non-function nested entity scope. + return_scope_stack_.back().nested_scope_index = ScopeIndex::None; + } + } else { + CARBON_CHECK(!scope.has_returned_var); } VerifyNextCompileTimeBindIndex("Pop", scope); @@ -218,11 +256,7 @@ auto ScopeStack::Suspend() -> SuspendedScope { CARBON_CHECK(!scope_stack_.empty(), "No scope to suspend"); SuspendedScope result = {.entry = scope_stack_.pop_back_val(), .suspended_items = {}}; - if (result.entry.is_lexical_scope()) { - CARBON_CHECK(destroy_id_stack_.PeekArray().empty(), - "Missing support to suspend scopes with destructed storage"); - destroy_id_stack_.PopArray(); - } else { + if (!result.entry.is_lexical_scope()) { non_lexical_scope_stack_.pop_back(); } auto peek_compile_time_bindings = compile_time_binding_stack_.PeekArray(); @@ -265,9 +299,7 @@ auto ScopeStack::Restore(SuspendedScope scope) -> void { VerifyNextCompileTimeBindIndex("Restore", scope.entry); - if (scope.entry.is_lexical_scope()) { - destroy_id_stack_.PushArray(); - } else { + if (!scope.entry.is_lexical_scope()) { non_lexical_scope_stack_.push_back( {.scope_index = scope.entry.index, .name_scope_id = scope.entry.scope_id, diff --git a/toolchain/check/scope_stack.h b/toolchain/check/scope_stack.h index a0a5c08d523b..d38f208bd5d5 100644 --- a/toolchain/check/scope_stack.h +++ b/toolchain/check/scope_stack.h @@ -31,17 +31,6 @@ class ScopeStack { SemIR::InstBlockId continue_target; }; - // A scope in which `return` can be used. - struct ReturnScope { - // The declaration from which we can return. Inside a function, this will - // be a `FunctionDecl`. - SemIR::InstId decl_id; - - // The value corresponding to the current `returned var`, if any. Will be - // set and unset as `returned var`s are declared and go out of scope. - SemIR::InstId returned_var = SemIR::InstId::None; - }; - // A non-lexical scope in which unqualified lookup may be required. struct NonLexicalScope { // The index of the scope in the scope stack. @@ -57,17 +46,24 @@ class ScopeStack { // Information about a scope that has been temporarily removed from the stack. struct SuspendedScope; - // Pushes a scope onto scope_stack_. NameScopeId::None is used for new - // scopes. lexical_lookup_has_load_error is used to limit diagnostics when a - // given namespace may contain a mix of both successful and failed name - // imports. - auto Push(SemIR::InstId scope_inst_id = SemIR::InstId::None, - SemIR::NameScopeId scope_id = SemIR::NameScopeId::None, - SemIR::SpecificId specific_id = SemIR::SpecificId::None, - bool lexical_lookup_has_load_error = false) -> void; + // Pushes a scope for a declaration name's parameters. + auto PushForDeclName() -> void; - // Pops the top scope from scope_stack_, cleaning up names from - // lexical_lookup_. + // Pushes a non-function entity scope. Functions must use + // `PushForFunctionBody` instead. + auto PushForEntity(SemIR::InstId scope_inst_id, SemIR::NameScopeId scope_id, + SemIR::SpecificId specific_id, + bool lexical_lookup_has_load_error = false) -> void; + + // Pushes a scope which should be in the same region as the current scope. + // These can be in a function without breaking `return` scoping. For example, + // this is used by struct literals and code blocks. + auto PushForSameRegion() -> void; + + // Pushes a function scope. + auto PushForFunctionBody(SemIR::InstId scope_inst_id) -> void; + + // Pops the top scope from scope_stack_. Removes names from lexical_lookup_. auto Pop() -> void; // Pops the top scope from scope_stack_ if it contains no names. @@ -98,8 +94,12 @@ class ScopeStack { return Peek().specific_id; } - // Returns true if current scope is lexical. - auto PeekIsLexicalScope() const -> bool { return Peek().is_lexical_scope(); } + // Returns true if the current scope is inside a function scope (either the + // scope itself, or a lexical scope), without an intervening entity scope. + auto IsInFunctionScope() const -> bool { + return !return_scope_stack_.empty() && + !return_scope_stack_.back().nested_scope_index.has_value(); + } // Returns the current scope, if it is of the specified kind. Otherwise, // returns nullopt. @@ -117,6 +117,19 @@ class ScopeStack { // is already a `returned var`, returns it instead. auto SetReturnedVarOrGetExisting(SemIR::InstId inst_id) -> SemIR::InstId; + // Returns the `returned var` instruction that's currently in scope, or `None` + // if there isn't one. + auto GetReturnedVar() -> SemIR::InstId { + CARBON_CHECK(IsInFunctionScope(), "Handling return but not in a function"); + return return_scope_stack_.back().returned_var; + } + + // Returns the decl ID for the current return scope. + auto GetReturnScopeDeclId() -> SemIR::InstId { + CARBON_CHECK(IsInFunctionScope(), "Handling return but not in a function"); + return return_scope_stack_.back().decl_id; + } + // Looks up the name `name_id` in the current scope and enclosing scopes, but // do not look past `scope_index`. Returns the existing lookup result, if any. auto LookupInLexicalScopesWithin(SemIR::NameId name_id, @@ -160,10 +173,6 @@ class ScopeStack { // Runs verification that the processing cleanly finished. auto VerifyOnFinish() const -> void; - auto return_scope_stack() -> llvm::SmallVector& { - return return_scope_stack_; - } - auto break_continue_stack() -> llvm::SmallVector& { return break_continue_stack_; } @@ -219,6 +228,35 @@ class ScopeStack { Set names = {}; }; + // A scope in which `return` can be used. + struct ReturnScope { + // The `FunctionDecl`. + SemIR::InstId decl_id; + + // The value corresponding to the current `returned var`, if any. Will be + // set and unset as `returned var`s are declared and go out of scope. + SemIR::InstId returned_var = SemIR::InstId::None; + + // When a nested scope interrupts a return scope, this is the index of the + // outermost interrupting scope (the one closest to the function scope). + // This can then be used to determine whether we're actually inside the most + // recent `ReturnScope`, or inside a different entity scope. + // + // This won't be set for functions directly inside functions, because they + // will have their own `ReturnScope`. + + // For example, when a `class` is inside a `fn`, it interrupts the function + // body by setting this on `PushEntity`; `Pop` will set it back to `None`. + ScopeIndex nested_scope_index = ScopeIndex::None; + }; + + // Pushes a scope onto scope_stack_. NameScopeId::None is used for new scopes. + // lexical_lookup_has_load_error is used to limit diagnostics when a given + // namespace may contain a mix of both successful and failed name imports. + auto Push(SemIR::InstId scope_inst_id, SemIR::NameScopeId scope_id, + SemIR::SpecificId specific_id, bool lexical_lookup_has_load_error) + -> void; + auto Peek() const -> const ScopeStackEntry& { return scope_stack_.back(); } // Returns whether lexical lookup currently has any load errors. @@ -227,6 +265,15 @@ class ScopeStack { scope_stack_.back().lexical_lookup_has_load_error; } + // If inside a return scope, marks a nested scope (see `nested_scope_index`). + // Called after pushing the new scope. + auto MarkNestingIfInReturnScope() -> void { + if (!return_scope_stack_.empty() && + !return_scope_stack_.back().nested_scope_index.has_value()) { + return_scope_stack_.back().nested_scope_index = scope_stack_.back().index; + } + } + // Checks that the provided scope's `next_compile_time_bind_index` matches the // full size of the current `compile_time_binding_stack_`. The values should // always match, and this is used to validate the correspondence during @@ -246,8 +293,8 @@ class ScopeStack { // A stack for scope context. llvm::SmallVector scope_stack_; - // A stack of `destroy` functions to call. This only has entries for lexical - // scopes, because non-lexical scopes don't have destruction on scope exit. + // A stack of `destroy` functions to call. This only has entries inside of + // function bodies, where destruction on scope exit is required. ArrayStack destroy_id_stack_; // Information about non-lexical scopes. This is a subset of the entries and diff --git a/toolchain/check/testdata/alias/fail_control_flow.carbon b/toolchain/check/testdata/alias/fail_control_flow.carbon index 6ac634cd655c..37859b7aee44 100644 --- a/toolchain/check/testdata/alias/fail_control_flow.carbon +++ b/toolchain/check/testdata/alias/fail_control_flow.carbon @@ -8,35 +8,93 @@ // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/alias/fail_control_flow.carbon -// CHECK:STDERR: fail_control_flow.carbon:[[@LINE+16]]:11: error: semantics TODO: `Control flow expressions are currently only supported inside functions.` [SemanticsTodo] +// --- fail_simple.carbon + +library "[[@TEST_NAME]]"; + +// CHECK:STDERR: fail_simple.carbon:[[@LINE+8]]:11: error: semantics TODO: `Control flow expressions are currently only supported inside functions.` [SemanticsTodo] // CHECK:STDERR: alias a = true or false; // CHECK:STDERR: ^~~~~~~ // CHECK:STDERR: -// CHECK:STDERR: fail_control_flow.carbon:[[@LINE+12]]:11: error: semantics TODO: `Control flow expressions are currently only supported inside functions.` [SemanticsTodo] -// CHECK:STDERR: alias a = true or false; -// CHECK:STDERR: ^~~~~~~~~~~~~ -// CHECK:STDERR: -// CHECK:STDERR: fail_control_flow.carbon:[[@LINE+8]]:11: error: semantics TODO: `Control flow expressions are currently only supported inside functions.` [SemanticsTodo] -// CHECK:STDERR: alias a = true or false; -// CHECK:STDERR: ^~~~~~~~~~~~~ -// CHECK:STDERR: -// CHECK:STDERR: fail_control_flow.carbon:[[@LINE+4]]:11: error: alias initializer must be a name reference [AliasRequiresNameRef] +// CHECK:STDERR: fail_simple.carbon:[[@LINE+4]]:11: error: semantics TODO: `Control flow expressions are currently only supported inside functions.` [SemanticsTodo] // CHECK:STDERR: alias a = true or false; // CHECK:STDERR: ^~~~~~~~~~~~~ // CHECK:STDERR: alias a = true or false; -// CHECK:STDOUT: --- fail_control_flow.carbon +// --- fail_nested.carbon + +library "[[@TEST_NAME]]"; + +base class C(B:! bool) {} + +fn F() { + class B { + // CHECK:STDERR: fail_nested.carbon:[[@LINE+4]]:20: error: semantics TODO: `Control flow expressions are currently only supported inside functions.` [SemanticsTodo] + // CHECK:STDERR: extend base: C(true or false); + // CHECK:STDERR: ^~~~~~~~~~~~~ + // CHECK:STDERR: + extend base: C(true or false); + } +} + +// CHECK:STDOUT: --- fail_simple.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file {} +// CHECK:STDOUT: +// CHECK:STDOUT: --- fail_nested.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %B.7dd: bool = bind_symbolic_name B, 0 [symbolic] +// CHECK:STDOUT: %B.patt: bool = symbolic_binding_pattern B, 0 [symbolic] +// CHECK:STDOUT: %C.type: type = generic_class_type @C [concrete] +// CHECK:STDOUT: %C.generic: %C.type = struct_value () [concrete] +// CHECK:STDOUT: %C: type = class_type @C, @C(%B.7dd) [symbolic] +// CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] +// CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] +// CHECK:STDOUT: %B.d97: type = class_type @B [concrete] // CHECK:STDOUT: %true: bool = bool_literal true [concrete] +// CHECK:STDOUT: %false: bool = bool_literal false [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: -// CHECK:STDOUT: imports { +// CHECK:STDOUT: file {} +// CHECK:STDOUT: +// CHECK:STDOUT: generic class @C(.inst27.loc4_14: bool) { +// CHECK:STDOUT: %B: bool = bind_symbolic_name B, 0 [symbolic = %B (constants.%B.7dd)] +// CHECK:STDOUT: %B.patt.loc4_14.2: bool = symbolic_binding_pattern B, 0 [symbolic = %B.patt.loc4_14.2 (constants.%B.patt)] +// CHECK:STDOUT: +// CHECK:STDOUT: !definition: +// CHECK:STDOUT: +// CHECK:STDOUT: class { +// CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete = constants.%empty_struct_type] +// CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete = constants.%complete_type] +// CHECK:STDOUT: complete_type_witness = %complete_type +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .Self = constants.%C +// CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: -// CHECK:STDOUT: file { -// CHECK:STDOUT: %.loc27: bool = block_arg [concrete = constants.%true] -// CHECK:STDOUT: %a: = bind_alias a, [concrete = ] +// CHECK:STDOUT: class @B { +// CHECK:STDOUT: %C.ref: %C.type = name_ref C, .inst32.loc4_24 [concrete = constants.%C.generic] +// CHECK:STDOUT: %true.loc12_20: bool = bool_literal true [concrete = constants.%true] +// CHECK:STDOUT: %.loc12: bool = not %true.loc12_20 [concrete = constants.%false] +// CHECK:STDOUT: %true.loc12_25: bool = bool_literal true [concrete = constants.%true] +// CHECK:STDOUT: if %.loc12 br !or.rhs else br !or.result(%true.loc12_25) +// CHECK:STDOUT: complete_type_witness = invalid +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .Self = constants.%B.d97 +// CHECK:STDOUT: .C = +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @F(); +// CHECK:STDOUT: +// CHECK:STDOUT: specific @C(constants.%B.7dd) { +// CHECK:STDOUT: %B => constants.%B.7dd +// CHECK:STDOUT: %B.patt.loc4_14.2 => constants.%B.patt // CHECK:STDOUT: } // CHECK:STDOUT: diff --git a/toolchain/check/testdata/class/no_prelude/destroy_calls.carbon b/toolchain/check/testdata/class/no_prelude/destroy_calls.carbon index e4d7dc7ad2ba..a4f2a35faf64 100644 --- a/toolchain/check/testdata/class/no_prelude/destroy_calls.carbon +++ b/toolchain/check/testdata/class/no_prelude/destroy_calls.carbon @@ -48,7 +48,6 @@ fn F() { var explicit_return: ExplicitReturn; var with_addr: WithAddr; if (true) { - // TODO: Destroy in nested scope. var in_scope: NoAddr; } } @@ -476,22 +475,24 @@ fn G() { F({}); } // CHECK:STDOUT: !if.then: // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %in_scope.patt: %NoAddr = binding_pattern in_scope -// CHECK:STDOUT: %.loc11: %NoAddr = var_pattern %in_scope.patt +// CHECK:STDOUT: %.loc10_5.1: %NoAddr = var_pattern %in_scope.patt // CHECK:STDOUT: } // CHECK:STDOUT: %in_scope.var: ref %NoAddr = var in_scope // CHECK:STDOUT: %destroy.ref.4: %destroy.type.bc5 = name_ref destroy, imports.%Main.import_ref.7fe [concrete = constants.%destroy.60f] // CHECK:STDOUT: %destroy.bound.4: = bound_method %in_scope.var, %destroy.ref.4 -// CHECK:STDOUT: %NoAddr.ref.loc11: type = name_ref NoAddr, imports.%Main.NoAddr [concrete = constants.%NoAddr] +// CHECK:STDOUT: %NoAddr.ref.loc10: type = name_ref NoAddr, imports.%Main.NoAddr [concrete = constants.%NoAddr] // CHECK:STDOUT: %in_scope: ref %NoAddr = bind_name in_scope, %in_scope.var // CHECK:STDOUT: br !if.else // CHECK:STDOUT: // CHECK:STDOUT: !if.else: +// CHECK:STDOUT: %.loc10_5.2: %NoAddr = bind_value %in_scope.var +// CHECK:STDOUT: %destroy.call.1: init %empty_tuple.type = call %destroy.bound.4(%.loc10_5.2) // CHECK:STDOUT: %addr: %ptr.b4e = addr_of %with_addr.var -// CHECK:STDOUT: %destroy.call.1: init %empty_tuple.type = call %destroy.bound.3(%addr) +// CHECK:STDOUT: %destroy.call.2: init %empty_tuple.type = call %destroy.bound.3(%addr) // CHECK:STDOUT: %.loc7_3.2: %ExplicitReturn = bind_value %explicit_return.var -// CHECK:STDOUT: %destroy.call.2: init %empty_tuple.type = call %destroy.bound.2(%.loc7_3.2) +// CHECK:STDOUT: %destroy.call.3: init %empty_tuple.type = call %destroy.bound.2(%.loc7_3.2) // CHECK:STDOUT: %.loc6_3.2: %NoAddr = bind_value %no_addr.var -// CHECK:STDOUT: %destroy.call.3: init %empty_tuple.type = call %destroy.bound.1(%.loc6_3.2) +// CHECK:STDOUT: %destroy.call.4: init %empty_tuple.type = call %destroy.bound.1(%.loc6_3.2) // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: diff --git a/toolchain/check/testdata/if_expr/fail_not_in_function.carbon b/toolchain/check/testdata/if_expr/fail_not_in_function.carbon index e15203cf47d6..44c94f8932b4 100644 --- a/toolchain/check/testdata/if_expr/fail_not_in_function.carbon +++ b/toolchain/check/testdata/if_expr/fail_not_in_function.carbon @@ -8,133 +8,166 @@ // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/if_expr/fail_not_in_function.carbon -// CHECK:STDERR: fail_not_in_function.carbon:[[@LINE+16]]:14: error: semantics TODO: `Control flow expressions are currently only supported inside functions.` [SemanticsTodo] +// --- fail_basic.carbon + +library "[[@TEST_NAME]]"; + +// CHECK:STDERR: fail_basic.carbon:[[@LINE+12]]:14: error: semantics TODO: `Control flow expressions are currently only supported inside functions.` [SemanticsTodo] // CHECK:STDERR: let x: i32 = if true then 1 else 0; // CHECK:STDERR: ^~~~~~~ // CHECK:STDERR: -// CHECK:STDERR: fail_not_in_function.carbon:[[@LINE+12]]:22: error: semantics TODO: `Control flow expressions are currently only supported inside functions.` [SemanticsTodo] +// CHECK:STDERR: fail_basic.carbon:[[@LINE+8]]:22: error: semantics TODO: `Control flow expressions are currently only supported inside functions.` [SemanticsTodo] // CHECK:STDERR: let x: i32 = if true then 1 else 0; // CHECK:STDERR: ^~~~~~ // CHECK:STDERR: -// CHECK:STDERR: fail_not_in_function.carbon:[[@LINE+8]]:14: error: semantics TODO: `Control flow expressions are currently only supported inside functions.` [SemanticsTodo] +// CHECK:STDERR: fail_basic.carbon:[[@LINE+4]]:14: error: semantics TODO: `Control flow expressions are currently only supported inside functions.` [SemanticsTodo] // CHECK:STDERR: let x: i32 = if true then 1 else 0; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: -// CHECK:STDERR: fail_not_in_function.carbon:[[@LINE+4]]:14: error: semantics TODO: `Control flow expressions are currently only supported inside functions.` [SemanticsTodo] -// CHECK:STDERR: let x: i32 = if true then 1 else 0; -// CHECK:STDERR: ^~~~~~~ -// CHECK:STDERR: let x: i32 = if true then 1 else 0; -// CHECK:STDERR: fail_not_in_function.carbon:[[@LINE+8]]:8: error: semantics TODO: `Control flow expressions are currently only supported inside functions.` [SemanticsTodo] +// --- fail_types.carbon + +library "[[@TEST_NAME]]"; + +// CHECK:STDERR: fail_types.carbon:[[@LINE+4]]:8: error: semantics TODO: `Control flow expressions are currently only supported inside functions.` [SemanticsTodo] // CHECK:STDERR: var y: if true then i32 else f64; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: -// CHECK:STDERR: fail_not_in_function.carbon:[[@LINE+4]]:8: error: semantics TODO: `Control flow expressions are currently only supported inside functions.` [SemanticsTodo] -// CHECK:STDERR: var y: if true then i32 else f64; -// CHECK:STDERR: ^~~~~~~ -// CHECK:STDERR: var y: if true then i32 else f64; -// CHECK:STDERR: fail_not_in_function.carbon:[[@LINE+8]]:9: error: semantics TODO: `Control flow expressions are currently only supported inside functions.` [SemanticsTodo] +// --- fail_in_param.carbon + +library "[[@TEST_NAME]]"; + +// CHECK:STDERR: fail_in_param.carbon:[[@LINE+4]]:9: error: semantics TODO: `Control flow expressions are currently only supported inside functions.` [SemanticsTodo] // CHECK:STDERR: fn F(a: if true then i32 else f64); // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: -// CHECK:STDERR: fail_not_in_function.carbon:[[@LINE+4]]:9: error: semantics TODO: `Control flow expressions are currently only supported inside functions.` [SemanticsTodo] -// CHECK:STDERR: fn F(a: if true then i32 else f64); -// CHECK:STDERR: ^~~~~~~ -// CHECK:STDERR: fn F(a: if true then i32 else f64); +// --- fail_class.carbon + +library "[[@TEST_NAME]]"; + class C { - // CHECK:STDERR: fail_not_in_function.carbon:[[@LINE+16]]:10: error: semantics TODO: `Control flow expressions are currently only supported inside functions.` [SemanticsTodo] + // CHECK:STDERR: fail_class.carbon:[[@LINE+12]]:10: error: semantics TODO: `Control flow expressions are currently only supported inside functions.` [SemanticsTodo] // CHECK:STDERR: var n: if true then i32 else f64; // CHECK:STDERR: ^~~~~~~ // CHECK:STDERR: - // CHECK:STDERR: fail_not_in_function.carbon:[[@LINE+12]]:18: error: semantics TODO: `Control flow expressions are currently only supported inside functions.` [SemanticsTodo] + // CHECK:STDERR: fail_class.carbon:[[@LINE+8]]:18: error: semantics TODO: `Control flow expressions are currently only supported inside functions.` [SemanticsTodo] // CHECK:STDERR: var n: if true then i32 else f64; // CHECK:STDERR: ^~~~~~~~ // CHECK:STDERR: - // CHECK:STDERR: fail_not_in_function.carbon:[[@LINE+8]]:10: error: semantics TODO: `Control flow expressions are currently only supported inside functions.` [SemanticsTodo] + // CHECK:STDERR: fail_class.carbon:[[@LINE+4]]:10: error: semantics TODO: `Control flow expressions are currently only supported inside functions.` [SemanticsTodo] // CHECK:STDERR: var n: if true then i32 else f64; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: - // CHECK:STDERR: fail_not_in_function.carbon:[[@LINE+4]]:10: error: semantics TODO: `Control flow expressions are currently only supported inside functions.` [SemanticsTodo] - // CHECK:STDERR: var n: if true then i32 else f64; - // CHECK:STDERR: ^~~~~~~ - // CHECK:STDERR: var n: if true then i32 else f64; } -// CHECK:STDOUT: --- fail_not_in_function.carbon +// --- fail_nested_class.carbon + +library "[[@TEST_NAME]]"; + +base class C(T:! type) {} + +fn F() { + class B { + // CHECK:STDERR: fail_nested_class.carbon:[[@LINE+4]]:20: error: semantics TODO: `Control flow expressions are currently only supported inside functions.` [SemanticsTodo] + // CHECK:STDERR: extend base: C(if true then i32 else i32); + // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~ + // CHECK:STDERR: + extend base: C(if true then i32 else i32); + } +} + +// CHECK:STDOUT: --- fail_basic.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file {} +// CHECK:STDOUT: +// CHECK:STDOUT: --- fail_types.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file {} +// CHECK:STDOUT: +// CHECK:STDOUT: --- fail_in_param.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file {} +// CHECK:STDOUT: +// CHECK:STDOUT: --- fail_class.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { -// CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] -// CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] -// CHECK:STDOUT: %true: bool = bool_literal true [concrete] -// CHECK:STDOUT: %F.type: type = fn_type @F [concrete] -// CHECK:STDOUT: %F: %F.type = struct_value () [concrete] // CHECK:STDOUT: %C: type = class_type @C [concrete] +// CHECK:STDOUT: %true: bool = bool_literal true [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: -// CHECK:STDOUT: imports { -// CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { -// CHECK:STDOUT: .Int = %Core.Int -// CHECK:STDOUT: .ImplicitAs = %Core.ImplicitAs -// CHECK:STDOUT: .Float = %Core.Float -// CHECK:STDOUT: import Core//prelude -// CHECK:STDOUT: import Core//prelude/... -// CHECK:STDOUT: } -// CHECK:STDOUT: } -// CHECK:STDOUT: -// CHECK:STDOUT: file { -// CHECK:STDOUT: package: = namespace [concrete] { -// CHECK:STDOUT: .Core = imports.%Core -// CHECK:STDOUT: .x = %x -// CHECK:STDOUT: .y = %y -// CHECK:STDOUT: .F = %F.decl -// CHECK:STDOUT: .C = %C.decl -// CHECK:STDOUT: } -// CHECK:STDOUT: %Core.import = import Core -// CHECK:STDOUT: name_binding_decl { -// CHECK:STDOUT: %x.patt: %i32 = binding_pattern x -// CHECK:STDOUT: } -// CHECK:STDOUT: %.loc27: type = splice_block %i32 [concrete = constants.%i32] { -// CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete = constants.%int_32] -// CHECK:STDOUT: %i32: type = class_type @Int, @Int(constants.%int_32) [concrete = constants.%i32] -// CHECK:STDOUT: } -// CHECK:STDOUT: %x: %i32 = bind_name x, .inst1317.loc27_14 -// CHECK:STDOUT: name_binding_decl { -// CHECK:STDOUT: %y.patt: %i32 = binding_pattern y -// CHECK:STDOUT: %.loc37: %i32 = var_pattern %y.patt -// CHECK:STDOUT: } -// CHECK:STDOUT: %y.var: ref %i32 = var y -// CHECK:STDOUT: %y: ref %i32 = bind_name y, %y.var -// CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] { -// CHECK:STDOUT: %a.patt: %i32 = binding_pattern a -// CHECK:STDOUT: %a.param_patt: %i32 = value_param_pattern %a.patt, call_param0 -// CHECK:STDOUT: } { -// CHECK:STDOUT: %a.param: %i32 = value_param call_param0 -// CHECK:STDOUT: %a: %i32 = bind_name a, %a.param -// CHECK:STDOUT: } -// CHECK:STDOUT: %C.decl: type = class_decl @C [concrete = constants.%C] {} {} -// CHECK:STDOUT: } +// CHECK:STDOUT: file {} // CHECK:STDOUT: // CHECK:STDOUT: class @C { // CHECK:STDOUT: %true: bool = bool_literal true [concrete = constants.%true] // CHECK:STDOUT: if %true br !if.expr.then else br !if.expr.else -// CHECK:STDOUT: complete_type_witness = .inst1384.loc67_1 +// CHECK:STDOUT: complete_type_witness = invalid // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%C -// CHECK:STDOUT: .n = .inst1381.loc66_8 // CHECK:STDOUT: } // CHECK:STDOUT: -// CHECK:STDOUT: fn @F(%a.param: %i32); +// CHECK:STDOUT: --- fail_nested_class.carbon // CHECK:STDOUT: -// CHECK:STDOUT: fn @__global_init() { -// CHECK:STDOUT: !entry: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %T: type = bind_symbolic_name T, 0 [symbolic] +// CHECK:STDOUT: %T.patt: type = symbolic_binding_pattern T, 0 [symbolic] +// CHECK:STDOUT: %C.type: type = generic_class_type @C [concrete] +// CHECK:STDOUT: %C.generic: %C.type = struct_value () [concrete] +// CHECK:STDOUT: %C: type = class_type @C, @C(%T) [symbolic] +// CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] +// CHECK:STDOUT: %complete_type.357: = complete_type_witness %empty_struct_type [concrete] +// CHECK:STDOUT: %B: type = class_type @B [concrete] +// CHECK:STDOUT: %true: bool = bool_literal true [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file {} +// CHECK:STDOUT: +// CHECK:STDOUT: generic class @C(.inst17.loc4_14: type) { +// CHECK:STDOUT: %T: type = bind_symbolic_name T, 0 [symbolic = %T (constants.%T)] +// CHECK:STDOUT: %T.patt.loc4_14.2: type = symbolic_binding_pattern T, 0 [symbolic = %T.patt.loc4_14.2 (constants.%T.patt)] +// CHECK:STDOUT: +// CHECK:STDOUT: !definition: +// CHECK:STDOUT: +// CHECK:STDOUT: class { +// CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete = constants.%empty_struct_type] +// CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete = constants.%complete_type.357] +// CHECK:STDOUT: complete_type_witness = %complete_type +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .Self = constants.%C +// CHECK:STDOUT: } +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: class @B { +// CHECK:STDOUT: %C.ref: %C.type = name_ref C, .inst21.loc4_24 [concrete = constants.%C.generic] // CHECK:STDOUT: %true: bool = bool_literal true [concrete = constants.%true] // CHECK:STDOUT: if %true br !if.expr.then else br !if.expr.else +// CHECK:STDOUT: complete_type_witness = invalid +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .Self = constants.%B +// CHECK:STDOUT: .C = +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @F(); +// CHECK:STDOUT: +// CHECK:STDOUT: specific @C(constants.%T) { +// CHECK:STDOUT: %T => constants.%T +// CHECK:STDOUT: %T.patt.loc4_14.2 => constants.%T.patt // CHECK:STDOUT: } // CHECK:STDOUT: diff --git a/toolchain/check/testdata/operators/builtin/fail_and_or_not_in_function.carbon b/toolchain/check/testdata/operators/builtin/fail_and_or_not_in_function.carbon index 839b397bf755..f8907a9f406a 100644 --- a/toolchain/check/testdata/operators/builtin/fail_and_or_not_in_function.carbon +++ b/toolchain/check/testdata/operators/builtin/fail_and_or_not_in_function.carbon @@ -8,155 +8,80 @@ // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/operators/builtin/fail_and_or_not_in_function.carbon +// --- fail_and.carbon + +library "[[@TEST_NAME]]"; + // TODO: Make this a compile-time function. fn F(b: bool) -> type { return if b then i32 else f64; } // TODO: Short-circuit operators should be permitted outside functions. -// CHECK:STDERR: fail_and_or_not_in_function.carbon:[[@LINE+10]]:13: error: semantics TODO: `Control flow expressions are currently only supported inside functions.` [SemanticsTodo] +// CHECK:STDERR: fail_and.carbon:[[@LINE+4]]:13: error: semantics TODO: `Control flow expressions are currently only supported inside functions.` [SemanticsTodo] // CHECK:STDERR: var and_: F(true and true); // CHECK:STDERR: ^~~~~~~~~~~~~ // CHECK:STDERR: -// CHECK:STDERR: fail_and_or_not_in_function.carbon:[[@LINE+6]]:11: error: cannot evaluate type expression [TypeExprEvaluationFailure] -// CHECK:STDERR: var and_: F(true and true); -// CHECK:STDERR: ^~~~~~~~~~~~~~~~ -// CHECK:STDERR: -// CHECK:STDERR: fail_and_or_not_in_function.carbon: error: semantics TODO: `Control flow expressions are currently only supported inside functions.` [SemanticsTodo] -// CHECK:STDERR: var and_: F(true and true); -// CHECK:STDERR: fail_and_or_not_in_function.carbon:[[@LINE+12]]:21: error: semantics TODO: `Control flow expressions are currently only supported inside functions.` [SemanticsTodo] +// --- fail_and_val.carbon + +library "[[@TEST_NAME]]"; + +// CHECK:STDERR: fail_and_val.carbon:[[@LINE+8]]:21: error: semantics TODO: `Control flow expressions are currently only supported inside functions.` [SemanticsTodo] // CHECK:STDERR: var and_val: bool = true and true; // CHECK:STDERR: ^~~~~~~~ // CHECK:STDERR: -// CHECK:STDERR: fail_and_or_not_in_function.carbon:[[@LINE+8]]:21: error: semantics TODO: `Control flow expressions are currently only supported inside functions.` [SemanticsTodo] -// CHECK:STDERR: var and_val: bool = true and true; -// CHECK:STDERR: ^~~~~~~~~~~~~ -// CHECK:STDERR: -// CHECK:STDERR: fail_and_or_not_in_function.carbon:[[@LINE+4]]:21: error: semantics TODO: `Control flow expressions are currently only supported inside functions.` [SemanticsTodo] +// CHECK:STDERR: fail_and_val.carbon:[[@LINE+4]]:21: error: semantics TODO: `Control flow expressions are currently only supported inside functions.` [SemanticsTodo] // CHECK:STDERR: var and_val: bool = true and true; // CHECK:STDERR: ^~~~~~~~~~~~~ // CHECK:STDERR: var and_val: bool = true and true; -// CHECK:STDERR: fail_and_or_not_in_function.carbon:[[@LINE+10]]:12: error: semantics TODO: `Control flow expressions are currently only supported inside functions.` [SemanticsTodo] +// --- fail_or.carbon + +library "[[@TEST_NAME]]"; + +// TODO: Make this a compile-time function. +fn F(b: bool) -> type { + return if b then i32 else f64; +} + +// CHECK:STDERR: fail_or.carbon:[[@LINE+4]]:12: error: semantics TODO: `Control flow expressions are currently only supported inside functions.` [SemanticsTodo] // CHECK:STDERR: var or_: F(true or true); // CHECK:STDERR: ^~~~~~~~~~~~ // CHECK:STDERR: -// CHECK:STDERR: fail_and_or_not_in_function.carbon:[[@LINE+6]]:10: error: cannot evaluate type expression [TypeExprEvaluationFailure] -// CHECK:STDERR: var or_: F(true or true); -// CHECK:STDERR: ^~~~~~~~~~~~~~~ -// CHECK:STDERR: -// CHECK:STDERR: fail_and_or_not_in_function.carbon: error: semantics TODO: `Control flow expressions are currently only supported inside functions.` [SemanticsTodo] -// CHECK:STDERR: var or_: F(true or true); -// CHECK:STDERR: fail_and_or_not_in_function.carbon:[[@LINE+12]]:20: error: semantics TODO: `Control flow expressions are currently only supported inside functions.` [SemanticsTodo] +// --- fail_or_val.carbon + +library "[[@TEST_NAME]]"; + +// CHECK:STDERR: fail_or_val.carbon:[[@LINE+8]]:20: error: semantics TODO: `Control flow expressions are currently only supported inside functions.` [SemanticsTodo] // CHECK:STDERR: var or_val: bool = true or true; // CHECK:STDERR: ^~~~~~~ // CHECK:STDERR: -// CHECK:STDERR: fail_and_or_not_in_function.carbon:[[@LINE+8]]:20: error: semantics TODO: `Control flow expressions are currently only supported inside functions.` [SemanticsTodo] -// CHECK:STDERR: var or_val: bool = true or true; -// CHECK:STDERR: ^~~~~~~~~~~~ -// CHECK:STDERR: -// CHECK:STDERR: fail_and_or_not_in_function.carbon:[[@LINE+4]]:20: error: semantics TODO: `Control flow expressions are currently only supported inside functions.` [SemanticsTodo] +// CHECK:STDERR: fail_or_val.carbon:[[@LINE+4]]:20: error: semantics TODO: `Control flow expressions are currently only supported inside functions.` [SemanticsTodo] // CHECK:STDERR: var or_val: bool = true or true; // CHECK:STDERR: ^~~~~~~~~~~~ // CHECK:STDERR: var or_val: bool = true or true; -// CHECK:STDOUT: --- fail_and_or_not_in_function.carbon +// CHECK:STDOUT: --- fail_and.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { -// CHECK:STDOUT: %Bool.type: type = fn_type @Bool [concrete] -// CHECK:STDOUT: %Bool: %Bool.type = struct_value () [concrete] -// CHECK:STDOUT: %F.type: type = fn_type @F [concrete] -// CHECK:STDOUT: %F: %F.type = struct_value () [concrete] // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %int_64: Core.IntLiteral = int_value 64 [concrete] // CHECK:STDOUT: %Float.type: type = fn_type @Float [concrete] // CHECK:STDOUT: %Float: %Float.type = struct_value () [concrete] -// CHECK:STDOUT: %true: bool = bool_literal true [concrete] -// CHECK:STDOUT: %false: bool = bool_literal false [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: -// CHECK:STDOUT: imports { -// CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { -// CHECK:STDOUT: .Bool = %Core.Bool -// CHECK:STDOUT: .Int = %Core.Int -// CHECK:STDOUT: .Float = %Core.Float -// CHECK:STDOUT: import Core//prelude -// CHECK:STDOUT: import Core//prelude/... -// CHECK:STDOUT: } -// CHECK:STDOUT: } -// CHECK:STDOUT: -// CHECK:STDOUT: file { -// CHECK:STDOUT: package: = namespace [concrete] { -// CHECK:STDOUT: .Core = imports.%Core -// CHECK:STDOUT: .F = %F.decl -// CHECK:STDOUT: .and_ = %and_ -// CHECK:STDOUT: .and_val = %and_val -// CHECK:STDOUT: .or_ = %or_ -// CHECK:STDOUT: .or_val = %or_val -// CHECK:STDOUT: } -// CHECK:STDOUT: %Core.import = import Core -// CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] { -// CHECK:STDOUT: %b.patt: bool = binding_pattern b -// CHECK:STDOUT: %b.param_patt: bool = value_param_pattern %b.patt, call_param0 -// CHECK:STDOUT: %return.patt: type = return_slot_pattern -// CHECK:STDOUT: %return.param_patt: type = out_param_pattern %return.patt, call_param1 -// CHECK:STDOUT: } { -// CHECK:STDOUT: %b.param: bool = value_param call_param0 -// CHECK:STDOUT: %.loc12_9.1: type = splice_block %.loc12_9.3 [concrete = bool] { -// CHECK:STDOUT: %bool.make_type: init type = call constants.%Bool() [concrete = bool] -// CHECK:STDOUT: %.loc12_9.2: type = value_of_initializer %bool.make_type [concrete = bool] -// CHECK:STDOUT: %.loc12_9.3: type = converted %bool.make_type, %.loc12_9.2 [concrete = bool] -// CHECK:STDOUT: } -// CHECK:STDOUT: %b: bool = bind_name b, %b.param -// CHECK:STDOUT: %return.param: ref type = out_param call_param1 -// CHECK:STDOUT: %return: ref type = return_slot %return.param -// CHECK:STDOUT: } -// CHECK:STDOUT: name_binding_decl { -// CHECK:STDOUT: %and_.patt: = binding_pattern and_ -// CHECK:STDOUT: %.loc27: = var_pattern %and_.patt -// CHECK:STDOUT: } -// CHECK:STDOUT: %and_.var: ref = var and_ -// CHECK:STDOUT: %and_: = bind_name and_, -// CHECK:STDOUT: name_binding_decl { -// CHECK:STDOUT: %and_val.patt: bool = binding_pattern and_val -// CHECK:STDOUT: %.loc41_1: bool = var_pattern %and_val.patt -// CHECK:STDOUT: } -// CHECK:STDOUT: %and_val.var: ref bool = var and_val -// CHECK:STDOUT: %.loc41_14.1: type = splice_block %.loc41_14.3 [concrete = bool] { -// CHECK:STDOUT: %bool.make_type.loc41: init type = call constants.%Bool() [concrete = bool] -// CHECK:STDOUT: %.loc41_14.2: type = value_of_initializer %bool.make_type.loc41 [concrete = bool] -// CHECK:STDOUT: %.loc41_14.3: type = converted %bool.make_type.loc41, %.loc41_14.2 [concrete = bool] -// CHECK:STDOUT: } -// CHECK:STDOUT: %and_val: ref bool = bind_name and_val, %and_val.var -// CHECK:STDOUT: name_binding_decl { -// CHECK:STDOUT: %or_.patt: = binding_pattern or_ -// CHECK:STDOUT: %.loc53: = var_pattern %or_.patt -// CHECK:STDOUT: } -// CHECK:STDOUT: %or_.var: ref = var or_ -// CHECK:STDOUT: %or_: = bind_name or_, -// CHECK:STDOUT: name_binding_decl { -// CHECK:STDOUT: %or_val.patt: bool = binding_pattern or_val -// CHECK:STDOUT: %.loc67_1: bool = var_pattern %or_val.patt -// CHECK:STDOUT: } -// CHECK:STDOUT: %or_val.var: ref bool = var or_val -// CHECK:STDOUT: %.loc67_13.1: type = splice_block %.loc67_13.3 [concrete = bool] { -// CHECK:STDOUT: %bool.make_type.loc67: init type = call constants.%Bool() [concrete = bool] -// CHECK:STDOUT: %.loc67_13.2: type = value_of_initializer %bool.make_type.loc67 [concrete = bool] -// CHECK:STDOUT: %.loc67_13.3: type = converted %bool.make_type.loc67, %.loc67_13.2 [concrete = bool] -// CHECK:STDOUT: } -// CHECK:STDOUT: %or_val: ref bool = bind_name or_val, %or_val.var -// CHECK:STDOUT: } +// CHECK:STDOUT: file {} // CHECK:STDOUT: // CHECK:STDOUT: fn @F(%b.param: bool) -> type { // CHECK:STDOUT: !entry: -// CHECK:STDOUT: %b.ref: bool = name_ref b, %b +// CHECK:STDOUT: %b.ref: bool = name_ref b, .inst27.loc5_6 // CHECK:STDOUT: if %b.ref br !if.expr.then else br !if.expr.else // CHECK:STDOUT: // CHECK:STDOUT: !if.expr.then: @@ -167,19 +92,60 @@ var or_val: bool = true or true; // CHECK:STDOUT: !if.expr.else: // CHECK:STDOUT: %int_64: Core.IntLiteral = int_value 64 [concrete = constants.%int_64] // CHECK:STDOUT: %float.make_type: init type = call constants.%Float(%int_64) [concrete = f64] -// CHECK:STDOUT: %.loc13_24.1: type = value_of_initializer %float.make_type [concrete = f64] -// CHECK:STDOUT: %.loc13_24.2: type = converted %float.make_type, %.loc13_24.1 [concrete = f64] -// CHECK:STDOUT: br !if.expr.result(%.loc13_24.2) +// CHECK:STDOUT: %.loc6_24.1: type = value_of_initializer %float.make_type [concrete = f64] +// CHECK:STDOUT: %.loc6_24.2: type = converted %float.make_type, %.loc6_24.1 [concrete = f64] +// CHECK:STDOUT: br !if.expr.result(%.loc6_24.2) // CHECK:STDOUT: // CHECK:STDOUT: !if.expr.result: -// CHECK:STDOUT: %.loc13_10: type = block_arg !if.expr.result -// CHECK:STDOUT: return %.loc13_10 +// CHECK:STDOUT: %.loc6_10: type = block_arg !if.expr.result +// CHECK:STDOUT: return %.loc6_10 // CHECK:STDOUT: } // CHECK:STDOUT: -// CHECK:STDOUT: fn @__global_init() { +// CHECK:STDOUT: --- fail_and_val.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file {} +// CHECK:STDOUT: +// CHECK:STDOUT: --- fail_or.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] +// CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] +// CHECK:STDOUT: %int_64: Core.IntLiteral = int_value 64 [concrete] +// CHECK:STDOUT: %Float.type: type = fn_type @Float [concrete] +// CHECK:STDOUT: %Float: %Float.type = struct_value () [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file {} +// CHECK:STDOUT: +// CHECK:STDOUT: fn @F(%b.param: bool) -> type { // CHECK:STDOUT: !entry: -// CHECK:STDOUT: %true: bool = bool_literal true [concrete = constants.%true] -// CHECK:STDOUT: %false: bool = bool_literal false [concrete = constants.%false] -// CHECK:STDOUT: if %true br !and.rhs else br !and.result(%false) +// CHECK:STDOUT: %b.ref: bool = name_ref b, .inst27.loc5_6 +// CHECK:STDOUT: if %b.ref br !if.expr.then else br !if.expr.else +// CHECK:STDOUT: +// CHECK:STDOUT: !if.expr.then: +// CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete = constants.%int_32] +// CHECK:STDOUT: %i32: type = class_type @Int, @Int(constants.%int_32) [concrete = constants.%i32] +// CHECK:STDOUT: br !if.expr.result(%i32) +// CHECK:STDOUT: +// CHECK:STDOUT: !if.expr.else: +// CHECK:STDOUT: %int_64: Core.IntLiteral = int_value 64 [concrete = constants.%int_64] +// CHECK:STDOUT: %float.make_type: init type = call constants.%Float(%int_64) [concrete = f64] +// CHECK:STDOUT: %.loc6_24.1: type = value_of_initializer %float.make_type [concrete = f64] +// CHECK:STDOUT: %.loc6_24.2: type = converted %float.make_type, %.loc6_24.1 [concrete = f64] +// CHECK:STDOUT: br !if.expr.result(%.loc6_24.2) +// CHECK:STDOUT: +// CHECK:STDOUT: !if.expr.result: +// CHECK:STDOUT: %.loc6_10: type = block_arg !if.expr.result +// CHECK:STDOUT: return %.loc6_10 // CHECK:STDOUT: } // CHECK:STDOUT: +// CHECK:STDOUT: --- fail_or_val.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file {} +// CHECK:STDOUT: diff --git a/toolchain/check/testdata/var/fail_todo_control_flow_init.carbon b/toolchain/check/testdata/var/fail_todo_control_flow_init.carbon index 4367eaa696ad..a64adac9cd6d 100644 --- a/toolchain/check/testdata/var/fail_todo_control_flow_init.carbon +++ b/toolchain/check/testdata/var/fail_todo_control_flow_init.carbon @@ -8,143 +8,95 @@ // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/var/fail_todo_control_flow_init.carbon -// CHECK:STDERR: fail_todo_control_flow_init.carbon:[[@LINE+16]]:13: error: semantics TODO: `Control flow expressions are currently only supported inside functions.` [SemanticsTodo] +// --- fail_if_true.carbon + +library "[[@TEST_NAME]]"; + +// CHECK:STDERR: fail_if_true.carbon:[[@LINE+12]]:13: error: semantics TODO: `Control flow expressions are currently only supported inside functions.` [SemanticsTodo] // CHECK:STDERR: var x: () = if true then () else (); // CHECK:STDERR: ^~~~~~~ // CHECK:STDERR: -// CHECK:STDERR: fail_todo_control_flow_init.carbon:[[@LINE+12]]:21: error: semantics TODO: `Control flow expressions are currently only supported inside functions.` [SemanticsTodo] +// CHECK:STDERR: fail_if_true.carbon:[[@LINE+8]]:21: error: semantics TODO: `Control flow expressions are currently only supported inside functions.` [SemanticsTodo] // CHECK:STDERR: var x: () = if true then () else (); // CHECK:STDERR: ^~~~~~~ // CHECK:STDERR: -// CHECK:STDERR: fail_todo_control_flow_init.carbon:[[@LINE+8]]:13: error: semantics TODO: `Control flow expressions are currently only supported inside functions.` [SemanticsTodo] +// CHECK:STDERR: fail_if_true.carbon:[[@LINE+4]]:13: error: semantics TODO: `Control flow expressions are currently only supported inside functions.` [SemanticsTodo] // CHECK:STDERR: var x: () = if true then () else (); // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: -// CHECK:STDERR: fail_todo_control_flow_init.carbon:[[@LINE+4]]:13: error: semantics TODO: `Control flow expressions are currently only supported inside functions.` [SemanticsTodo] -// CHECK:STDERR: var x: () = if true then () else (); -// CHECK:STDERR: ^~~~~~~ -// CHECK:STDERR: var x: () = if true then () else (); -// CHECK:STDERR: fail_todo_control_flow_init.carbon:[[@LINE+16]]:14: error: semantics TODO: `Control flow expressions are currently only supported inside functions.` [SemanticsTodo] +// --- fail_if_false.carbon + +library "[[@TEST_NAME]]"; + +// CHECK:STDERR: fail_if_false.carbon:[[@LINE+12]]:14: error: semantics TODO: `Control flow expressions are currently only supported inside functions.` [SemanticsTodo] // CHECK:STDERR: var x2: () = if false then () else (); // CHECK:STDERR: ^~~~~~~~ // CHECK:STDERR: -// CHECK:STDERR: fail_todo_control_flow_init.carbon:[[@LINE+12]]:23: error: semantics TODO: `Control flow expressions are currently only supported inside functions.` [SemanticsTodo] +// CHECK:STDERR: fail_if_false.carbon:[[@LINE+8]]:23: error: semantics TODO: `Control flow expressions are currently only supported inside functions.` [SemanticsTodo] // CHECK:STDERR: var x2: () = if false then () else (); // CHECK:STDERR: ^~~~~~~ // CHECK:STDERR: -// CHECK:STDERR: fail_todo_control_flow_init.carbon:[[@LINE+8]]:14: error: semantics TODO: `Control flow expressions are currently only supported inside functions.` [SemanticsTodo] +// CHECK:STDERR: fail_if_false.carbon:[[@LINE+4]]:14: error: semantics TODO: `Control flow expressions are currently only supported inside functions.` [SemanticsTodo] // CHECK:STDERR: var x2: () = if false then () else (); // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: -// CHECK:STDERR: fail_todo_control_flow_init.carbon:[[@LINE+4]]:14: error: semantics TODO: `Control flow expressions are currently only supported inside functions.` [SemanticsTodo] -// CHECK:STDERR: var x2: () = if false then () else (); -// CHECK:STDERR: ^~~~~~~~ -// CHECK:STDERR: var x2: () = if false then () else (); -// CHECK:STDERR: fail_todo_control_flow_init.carbon:[[@LINE+12]]:15: error: semantics TODO: `Control flow expressions are currently only supported inside functions.` [SemanticsTodo] +// --- fail_true_or_false.carbon + +library "[[@TEST_NAME]]"; + +// CHECK:STDERR: fail_true_or_false.carbon:[[@LINE+8]]:15: error: semantics TODO: `Control flow expressions are currently only supported inside functions.` [SemanticsTodo] // CHECK:STDERR: var y: bool = true or false; // CHECK:STDERR: ^~~~~~~ // CHECK:STDERR: -// CHECK:STDERR: fail_todo_control_flow_init.carbon:[[@LINE+8]]:15: error: semantics TODO: `Control flow expressions are currently only supported inside functions.` [SemanticsTodo] -// CHECK:STDERR: var y: bool = true or false; -// CHECK:STDERR: ^~~~~~~~~~~~~ -// CHECK:STDERR: -// CHECK:STDERR: fail_todo_control_flow_init.carbon:[[@LINE+4]]:15: error: semantics TODO: `Control flow expressions are currently only supported inside functions.` [SemanticsTodo] +// CHECK:STDERR: fail_true_or_false.carbon:[[@LINE+4]]:15: error: semantics TODO: `Control flow expressions are currently only supported inside functions.` [SemanticsTodo] // CHECK:STDERR: var y: bool = true or false; // CHECK:STDERR: ^~~~~~~~~~~~~ // CHECK:STDERR: var y: bool = true or false; -// CHECK:STDERR: fail_todo_control_flow_init.carbon:[[@LINE+12]]:16: error: semantics TODO: `Control flow expressions are currently only supported inside functions.` [SemanticsTodo] +// --- fail_false_or_true.carbon + +library "[[@TEST_NAME]]"; + +// CHECK:STDERR: fail_false_or_true.carbon:[[@LINE+8]]:16: error: semantics TODO: `Control flow expressions are currently only supported inside functions.` [SemanticsTodo] // CHECK:STDERR: var y2: bool = false or true; // CHECK:STDERR: ^~~~~~~~ // CHECK:STDERR: -// CHECK:STDERR: fail_todo_control_flow_init.carbon:[[@LINE+8]]:16: error: semantics TODO: `Control flow expressions are currently only supported inside functions.` [SemanticsTodo] -// CHECK:STDERR: var y2: bool = false or true; -// CHECK:STDERR: ^~~~~~~~~~~~~ -// CHECK:STDERR: -// CHECK:STDERR: fail_todo_control_flow_init.carbon:[[@LINE+4]]:16: error: semantics TODO: `Control flow expressions are currently only supported inside functions.` [SemanticsTodo] +// CHECK:STDERR: fail_false_or_true.carbon:[[@LINE+4]]:16: error: semantics TODO: `Control flow expressions are currently only supported inside functions.` [SemanticsTodo] // CHECK:STDERR: var y2: bool = false or true; // CHECK:STDERR: ^~~~~~~~~~~~~ // CHECK:STDERR: var y2: bool = false or true; -// CHECK:STDOUT: --- fail_todo_control_flow_init.carbon +// CHECK:STDOUT: --- fail_if_true.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { -// CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] -// CHECK:STDOUT: %true: bool = bool_literal true [concrete] -// CHECK:STDOUT: %Bool.type: type = fn_type @Bool [concrete] -// CHECK:STDOUT: %Bool: %Bool.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: -// CHECK:STDOUT: imports { -// CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { -// CHECK:STDOUT: .Bool = %Core.Bool -// CHECK:STDOUT: import Core//prelude -// CHECK:STDOUT: import Core//prelude/... -// CHECK:STDOUT: } +// CHECK:STDOUT: file {} +// CHECK:STDOUT: +// CHECK:STDOUT: --- fail_if_false.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { // CHECK:STDOUT: } // CHECK:STDOUT: -// CHECK:STDOUT: file { -// CHECK:STDOUT: package: = namespace [concrete] { -// CHECK:STDOUT: .Core = imports.%Core -// CHECK:STDOUT: .x = %x -// CHECK:STDOUT: .x2 = %x2 -// CHECK:STDOUT: .y = %y -// CHECK:STDOUT: .y2 = %y2 -// CHECK:STDOUT: } -// CHECK:STDOUT: %Core.import = import Core -// CHECK:STDOUT: name_binding_decl { -// CHECK:STDOUT: %x.patt: %empty_tuple.type = binding_pattern x -// CHECK:STDOUT: %.loc27_1: %empty_tuple.type = var_pattern %x.patt -// CHECK:STDOUT: } -// CHECK:STDOUT: %x.var: ref %empty_tuple.type = var x -// CHECK:STDOUT: %.loc27_9.1: type = splice_block %.loc27_9.3 [concrete = constants.%empty_tuple.type] { -// CHECK:STDOUT: %.loc27_9.2: %empty_tuple.type = tuple_literal () -// CHECK:STDOUT: %.loc27_9.3: type = converted %.loc27_9.2, constants.%empty_tuple.type [concrete = constants.%empty_tuple.type] -// CHECK:STDOUT: } -// CHECK:STDOUT: %x: ref %empty_tuple.type = bind_name x, %x.var -// CHECK:STDOUT: name_binding_decl { -// CHECK:STDOUT: %x2.patt: %empty_tuple.type = binding_pattern x2 -// CHECK:STDOUT: %.loc45_1: %empty_tuple.type = var_pattern %x2.patt -// CHECK:STDOUT: } -// CHECK:STDOUT: %x2.var: ref %empty_tuple.type = var x2 -// CHECK:STDOUT: %.loc45_10.1: type = splice_block %.loc45_10.3 [concrete = constants.%empty_tuple.type] { -// CHECK:STDOUT: %.loc45_10.2: %empty_tuple.type = tuple_literal () -// CHECK:STDOUT: %.loc45_10.3: type = converted %.loc45_10.2, constants.%empty_tuple.type [concrete = constants.%empty_tuple.type] -// CHECK:STDOUT: } -// CHECK:STDOUT: %x2: ref %empty_tuple.type = bind_name x2, %x2.var -// CHECK:STDOUT: name_binding_decl { -// CHECK:STDOUT: %y.patt: bool = binding_pattern y -// CHECK:STDOUT: %.loc59_1: bool = var_pattern %y.patt -// CHECK:STDOUT: } -// CHECK:STDOUT: %y.var: ref bool = var y -// CHECK:STDOUT: %.loc59_8.1: type = splice_block %.loc59_8.3 [concrete = bool] { -// CHECK:STDOUT: %bool.make_type.loc59: init type = call constants.%Bool() [concrete = bool] -// CHECK:STDOUT: %.loc59_8.2: type = value_of_initializer %bool.make_type.loc59 [concrete = bool] -// CHECK:STDOUT: %.loc59_8.3: type = converted %bool.make_type.loc59, %.loc59_8.2 [concrete = bool] -// CHECK:STDOUT: } -// CHECK:STDOUT: %y: ref bool = bind_name y, %y.var -// CHECK:STDOUT: name_binding_decl { -// CHECK:STDOUT: %y2.patt: bool = binding_pattern y2 -// CHECK:STDOUT: %.loc73_1: bool = var_pattern %y2.patt -// CHECK:STDOUT: } -// CHECK:STDOUT: %y2.var: ref bool = var y2 -// CHECK:STDOUT: %.loc73_9.1: type = splice_block %.loc73_9.3 [concrete = bool] { -// CHECK:STDOUT: %bool.make_type.loc73: init type = call constants.%Bool() [concrete = bool] -// CHECK:STDOUT: %.loc73_9.2: type = value_of_initializer %bool.make_type.loc73 [concrete = bool] -// CHECK:STDOUT: %.loc73_9.3: type = converted %bool.make_type.loc73, %.loc73_9.2 [concrete = bool] -// CHECK:STDOUT: } -// CHECK:STDOUT: %y2: ref bool = bind_name y2, %y2.var +// CHECK:STDOUT: file {} +// CHECK:STDOUT: +// CHECK:STDOUT: --- fail_true_or_false.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { // CHECK:STDOUT: } // CHECK:STDOUT: -// CHECK:STDOUT: fn @__global_init() { -// CHECK:STDOUT: !entry: -// CHECK:STDOUT: %true: bool = bool_literal true [concrete = constants.%true] -// CHECK:STDOUT: if %true br !if.expr.then else br !if.expr.else +// CHECK:STDOUT: file {} +// CHECK:STDOUT: +// CHECK:STDOUT: --- fail_false_or_true.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { // CHECK:STDOUT: } // CHECK:STDOUT: +// CHECK:STDOUT: file {} +// CHECK:STDOUT: