diff --git a/common/BUILD b/common/BUILD index b0b96fa8ea59..f1a5aaa6a06a 100644 --- a/common/BUILD +++ b/common/BUILD @@ -331,6 +331,11 @@ sh_test( ], ) +cc_library( + name = "move_only", + hdrs = ["move_only.h"], +) + cc_library( name = "ostream", hdrs = ["ostream.h"], diff --git a/common/array_stack.h b/common/array_stack.h index c021396390a0..f2e4b66a3afd 100644 --- a/common/array_stack.h +++ b/common/array_stack.h @@ -66,17 +66,17 @@ class ArrayStack { auto PeekAllValues() const -> llvm::ArrayRef { return values_; } // Appends a value to the top array on the stack. - auto AppendToTop(ValueT value) -> void { + auto AppendToTop(const ValueT& value) -> void { CARBON_CHECK(!array_offsets_.empty(), "Must call PushArray before AppendToTop."); values_.push_back(value); } - // Prepends a value to the top array on the stack. - auto PrependToTop(ValueT value) -> void { + // Appends a value to the top array on the stack. + auto AppendToTop(ValueT&& value) -> void { CARBON_CHECK(!array_offsets_.empty(), - "Must call PushArray before PrependToTop."); - values_.insert(values_.begin() + array_offsets_.back(), value); + "Must call PushArray before AppendToTop."); + values_.push_back(std::move(value)); } // Adds multiple values to the top array on the stack. diff --git a/common/move_only.h b/common/move_only.h new file mode 100644 index 000000000000..f31e51e4dff5 --- /dev/null +++ b/common/move_only.h @@ -0,0 +1,28 @@ +// 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 + +#ifndef CARBON_COMMON_MOVE_ONLY_H_ +#define CARBON_COMMON_MOVE_ONLY_H_ + +namespace Carbon { + +// A base class that indicates a type is move-only. Typically this can be +// achieved by declaring the move constructor and move assignment yourself; this +// type should be used only when doing that is not feasible, such as when +// aggregate initialization is still desired. +// +// This class uses CRTP to ensure that each MoveOnly base class has a different +// type. This is important to avoid the compiler adding extra padding to derived +// classes to give multiple MoveOnly subobjects of the same type different +// addresses. +template +struct MoveOnly { + MoveOnly() = default; + MoveOnly(MoveOnly&&) noexcept = default; + auto operator=(MoveOnly&&) noexcept -> MoveOnly& = default; +}; + +} // namespace Carbon + +#endif // CARBON_COMMON_MOVE_ONLY_H_ diff --git a/toolchain/check/BUILD b/toolchain/check/BUILD index 3fd65943d845..7035a2d867fc 100644 --- a/toolchain/check/BUILD +++ b/toolchain/check/BUILD @@ -64,6 +64,7 @@ cc_library( "decl_introducer_state.h", "decl_name_stack.h", "deduce.h", + "deferred_definition_scope.h", "diagnostic_helpers.h", "eval.h", "eval_inst.h", @@ -254,6 +255,7 @@ cc_library( deps = [ "//common:array_stack", "//common:check", + "//common:move_only", "//common:ostream", "//common:set", "//common:vlog", diff --git a/toolchain/check/context.h b/toolchain/check/context.h index 3016c38261a6..234f36f7e4ce 100644 --- a/toolchain/check/context.h +++ b/toolchain/check/context.h @@ -13,6 +13,7 @@ #include "toolchain/base/value_store.h" #include "toolchain/check/decl_introducer_state.h" #include "toolchain/check/decl_name_stack.h" +#include "toolchain/check/deferred_definition_scope.h" #include "toolchain/check/diagnostic_helpers.h" #include "toolchain/check/full_pattern_stack.h" #include "toolchain/check/generic_region_stack.h" @@ -128,6 +129,10 @@ class Context { return scope_stack_.full_pattern_stack(); } + auto deferred_definition_scope_stack() -> DeferredDefinitionScopeStack& { + return deferred_definition_scope_stack_; + } + auto generic_region_stack() -> GenericRegionStack& { return generic_region_stack_; } @@ -337,6 +342,9 @@ class Context { // The stack of scopes we are currently within. ScopeStack scope_stack_; + // The stack of non-nested deferred definition scopes we are currently within. + DeferredDefinitionScopeStack deferred_definition_scope_stack_; + // The stack of generic regions we are currently within. GenericRegionStack generic_region_stack_; diff --git a/toolchain/check/decl_name_stack.cpp b/toolchain/check/decl_name_stack.cpp index b0427eee7d2c..cc60112b0c5e 100644 --- a/toolchain/check/decl_name_stack.cpp +++ b/toolchain/check/decl_name_stack.cpp @@ -108,7 +108,7 @@ auto DeclNameStack::Suspend() -> SuspendedName { return result; } -auto DeclNameStack::Restore(SuspendedName sus) -> void { +auto DeclNameStack::Restore(SuspendedName&& sus) -> void { // The parent state must be the same when a name is restored. CARBON_CHECK(context_->scope_stack().PeekIndex() == sus.name_context.initial_scope_index, diff --git a/toolchain/check/decl_name_stack.h b/toolchain/check/decl_name_stack.h index b9ac40da5e2c..936c923674bc 100644 --- a/toolchain/check/decl_name_stack.h +++ b/toolchain/check/decl_name_stack.h @@ -165,7 +165,9 @@ class DeclNameStack { // Information about a declaration name that has been temporarily removed from // the stack and will later be restored. Names can only be suspended once they // are finished. - struct SuspendedName { + // + // This type is large, so moves of this type should be avoided. + struct SuspendedName : public MoveOnly { // The declaration name information. NameContext name_context; @@ -236,7 +238,7 @@ class DeclNameStack { auto Suspend() -> SuspendedName; // Restore a previously suspended name. - auto Restore(SuspendedName sus) -> void; + auto Restore(SuspendedName&& sus) -> void; // Adds a name to name lookup. Assumes duplicates are already handled. auto AddName(NameContext name_context, SemIR::InstId target_id, diff --git a/toolchain/check/deferred_definition_scope.h b/toolchain/check/deferred_definition_scope.h new file mode 100644 index 000000000000..ca5511a6183e --- /dev/null +++ b/toolchain/check/deferred_definition_scope.h @@ -0,0 +1,68 @@ +// 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 + +#ifndef CARBON_TOOLCHAIN_CHECK_DEFERRED_DEFINITION_SCOPE_H_ +#define CARBON_TOOLCHAIN_CHECK_DEFERRED_DEFINITION_SCOPE_H_ + +#include "common/array_stack.h" +#include "toolchain/check/scope_stack.h" +#include "toolchain/sem_ir/inst.h" + +namespace Carbon::Check { + +// A thunk that has been declared but not yet defined. +// +// This type is large, so moves of this type should be avoided. +struct PendingThunk : public MoveOnly { + SemIR::FunctionId signature_id; + SemIR::FunctionId function_id; + SemIR::InstId decl_id; + SemIR::InstId callee_id; + ScopeStack::SuspendedScope scope; +}; + +// A stack of the current non-nested deferred definition scopes. For example, in +// this code: +// +// class A { +// class B { +// fn F() { +// class C { +// // ... +// } +// } +// } +// } +// +// ... we have two non-nested deferred definition scopes: one for class A, and +// one for class C. The scope for class B is nested, so is not tracked. +// +// At the end of each such scope, pending function definitions for functions +// defined inline are processed. At this location, we also generate bodies for +// thunks generated by checking. +class DeferredDefinitionScopeStack { + public: + // Push a new scope. + auto Push() -> void { pending_thunks_.PushArray(); } + + // Pop a scope. + auto Pop() -> void { pending_thunks_.PopArray(); } + + // Add a pending thunk definition for the current scope. + auto AddPendingThunk(PendingThunk&& thunk) -> void { + pending_thunks_.AppendToTop(std::move(thunk)); + } + + // Peek the list of pending thunks in this scope. + auto PeekPendingThunks() -> llvm::MutableArrayRef { + return pending_thunks_.PeekArray(); + } + + private: + ArrayStack pending_thunks_; +}; + +} // namespace Carbon::Check + +#endif // CARBON_TOOLCHAIN_CHECK_DEFERRED_DEFINITION_SCOPE_H_ diff --git a/toolchain/check/deferred_definition_worklist.cpp b/toolchain/check/deferred_definition_worklist.cpp index 8259a4545c83..3fa654f2a914 100644 --- a/toolchain/check/deferred_definition_worklist.cpp +++ b/toolchain/check/deferred_definition_worklist.cpp @@ -33,7 +33,7 @@ auto DeferredDefinitionWorklist::SuspendFunctionAndPush( } auto DeferredDefinitionWorklist::PushEnterDeferredDefinitionScope( - Context& context) -> void { + Context& context) -> bool { bool nested = !entered_scopes_.empty() && entered_scopes_.back().scope_index == context.decl_name_stack().PeekInitialScopeIndex(); @@ -43,25 +43,30 @@ auto DeferredDefinitionWorklist::PushEnterDeferredDefinitionScope( .suspended_name = std::nullopt, .in_deferred_definition_scope = nested}); CARBON_VLOG("{0}Push EnterDeferredDefinitionScope {1}\n", VlogPrefix, nested ? "(nested)" : "(non-nested)"); + return !nested; } auto DeferredDefinitionWorklist::SuspendFinishedScopeAndPush(Context& context) - -> bool { + -> FinishedScopeKind { auto start_index = entered_scopes_.pop_back_val().worklist_start_index; // If we've not found any deferred definitions in this scope, clean up the // stack. if (start_index == worklist_.size() - 1) { context.decl_name_stack().PopScope(); - worklist_.pop_back(); + auto enter_scope = + get(worklist_.pop_back_val()); CARBON_VLOG("{0}Pop EnterDeferredDefinitionScope (empty)\n", VlogPrefix); - return false; + return enter_scope.in_deferred_definition_scope + ? FinishedScopeKind::Nested + : FinishedScopeKind::NonNestedEmpty; } // If we're finishing a nested deferred definition scope, keep track of that // but don't type-check deferred definitions now. - auto& enter_scope = get(worklist_[start_index]); - if (enter_scope.in_deferred_definition_scope) { + if (auto& enter_scope = + get(worklist_[start_index]); + enter_scope.in_deferred_definition_scope) { // This is a nested deferred definition scope. Suspend the inner scope so we // can restore it when we come to type-check the deferred definitions. enter_scope.suspended_name = context.decl_name_stack().Suspend(); @@ -70,7 +75,7 @@ auto DeferredDefinitionWorklist::SuspendFinishedScopeAndPush(Context& context) worklist_.push_back( LeaveDeferredDefinitionScope{.in_deferred_definition_scope = true}); CARBON_VLOG("{0}Push LeaveDeferredDefinitionScope (nested)\n", VlogPrefix); - return false; + return FinishedScopeKind::Nested; } // We're at the end of a non-nested deferred definition scope. Prepare to @@ -95,10 +100,11 @@ auto DeferredDefinitionWorklist::SuspendFinishedScopeAndPush(Context& context) worklist_.pop_back(); CARBON_VLOG("{0}Handle EnterDeferredDefinitionScope (non-nested)\n", VlogPrefix); - return true; + return FinishedScopeKind::NonNestedWithWork; } -auto DeferredDefinitionWorklist::Pop() -> Task { +auto DeferredDefinitionWorklist::Pop( + llvm::function_refvoid> handle_fn) -> void { if (vlog_stream_) { VariantMatch( worklist_.back(), @@ -118,7 +124,8 @@ auto DeferredDefinitionWorklist::Pop() -> Task { }); } - return worklist_.pop_back_val(); + handle_fn(std::move(worklist_.back())); + worklist_.pop_back(); } } // namespace Carbon::Check diff --git a/toolchain/check/deferred_definition_worklist.h b/toolchain/check/deferred_definition_worklist.h index 9859210e8ea3..97377f9c92b7 100644 --- a/toolchain/check/deferred_definition_worklist.h +++ b/toolchain/check/deferred_definition_worklist.h @@ -67,24 +67,35 @@ class DeferredDefinitionWorklist { explicit DeferredDefinitionWorklist(llvm::raw_ostream* vlog_stream); - // Suspend the current function definition and push a task onto the worklist + // Suspends the current function definition and push a task onto the worklist // to finish it later. auto SuspendFunctionAndPush(Context& context, Parse::DeferredDefinitionIndex index, Parse::FunctionDefinitionStartId node_id) -> void; - // Push a task to re-enter a function scope, so that functions defined within - // it are type-checked in the right context. - auto PushEnterDeferredDefinitionScope(Context& context) -> void; + // Pushes a task to re-enter a function scope, so that functions defined + // within it are type-checked in the right context. Returns whether a + // non-nested scope was entered. + auto PushEnterDeferredDefinitionScope(Context& context) -> bool; - // Suspend the current deferred definition scope, which is finished but still - // on the decl_name_stack, and push a task to leave the scope when we're + // The kind of scope that we just finished. + enum class FinishedScopeKind { + // We finished a nested scope. No further action is taken at this point. + Nested, + // We finished a non-nested scope that has no further actions to perform. + NonNestedEmpty, + // We finished a non-nested scope that has further actions to perform. + NonNestedWithWork, + }; + + // Suspends the current deferred definition scope, which is finished but still + // on the decl_name_stack, and pushes a task to leave the scope when we're // type-checking deferred definitions. Returns `true` if the current list of // deferred definitions should be type-checked immediately. - auto SuspendFinishedScopeAndPush(Context& context) -> bool; + auto SuspendFinishedScopeAndPush(Context& context) -> FinishedScopeKind; - // Pop the next task off the worklist. - auto Pop() -> Task; + // Pop and handle the next task on the worklist. + auto Pop(llvm::function_refvoid> handle_fn) -> void; // CHECK that the work list has no further work. auto VerifyEmpty() { diff --git a/toolchain/check/function.h b/toolchain/check/function.h index 3b7f7566c99f..991065edf804 100644 --- a/toolchain/check/function.h +++ b/toolchain/check/function.h @@ -16,7 +16,9 @@ namespace Carbon::Check { // State saved for a function definition that has been suspended after // processing its declaration and before processing its body. This is used for // inline method handling. -struct SuspendedFunction { +// +// This type is large, so moves of this type should be avoided. +struct SuspendedFunction : public MoveOnly { // The function that was declared. SemIR::FunctionId function_id; // The instruction ID of the FunctionDecl instruction. diff --git a/toolchain/check/handle.h b/toolchain/check/handle.h index ffc8eac07852..2540b55da29f 100644 --- a/toolchain/check/handle.h +++ b/toolchain/check/handle.h @@ -28,7 +28,7 @@ auto HandleFunctionDefinitionSuspend(Context& context, // Handle resuming the definition of a function, after a previous suspension. auto HandleFunctionDefinitionResume(Context& context, Parse::FunctionDefinitionStartId node_id, - SuspendedFunction suspended_fn) -> void; + SuspendedFunction&& suspended_fn) -> void; } // namespace Carbon::Check diff --git a/toolchain/check/handle_function.cpp b/toolchain/check/handle_function.cpp index 24236d8f535f..c0fbff8f3791 100644 --- a/toolchain/check/handle_function.cpp +++ b/toolchain/check/handle_function.cpp @@ -594,8 +594,8 @@ auto HandleFunctionDefinitionSuspend(Context& context, auto HandleFunctionDefinitionResume(Context& context, Parse::FunctionDefinitionStartId node_id, - SuspendedFunction suspended_fn) -> void { - context.decl_name_stack().Restore(suspended_fn.saved_name_state); + SuspendedFunction&& suspended_fn) -> void { + context.decl_name_stack().Restore(std::move(suspended_fn.saved_name_state)); HandleFunctionDefinitionAfterSignature( context, node_id, suspended_fn.function_id, suspended_fn.decl_id); } diff --git a/toolchain/check/node_id_traversal.cpp b/toolchain/check/node_id_traversal.cpp index de44c93249d5..0724d23ba05a 100644 --- a/toolchain/check/node_id_traversal.cpp +++ b/toolchain/check/node_id_traversal.cpp @@ -8,7 +8,9 @@ #include #include +#include "toolchain/check/deferred_definition_worklist.h" #include "toolchain/check/handle.h" +#include "toolchain/check/thunk.h" namespace Carbon::Check { @@ -29,9 +31,13 @@ auto NodeIdTraversal::Next() -> std::optional { // should check, restore its suspended state, and add a corresponding // `Chunk` to the top of the chunk list. if (chunks_.back().checking_deferred_definitions) { - std::visit( - [&](auto&& task) { PerformTask(std::forward(task)); }, - worklist_.Pop()); + worklist_.Pop([&](DeferredDefinitionWorklist::Task&& task) { + std::visit( + [&](auto&& task) { + PerformTask(std::forward(task)); + }, + std::move(task)); + }); continue; } @@ -111,15 +117,33 @@ auto NodeIdTraversal::Handle(Parse::NodeKind parse_kind) -> void { // When we reach the start of a deferred definition scope, add a task to the // worklist to check future skipped definitions in the new context. if (IsStartOfDeferredDefinitionScope(parse_kind)) { - worklist_.PushEnterDeferredDefinitionScope(*context_); + if (worklist_.PushEnterDeferredDefinitionScope(*context_)) { + // Track that we're within a new non-nested deferred definition scope. + context_->deferred_definition_scope_stack().Push(); + } } // When we reach the end of a deferred definition scope, add a task to the // worklist to leave the scope. If this is not a nested scope, start // checking the deferred definitions now. if (IsEndOfDeferredDefinitionScope(parse_kind)) { - chunks_.back().checking_deferred_definitions = - worklist_.SuspendFinishedScopeAndPush(*context_); + auto scope_kind = worklist_.SuspendFinishedScopeAndPush(*context_); + + // At the end of a non-nested scope, define any pending thunks and clean up + // the stack. + if (scope_kind != DeferredDefinitionWorklist::FinishedScopeKind::Nested) { + for (auto& thunk : + context_->deferred_definition_scope_stack().PeekPendingThunks()) { + BuildThunkDefinition(*context_, std::move(thunk)); + } + context_->deferred_definition_scope_stack().Pop(); + } + + // If we have function definitions in this scope, process them next. + if (scope_kind == + DeferredDefinitionWorklist::FinishedScopeKind::NonNestedWithWork) { + chunks_.back().checking_deferred_definitions = true; + } } } diff --git a/toolchain/check/scope_stack.cpp b/toolchain/check/scope_stack.cpp index 67a5e70110dc..4fca6e91f7b0 100644 --- a/toolchain/check/scope_stack.cpp +++ b/toolchain/check/scope_stack.cpp @@ -286,7 +286,7 @@ auto ScopeStack::Suspend() -> SuspendedScope { return result; } -auto ScopeStack::Restore(SuspendedScope scope) -> void { +auto ScopeStack::Restore(SuspendedScope&& scope) -> void { compile_time_binding_stack_.PushArray(); for (auto [index, inst_id] : scope.suspended_items) { if (index == SuspendedScope::ScopeItem::IndexForCompileTimeBinding) { diff --git a/toolchain/check/scope_stack.h b/toolchain/check/scope_stack.h index d38f208bd5d5..5cd63cf2a709 100644 --- a/toolchain/check/scope_stack.h +++ b/toolchain/check/scope_stack.h @@ -6,6 +6,7 @@ #define CARBON_TOOLCHAIN_CHECK_SCOPE_STACK_H_ #include "common/array_stack.h" +#include "common/move_only.h" #include "common/set.h" #include "llvm/ADT/SmallVector.h" #include "toolchain/check/full_pattern_stack.h" @@ -44,6 +45,7 @@ class ScopeStack { }; // Information about a scope that has been temporarily removed from the stack. + // This type is large, so moves of this type should be avoided. struct SuspendedScope; // Pushes a scope for a declaration name's parameters. @@ -168,7 +170,7 @@ class ScopeStack { auto Suspend() -> SuspendedScope; // Restores a suspended scope stack entry. - auto Restore(SuspendedScope scope) -> void; + auto Restore(SuspendedScope&& scope) -> void; // Runs verification that the processing cleanly finished. auto VerifyOnFinish() const -> void; @@ -189,7 +191,7 @@ class ScopeStack { private: // An entry in scope_stack_. - struct ScopeStackEntry { + struct ScopeStackEntry : public MoveOnly { auto is_lexical_scope() const -> bool { return !scope_id.has_value(); } // The sequential index of this scope entry within the file. @@ -315,7 +317,7 @@ class ScopeStack { FullPatternStack full_pattern_stack_; }; -struct ScopeStack::SuspendedScope { +struct ScopeStack::SuspendedScope : public MoveOnly { // An item that was suspended within this scope. This represents either a // lexical lookup entry in this scope, or a compile time binding entry in this // scope. diff --git a/toolchain/check/testdata/impl/fail_impl_bad_assoc_fn.carbon b/toolchain/check/testdata/impl/fail_impl_bad_assoc_fn.carbon index 3dc095c40855..6fed95cea70d 100644 --- a/toolchain/check/testdata/impl/fail_impl_bad_assoc_fn.carbon +++ b/toolchain/check/testdata/impl/fail_impl_bad_assoc_fn.carbon @@ -129,45 +129,45 @@ class FMissingImplicitParam { class FMissingReturnType { impl as J { - // CHECK:STDERR: fail_impl_bad_assoc_fn.carbon:[[@LINE+13]]:5: error: cannot implicitly convert expression of type `()` to `bool` [ConversionFailure] + // CHECK:STDERR: fail_impl_bad_assoc_fn.carbon:[[@LINE+16]]:5: error: cannot implicitly convert expression of type `()` to `bool` [ConversionFailure] // CHECK:STDERR: fn F[self: bool](b: bool); // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~ - // CHECK:STDERR: fail_impl_bad_assoc_fn.carbon:[[@LINE+10]]:5: note: type `()` does not implement interface `Core.ImplicitAs(bool)` [MissingImplInMemberAccessNote] + // CHECK:STDERR: fail_impl_bad_assoc_fn.carbon:[[@LINE+13]]:5: note: type `()` does not implement interface `Core.ImplicitAs(bool)` [MissingImplInMemberAccessNote] // CHECK:STDERR: fn F[self: bool](b: bool); // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fail_impl_bad_assoc_fn.carbon:[[@LINE-39]]:15: note: while building thunk to match the signature of this function [ThunkSignature] // CHECK:STDERR: interface J { fn F[self: bool](b: bool) -> bool; } // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: - // CHECK:STDERR: fail_impl_bad_assoc_fn.carbon:[[@LINE-43]]:32: error: forming value of incomplete type `FDifferentParamType` [IncompleteTypeInValueConversion] + // CHECK:STDERR: fail_impl_bad_assoc_fn.carbon:[[@LINE-43]]:32: error: cannot implicitly convert expression of type `bool` to `FDifferentParamType` [ConversionFailure] + // CHECK:STDERR: interface J { fn F[self: bool](b: bool) -> bool; } + // CHECK:STDERR: ^~~~~~~ + // CHECK:STDERR: fail_impl_bad_assoc_fn.carbon:[[@LINE-46]]:32: note: type `bool` does not implement interface `Core.ImplicitAs(FDifferentParamType)` [MissingImplInMemberAccessNote] // CHECK:STDERR: interface J { fn F[self: bool](b: bool) -> bool; } // CHECK:STDERR: ^~~~~~~ fn F[self: bool](b: bool); } } -// CHECK:STDERR: fail_impl_bad_assoc_fn.carbon:[[@LINE+3]]:1: note: class is incomplete within its definition [ClassIncompleteWithinDefinition] -// CHECK:STDERR: class FDifferentParamType { -// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~ class FDifferentParamType { impl as J { - // CHECK:STDERR: fail_impl_bad_assoc_fn.carbon:[[@LINE+10]]:22: note: initializing function parameter [InCallToFunctionParam] + // CHECK:STDERR: fail_impl_bad_assoc_fn.carbon:[[@LINE+13]]:22: note: initializing function parameter [InCallToFunctionParam] // CHECK:STDERR: fn F[self: bool](b: Self) -> bool; // CHECK:STDERR: ^~~~~~~ // CHECK:STDERR: fail_impl_bad_assoc_fn.carbon:[[@LINE-58]]:15: note: while building thunk to match the signature of this function [ThunkSignature] // CHECK:STDERR: interface J { fn F[self: bool](b: bool) -> bool; } // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: - // CHECK:STDERR: fail_impl_bad_assoc_fn.carbon:[[@LINE-62]]:20: error: forming value of incomplete type `FDifferentImplicitParamType` [IncompleteTypeInValueConversion] + // CHECK:STDERR: fail_impl_bad_assoc_fn.carbon:[[@LINE-62]]:20: error: cannot implicitly convert expression of type `bool` to `FDifferentImplicitParamType` [ConversionFailure] + // CHECK:STDERR: interface J { fn F[self: bool](b: bool) -> bool; } + // CHECK:STDERR: ^~~~~~~~~~ + // CHECK:STDERR: fail_impl_bad_assoc_fn.carbon:[[@LINE-65]]:20: note: type `bool` does not implement interface `Core.ImplicitAs(FDifferentImplicitParamType)` [MissingImplInMemberAccessNote] // CHECK:STDERR: interface J { fn F[self: bool](b: bool) -> bool; } // CHECK:STDERR: ^~~~~~~~~~ fn F[self: bool](b: Self) -> bool; } } -// CHECK:STDERR: fail_impl_bad_assoc_fn.carbon:[[@LINE+3]]:1: note: class is incomplete within its definition [ClassIncompleteWithinDefinition] -// CHECK:STDERR: class FDifferentImplicitParamType { -// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ class FDifferentImplicitParamType { impl as J { // CHECK:STDERR: fail_impl_bad_assoc_fn.carbon:[[@LINE+7]]:10: note: initializing function parameter [InCallToFunctionParam] @@ -183,16 +183,13 @@ class FDifferentImplicitParamType { class FDifferentReturnType { impl as J { - // CHECK:STDERR: fail_impl_bad_assoc_fn.carbon:[[@LINE+13]]:5: error: function returns incomplete type `FDifferentReturnType` [IncompleteTypeInFunctionReturnType] + // CHECK:STDERR: fail_impl_bad_assoc_fn.carbon:[[@LINE+10]]:5: error: cannot implicitly convert expression of type `FDifferentReturnType` to `bool` [ConversionFailure] // CHECK:STDERR: fn F[self: bool](b: bool) -> Self; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - // CHECK:STDERR: fail_impl_bad_assoc_fn.carbon:[[@LINE-5]]:1: note: class is incomplete within its definition [ClassIncompleteWithinDefinition] - // CHECK:STDERR: class FDifferentReturnType { - // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~ - // CHECK:STDERR: fail_impl_bad_assoc_fn.carbon:[[@LINE+7]]:31: note: return type declared here [IncompleteReturnTypeHere] + // CHECK:STDERR: fail_impl_bad_assoc_fn.carbon:[[@LINE+7]]:5: note: type `FDifferentReturnType` does not implement interface `Core.ImplicitAs(bool)` [MissingImplInMemberAccessNote] // CHECK:STDERR: fn F[self: bool](b: bool) -> Self; - // CHECK:STDERR: ^~~~~~~ - // CHECK:STDERR: fail_impl_bad_assoc_fn.carbon:[[@LINE-96]]:15: note: while building thunk to match the signature of this function [ThunkSignature] + // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + // CHECK:STDERR: fail_impl_bad_assoc_fn.carbon:[[@LINE-93]]:15: note: while building thunk to match the signature of this function [ThunkSignature] // CHECK:STDERR: interface J { fn F[self: bool](b: bool) -> bool; } // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: @@ -201,93 +198,37 @@ class FDifferentReturnType { } interface SelfNested { - // CHECK:STDERR: fail_impl_bad_assoc_fn.carbon:[[@LINE+3]]:41: error: function returns incomplete type `array(SelfNestedBadParam, 4)` [IncompleteTypeInFunctionReturnType] + // CHECK:STDERR: fail_impl_bad_assoc_fn.carbon:[[@LINE+6]]:8: error: cannot implicitly convert expression of type `SelfNestedBadParam` to `i32` [ConversionFailure] // CHECK:STDERR: fn F(x: (Self*, {.x: Self, .y: i32})) -> array(Self, 4); - // CHECK:STDERR: ^~~~~~~~~~~~~~~~~ + // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + // CHECK:STDERR: fail_impl_bad_assoc_fn.carbon:[[@LINE+3]]:8: note: type `SelfNestedBadParam` does not implement interface `Core.ImplicitAs(i32)` [MissingImplInMemberAccessNote] + // CHECK:STDERR: fn F(x: (Self*, {.x: Self, .y: i32})) -> array(Self, 4); + // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ fn F(x: (Self*, {.x: Self, .y: i32})) -> array(Self, 4); } -// CHECK:STDERR: fail_impl_bad_assoc_fn.carbon:[[@LINE+3]]:1: note: class is incomplete within its definition [ClassIncompleteWithinDefinition] -// CHECK:STDERR: class SelfNestedBadParam { -// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~ class SelfNestedBadParam { impl as SelfNested { - // CHECK:STDERR: fail_impl_bad_assoc_fn.carbon:[[@LINE+43]]:5: note: while building thunk calling this function [ThunkCallee] - // CHECK:STDERR: fn F(x: (SelfNestedBadParam*, {.x: i32, .y: i32})) -> array(SelfNestedBadParam, 4); - // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - // CHECK:STDERR: - // CHECK:STDERR: fail_impl_bad_assoc_fn.carbon:[[@LINE-12]]:8: error: parameter has incomplete type `(SelfNestedBadParam*, {.x: SelfNestedBadParam, .y: i32})` in function definition [IncompleteTypeInFunctionParam] - // CHECK:STDERR: fn F(x: (Self*, {.x: Self, .y: i32})) -> array(Self, 4); - // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - // CHECK:STDERR: fail_impl_bad_assoc_fn.carbon:[[@LINE-9]]:1: note: class is incomplete within its definition [ClassIncompleteWithinDefinition] - // CHECK:STDERR: class SelfNestedBadParam { - // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~ - // CHECK:STDERR: fail_impl_bad_assoc_fn.carbon:[[@LINE+33]]:5: note: while building thunk calling this function [ThunkCallee] - // CHECK:STDERR: fn F(x: (SelfNestedBadParam*, {.x: i32, .y: i32})) -> array(SelfNestedBadParam, 4); - // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - // CHECK:STDERR: - // CHECK:STDERR: fail_impl_bad_assoc_fn.carbon:[[@LINE+29]]:5: error: function returns incomplete type `array(SelfNestedBadParam, 4)` [IncompleteTypeInFunctionReturnType] - // CHECK:STDERR: fn F(x: (SelfNestedBadParam*, {.x: i32, .y: i32})) -> array(SelfNestedBadParam, 4); - // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - // CHECK:STDERR: fail_impl_bad_assoc_fn.carbon:[[@LINE-19]]:1: note: class is incomplete within its definition [ClassIncompleteWithinDefinition] - // CHECK:STDERR: class SelfNestedBadParam { - // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~ - // CHECK:STDERR: fail_impl_bad_assoc_fn.carbon:[[@LINE+23]]:56: note: return type declared here [IncompleteReturnTypeHere] - // CHECK:STDERR: fn F(x: (SelfNestedBadParam*, {.x: i32, .y: i32})) -> array(SelfNestedBadParam, 4); - // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - // CHECK:STDERR: fail_impl_bad_assoc_fn.carbon:[[@LINE-31]]:3: note: while building thunk to match the signature of this function [ThunkSignature] - // CHECK:STDERR: fn F(x: (Self*, {.x: Self, .y: i32})) -> array(Self, 4); - // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - // CHECK:STDERR: - // CHECK:STDERR: fail_impl_bad_assoc_fn.carbon:[[@LINE-35]]:8: error: cannot implicitly convert expression of type `SelfNestedBadParam` to `i32` [ConversionFailure] - // CHECK:STDERR: fn F(x: (Self*, {.x: Self, .y: i32})) -> array(Self, 4); - // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - // CHECK:STDERR: fail_impl_bad_assoc_fn.carbon:[[@LINE-38]]:8: note: type `SelfNestedBadParam` does not implement interface `Core.ImplicitAs(i32)` [MissingImplInMemberAccessNote] - // CHECK:STDERR: fn F(x: (Self*, {.x: Self, .y: i32})) -> array(Self, 4); - // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - // CHECK:STDERR: fail_impl_bad_assoc_fn.carbon:[[@LINE+10]]:10: note: initializing function parameter [InCallToFunctionParam] + // CHECK:STDERR: fail_impl_bad_assoc_fn.carbon:[[@LINE+7]]:10: note: initializing function parameter [InCallToFunctionParam] // CHECK:STDERR: fn F(x: (SelfNestedBadParam*, {.x: i32, .y: i32})) -> array(SelfNestedBadParam, 4); // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - // CHECK:STDERR: fail_impl_bad_assoc_fn.carbon:[[@LINE-44]]:3: note: while building thunk to match the signature of this function [ThunkSignature] + // CHECK:STDERR: fail_impl_bad_assoc_fn.carbon:[[@LINE-8]]:3: note: while building thunk to match the signature of this function [ThunkSignature] // CHECK:STDERR: fn F(x: (Self*, {.x: Self, .y: i32})) -> array(Self, 4); // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: - // CHECK:STDERR: fail_impl_bad_assoc_fn.carbon:[[@LINE-48]]:41: error: function returns incomplete type `array(SelfNestedBadReturnType, 4)` [IncompleteTypeInFunctionReturnType] - // CHECK:STDERR: fn F(x: (Self*, {.x: Self, .y: i32})) -> array(Self, 4); - // CHECK:STDERR: ^~~~~~~~~~~~~~~~~ fn F(x: (SelfNestedBadParam*, {.x: i32, .y: i32})) -> array(SelfNestedBadParam, 4); } } -// CHECK:STDERR: fail_impl_bad_assoc_fn.carbon:[[@LINE+3]]:1: note: class is incomplete within its definition [ClassIncompleteWithinDefinition] -// CHECK:STDERR: class SelfNestedBadReturnType { -// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ class SelfNestedBadReturnType { impl as SelfNested { - // CHECK:STDERR: fail_impl_bad_assoc_fn.carbon:[[@LINE+27]]:5: note: while building thunk calling this function [ThunkCallee] + // CHECK:STDERR: fail_impl_bad_assoc_fn.carbon:[[@LINE+10]]:5: error: cannot implicitly convert expression of type `array(SelfNestedBadParam, 4)` to `array(SelfNestedBadReturnType, 4)` [ConversionFailure] // CHECK:STDERR: fn F(x: (SelfNestedBadReturnType*, {.x: SelfNestedBadReturnType, .y: i32})) -> array(SelfNestedBadParam, 4); // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - // CHECK:STDERR: - // CHECK:STDERR: fail_impl_bad_assoc_fn.carbon:[[@LINE-64]]:8: error: parameter has incomplete type `(SelfNestedBadReturnType*, {.x: SelfNestedBadReturnType, .y: i32})` in function definition [IncompleteTypeInFunctionParam] - // CHECK:STDERR: fn F(x: (Self*, {.x: Self, .y: i32})) -> array(Self, 4); - // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - // CHECK:STDERR: fail_impl_bad_assoc_fn.carbon:[[@LINE-9]]:1: note: class is incomplete within its definition [ClassIncompleteWithinDefinition] - // CHECK:STDERR: class SelfNestedBadReturnType { - // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - // CHECK:STDERR: fail_impl_bad_assoc_fn.carbon:[[@LINE+17]]:5: note: while building thunk calling this function [ThunkCallee] + // CHECK:STDERR: fail_impl_bad_assoc_fn.carbon:[[@LINE+7]]:5: note: type `array(SelfNestedBadParam, 4)` does not implement interface `Core.ImplicitAs(array(SelfNestedBadReturnType, 4))` [MissingImplInMemberAccessNote] // CHECK:STDERR: fn F(x: (SelfNestedBadReturnType*, {.x: SelfNestedBadReturnType, .y: i32})) -> array(SelfNestedBadParam, 4); // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - // CHECK:STDERR: - // CHECK:STDERR: fail_impl_bad_assoc_fn.carbon:[[@LINE-74]]:8: error: forming value of incomplete type `(SelfNestedBadReturnType*, {.x: SelfNestedBadReturnType, .y: i32})` [IncompleteTypeInValueConversion] - // CHECK:STDERR: fn F(x: (Self*, {.x: Self, .y: i32})) -> array(Self, 4); - // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - // CHECK:STDERR: fail_impl_bad_assoc_fn.carbon:[[@LINE-19]]:1: note: class is incomplete within its definition [ClassIncompleteWithinDefinition] - // CHECK:STDERR: class SelfNestedBadReturnType { - // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - // CHECK:STDERR: fail_impl_bad_assoc_fn.carbon:[[@LINE+7]]:10: note: initializing function parameter [InCallToFunctionParam] - // CHECK:STDERR: fn F(x: (SelfNestedBadReturnType*, {.x: SelfNestedBadReturnType, .y: i32})) -> array(SelfNestedBadParam, 4); - // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - // CHECK:STDERR: fail_impl_bad_assoc_fn.carbon:[[@LINE-83]]:3: note: while building thunk to match the signature of this function [ThunkSignature] + // CHECK:STDERR: fail_impl_bad_assoc_fn.carbon:[[@LINE-24]]:3: note: while building thunk to match the signature of this function [ThunkSignature] // CHECK:STDERR: fn F(x: (Self*, {.x: Self, .y: i32})) -> array(Self, 4); // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: @@ -747,28 +688,28 @@ class SelfNestedBadReturnType { // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: impl @impl.1a7: %Self.ref as %J.ref { -// CHECK:STDOUT: %F.decl.loc145_30.1: %F.type.123d7a.1 = fn_decl @F.12 [concrete = constants.%F.c7d02d.1] { +// CHECK:STDOUT: %F.decl.loc148_30.1: %F.type.123d7a.1 = fn_decl @F.12 [concrete = constants.%F.c7d02d.1] { // CHECK:STDOUT: %self.patt: %pattern_type.831 = binding_pattern self [concrete] // CHECK:STDOUT: %self.param_patt: %pattern_type.831 = value_param_pattern %self.patt, call_param0 [concrete] // CHECK:STDOUT: %b.patt: %pattern_type.831 = binding_pattern b [concrete] // CHECK:STDOUT: %b.param_patt: %pattern_type.831 = value_param_pattern %b.patt, call_param1 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %self.param: bool = value_param call_param0 -// CHECK:STDOUT: %.loc145_16.1: type = splice_block %.loc145_16.3 [concrete = bool] { -// CHECK:STDOUT: %bool.make_type.loc145_16: init type = call constants.%Bool() [concrete = bool] -// CHECK:STDOUT: %.loc145_16.2: type = value_of_initializer %bool.make_type.loc145_16 [concrete = bool] -// CHECK:STDOUT: %.loc145_16.3: type = converted %bool.make_type.loc145_16, %.loc145_16.2 [concrete = bool] +// CHECK:STDOUT: %.loc148_16.1: type = splice_block %.loc148_16.3 [concrete = bool] { +// CHECK:STDOUT: %bool.make_type.loc148_16: init type = call constants.%Bool() [concrete = bool] +// CHECK:STDOUT: %.loc148_16.2: type = value_of_initializer %bool.make_type.loc148_16 [concrete = bool] +// CHECK:STDOUT: %.loc148_16.3: type = converted %bool.make_type.loc148_16, %.loc148_16.2 [concrete = bool] // CHECK:STDOUT: } // CHECK:STDOUT: %self: bool = bind_name self, %self.param // CHECK:STDOUT: %b.param: bool = value_param call_param1 -// CHECK:STDOUT: %.loc145_25.1: type = splice_block %.loc145_25.3 [concrete = bool] { -// CHECK:STDOUT: %bool.make_type.loc145_25: init type = call constants.%Bool() [concrete = bool] -// CHECK:STDOUT: %.loc145_25.2: type = value_of_initializer %bool.make_type.loc145_25 [concrete = bool] -// CHECK:STDOUT: %.loc145_25.3: type = converted %bool.make_type.loc145_25, %.loc145_25.2 [concrete = bool] +// CHECK:STDOUT: %.loc148_25.1: type = splice_block %.loc148_25.3 [concrete = bool] { +// CHECK:STDOUT: %bool.make_type.loc148_25: init type = call constants.%Bool() [concrete = bool] +// CHECK:STDOUT: %.loc148_25.2: type = value_of_initializer %bool.make_type.loc148_25 [concrete = bool] +// CHECK:STDOUT: %.loc148_25.3: type = converted %bool.make_type.loc148_25, %.loc148_25.2 [concrete = bool] // CHECK:STDOUT: } // CHECK:STDOUT: %b: bool = bind_name b, %b.param // CHECK:STDOUT: } -// CHECK:STDOUT: %F.decl.loc145_30.2: %F.type.123d7a.2 = fn_decl @F.13 [concrete = constants.%F.c7d02d.2] { +// CHECK:STDOUT: %F.decl.loc148_30.2: %F.type.123d7a.2 = fn_decl @F.13 [concrete = constants.%F.c7d02d.2] { // CHECK:STDOUT: %self.patt: %pattern_type.831 = binding_pattern self [concrete] // CHECK:STDOUT: %self.param_patt: %pattern_type.831 = value_param_pattern %self.patt, call_param0 [concrete] // CHECK:STDOUT: %b.patt: %pattern_type.831 = binding_pattern b [concrete] @@ -785,12 +726,12 @@ class SelfNestedBadReturnType { // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: !members: -// CHECK:STDOUT: .F = %F.decl.loc145_30.1 +// CHECK:STDOUT: .F = %F.decl.loc148_30.1 // CHECK:STDOUT: witness = @FMissingReturnType.%J.impl_witness // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: impl @impl.f2b: %Self.ref as %J.ref { -// CHECK:STDOUT: %F.decl.loc164_38.1: %F.type.6b537d.1 = fn_decl @F.14 [concrete = constants.%F.04313a.1] { +// CHECK:STDOUT: %F.decl.loc167_38.1: %F.type.6b537d.1 = fn_decl @F.14 [concrete = constants.%F.04313a.1] { // CHECK:STDOUT: %self.patt: %pattern_type.831 = binding_pattern self [concrete] // CHECK:STDOUT: %self.param_patt: %pattern_type.831 = value_param_pattern %self.patt, call_param0 [concrete] // CHECK:STDOUT: %b.patt: %pattern_type.b90 = binding_pattern b [concrete] @@ -798,14 +739,14 @@ class SelfNestedBadReturnType { // CHECK:STDOUT: %return.patt: %pattern_type.831 = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.831 = out_param_pattern %return.patt, call_param2 [concrete] // CHECK:STDOUT: } { -// CHECK:STDOUT: %bool.make_type.loc164_34: init type = call constants.%Bool() [concrete = bool] -// CHECK:STDOUT: %.loc164_34.1: type = value_of_initializer %bool.make_type.loc164_34 [concrete = bool] -// CHECK:STDOUT: %.loc164_34.2: type = converted %bool.make_type.loc164_34, %.loc164_34.1 [concrete = bool] +// CHECK:STDOUT: %bool.make_type.loc167_34: init type = call constants.%Bool() [concrete = bool] +// CHECK:STDOUT: %.loc167_34.1: type = value_of_initializer %bool.make_type.loc167_34 [concrete = bool] +// CHECK:STDOUT: %.loc167_34.2: type = converted %bool.make_type.loc167_34, %.loc167_34.1 [concrete = bool] // CHECK:STDOUT: %self.param: bool = value_param call_param0 -// CHECK:STDOUT: %.loc164_16.1: type = splice_block %.loc164_16.3 [concrete = bool] { -// CHECK:STDOUT: %bool.make_type.loc164_16: init type = call constants.%Bool() [concrete = bool] -// CHECK:STDOUT: %.loc164_16.2: type = value_of_initializer %bool.make_type.loc164_16 [concrete = bool] -// CHECK:STDOUT: %.loc164_16.3: type = converted %bool.make_type.loc164_16, %.loc164_16.2 [concrete = bool] +// CHECK:STDOUT: %.loc167_16.1: type = splice_block %.loc167_16.3 [concrete = bool] { +// CHECK:STDOUT: %bool.make_type.loc167_16: init type = call constants.%Bool() [concrete = bool] +// CHECK:STDOUT: %.loc167_16.2: type = value_of_initializer %bool.make_type.loc167_16 [concrete = bool] +// CHECK:STDOUT: %.loc167_16.3: type = converted %bool.make_type.loc167_16, %.loc167_16.2 [concrete = bool] // CHECK:STDOUT: } // CHECK:STDOUT: %self: bool = bind_name self, %self.param // CHECK:STDOUT: %b.param: %FDifferentParamType = value_param call_param1 @@ -814,7 +755,7 @@ class SelfNestedBadReturnType { // CHECK:STDOUT: %return.param: ref bool = out_param call_param2 // CHECK:STDOUT: %return: ref bool = return_slot %return.param // CHECK:STDOUT: } -// CHECK:STDOUT: %F.decl.loc164_38.2: %F.type.6b537d.2 = fn_decl @F.15 [concrete = constants.%F.04313a.2] { +// CHECK:STDOUT: %F.decl.loc167_38.2: %F.type.6b537d.2 = fn_decl @F.15 [concrete = constants.%F.04313a.2] { // CHECK:STDOUT: %self.patt: %pattern_type.831 = binding_pattern self [concrete] // CHECK:STDOUT: %self.param_patt: %pattern_type.831 = value_param_pattern %self.patt, call_param0 [concrete] // CHECK:STDOUT: %b.patt: %pattern_type.831 = binding_pattern b [concrete] @@ -831,7 +772,7 @@ class SelfNestedBadReturnType { // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: !members: -// CHECK:STDOUT: .F = %F.decl.loc164_38.1 +// CHECK:STDOUT: .F = %F.decl.loc167_38.1 // CHECK:STDOUT: witness = @FDifferentParamType.%J.impl_witness // CHECK:STDOUT: } // CHECK:STDOUT: @@ -882,7 +823,7 @@ class SelfNestedBadReturnType { // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: impl @impl.fcc: %Self.ref as %J.ref { -// CHECK:STDOUT: %F.decl.loc199_38.1: %F.type.d3b58f.1 = fn_decl @F.18 [concrete = constants.%F.be86c9.1] { +// CHECK:STDOUT: %F.decl.loc196_38.1: %F.type.d3b58f.1 = fn_decl @F.18 [concrete = constants.%F.be86c9.1] { // CHECK:STDOUT: %self.patt: %pattern_type.831 = binding_pattern self [concrete] // CHECK:STDOUT: %self.param_patt: %pattern_type.831 = value_param_pattern %self.patt, call_param0 [concrete] // CHECK:STDOUT: %b.patt: %pattern_type.831 = binding_pattern b [concrete] @@ -892,23 +833,23 @@ class SelfNestedBadReturnType { // CHECK:STDOUT: } { // CHECK:STDOUT: %Self.ref: type = name_ref Self, constants.%FDifferentReturnType [concrete = constants.%FDifferentReturnType] // CHECK:STDOUT: %self.param: bool = value_param call_param0 -// CHECK:STDOUT: %.loc199_16.1: type = splice_block %.loc199_16.3 [concrete = bool] { -// CHECK:STDOUT: %bool.make_type.loc199_16: init type = call constants.%Bool() [concrete = bool] -// CHECK:STDOUT: %.loc199_16.2: type = value_of_initializer %bool.make_type.loc199_16 [concrete = bool] -// CHECK:STDOUT: %.loc199_16.3: type = converted %bool.make_type.loc199_16, %.loc199_16.2 [concrete = bool] +// CHECK:STDOUT: %.loc196_16.1: type = splice_block %.loc196_16.3 [concrete = bool] { +// CHECK:STDOUT: %bool.make_type.loc196_16: init type = call constants.%Bool() [concrete = bool] +// CHECK:STDOUT: %.loc196_16.2: type = value_of_initializer %bool.make_type.loc196_16 [concrete = bool] +// CHECK:STDOUT: %.loc196_16.3: type = converted %bool.make_type.loc196_16, %.loc196_16.2 [concrete = bool] // CHECK:STDOUT: } // CHECK:STDOUT: %self: bool = bind_name self, %self.param // CHECK:STDOUT: %b.param: bool = value_param call_param1 -// CHECK:STDOUT: %.loc199_25.1: type = splice_block %.loc199_25.3 [concrete = bool] { -// CHECK:STDOUT: %bool.make_type.loc199_25: init type = call constants.%Bool() [concrete = bool] -// CHECK:STDOUT: %.loc199_25.2: type = value_of_initializer %bool.make_type.loc199_25 [concrete = bool] -// CHECK:STDOUT: %.loc199_25.3: type = converted %bool.make_type.loc199_25, %.loc199_25.2 [concrete = bool] +// CHECK:STDOUT: %.loc196_25.1: type = splice_block %.loc196_25.3 [concrete = bool] { +// CHECK:STDOUT: %bool.make_type.loc196_25: init type = call constants.%Bool() [concrete = bool] +// CHECK:STDOUT: %.loc196_25.2: type = value_of_initializer %bool.make_type.loc196_25 [concrete = bool] +// CHECK:STDOUT: %.loc196_25.3: type = converted %bool.make_type.loc196_25, %.loc196_25.2 [concrete = bool] // CHECK:STDOUT: } // CHECK:STDOUT: %b: bool = bind_name b, %b.param // CHECK:STDOUT: %return.param: ref %FDifferentReturnType = out_param call_param2 // CHECK:STDOUT: %return: ref %FDifferentReturnType = return_slot %return.param // CHECK:STDOUT: } -// CHECK:STDOUT: %F.decl.loc199_38.2: %F.type.d3b58f.2 = fn_decl @F.19 [concrete = constants.%F.be86c9.2] { +// CHECK:STDOUT: %F.decl.loc196_38.2: %F.type.d3b58f.2 = fn_decl @F.19 [concrete = constants.%F.be86c9.2] { // CHECK:STDOUT: %self.patt: %pattern_type.831 = binding_pattern self [concrete] // CHECK:STDOUT: %self.param_patt: %pattern_type.831 = value_param_pattern %self.patt, call_param0 [concrete] // CHECK:STDOUT: %b.patt: %pattern_type.831 = binding_pattern b [concrete] @@ -925,37 +866,37 @@ class SelfNestedBadReturnType { // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: !members: -// CHECK:STDOUT: .F = %F.decl.loc199_38.1 +// CHECK:STDOUT: .F = %F.decl.loc196_38.1 // CHECK:STDOUT: witness = @FDifferentReturnType.%J.impl_witness // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: impl @impl.6a5: %Self.ref as %SelfNested.ref { -// CHECK:STDOUT: %F.decl.loc258_87.1: %F.type.f90165.1 = fn_decl @F.21 [concrete = constants.%F.fa8d04.1] { +// CHECK:STDOUT: %F.decl.loc219_87.1: %F.type.f90165.1 = fn_decl @F.21 [concrete = constants.%F.fa8d04.1] { // CHECK:STDOUT: %x.patt: %pattern_type.714 = binding_pattern x [concrete] // CHECK:STDOUT: %x.param_patt: %pattern_type.714 = value_param_pattern %x.patt, call_param0 [concrete] // CHECK:STDOUT: %return.patt: %pattern_type.1f1 = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.1f1 = out_param_pattern %return.patt, call_param1 [concrete] // CHECK:STDOUT: } { -// CHECK:STDOUT: %SelfNestedBadParam.ref.loc258_65: type = name_ref SelfNestedBadParam, file.%SelfNestedBadParam.decl [concrete = constants.%SelfNestedBadParam] +// CHECK:STDOUT: %SelfNestedBadParam.ref.loc219_65: type = name_ref SelfNestedBadParam, file.%SelfNestedBadParam.decl [concrete = constants.%SelfNestedBadParam] // CHECK:STDOUT: %int_4: Core.IntLiteral = int_value 4 [concrete = constants.%int_4] -// CHECK:STDOUT: %array_type: type = array_type %int_4, %SelfNestedBadParam.ref.loc258_65 [concrete = constants.%array_type.a41] +// CHECK:STDOUT: %array_type: type = array_type %int_4, %SelfNestedBadParam.ref.loc219_65 [concrete = constants.%array_type.a41] // CHECK:STDOUT: %x.param: %tuple.type.a7d = value_param call_param0 -// CHECK:STDOUT: %.loc258_53.1: type = splice_block %.loc258_53.3 [concrete = constants.%tuple.type.a7d] { -// CHECK:STDOUT: %SelfNestedBadParam.ref.loc258_14: type = name_ref SelfNestedBadParam, file.%SelfNestedBadParam.decl [concrete = constants.%SelfNestedBadParam] -// CHECK:STDOUT: %ptr: type = ptr_type %SelfNestedBadParam.ref.loc258_14 [concrete = constants.%ptr.4cd] -// CHECK:STDOUT: %int_32.loc258_40: Core.IntLiteral = int_value 32 [concrete = constants.%int_32] -// CHECK:STDOUT: %i32.loc258_40: type = class_type @Int, @Int(constants.%int_32) [concrete = constants.%i32] -// CHECK:STDOUT: %int_32.loc258_49: Core.IntLiteral = int_value 32 [concrete = constants.%int_32] -// CHECK:STDOUT: %i32.loc258_49: type = class_type @Int, @Int(constants.%int_32) [concrete = constants.%i32] +// CHECK:STDOUT: %.loc219_53.1: type = splice_block %.loc219_53.3 [concrete = constants.%tuple.type.a7d] { +// CHECK:STDOUT: %SelfNestedBadParam.ref.loc219_14: type = name_ref SelfNestedBadParam, file.%SelfNestedBadParam.decl [concrete = constants.%SelfNestedBadParam] +// CHECK:STDOUT: %ptr: type = ptr_type %SelfNestedBadParam.ref.loc219_14 [concrete = constants.%ptr.4cd] +// CHECK:STDOUT: %int_32.loc219_40: Core.IntLiteral = int_value 32 [concrete = constants.%int_32] +// CHECK:STDOUT: %i32.loc219_40: type = class_type @Int, @Int(constants.%int_32) [concrete = constants.%i32] +// CHECK:STDOUT: %int_32.loc219_49: Core.IntLiteral = int_value 32 [concrete = constants.%int_32] +// CHECK:STDOUT: %i32.loc219_49: type = class_type @Int, @Int(constants.%int_32) [concrete = constants.%i32] // CHECK:STDOUT: %struct_type.x.y: type = struct_type {.x: %i32, .y: %i32} [concrete = constants.%struct_type.x.y.871] -// CHECK:STDOUT: %.loc258_53.2: %tuple.type.24b = tuple_literal (%ptr, %struct_type.x.y) -// CHECK:STDOUT: %.loc258_53.3: type = converted %.loc258_53.2, constants.%tuple.type.a7d [concrete = constants.%tuple.type.a7d] +// CHECK:STDOUT: %.loc219_53.2: %tuple.type.24b = tuple_literal (%ptr, %struct_type.x.y) +// CHECK:STDOUT: %.loc219_53.3: type = converted %.loc219_53.2, constants.%tuple.type.a7d [concrete = constants.%tuple.type.a7d] // CHECK:STDOUT: } // CHECK:STDOUT: %x: %tuple.type.a7d = bind_name x, %x.param // CHECK:STDOUT: %return.param: ref %array_type.a41 = out_param call_param1 // CHECK:STDOUT: %return: ref %array_type.a41 = return_slot %return.param // CHECK:STDOUT: } -// CHECK:STDOUT: %F.decl.loc258_87.2: %F.type.f90165.2 = fn_decl @F.22 [concrete = constants.%F.fa8d04.2] { +// CHECK:STDOUT: %F.decl.loc219_87.2: %F.type.f90165.2 = fn_decl @F.22 [concrete = constants.%F.fa8d04.2] { // CHECK:STDOUT: %x.patt: %pattern_type.a5c = binding_pattern x [concrete] // CHECK:STDOUT: %x.param_patt: %pattern_type.a5c = value_param_pattern %x.patt, call_param0 [concrete] // CHECK:STDOUT: %return.patt: %pattern_type.1f1 = return_slot_pattern [concrete] @@ -969,12 +910,12 @@ class SelfNestedBadReturnType { // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .SelfNestedBadParam = -// CHECK:STDOUT: .F = %F.decl.loc258_87.1 +// CHECK:STDOUT: .F = %F.decl.loc219_87.1 // CHECK:STDOUT: witness = @SelfNestedBadParam.%SelfNested.impl_witness // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: impl @impl.bfc: %Self.ref as %SelfNested.ref { -// CHECK:STDOUT: %F.decl.loc294_112.1: %F.type.0e7d1d.1 = fn_decl @F.23 [concrete = constants.%F.0bc78a.1] { +// CHECK:STDOUT: %F.decl.loc235_112.1: %F.type.0e7d1d.1 = fn_decl @F.23 [concrete = constants.%F.0bc78a.1] { // CHECK:STDOUT: %x.patt: %pattern_type.23f = binding_pattern x [concrete] // CHECK:STDOUT: %x.param_patt: %pattern_type.23f = value_param_pattern %x.patt, call_param0 [concrete] // CHECK:STDOUT: %return.patt: %pattern_type.1f1 = return_slot_pattern [concrete] @@ -984,21 +925,21 @@ class SelfNestedBadReturnType { // CHECK:STDOUT: %int_4: Core.IntLiteral = int_value 4 [concrete = constants.%int_4] // CHECK:STDOUT: %array_type: type = array_type %int_4, %SelfNestedBadParam.ref [concrete = constants.%array_type.a41] // CHECK:STDOUT: %x.param: %tuple.type.eb9 = value_param call_param0 -// CHECK:STDOUT: %.loc294_78.1: type = splice_block %.loc294_78.3 [concrete = constants.%tuple.type.eb9] { -// CHECK:STDOUT: %SelfNestedBadReturnType.ref.loc294_14: type = name_ref SelfNestedBadReturnType, file.%SelfNestedBadReturnType.decl [concrete = constants.%SelfNestedBadReturnType] -// CHECK:STDOUT: %ptr: type = ptr_type %SelfNestedBadReturnType.ref.loc294_14 [concrete = constants.%ptr.612] -// CHECK:STDOUT: %SelfNestedBadReturnType.ref.loc294_45: type = name_ref SelfNestedBadReturnType, file.%SelfNestedBadReturnType.decl [concrete = constants.%SelfNestedBadReturnType] +// CHECK:STDOUT: %.loc235_78.1: type = splice_block %.loc235_78.3 [concrete = constants.%tuple.type.eb9] { +// CHECK:STDOUT: %SelfNestedBadReturnType.ref.loc235_14: type = name_ref SelfNestedBadReturnType, file.%SelfNestedBadReturnType.decl [concrete = constants.%SelfNestedBadReturnType] +// CHECK:STDOUT: %ptr: type = ptr_type %SelfNestedBadReturnType.ref.loc235_14 [concrete = constants.%ptr.612] +// CHECK:STDOUT: %SelfNestedBadReturnType.ref.loc235_45: type = name_ref SelfNestedBadReturnType, file.%SelfNestedBadReturnType.decl [concrete = constants.%SelfNestedBadReturnType] // 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: %struct_type.x.y: type = struct_type {.x: %SelfNestedBadReturnType, .y: %i32} [concrete = constants.%struct_type.x.y.ac5] -// CHECK:STDOUT: %.loc294_78.2: %tuple.type.24b = tuple_literal (%ptr, %struct_type.x.y) -// CHECK:STDOUT: %.loc294_78.3: type = converted %.loc294_78.2, constants.%tuple.type.eb9 [concrete = constants.%tuple.type.eb9] +// CHECK:STDOUT: %.loc235_78.2: %tuple.type.24b = tuple_literal (%ptr, %struct_type.x.y) +// CHECK:STDOUT: %.loc235_78.3: type = converted %.loc235_78.2, constants.%tuple.type.eb9 [concrete = constants.%tuple.type.eb9] // CHECK:STDOUT: } // CHECK:STDOUT: %x: %tuple.type.eb9 = bind_name x, %x.param // CHECK:STDOUT: %return.param: ref %array_type.a41 = out_param call_param1 // CHECK:STDOUT: %return: ref %array_type.a41 = return_slot %return.param // CHECK:STDOUT: } -// CHECK:STDOUT: %F.decl.loc294_112.2: %F.type.0e7d1d.2 = fn_decl @F.24 [concrete = constants.%F.0bc78a.2] { +// CHECK:STDOUT: %F.decl.loc235_112.2: %F.type.0e7d1d.2 = fn_decl @F.24 [concrete = constants.%F.0bc78a.2] { // CHECK:STDOUT: %x.patt: %pattern_type.23f = binding_pattern x [concrete] // CHECK:STDOUT: %x.param_patt: %pattern_type.23f = value_param_pattern %x.patt, call_param0 [concrete] // CHECK:STDOUT: %return.patt: %pattern_type.f56 = return_slot_pattern [concrete] @@ -1013,7 +954,7 @@ class SelfNestedBadReturnType { // CHECK:STDOUT: !members: // CHECK:STDOUT: .SelfNestedBadReturnType = // CHECK:STDOUT: .SelfNestedBadParam = -// CHECK:STDOUT: .F = %F.decl.loc294_112.1 +// CHECK:STDOUT: .F = %F.decl.loc235_112.1 // CHECK:STDOUT: witness = @SelfNestedBadReturnType.%SelfNested.impl_witness // CHECK:STDOUT: } // CHECK:STDOUT: @@ -1153,7 +1094,7 @@ class SelfNestedBadReturnType { // CHECK:STDOUT: %Self.ref: type = name_ref Self, constants.%FMissingReturnType [concrete = constants.%FMissingReturnType] // CHECK:STDOUT: %J.ref: type = name_ref J, file.%J.decl [concrete = constants.%J.type] // CHECK:STDOUT: } -// CHECK:STDOUT: %J.impl_witness_table = impl_witness_table (@impl.1a7.%F.decl.loc145_30.2), @impl.1a7 [concrete] +// CHECK:STDOUT: %J.impl_witness_table = impl_witness_table (@impl.1a7.%F.decl.loc148_30.2), @impl.1a7 [concrete] // CHECK:STDOUT: %J.impl_witness: = impl_witness %J.impl_witness_table [concrete = constants.%J.impl_witness.e75] // 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] @@ -1169,7 +1110,7 @@ class SelfNestedBadReturnType { // CHECK:STDOUT: %Self.ref: type = name_ref Self, constants.%FDifferentParamType [concrete = constants.%FDifferentParamType] // CHECK:STDOUT: %J.ref: type = name_ref J, file.%J.decl [concrete = constants.%J.type] // CHECK:STDOUT: } -// CHECK:STDOUT: %J.impl_witness_table = impl_witness_table (@impl.f2b.%F.decl.loc164_38.2), @impl.f2b [concrete] +// CHECK:STDOUT: %J.impl_witness_table = impl_witness_table (@impl.f2b.%F.decl.loc167_38.2), @impl.f2b [concrete] // CHECK:STDOUT: %J.impl_witness: = impl_witness %J.impl_witness_table [concrete = constants.%J.impl_witness.cbe] // 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] @@ -1201,7 +1142,7 @@ class SelfNestedBadReturnType { // CHECK:STDOUT: %Self.ref: type = name_ref Self, constants.%FDifferentReturnType [concrete = constants.%FDifferentReturnType] // CHECK:STDOUT: %J.ref: type = name_ref J, file.%J.decl [concrete = constants.%J.type] // CHECK:STDOUT: } -// CHECK:STDOUT: %J.impl_witness_table = impl_witness_table (@impl.fcc.%F.decl.loc199_38.2), @impl.fcc [concrete] +// CHECK:STDOUT: %J.impl_witness_table = impl_witness_table (@impl.fcc.%F.decl.loc196_38.2), @impl.fcc [concrete] // CHECK:STDOUT: %J.impl_witness: = impl_witness %J.impl_witness_table [concrete = constants.%J.impl_witness.bc1] // 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] @@ -1217,7 +1158,7 @@ class SelfNestedBadReturnType { // CHECK:STDOUT: %Self.ref: type = name_ref Self, constants.%SelfNestedBadParam [concrete = constants.%SelfNestedBadParam] // CHECK:STDOUT: %SelfNested.ref: type = name_ref SelfNested, file.%SelfNested.decl [concrete = constants.%SelfNested.type] // CHECK:STDOUT: } -// CHECK:STDOUT: %SelfNested.impl_witness_table = impl_witness_table (@impl.6a5.%F.decl.loc258_87.2), @impl.6a5 [concrete] +// CHECK:STDOUT: %SelfNested.impl_witness_table = impl_witness_table (@impl.6a5.%F.decl.loc219_87.2), @impl.6a5 [concrete] // CHECK:STDOUT: %SelfNested.impl_witness: = impl_witness %SelfNested.impl_witness_table [concrete = constants.%SelfNested.impl_witness.e31] // 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] @@ -1234,7 +1175,7 @@ class SelfNestedBadReturnType { // CHECK:STDOUT: %Self.ref: type = name_ref Self, constants.%SelfNestedBadReturnType [concrete = constants.%SelfNestedBadReturnType] // CHECK:STDOUT: %SelfNested.ref: type = name_ref SelfNested, file.%SelfNested.decl [concrete = constants.%SelfNested.type] // CHECK:STDOUT: } -// CHECK:STDOUT: %SelfNested.impl_witness_table = impl_witness_table (@impl.bfc.%F.decl.loc294_112.2), @impl.bfc [concrete] +// CHECK:STDOUT: %SelfNested.impl_witness_table = impl_witness_table (@impl.bfc.%F.decl.loc235_112.2), @impl.bfc [concrete] // CHECK:STDOUT: %SelfNested.impl_witness: = impl_witness %SelfNested.impl_witness_table [concrete = constants.%SelfNested.impl_witness.d5e] // 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] @@ -1301,10 +1242,10 @@ class SelfNestedBadReturnType { // CHECK:STDOUT: fn @F.13(%self.param: bool, %b.param: bool) -> bool { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %self.ref: bool = name_ref self, %self.param -// CHECK:STDOUT: %F.bound: = bound_method %self.ref, @impl.1a7.%F.decl.loc145_30.1 +// CHECK:STDOUT: %F.bound: = bound_method %self.ref, @impl.1a7.%F.decl.loc148_30.1 // CHECK:STDOUT: %b.ref: bool = name_ref b, %b.param // CHECK:STDOUT: %F.call: init %empty_tuple.type = call %F.bound(%self.ref, %b.ref) -// CHECK:STDOUT: %.loc145: bool = converted %F.call, [concrete = ] +// CHECK:STDOUT: %.loc148: bool = converted %F.call, [concrete = ] // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: @@ -1313,12 +1254,13 @@ class SelfNestedBadReturnType { // CHECK:STDOUT: fn @F.15(%self.param: bool, %b.param: bool) -> bool { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %self.ref: bool = name_ref self, %self.param -// CHECK:STDOUT: %F.bound: = bound_method %self.ref, @impl.f2b.%F.decl.loc164_38.1 +// CHECK:STDOUT: %F.bound: = bound_method %self.ref, @impl.f2b.%F.decl.loc167_38.1 // CHECK:STDOUT: %b.ref: bool = name_ref b, %b.param +// CHECK:STDOUT: %.loc99: %FDifferentParamType = converted %b.ref, [concrete = ] // CHECK:STDOUT: %F.call: init bool = call %F.bound(%self.ref, ) -// CHECK:STDOUT: %.loc164_38.1: bool = value_of_initializer %F.call -// CHECK:STDOUT: %.loc164_38.2: bool = converted %F.call, %.loc164_38.1 -// CHECK:STDOUT: return %.loc164_38.2 +// CHECK:STDOUT: %.loc167_38.1: bool = value_of_initializer %F.call +// CHECK:STDOUT: %.loc167_38.2: bool = converted %F.call, %.loc167_38.1 +// CHECK:STDOUT: return %.loc167_38.2 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @F.16(%self.param: %FDifferentImplicitParamType, %b.param: bool) -> bool; @@ -1328,20 +1270,23 @@ class SelfNestedBadReturnType { // CHECK:STDOUT: %self.ref: bool = name_ref self, %self.param // CHECK:STDOUT: %F.bound: = bound_method %self.ref, @impl.db4.%F.decl.loc180_38.1 // CHECK:STDOUT: %b.ref: bool = name_ref b, %b.param +// CHECK:STDOUT: %.loc99: %FDifferentImplicitParamType = converted %self.ref, [concrete = ] // CHECK:STDOUT: %F.call: init bool = call %F.bound(, %b.ref) // CHECK:STDOUT: %.loc180_38.1: bool = value_of_initializer %F.call // CHECK:STDOUT: %.loc180_38.2: bool = converted %F.call, %.loc180_38.1 // CHECK:STDOUT: return %.loc180_38.2 // CHECK:STDOUT: } // CHECK:STDOUT: -// CHECK:STDOUT: fn @F.18(%self.param: bool, %b.param: bool) -> %FDifferentReturnType; +// CHECK:STDOUT: fn @F.18(%self.param: bool, %b.param: bool) -> %return.param: %FDifferentReturnType; // CHECK:STDOUT: // CHECK:STDOUT: fn @F.19(%self.param: bool, %b.param: bool) -> bool { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %self.ref: bool = name_ref self, %self.param -// CHECK:STDOUT: %F.bound: = bound_method %self.ref, @impl.fcc.%F.decl.loc199_38.1 +// CHECK:STDOUT: %F.bound: = bound_method %self.ref, @impl.fcc.%F.decl.loc196_38.1 // CHECK:STDOUT: %b.ref: bool = name_ref b, %b.param -// CHECK:STDOUT: %F.call: init = call %F.bound(%self.ref, %b.ref) +// CHECK:STDOUT: %.loc196_38.1: ref %FDifferentReturnType = temporary_storage +// CHECK:STDOUT: %F.call: init %FDifferentReturnType = call %F.bound(%self.ref, %b.ref) to %.loc196_38.1 +// CHECK:STDOUT: %.loc196_38.2: bool = converted %F.call, [concrete = ] // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: @@ -1363,22 +1308,24 @@ class SelfNestedBadReturnType { // CHECK:STDOUT: fn @F.22(%x.param: %tuple.type.9c9) -> %return.param: %array_type.a41 { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %x.ref: %tuple.type.9c9 = name_ref x, %x.param +// CHECK:STDOUT: %.loc207_41: ref %array_type.a41 = splice_block %return {} // CHECK:STDOUT: %tuple.elem0: %ptr.4cd = tuple_access %x.ref, element0 // CHECK:STDOUT: %tuple.elem1: %struct_type.x.y.a89 = tuple_access %x.ref, element1 // CHECK:STDOUT: %.loc207_9.1: %SelfNestedBadParam = struct_access %tuple.elem1, element0 // CHECK:STDOUT: %.loc207_9.2: %i32 = converted %.loc207_9.1, [concrete = ] -// CHECK:STDOUT: %F.call: init = call @impl.6a5.%F.decl.loc258_87.1() -// CHECK:STDOUT: return +// CHECK:STDOUT: %F.call: init %array_type.a41 = call @impl.6a5.%F.decl.loc219_87.1() to %.loc207_41 +// CHECK:STDOUT: return %F.call to %return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @F.23(%x.param: %tuple.type.eb9) -> %return.param: %array_type.a41; // CHECK:STDOUT: -// CHECK:STDOUT: fn @F.24(%x.param: %tuple.type.eb9) -> %array_type.126 { +// CHECK:STDOUT: fn @F.24(%x.param: %tuple.type.eb9) -> %return.param: %array_type.126 { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %x.ref: %tuple.type.eb9 = name_ref x, %x.param -// CHECK:STDOUT: %.loc294: ref %array_type.a41 = temporary_storage -// CHECK:STDOUT: %F.call: init %array_type.a41 = call @impl.bfc.%F.decl.loc294_112.1() to %.loc294 -// CHECK:STDOUT: return +// CHECK:STDOUT: %.loc235_112.1: ref %array_type.a41 = temporary_storage +// CHECK:STDOUT: %F.call: init %array_type.a41 = call @impl.bfc.%F.decl.loc235_112.1(%x.ref) to %.loc235_112.1 +// CHECK:STDOUT: %.loc235_112.2: %array_type.126 = converted %F.call, [concrete = ] +// CHECK:STDOUT: return to %return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @F.1(constants.%Self.826) {} diff --git a/toolchain/check/testdata/impl/min_prelude/impl_thunk.carbon b/toolchain/check/testdata/impl/min_prelude/impl_thunk.carbon new file mode 100644 index 000000000000..869f6920cbff --- /dev/null +++ b/toolchain/check/testdata/impl/min_prelude/impl_thunk.carbon @@ -0,0 +1,42 @@ +// 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/min_prelude/convert.carbon +// EXTRA-ARGS: --no-dump-sem-ir --custom-core +// +// AUTOUPDATE +// TIP: To test this file alone, run: +// TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/impl/min_prelude/impl_thunk.carbon +// TIP: To dump output, run: +// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/impl/min_prelude/impl_thunk.carbon + +// --- convert_in_class.carbon + +library "[[@TEST_NAME]]"; + +interface X(T:! type, U:! type) { + fn F(t: T) -> U; +} + +class ConvertsToA {} +class ConvertsToB {} + +// Check that we don't try to define a thunk for `A.B.(as X).F` until we reach +// the end of `A`. If we tried earlier, we wouldn't find a conversion from +// `ConvertsToA` to `A` or from `ConvertsToB` to `B`. +class A { + class B { + impl as X(ConvertsToA, B) { + fn F(a: A) -> ConvertsToB; + } + + impl ConvertsToB as Core.ImplicitAs(B) { + fn Convert[self: Self]() -> B { return {}; } + } + } + + impl ConvertsToA as Core.ImplicitAs(A) { + fn Convert[self: Self]() -> A { return {}; } + } +} diff --git a/toolchain/check/testdata/impl/no_prelude/impl_thunk.carbon b/toolchain/check/testdata/impl/no_prelude/impl_thunk.carbon index 73eeb64158da..ceee265a0cdb 100644 --- a/toolchain/check/testdata/impl/no_prelude/impl_thunk.carbon +++ b/toolchain/check/testdata/impl/no_prelude/impl_thunk.carbon @@ -203,6 +203,42 @@ impl C as I { fn NoReturn() -> C; } +// --- fail_param_type_incomplete.carbon + +library "[[@TEST_NAME]]"; + +interface I(T:! type) { + // CHECK:STDERR: fail_param_type_incomplete.carbon:[[@LINE+3]]:8: error: parameter has incomplete type `B` in function definition [IncompleteTypeInFunctionParam] + // CHECK:STDERR: fn F(a: T); + // CHECK:STDERR: ^~~~ + fn F(a: T); +} + +// CHECK:STDERR: fail_param_type_incomplete.carbon:[[@LINE+3]]:1: note: class was forward declared here [ClassForwardDeclaredHere] +// CHECK:STDERR: class B; +// CHECK:STDERR: ^~~~~~~~ +class B; + +class C { + impl as I(B) { + // CHECK:STDERR: fail_param_type_incomplete.carbon:[[@LINE+14]]:5: note: while building thunk calling this function [ThunkCallee] + // CHECK:STDERR: fn F(c: C); + // CHECK:STDERR: ^~~~~~~~~~~ + // CHECK:STDERR: + // CHECK:STDERR: fail_param_type_incomplete.carbon:[[@LINE-14]]:8: error: `Core.ImplicitAs` implicitly referenced here, but package `Core` not found [CoreNotFound] + // CHECK:STDERR: fn F(a: T); + // CHECK:STDERR: ^~~~ + // CHECK:STDERR: fail_param_type_incomplete.carbon:[[@LINE+7]]:10: note: initializing function parameter [InCallToFunctionParam] + // CHECK:STDERR: fn F(c: C); + // CHECK:STDERR: ^~~~ + // CHECK:STDERR: fail_param_type_incomplete.carbon:[[@LINE-20]]:3: note: while building thunk to match the signature of this function [ThunkSignature] + // CHECK:STDERR: fn F(a: T); + // CHECK:STDERR: ^~~~~~~~~~~ + // CHECK:STDERR: + fn F(c: C); + } +} + // CHECK:STDOUT: --- no_thunk_param_name_differs.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { @@ -1546,9 +1582,9 @@ impl C as I { // CHECK:STDOUT: %I.facet: %I.type = facet_value %empty_tuple.type, (%I.impl_witness) [concrete] // CHECK:STDOUT: %HasReturn.type.c73908.2: type = fn_type @HasReturn.3 [concrete] // CHECK:STDOUT: %HasReturn.424378.2: %HasReturn.type.c73908.2 = struct_value () [concrete] -// CHECK:STDOUT: %empty_tuple: %empty_tuple.type = tuple_value () [concrete] // CHECK:STDOUT: %EmptyTupleReturn.type.9ea74f.2: type = fn_type @EmptyTupleReturn.3 [concrete] // CHECK:STDOUT: %EmptyTupleReturn.282179.2: %EmptyTupleReturn.type.9ea74f.2 = struct_value () [concrete] +// CHECK:STDOUT: %empty_tuple: %empty_tuple.type = tuple_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { @@ -1800,3 +1836,172 @@ impl C as I { // CHECK:STDOUT: // CHECK:STDOUT: specific @NoReturn.1(constants.%I.facet.be9) {} // CHECK:STDOUT: +// CHECK:STDOUT: --- fail_param_type_incomplete.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %T: type = bind_symbolic_name T, 0 [symbolic] +// CHECK:STDOUT: %pattern_type.98f: type = pattern_type type [concrete] +// CHECK:STDOUT: %I.type.dac: type = generic_interface_type @I [concrete] +// CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] +// CHECK:STDOUT: %I.generic: %I.type.dac = struct_value () [concrete] +// CHECK:STDOUT: %I.type.325: type = facet_type <@I, @I(%T)> [symbolic] +// CHECK:STDOUT: %Self.209: %I.type.325 = bind_symbolic_name Self, 1 [symbolic] +// CHECK:STDOUT: %pattern_type.7dc: type = pattern_type %T [symbolic] +// CHECK:STDOUT: %F.type.2ae: type = fn_type @F.1, @I(%T) [symbolic] +// CHECK:STDOUT: %F.bb2: %F.type.2ae = struct_value () [symbolic] +// CHECK:STDOUT: %I.assoc_type.1e5: type = assoc_entity_type @I, @I(%T) [symbolic] +// CHECK:STDOUT: %assoc0.8f0: %I.assoc_type.1e5 = assoc_entity element0, @I.%F.decl [symbolic] +// CHECK:STDOUT: %B: type = class_type @B [concrete] +// CHECK:STDOUT: %C: type = class_type @C [concrete] +// CHECK:STDOUT: %I.type.a79: type = facet_type <@I, @I(%B)> [concrete] +// CHECK:STDOUT: %Self.fe1: %I.type.a79 = bind_symbolic_name Self, 1 [symbolic] +// CHECK:STDOUT: %F.type.7f2: type = fn_type @F.1, @I(%B) [concrete] +// CHECK:STDOUT: %F.e0a: %F.type.7f2 = struct_value () [concrete] +// CHECK:STDOUT: %I.assoc_type.5ff: type = assoc_entity_type @I, @I(%B) [concrete] +// CHECK:STDOUT: %assoc0.470: %I.assoc_type.5ff = assoc_entity element0, @I.%F.decl [concrete] +// CHECK:STDOUT: %I.impl_witness: = impl_witness @C.%I.impl_witness_table [concrete] +// CHECK:STDOUT: %pattern_type.c48: type = pattern_type %C [concrete] +// CHECK:STDOUT: %F.type.17e183.1: type = fn_type @F.2 [concrete] +// CHECK:STDOUT: %F.4f1533.1: %F.type.17e183.1 = struct_value () [concrete] +// CHECK:STDOUT: %I.facet: %I.type.a79 = facet_value %C, (%I.impl_witness) [concrete] +// CHECK:STDOUT: %pattern_type.049: type = pattern_type %B [concrete] +// CHECK:STDOUT: %F.type.17e183.2: type = fn_type @F.3 [concrete] +// CHECK:STDOUT: %F.4f1533.2: %F.type.17e183.2 = struct_value () [concrete] +// CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] +// CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [concrete] { +// CHECK:STDOUT: .I = %I.decl +// CHECK:STDOUT: .B = %B.decl +// CHECK:STDOUT: .C = %C.decl +// CHECK:STDOUT: } +// CHECK:STDOUT: %I.decl: %I.type.dac = interface_decl @I [concrete = constants.%I.generic] { +// CHECK:STDOUT: %T.patt: %pattern_type.98f = symbolic_binding_pattern T, 0 [concrete] +// CHECK:STDOUT: } { +// CHECK:STDOUT: %T.loc4_13.1: type = bind_symbolic_name T, 0 [symbolic = %T.loc4_13.2 (constants.%T)] +// CHECK:STDOUT: } +// CHECK:STDOUT: %B.decl: type = class_decl @B [concrete = constants.%B] {} {} +// CHECK:STDOUT: %C.decl: type = class_decl @C [concrete = constants.%C] {} {} +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: generic interface @I(%T.loc4_13.1: type) { +// CHECK:STDOUT: %T.loc4_13.2: type = bind_symbolic_name T, 0 [symbolic = %T.loc4_13.2 (constants.%T)] +// CHECK:STDOUT: +// CHECK:STDOUT: !definition: +// CHECK:STDOUT: %I.type: type = facet_type <@I, @I(%T.loc4_13.2)> [symbolic = %I.type (constants.%I.type.325)] +// CHECK:STDOUT: %Self.2: @I.%I.type (%I.type.325) = bind_symbolic_name Self, 1 [symbolic = %Self.2 (constants.%Self.209)] +// CHECK:STDOUT: %F.type: type = fn_type @F.1, @I(%T.loc4_13.2) [symbolic = %F.type (constants.%F.type.2ae)] +// CHECK:STDOUT: %F: @I.%F.type (%F.type.2ae) = struct_value () [symbolic = %F (constants.%F.bb2)] +// CHECK:STDOUT: %I.assoc_type: type = assoc_entity_type @I, @I(%T.loc4_13.2) [symbolic = %I.assoc_type (constants.%I.assoc_type.1e5)] +// CHECK:STDOUT: %assoc0.loc8_13.2: @I.%I.assoc_type (%I.assoc_type.1e5) = assoc_entity element0, %F.decl [symbolic = %assoc0.loc8_13.2 (constants.%assoc0.8f0)] +// CHECK:STDOUT: +// CHECK:STDOUT: interface { +// CHECK:STDOUT: %Self.1: @I.%I.type (%I.type.325) = bind_symbolic_name Self, 1 [symbolic = %Self.2 (constants.%Self.209)] +// CHECK:STDOUT: %F.decl: @I.%F.type (%F.type.2ae) = fn_decl @F.1 [symbolic = @I.%F (constants.%F.bb2)] { +// CHECK:STDOUT: %a.patt: @F.1.%pattern_type (%pattern_type.7dc) = binding_pattern a [concrete] +// CHECK:STDOUT: %a.param_patt: @F.1.%pattern_type (%pattern_type.7dc) = value_param_pattern %a.patt, call_param0 [concrete] +// CHECK:STDOUT: } { +// CHECK:STDOUT: %a.param: @F.1.%T (%T) = value_param call_param0 +// CHECK:STDOUT: %T.ref: type = name_ref T, @I.%T.loc4_13.1 [symbolic = %T (constants.%T)] +// CHECK:STDOUT: %a: @F.1.%T (%T) = bind_name a, %a.param +// CHECK:STDOUT: } +// CHECK:STDOUT: %assoc0.loc8_13.1: @I.%I.assoc_type (%I.assoc_type.1e5) = assoc_entity element0, %F.decl [symbolic = %assoc0.loc8_13.2 (constants.%assoc0.8f0)] +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .Self = %Self.1 +// CHECK:STDOUT: .T = +// CHECK:STDOUT: .F = %assoc0.loc8_13.1 +// CHECK:STDOUT: witness = (%F.decl) +// CHECK:STDOUT: } +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: impl @impl: %Self.ref as %I.type { +// CHECK:STDOUT: %F.decl.loc32_15.1: %F.type.17e183.1 = fn_decl @F.2 [concrete = constants.%F.4f1533.1] { +// CHECK:STDOUT: %c.patt: %pattern_type.c48 = binding_pattern c [concrete] +// CHECK:STDOUT: %c.param_patt: %pattern_type.c48 = value_param_pattern %c.patt, call_param0 [concrete] +// CHECK:STDOUT: } { +// CHECK:STDOUT: %c.param: %C = value_param call_param0 +// CHECK:STDOUT: %C.ref: type = name_ref C, file.%C.decl [concrete = constants.%C] +// CHECK:STDOUT: %c: %C = bind_name c, %c.param +// CHECK:STDOUT: } +// CHECK:STDOUT: %F.decl.loc32_15.2: %F.type.17e183.2 = fn_decl @F.3 [concrete = constants.%F.4f1533.2] { +// CHECK:STDOUT: %a.patt: %pattern_type.049 = binding_pattern a [concrete] +// CHECK:STDOUT: %a.param_patt: %pattern_type.049 = value_param_pattern %a.patt, call_param0 [concrete] +// CHECK:STDOUT: } { +// CHECK:STDOUT: %a.param: %B = value_param call_param0 +// CHECK:STDOUT: %a: %B = bind_name a, %a.param +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .C = +// CHECK:STDOUT: .F = %F.decl.loc32_15.1 +// CHECK:STDOUT: witness = @C.%I.impl_witness +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: class @B; +// CHECK:STDOUT: +// CHECK:STDOUT: class @C { +// CHECK:STDOUT: impl_decl @impl [concrete] {} { +// CHECK:STDOUT: %Self.ref: type = name_ref Self, constants.%C [concrete = constants.%C] +// CHECK:STDOUT: %I.ref: %I.type.dac = name_ref I, file.%I.decl [concrete = constants.%I.generic] +// CHECK:STDOUT: %B.ref: type = name_ref B, file.%B.decl [concrete = constants.%B] +// CHECK:STDOUT: %I.type: type = facet_type <@I, @I(constants.%B)> [concrete = constants.%I.type.a79] +// CHECK:STDOUT: } +// CHECK:STDOUT: %I.impl_witness_table = impl_witness_table (@impl.%F.decl.loc32_15.2), @impl [concrete] +// CHECK:STDOUT: %I.impl_witness: = impl_witness %I.impl_witness_table [concrete = constants.%I.impl_witness] +// 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: .I = +// CHECK:STDOUT: .B = +// CHECK:STDOUT: .C = +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: generic fn @F.1(@I.%T.loc4_13.1: type, @I.%Self.1: @I.%I.type (%I.type.325)) { +// CHECK:STDOUT: %T: type = bind_symbolic_name T, 0 [symbolic = %T (constants.%T)] +// CHECK:STDOUT: %pattern_type: type = pattern_type %T [symbolic = %pattern_type (constants.%pattern_type.7dc)] +// CHECK:STDOUT: +// CHECK:STDOUT: fn(%a.param: @F.1.%T (%T)); +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @F.2(%c.param: %C); +// CHECK:STDOUT: +// CHECK:STDOUT: fn @F.3(%a.param: %B) { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: %a.ref: %B = name_ref a, %a.param +// CHECK:STDOUT: %.loc8: %C = converted %a.ref, [concrete = ] +// CHECK:STDOUT: %F.call: init %empty_tuple.type = call @impl.%F.decl.loc32_15.1() +// CHECK:STDOUT: return +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: specific @I(constants.%T) { +// CHECK:STDOUT: %T.loc4_13.2 => constants.%T +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: specific @F.1(constants.%T, constants.%Self.209) { +// CHECK:STDOUT: %T => constants.%T +// CHECK:STDOUT: %pattern_type => constants.%pattern_type.7dc +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: specific @I(constants.%B) { +// CHECK:STDOUT: %T.loc4_13.2 => constants.%B +// CHECK:STDOUT: +// CHECK:STDOUT: !definition: +// CHECK:STDOUT: %I.type => constants.%I.type.a79 +// CHECK:STDOUT: %Self.2 => constants.%Self.fe1 +// CHECK:STDOUT: %F.type => constants.%F.type.7f2 +// CHECK:STDOUT: %F => constants.%F.e0a +// CHECK:STDOUT: %I.assoc_type => constants.%I.assoc_type.5ff +// CHECK:STDOUT: %assoc0.loc8_13.2 => constants.%assoc0.470 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: specific @F.1(constants.%B, constants.%I.facet) { +// CHECK:STDOUT: %T => constants.%B +// CHECK:STDOUT: %pattern_type => constants.%pattern_type.049 +// CHECK:STDOUT: } +// CHECK:STDOUT: diff --git a/toolchain/check/thunk.cpp b/toolchain/check/thunk.cpp index 48112f053aca..7c7bf445a682 100644 --- a/toolchain/check/thunk.cpp +++ b/toolchain/check/thunk.cpp @@ -6,6 +6,7 @@ #include "toolchain/base/kind_switch.h" #include "toolchain/check/call.h" +#include "toolchain/check/deferred_definition_scope.h" #include "toolchain/check/diagnostic_helpers.h" #include "toolchain/check/function.h" #include "toolchain/check/generic.h" @@ -216,6 +217,71 @@ static auto CloneFunctionDecl(Context& context, SemIR::LocId loc_id, return {function_decl.function_id, decl_id}; } +static auto HasDeclaredReturnType(Context& context, + SemIR::FunctionId function_id) -> bool { + return context.functions() + .Get(function_id) + .return_slot_pattern_id.has_value(); +} + +auto BuildThunk(Context& context, SemIR::FunctionId signature_id, + SemIR::SpecificId signature_specific_id, + SemIR::InstId callee_id) -> SemIR::InstId { + auto callee = SemIR::GetCalleeFunction(context.sem_ir(), callee_id); + + // Check whether we can use the given function without a thunk. + // TODO: For virtual functions, we want different rules for checking `self`. + // TODO: This is too strict; for example, we should not compare parameter + // names here. + if (CheckFunctionTypeMatches( + context, context.functions().Get(callee.function_id), + context.functions().Get(signature_id), signature_specific_id, + /*check_syntax=*/false, /*check_self=*/true, /*diagnose=*/false)) { + return callee_id; + } + + // From P3763: + // If the function in the interface does not have a return type, the + // program is invalid if the function in the impl specifies a return type. + // + // Call into the redeclaration checking logic to produce a suitable error. + // + // TODO: Consider a different rule: always use an explicit return type for the + // thunk, and always convert the result of the wrapped call to the return type + // of the thunk. + if (!HasDeclaredReturnType(context, signature_id) && + HasDeclaredReturnType(context, callee.function_id)) { + bool success = CheckFunctionReturnTypeMatches( + context, context.functions().Get(callee.function_id), + context.functions().Get(signature_id), signature_specific_id); + CARBON_CHECK(!success, "Return type unexpectedly matches"); + return SemIR::ErrorInst::InstId; + } + + // Create a scope for the function's parameters and generic parameters. + context.scope_stack().PushForDeclName(); + + // We can't use the function directly. Build a thunk. + // TODO: Check for and diagnose obvious reasons why this will fail, such as + // arity mismatch, before trying to build the thunk. + auto [function_id, thunk_id] = + CloneFunctionDecl(context, SemIR::LocId(callee_id), signature_id, + signature_specific_id, callee.function_id); + + // Register the thunk to be defined when we reach the end of the enclosing + // deferred definition scope, for example an `impl` or `class` definition, as + // if the thunk's body were written inline in this location. + context.deferred_definition_scope_stack().AddPendingThunk({ + .signature_id = signature_id, + .function_id = function_id, + .decl_id = thunk_id, + .callee_id = callee_id, + .scope = context.scope_stack().Suspend(), + }); + + return thunk_id; +} + // Build an expression that names the value matched by a pattern. static auto BuildPatternRef(Context& context, SemIR::FunctionId function_id, SemIR::InstId pattern_id) -> SemIR::InstId { @@ -285,13 +351,6 @@ static auto BuildThunkCall(Context& context, SemIR::FunctionId function_id, return PerformCall(context, loc_id, callee_id, args); } -static auto HasDeclaredReturnType(Context& context, - SemIR::FunctionId function_id) -> bool { - return context.functions() - .Get(function_id) - .return_slot_pattern_id.has_value(); -} - // Given a declaration of a thunk and the function that it should call, build // the thunk body. static auto BuildThunkDefinition(Context& context, @@ -352,60 +411,13 @@ static auto BuildThunkDefinition(Context& context, FinishGenericDefinition(context, function.generic_id); } -auto BuildThunk(Context& context, SemIR::FunctionId signature_id, - SemIR::SpecificId signature_specific_id, - SemIR::InstId callee_id) -> SemIR::InstId { - auto callee = SemIR::GetCalleeFunction(context.sem_ir(), callee_id); +auto BuildThunkDefinition(Context& context, PendingThunk&& thunk) -> void { + context.scope_stack().Restore(std::move(thunk.scope)); - // Check whether we can use the given function without a thunk. - // TODO: For virtual functions, we want different rules for checking `self`. - // TODO: This is too strict; for example, we should not compare parameter - // names here. - if (CheckFunctionTypeMatches( - context, context.functions().Get(callee.function_id), - context.functions().Get(signature_id), signature_specific_id, - /*check_syntax=*/false, /*check_self=*/true, /*diagnose=*/false)) { - return callee_id; - } - - // From P3763: - // If the function in the interface does not have a return type, the - // program is invalid if the function in the impl specifies a return type. - // - // Call into the redeclaration checking logic to produce a suitable error. - // - // TODO: Consider a different rule: always use an explicit return type for the - // thunk, and always convert the result of the wrapped call to the return type - // of the thunk. - if (!HasDeclaredReturnType(context, signature_id) && - HasDeclaredReturnType(context, callee.function_id)) { - bool success = CheckFunctionReturnTypeMatches( - context, context.functions().Get(callee.function_id), - context.functions().Get(signature_id), signature_specific_id); - CARBON_CHECK(!success, "Return type unexpectedly matches"); - return SemIR::ErrorInst::InstId; - } - - // Create a scope for the function's parameters and generic parameters. - context.scope_stack().PushForDeclName(); - - // We can't use the function directly. Build a thunk. - // TODO: Check for and diagnose obvious reasons why this will fail, such as - // arity mismatch, before trying to build the thunk. - auto [function_id, thunk_id] = - CloneFunctionDecl(context, SemIR::LocId(callee_id), signature_id, - signature_specific_id, callee.function_id); - - // Define the thunk. - // TODO: We should delay doing this until we get to the end of the enclosing - // deferred definition scope, if there is one. For example, an `impl` inside a - // `class` definition should have its thunks defined at the end of the class, - // like they would be if they were defined inline. - BuildThunkDefinition(context, signature_id, function_id, thunk_id, callee_id); + BuildThunkDefinition(context, thunk.signature_id, thunk.function_id, + thunk.decl_id, thunk.callee_id); context.scope_stack().Pop(); - - return thunk_id; } } // namespace Carbon::Check diff --git a/toolchain/check/thunk.h b/toolchain/check/thunk.h index 61960b8116d3..de7db65efdd3 100644 --- a/toolchain/check/thunk.h +++ b/toolchain/check/thunk.h @@ -6,6 +6,7 @@ #define CARBON_TOOLCHAIN_CHECK_THUNK_H_ #include "toolchain/check/context.h" +#include "toolchain/check/deferred_definition_scope.h" #include "toolchain/sem_ir/ids.h" namespace Carbon::Check { @@ -17,6 +18,10 @@ auto BuildThunk(Context& context, SemIR::FunctionId signature_id, SemIR::SpecificId signature_specific_id, SemIR::InstId callee_id) -> SemIR::InstId; +// Builds the definition for a thunk whose definition was deferred until the end +// of the enclosing scope. +auto BuildThunkDefinition(Context& context, PendingThunk&& thunk) -> void; + } // namespace Carbon::Check #endif // CARBON_TOOLCHAIN_CHECK_THUNK_H_