From e060342411df48aa9c3f92a105376ed8e86e0db1 Mon Sep 17 00:00:00 2001 From: Richard Smith Date: Wed, 7 May 2025 15:20:39 -0700 Subject: [PATCH] Defer building thunks until the end of the enclosing definition. (#5403) Instead of building the definition of a thunk immediately when we generate the thunk declaration, wait until we reach the `}` of the outermost class, interface, etc. -- at the same time when we would parse the definition of the thunk if it were defined inline. This fixes issues where we fail to define the thunk because it requires an enclosing class to be complete, or its definition depends on something declared later in the enclosing class. Make the representation of a suspended function scope, and its constituent suspended components, be move-only, and switch to passing it around by rvalue reference instead of by value because it's expensive both to move and especially to copy. --------- Co-authored-by: Jon Ross-Perkins --- common/BUILD | 5 + common/array_stack.h | 10 +- common/move_only.h | 28 ++ toolchain/check/BUILD | 2 + toolchain/check/context.h | 8 + toolchain/check/decl_name_stack.cpp | 2 +- toolchain/check/decl_name_stack.h | 6 +- toolchain/check/deferred_definition_scope.h | 68 +++++ .../check/deferred_definition_worklist.cpp | 27 +- .../check/deferred_definition_worklist.h | 29 +- toolchain/check/function.h | 4 +- toolchain/check/handle.h | 2 +- toolchain/check/handle_function.cpp | 4 +- toolchain/check/node_id_traversal.cpp | 36 ++- toolchain/check/scope_stack.cpp | 2 +- toolchain/check/scope_stack.h | 8 +- .../impl/fail_impl_bad_assoc_fn.carbon | 265 +++++++----------- .../impl/min_prelude/impl_thunk.carbon | 42 +++ .../impl/no_prelude/impl_thunk.carbon | 207 +++++++++++++- toolchain/check/thunk.cpp | 128 +++++---- toolchain/check/thunk.h | 5 + 21 files changed, 629 insertions(+), 259 deletions(-) create mode 100644 common/move_only.h create mode 100644 toolchain/check/deferred_definition_scope.h create mode 100644 toolchain/check/testdata/impl/min_prelude/impl_thunk.carbon 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_