diff --git a/toolchain/check/BUILD b/toolchain/check/BUILD index d084e57a7794..221c3014b56d 100644 --- a/toolchain/check/BUILD +++ b/toolchain/check/BUILD @@ -23,6 +23,7 @@ cc_library( "convert.cpp", "decl_name_stack.cpp", "deduce.cpp", + "deferred_definition_worklist.cpp", "eval.cpp", "eval_inst.cpp", "facet_type.cpp", @@ -66,7 +67,7 @@ cc_library( "decl_introducer_state.h", "decl_name_stack.h", "deduce.h", - "deferred_definition_scope.h", + "deferred_definition_worklist.h", "diagnostic_helpers.h", "eval.h", "eval_inst.h", @@ -110,6 +111,7 @@ cc_library( "//common:array_stack", "//common:check", "//common:concepts", + "//common:emplace_by_calling", "//common:find", "//common:map", "//common:ostream", @@ -163,8 +165,6 @@ cc_library( "check.cpp", "check_unit.cpp", "check_unit.h", - "deferred_definition_worklist.cpp", - "deferred_definition_worklist.h", "handle.h", "node_id_traversal.cpp", "node_id_traversal.h", @@ -179,7 +179,6 @@ cc_library( ":diagnostic_emitter", ":dump", "//common:check", - "//common:emplace_by_calling", "//common:error", "//common:find", "//common:map", diff --git a/toolchain/check/check_unit.cpp b/toolchain/check/check_unit.cpp index a0859cfe917f..c34651fa6f1d 100644 --- a/toolchain/check/check_unit.cpp +++ b/toolchain/check/check_unit.cpp @@ -367,7 +367,7 @@ auto CheckUnit::ImportOtherPackages(SemIR::TypeId namespace_type_id) -> void { // for example if an unrecoverable state is encountered. // NOLINTNEXTLINE(readability-function-size) auto CheckUnit::ProcessNodeIds() -> bool { - NodeIdTraversal traversal(&context_, vlog_stream_); + NodeIdTraversal traversal(&context_); Parse::NodeId node_id = Parse::NodeId::None; diff --git a/toolchain/check/context.cpp b/toolchain/check/context.cpp index 9d067589db2c..767d13fdd28e 100644 --- a/toolchain/check/context.cpp +++ b/toolchain/check/context.cpp @@ -8,6 +8,7 @@ #include #include "common/check.h" +#include "toolchain/check/deferred_definition_worklist.h" namespace Carbon::Check { @@ -26,6 +27,7 @@ Context::Context(DiagnosticEmitterBase* emitter, args_type_info_stack_("args_type_info_stack_", *sem_ir, vlog_stream), decl_name_stack_(this), scope_stack_(sem_ir_), + deferred_definition_worklist_(vlog_stream), vtable_stack_("vtable_stack_", *sem_ir, vlog_stream), global_init_(this), region_stack_([this](SemIR::LocId loc_id, std::string label) { diff --git a/toolchain/check/context.h b/toolchain/check/context.h index ef96ee1b4e70..2dfa547d0b0f 100644 --- a/toolchain/check/context.h +++ b/toolchain/check/context.h @@ -13,7 +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/deferred_definition_worklist.h" #include "toolchain/check/diagnostic_helpers.h" #include "toolchain/check/full_pattern_stack.h" #include "toolchain/check/generic_region_stack.h" @@ -129,8 +129,8 @@ class Context { return scope_stack_.full_pattern_stack(); } - auto deferred_definition_scope_stack() -> DeferredDefinitionScopeStack& { - return deferred_definition_scope_stack_; + auto deferred_definition_worklist() -> DeferredDefinitionWorklist& { + return deferred_definition_worklist_; } auto generic_region_stack() -> GenericRegionStack& { @@ -340,8 +340,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 worklist of deferred definition tasks to perform at the end of the + // enclosing deferred definition scope. + DeferredDefinitionWorklist deferred_definition_worklist_; // The stack of generic regions we are currently within. GenericRegionStack generic_region_stack_; diff --git a/toolchain/check/deferred_definition_scope.h b/toolchain/check/deferred_definition_scope.h deleted file mode 100644 index ca5511a6183e..000000000000 --- a/toolchain/check/deferred_definition_scope.h +++ /dev/null @@ -1,68 +0,0 @@ -// 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 960c3325389b..277bf8acf54e 100644 --- a/toolchain/check/deferred_definition_worklist.cpp +++ b/toolchain/check/deferred_definition_worklist.cpp @@ -11,7 +11,7 @@ #include "common/emplace_by_calling.h" #include "common/vlog.h" #include "toolchain/base/kind_switch.h" -#include "toolchain/check/handle.h" +#include "toolchain/check/context.h" namespace Carbon::Check { @@ -25,17 +25,23 @@ DeferredDefinitionWorklist::DeferredDefinitionWorklist( } auto DeferredDefinitionWorklist::SuspendFunctionAndPush( - Context& context, Parse::DeferredDefinitionIndex index, - Parse::FunctionDefinitionStartId node_id) -> void { - // TODO: Investigate factoring out `HandleFunctionDefinitionSuspend` to make - // `DeferredDefinitionWorklist` reusable. + Parse::DeferredDefinitionIndex index, + llvm::function_refSuspendedFunction> suspend) -> void { worklist_.emplace_back(EmplaceByCalling([&] { - return CheckSkippedDefinition{ - index, HandleFunctionDefinitionSuspend(context, node_id)}; + return CheckSkippedDefinition{.definition_index = index, + .suspended_fn = suspend()}; })); CARBON_VLOG("{0}Push CheckSkippedDefinition {1}\n", VlogPrefix, index.index); } +auto DeferredDefinitionWorklist::SuspendThunkAndPush(Context& context, + ThunkInfo info) -> void { + worklist_.emplace_back(EmplaceByCalling([&] { + return DefineThunk{.info = info, .scope = context.scope_stack().Suspend()}; + })); + CARBON_VLOG("{0}Push DefineThunk {1}\n", VlogPrefix, info.function_id); +} + auto DeferredDefinitionWorklist::PushEnterDeferredDefinitionScope( Context& context) -> bool { bool nested = !entered_scopes_.empty() && diff --git a/toolchain/check/deferred_definition_worklist.h b/toolchain/check/deferred_definition_worklist.h index b13d6952822e..ca6acd80dbe7 100644 --- a/toolchain/check/deferred_definition_worklist.h +++ b/toolchain/check/deferred_definition_worklist.h @@ -11,7 +11,6 @@ #include "common/ostream.h" #include "llvm/ADT/SmallVector.h" #include "toolchain/check/decl_name_stack.h" -#include "toolchain/check/function.h" #include "toolchain/parse/tree.h" namespace Carbon::Check { @@ -20,15 +19,49 @@ namespace Carbon::Check { // in the right order. class DeferredDefinitionWorklist { public: + // 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. + // + // 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. + SemIR::InstId decl_id; + // The declaration name information of the function. This includes the scope + // information, such as parameter names. + DeclNameStack::SuspendedName saved_name_state; + }; + // A worklist task that indicates we should check a deferred function // definition that we previously skipped. - struct CheckSkippedDefinition { + // + // This type is large, so moves of this type should be avoided. + struct CheckSkippedDefinition : public MoveOnly { // The definition that we skipped. Parse::DeferredDefinitionIndex definition_index; // The suspended function. SuspendedFunction suspended_fn; }; + // A description of a thunk. + struct ThunkInfo { + SemIR::FunctionId signature_id; + SemIR::FunctionId function_id; + SemIR::InstId decl_id; + SemIR::InstId callee_id; + }; + + // A worklist task that indicates we should define a thunk that was previously + // declared. + // + // This type is large, so moves of this type should be avoided. + struct DefineThunk : public MoveOnly { + ThunkInfo info; + ScopeStack::SuspendedScope scope; + }; + // A worklist task that indicates we should enter a nested deferred definition // scope. We delay processing the contents of nested deferred definition // scopes until we reach the end of the parent scope. For example: @@ -46,7 +79,10 @@ class DeferredDefinitionWorklist { // } // C.G is type-checked here. // } // ``` - struct EnterNestedDeferredDefinitionScope { + // + // This type is large, so moves of this type should be avoided. + struct EnterNestedDeferredDefinitionScope + : public MoveOnly { // The suspended scope. This is only set once we reach the end of the scope. std::optional suspended_name; }; @@ -56,17 +92,21 @@ class DeferredDefinitionWorklist { struct LeaveNestedDeferredDefinitionScope {}; // A pending type-checking task. - using Task = - std::variant; + using Task = std::variant; explicit DeferredDefinitionWorklist(llvm::raw_ostream* vlog_stream); - // 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; + // Suspends the current function definition and pushes a task onto the + // worklist to finish it later. + auto SuspendFunctionAndPush( + Parse::DeferredDefinitionIndex index, + llvm::function_refSuspendedFunction> suspend) -> void; + + // Suspends the current thunk scope and pushes a task onto the worklist to + // define it later. + auto SuspendThunkAndPush(Context& context, ThunkInfo info) -> 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 diff --git a/toolchain/check/function.h b/toolchain/check/function.h index 991065edf804..289b1aa47c7e 100644 --- a/toolchain/check/function.h +++ b/toolchain/check/function.h @@ -13,21 +13,6 @@ 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. -// -// 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. - SemIR::InstId decl_id; - // The declaration name information of the function. This includes the scope - // information, such as parameter names. - DeclNameStack::SuspendedName saved_name_state; -}; - // Returns the ID of the self parameter pattern, or None. // TODO: Do this during initial traversal of implicit params. auto FindSelfPattern(Context& context, diff --git a/toolchain/check/handle.h b/toolchain/check/handle.h index 2540b55da29f..db53d8806dfc 100644 --- a/toolchain/check/handle.h +++ b/toolchain/check/handle.h @@ -6,6 +6,7 @@ #define CARBON_TOOLCHAIN_CHECK_HANDLE_H_ #include "toolchain/check/context.h" +#include "toolchain/check/deferred_definition_worklist.h" #include "toolchain/check/function.h" #include "toolchain/parse/node_ids.h" @@ -23,12 +24,12 @@ namespace Carbon::Check { // cleared out in between. auto HandleFunctionDefinitionSuspend(Context& context, Parse::FunctionDefinitionStartId node_id) - -> SuspendedFunction; + -> DeferredDefinitionWorklist::SuspendedFunction; // Handle resuming the definition of a function, after a previous suspension. -auto HandleFunctionDefinitionResume(Context& context, - Parse::FunctionDefinitionStartId node_id, - SuspendedFunction&& suspended_fn) -> void; +auto HandleFunctionDefinitionResume( + Context& context, Parse::FunctionDefinitionStartId node_id, + DeferredDefinitionWorklist::SuspendedFunction&& suspended_fn) -> void; } // namespace Carbon::Check diff --git a/toolchain/check/handle_function.cpp b/toolchain/check/handle_function.cpp index dff8c413b0e8..6ce6d47f524d 100644 --- a/toolchain/check/handle_function.cpp +++ b/toolchain/check/handle_function.cpp @@ -585,7 +585,7 @@ static auto HandleFunctionDefinitionAfterSignature( auto HandleFunctionDefinitionSuspend(Context& context, Parse::FunctionDefinitionStartId node_id) - -> SuspendedFunction { + -> DeferredDefinitionWorklist::SuspendedFunction { // Process the declaration portion of the function. auto [function_id, decl_id] = BuildFunctionDecl(context, node_id, /*is_definition=*/true); @@ -594,9 +594,9 @@ auto HandleFunctionDefinitionSuspend(Context& context, .saved_name_state = context.decl_name_stack().Suspend()}; } -auto HandleFunctionDefinitionResume(Context& context, - Parse::FunctionDefinitionStartId node_id, - SuspendedFunction&& suspended_fn) -> void { +auto HandleFunctionDefinitionResume( + Context& context, Parse::FunctionDefinitionStartId node_id, + DeferredDefinitionWorklist::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 b081cbea6a7e..ae100073a0bf 100644 --- a/toolchain/check/node_id_traversal.cpp +++ b/toolchain/check/node_id_traversal.cpp @@ -14,11 +14,10 @@ namespace Carbon::Check { -NodeIdTraversal::NodeIdTraversal(Context* context, - llvm::raw_ostream* vlog_stream) +NodeIdTraversal::NodeIdTraversal(Context* context) : context_(context), next_deferred_definition_(&context->parse_tree()), - worklist_(vlog_stream) { + worklist_(&context->deferred_definition_worklist()) { auto range = context->parse_tree().postorder(); chunks_.push_back({.it = range.begin(), .end = range.end(), @@ -31,15 +30,15 @@ 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) { - if (chunks_.back().next_worklist_index < worklist_.size()) { + if (chunks_.back().next_worklist_index < worklist().size()) { std::visit([&](auto& task) { PerformTask(std::move(task)); }, - worklist_[chunks_.back().next_worklist_index++]); + worklist()[chunks_.back().next_worklist_index++]); continue; } // Worklist is empty: discard the worklist items associated with this // chunk, and leave the scope. - worklist_.truncate(chunks_.back().first_worklist_index); + worklist().truncate(chunks_.back().first_worklist_index); // We reach here when // `DeferredDefinitionScope::SuspendFinishedScopeAndPush` returns // `NonNestedWithWork`. In this case it's our responsibility to pop the @@ -57,7 +56,7 @@ auto NodeIdTraversal::Next() -> std::optional { // If we're out of chunks, then we're done entirely. if (chunks_.empty()) { - worklist_.VerifyEmpty(); + worklist().VerifyEmpty(); return std::nullopt; } @@ -73,9 +72,10 @@ auto NodeIdTraversal::Next() -> std::optional { const auto& definition_info = context_->parse_tree().deferred_definitions().Get( next_deferred_definition_.index()); - worklist_.SuspendFunctionAndPush(*context_, - next_deferred_definition_.index(), - definition_info.start_id); + worklist().SuspendFunctionAndPush(next_deferred_definition_.index(), [&] { + return HandleFunctionDefinitionSuspend(*context_, + definition_info.start_id); + }); // Continue type-checking the parse tree after the end of the definition. chunks_.back().it = @@ -125,29 +125,16 @@ 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)) { - if (worklist_.PushEnterDeferredDefinitionScope(*context_)) { - // Track that we're within a new non-nested deferred definition scope. - context_->deferred_definition_scope_stack().Push(); - } + worklist().PushEnterDeferredDefinitionScope(*context_); } // 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)) { - auto scope_kind = 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 we have deferred tasks in this scope, perform them next. if (scope_kind == DeferredDefinitionWorklist::FinishedScopeKind::NonNestedWithWork) { chunks_.back().checking_deferred_definitions = true; @@ -184,12 +171,17 @@ auto NodeIdTraversal::PerformTask( .end = range.end(), .next_definition = next_deferred_definition_.index(), .checking_deferred_definitions = false, - .first_worklist_index = worklist_.size(), - .next_worklist_index = worklist_.size()}); + .first_worklist_index = worklist().size(), + .next_worklist_index = worklist().size()}); ++definition_index.index; next_deferred_definition_.SkipTo(definition_index); } +auto NodeIdTraversal::PerformTask( + DeferredDefinitionWorklist::DefineThunk&& define_thunk) -> void { + BuildThunkDefinition(*context_, std::move(define_thunk)); +} + NodeIdTraversal::NextDeferredDefinitionCache::NextDeferredDefinitionCache( const Parse::Tree* tree) : tree_(tree) { diff --git a/toolchain/check/node_id_traversal.h b/toolchain/check/node_id_traversal.h index 65e5d6954ade..babcfea334b1 100644 --- a/toolchain/check/node_id_traversal.h +++ b/toolchain/check/node_id_traversal.h @@ -18,7 +18,7 @@ namespace Carbon::Check { class NodeIdTraversal { public: // `context` must not be null. - explicit NodeIdTraversal(Context* context, llvm::raw_ostream* vlog_stream); + explicit NodeIdTraversal(Context* context); // Finds the next `NodeId` to type-check. Returns nullopt if the traversal is // complete. @@ -70,6 +70,8 @@ class NodeIdTraversal { size_t next_worklist_index; }; + auto worklist() -> DeferredDefinitionWorklist& { return *worklist_; } + // Re-enter a nested deferred definition scope. auto PerformTask( DeferredDefinitionWorklist::EnterNestedDeferredDefinitionScope&& enter) @@ -85,9 +87,13 @@ class NodeIdTraversal { DeferredDefinitionWorklist::CheckSkippedDefinition&& parse_definition) -> void; + // Define a thunk. + auto PerformTask(DeferredDefinitionWorklist::DefineThunk&& define_thunk) + -> void; + Context* context_; NextDeferredDefinitionCache next_deferred_definition_; - DeferredDefinitionWorklist worklist_; + DeferredDefinitionWorklist* worklist_; llvm::SmallVector chunks_; }; diff --git a/toolchain/check/testdata/impl/impl_thunk.carbon b/toolchain/check/testdata/impl/impl_thunk.carbon index 12e635bd8c22..35cb833aadfb 100644 --- a/toolchain/check/testdata/impl/impl_thunk.carbon +++ b/toolchain/check/testdata/impl/impl_thunk.carbon @@ -643,9 +643,9 @@ impl () as I({}) { // CHECK:STDOUT: %pattern_type.afe: type = pattern_type %ptr.79f [symbolic] // CHECK:STDOUT: %F.type.39e918.2: type = fn_type @F.3 [concrete] // CHECK:STDOUT: %F.c04b92.2: %F.type.39e918.2 = struct_value () [concrete] +// CHECK:STDOUT: %require_complete.4ae: = require_complete_type %U [symbolic] // CHECK:STDOUT: %require_complete.6e5: = require_complete_type %ptr.79f [symbolic] // CHECK:STDOUT: %F.specific_fn: = specific_function %F.c04b92.1, @F.2(%ptr.79f) [symbolic] -// CHECK:STDOUT: %require_complete.4ae: = require_complete_type %U [symbolic] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: impl @impl: %.loc8_7.2 as %I.ref { diff --git a/toolchain/check/testdata/impl/impl_thunk_min_prelude.carbon b/toolchain/check/testdata/impl/impl_thunk_min_prelude.carbon index 345c8f69923a..6853f299af6d 100644 --- a/toolchain/check/testdata/impl/impl_thunk_min_prelude.carbon +++ b/toolchain/check/testdata/impl/impl_thunk_min_prelude.carbon @@ -39,3 +39,42 @@ class A { fn Convert[self: Self]() -> A { return {}; } } } + +// --- fail_todo_out_of_line_thunk.carbon + +library "[[@TEST_NAME]]"; + +class Wrap(T:! type) {} + +interface OpWith(U:! type) { + fn Op[self: Self](u: U); +} + +impl forall [T:! type, U:! Core.ImplicitAs(Wrap(T))] Wrap(T) as OpWith(U) { + // CHECK:STDERR: fail_todo_out_of_line_thunk.carbon:[[@LINE+7]]:3: error: use of undefined generic function [MissingGenericFunctionDefinition] + // CHECK:STDERR: fn Op[self: Self](other: Self); + // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + // CHECK:STDERR: fail_todo_out_of_line_thunk.carbon:[[@LINE+4]]:3: note: generic function declared here [MissingGenericFunctionDefinitionHere] + // CHECK:STDERR: fn Op[self: Self](other: Self); + // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + // CHECK:STDERR: + fn Op[self: Self](other: Self); +} + +// TODO: Once we support the syntax for defining impl members out of line, +// define the above function here. +// fn (forall [T:! type, U:! Core.ImplicitAs(Wrap(T))] Wrap(T) as OpWith(U)).Op[self: Self](other: Self) {} + +// --- builtin_thunk.carbon + +library "[[@TEST_NAME]]"; + +class Wrap(T:! type) {} + +interface OpWith(U:! type) { + fn Op[self: Self](u: U); +} + +impl forall [T:! type, U:! Core.ImplicitAs(Wrap(T))] Wrap(T) as OpWith(U) { + fn Op[self: Self](other: Self) = "no_op"; +} diff --git a/toolchain/check/testdata/impl/import_thunk.carbon b/toolchain/check/testdata/impl/import_thunk.carbon index 525083ae19a1..8d21fd05ae54 100644 --- a/toolchain/check/testdata/impl/import_thunk.carbon +++ b/toolchain/check/testdata/impl/import_thunk.carbon @@ -112,8 +112,8 @@ fn G() { // CHECK:STDOUT: %I.facet: %I.type = facet_value %C.13320f.2, (%I.impl_witness) [symbolic] // CHECK:STDOUT: %F.type.0daaa1.2: type = fn_type @F.3, @impl(%Y) [symbolic] // CHECK:STDOUT: %F.49c1ac.2: %F.type.0daaa1.2 = struct_value () [symbolic] -// CHECK:STDOUT: %F.specific_fn: = specific_function %F.49c1ac.1, @F.2(%Y) [symbolic] // CHECK:STDOUT: %require_complete: = require_complete_type %C.13320f.2 [symbolic] +// CHECK:STDOUT: %F.specific_fn: = specific_function %F.49c1ac.1, @F.2(%Y) [symbolic] // CHECK:STDOUT: %C.val: %C.13320f.2 = struct_value () [symbolic] // CHECK:STDOUT: } // CHECK:STDOUT: diff --git a/toolchain/check/thunk.cpp b/toolchain/check/thunk.cpp index 0dd89c103f69..bb45d925acef 100644 --- a/toolchain/check/thunk.cpp +++ b/toolchain/check/thunk.cpp @@ -9,7 +9,7 @@ #include "toolchain/base/kind_switch.h" #include "toolchain/check/call.h" #include "toolchain/check/convert.h" -#include "toolchain/check/deferred_definition_scope.h" +#include "toolchain/check/deferred_definition_worklist.h" #include "toolchain/check/diagnostic_helpers.h" #include "toolchain/check/function.h" #include "toolchain/check/generic.h" @@ -272,13 +272,13 @@ auto BuildThunk(Context& context, SemIR::FunctionId signature_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(), - }); + context.deferred_definition_worklist().SuspendThunkAndPush( + context, { + .signature_id = signature_id, + .function_id = function_id, + .decl_id = thunk_id, + .callee_id = callee_id, + }); return thunk_id; } @@ -419,11 +419,13 @@ static auto BuildThunkDefinition(Context& context, FinishGenericDefinition(context, function.generic_id); } -auto BuildThunkDefinition(Context& context, PendingThunk&& thunk) -> void { - context.scope_stack().Restore(std::move(thunk.scope)); +auto BuildThunkDefinition(Context& context, + DeferredDefinitionWorklist::DefineThunk&& task) + -> void { + context.scope_stack().Restore(std::move(task.scope)); - BuildThunkDefinition(context, thunk.signature_id, thunk.function_id, - thunk.decl_id, thunk.callee_id); + BuildThunkDefinition(context, task.info.signature_id, task.info.function_id, + task.info.decl_id, task.info.callee_id); context.scope_stack().Pop(); } diff --git a/toolchain/check/thunk.h b/toolchain/check/thunk.h index de7db65efdd3..0f7a9c4b6479 100644 --- a/toolchain/check/thunk.h +++ b/toolchain/check/thunk.h @@ -6,7 +6,7 @@ #define CARBON_TOOLCHAIN_CHECK_THUNK_H_ #include "toolchain/check/context.h" -#include "toolchain/check/deferred_definition_scope.h" +#include "toolchain/check/deferred_definition_worklist.h" #include "toolchain/sem_ir/ids.h" namespace Carbon::Check { @@ -20,7 +20,9 @@ auto BuildThunk(Context& context, SemIR::FunctionId signature_id, // Builds the definition for a thunk whose definition was deferred until the end // of the enclosing scope. -auto BuildThunkDefinition(Context& context, PendingThunk&& thunk) -> void; +auto BuildThunkDefinition(Context& context, + DeferredDefinitionWorklist::DefineThunk&& task) + -> void; } // namespace Carbon::Check