Move generic stack operations into handle_impl.cpp (Refactor Impl construction 8/7) (#6484)

Instead of burying operations to pop the generic stack in
`GetOrAddImpl`, we move them up to handle_impl.cpp in `BuildImplDecl`,
which puts them at the same level as other operations on the generic
stack, like `StartGenericDecl` or `FinishGenericDefinition`.

To do so, we split `GetOrAddImpl` into a few pieces:
- `FindImplId` finds an existing Impl that matches the declaration, or
returns a LookupBucketRef and whether an error was diagnosed instead.
- `AddImpl` takes a fully built `Impl`, makes an `ImplId` for it, and
does additional steps for a new `Impl` verifying it and applying
`extend`.
- `AddImplWitnessForDeclaration` constructs the `Impl`'s witness, which
must be done between two generic steps in order to use the generic's
self specific but also add the witness instruction to the generic.

We group the logic to build the initial table in the definition and to
complete it in the definition together in `impl.cpp`. And we save a
lookup into the ImplStore by passing Impl by reference to
`FinishImplWitness`, as we now do for other similar functions in
`impl.h`.

This is based on #6470.
This commit is contained in:
Dana Jansens
2025-12-16 15:32:56 +00:00
committed by GitHub
parent 47e551141f
commit efec4e4658
6 changed files with 531 additions and 526 deletions
+74 -17
View File
@@ -5,6 +5,7 @@
#include <optional>
#include <utility>
#include "toolchain/base/kind_switch.h"
#include "toolchain/check/context.h"
#include "toolchain/check/convert.h"
#include "toolchain/check/decl_name_stack.h"
@@ -234,22 +235,78 @@ static auto BuildImplDecl(Context& context, Parse::AnyImplDeclId node_id)
auto impl_id = SemIR::ImplId::None;
{
SemIR::Impl impl_info = {
name_context.MakeEntityWithParamsBase(name, impl_decl_id,
/*is_extern=*/false,
SemIR::LibraryNameId::None),
{.self_id = self_type_inst_id,
.constraint_id = constraint_type_inst_id,
.interface = specific_interface,
.is_final = is_final}};
auto extend_node = introducer.modifier_node_id(ModifierOrder::Extend);
impl_id = GetOrAddImpl(context, node_id, name.implicit_params_loc_id,
impl_info, extend_node);
SemIR::Impl impl = {name_context.MakeEntityWithParamsBase(
name, impl_decl_id,
/*is_extern=*/false, SemIR::LibraryNameId::None),
{.self_id = self_type_inst_id,
.constraint_id = constraint_type_inst_id,
.interface = specific_interface,
.is_final = is_final}};
// There's a bunch of places that may represent a diagnostic that occurred
// in checking the impl up to this point, which we consolidate into this
// bool. Due to lack of an instruction to set to `ErrorInst`, an
// `InterfaceId::None` indicates that the interface could not be identified
// and an error was diagnosed.
bool impl_had_error =
context.types().GetTypeIdForTypeInstId(impl.self_id) ==
SemIR::ErrorInst::TypeId ||
context.types().GetTypeIdForTypeInstId(impl.constraint_id) ==
SemIR::ErrorInst::TypeId ||
!impl.interface.interface_id.has_value();
CARBON_KIND_SWITCH(FindImplId(context, impl)) {
case CARBON_KIND(RedeclaredImpl redeclared_impl): {
// This is a redeclaration of another impl, now held in `impl_id`.
impl_id = redeclared_impl.prev_impl_id;
// Note that we don't reconstruct the witness for a redeclaration, which
// was the instruction that came last in the first declaration's eval
// block. And FinishGenericRedecl allows the redecl to have fewer
// instructions to support this case.
const auto& prev_impl = context.impls().Get(impl_id);
FinishGenericRedecl(context, prev_impl.generic_id);
break;
}
case CARBON_KIND(NewImpl new_impl): {
// This is a new declaration (possibly with an attached definition).
// Create a new `impl_id`, filling the missing generic and witness in
// `Impl` structure.
impl_had_error |= new_impl.find_had_error;
impl.generic_id = BuildGeneric(context, impl_decl_id);
if (impl_had_error) {
// If there's any error in the construction of the impl, then the
// witness can't be constructed. We set it to `ErrorInst` to make the
// impl unusable for impl lookup.
impl.witness_id = SemIR::ErrorInst::InstId;
} else {
// This makes either a placeholder witness table or a full witness
// table. The full witness table is deferred to the impl definition
// unless the declaration uses rewrite constraints to set values of
// associated constants in the interface.
//
// The witness instruction contains the SelfSpecific that is
// constructed by BuildGeneric(), but the witness and its rewrites
// also must be part of the generic eval block by coming before
// FinishGenericDecl().
impl.witness_id = AddImplWitnessForDeclaration(
context, node_id, impl,
context.generics().GetSelfSpecific(impl.generic_id));
}
FinishGenericDecl(context, node_id, impl.generic_id);
auto extend_node = introducer.modifier_node_id(ModifierOrder::Extend);
impl_id = AddImpl(context, impl, new_impl.lookup_bucket, extend_node,
name.implicit_params_loc_id);
}
}
}
// `GetOrAddImpl` either filled in the `impl_info` and returned a fresh
// ImplId, or if we're redeclaring a previous impl, returned an existing
// ImplId. Write that ImplId into the ImplDecl instruction and finish it.
// `FindImplId` returned an existing ImplId, or we added a new id with
// `AddImpl` above. Write that ImplId into the ImplDecl instruction and finish
// it.
auto impl_decl = context.insts().GetAs<SemIR::ImplDecl>(impl_decl_id);
impl_decl.impl_id = impl_id;
ReplaceInstBeforeConstantUse(context, impl_decl_id, impl_decl);
@@ -309,10 +366,10 @@ auto HandleParseNode(Context& context, Parse::ImplDefinitionId /*node_id*/)
-> bool {
auto impl_id =
context.node_stack().Pop<Parse::NodeKind::ImplDefinitionStart>();
FinishImplWitness(context, impl_id);
auto& impl = context.impls().Get(impl_id);
FinishImplWitness(context, impl);
impl.defined = true;
FinishGenericDefinition(context, impl.generic_id);