Allow impl redecl in match_first after a definition (#7491)

Previously the last decl had to be the definition. Now we allow a
declaration after a definition, so that the user can write a match_first
block last, and put (re-)declarations of impls in it, after the
definitions have already been written elsewhere.

We track the location of the decl that was associated with a match_first
block so that we can correctly point to it in diagnostics when an impl
is written twice in match_first blocks. Since impls may not be
redeclared across an import boundary, we will never have a `SemIR::Impl`
with a match_first from a different file in a redeclaration, so we don't
need to import the location of a previous decl that was in a match_first
for diagnostics. As such we just store a LocId on the `SemIR::Impl`
struct.
This commit is contained in:
Dana Jansens
2026-07-15 14:12:37 +00:00
committed by GitHub
parent 474090f439
commit 168420f805
7 changed files with 92 additions and 52 deletions
+19 -12
View File
@@ -193,8 +193,10 @@ static auto PopImplIntroducerAndParamsAsNameComponent(
}
// Build an ImplDecl describing the signature of an impl. This handles the
// common logic shared by impl forward declarations and impl definitions.
static auto BuildImplDecl(Context& context, Parse::AnyImplDeclId node_id)
// common logic shared by impl forward declarations and impl definitions. It
// also sets the `definition_id` on the Impl structure.
static auto BuildImplDecl(Context& context, Parse::AnyImplDeclId node_id,
bool has_definition)
-> std::pair<SemIR::ImplId, SemIR::InstId> {
auto [constraint_node, constraint_id] =
context.node_stack().PopExprWithNodeId();
@@ -269,6 +271,9 @@ static auto BuildImplDecl(Context& context, Parse::AnyImplDeclId node_id)
.self_id = self_type_inst_id,
.constraint_id = constraint_type_inst_id,
.interface = specific_interface}};
if (has_definition) {
impl.definition_id = impl_decl_id;
}
// 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
@@ -306,6 +311,10 @@ static auto BuildImplDecl(Context& context, Parse::AnyImplDeclId node_id)
auto& prev_impl = context.impls().Get(impl_id);
FinishGenericRedecl(context, prev_impl.generic_id);
if (has_definition) {
prev_impl.definition_id = impl_decl_id;
}
if (auto match_first = context.match_first_context()) {
if (prev_impl.match_first_id.has_value()) {
if (!impl_had_error) {
@@ -316,16 +325,18 @@ static auto BuildImplDecl(Context& context, Parse::AnyImplDeclId node_id)
"previous declaration here");
context.emitter()
.Build(node_id, ImplInTwoMatchFirst)
.Note(prev_impl.latest_decl_id(), ImplInTwoMatchFirstNote)
.Note(prev_impl.decl_loc_in_match_first,
ImplInTwoMatchFirstNote)
.Emit();
}
impl_had_error = true;
}
if (!impl_had_error) {
prev_impl.is_final = match_first->is_final;
prev_impl.match_first_id = match_first->decl_id;
prev_impl.decl_loc_in_match_first = SemIR::LocId(impl_decl_id);
prev_impl.match_first_position = match_first->block_size;
prev_impl.match_first_is_final = match_first->is_final;
match_first->block_size += 1;
}
}
@@ -362,12 +373,10 @@ static auto BuildImplDecl(Context& context, Parse::AnyImplDeclId node_id)
impl.witness_block_id = context.inst_block_stack().Pop();
if (auto match_first = context.match_first_context()) {
// This should have been diagnosed above.
CARBON_CHECK(!impl.is_final);
impl.is_final = match_first->is_final;
impl.match_first_id = match_first->decl_id;
impl.decl_loc_in_match_first = SemIR::LocId(impl_decl_id);
impl.match_first_position = match_first->block_size;
impl.match_first_is_final = match_first->is_final;
match_first->block_size += 1;
}
}
@@ -392,7 +401,7 @@ static auto BuildImplDecl(Context& context, Parse::AnyImplDeclId node_id)
}
auto HandleParseNode(Context& context, Parse::ImplDeclId node_id) -> bool {
auto [impl_id, impl_decl_id] = BuildImplDecl(context, node_id);
auto [impl_id, impl_decl_id] = BuildImplDecl(context, node_id, false);
auto& impl = context.impls().Get(impl_id);
context.decl_name_stack().PopScope();
@@ -409,13 +418,11 @@ auto HandleParseNode(Context& context, Parse::ImplDeclId node_id) -> bool {
auto HandleParseNode(Context& context, Parse::ImplDefinitionStartId node_id)
-> bool {
auto [impl_id, impl_decl_id] = BuildImplDecl(context, node_id);
auto [impl_id, impl_decl_id] = BuildImplDecl(context, node_id, true);
auto& impl = context.impls().Get(impl_id);
CheckRequireDeclsSatisfied(context, node_id, impl);
CARBON_CHECK(!impl.has_definition_started());
impl.definition_id = impl_decl_id;
impl.scope_id =
context.name_scopes().Add(impl_decl_id, SemIR::NameId::None,
context.decl_name_stack().PeekParentScopeId());