Skip to content

Commit 9dc450e

Browse files
authored
Stop merging invalid impl redefinitions (#4798)
Fixes a crash, see the new regression test in toolchain/check/testdata/impl/no_prelude/generic_redeclaration.carbon. Stopping merging seems like the most straightforward way to prevent references to generic regions with the incorrect block.
1 parent a3e66d6 commit 9dc450e

File tree

4 files changed

+341
-65
lines changed

4 files changed

+341
-65
lines changed

toolchain/check/handle_impl.cpp

Lines changed: 21 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -295,6 +295,22 @@ static auto IsValidImplRedecl(Context& context, SemIR::Impl& new_impl,
295295
return false;
296296
}
297297

298+
if (prev_impl.has_definition_started()) {
299+
// Impls aren't merged in order to avoid generic region lookup into a
300+
// mismatching table.
301+
CARBON_DIAGNOSTIC(ImplRedefinition, Error,
302+
"redefinition of `impl {0} as {1}`", InstIdAsRawType,
303+
InstIdAsRawType);
304+
CARBON_DIAGNOSTIC(ImplPreviousDefinition, Note,
305+
"previous definition was here");
306+
context.emitter()
307+
.Build(new_impl.latest_decl_id(), ImplRedefinition, new_impl.self_id,
308+
new_impl.constraint_id)
309+
.Note(prev_impl.definition_id, ImplPreviousDefinition)
310+
.Emit();
311+
return false;
312+
}
313+
298314
// TODO: Only allow redeclaration in a match_first/impl_priority block.
299315

300316
// TODO: Merge information from the new declaration into the old one as
@@ -427,23 +443,11 @@ auto HandleParseNode(Context& context, Parse::ImplDefinitionStartId node_id)
427443
BuildImplDecl(context, node_id, /*is_definition=*/true);
428444
auto& impl_info = context.impls().Get(impl_id);
429445

430-
if (impl_info.has_definition_started()) {
431-
CARBON_DIAGNOSTIC(ImplRedefinition, Error,
432-
"redefinition of `impl {0} as {1}`", InstIdAsRawType,
433-
InstIdAsRawType);
434-
CARBON_DIAGNOSTIC(ImplPreviousDefinition, Note,
435-
"previous definition was here");
436-
context.emitter()
437-
.Build(node_id, ImplRedefinition, impl_info.self_id,
438-
impl_info.constraint_id)
439-
.Note(impl_info.definition_id, ImplPreviousDefinition)
440-
.Emit();
441-
} else {
442-
impl_info.definition_id = impl_decl_id;
443-
impl_info.scope_id = context.name_scopes().Add(
444-
impl_decl_id, SemIR::NameId::Invalid,
445-
context.decl_name_stack().PeekParentScopeId());
446-
}
446+
CARBON_CHECK(!impl_info.has_definition_started());
447+
impl_info.definition_id = impl_decl_id;
448+
impl_info.scope_id =
449+
context.name_scopes().Add(impl_decl_id, SemIR::NameId::Invalid,
450+
context.decl_name_stack().PeekParentScopeId());
447451

448452
context.scope_stack().Push(
449453
impl_decl_id, impl_info.scope_id,

toolchain/check/testdata/impl/fail_redefinition.carbon

Lines changed: 17 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -45,17 +45,18 @@ impl i32 as I {}
4545
// CHECK:STDOUT: }
4646
// CHECK:STDOUT: %Core.import = import Core
4747
// CHECK:STDOUT: %I.decl: type = interface_decl @I [template = constants.%I.type] {} {}
48-
// CHECK:STDOUT: impl_decl @impl [template] {} {
49-
// CHECK:STDOUT: %int_32.loc13: Core.IntLiteral = int_value 32 [template = constants.%int_32]
50-
// CHECK:STDOUT: %i32.loc13: type = class_type @Int, @Int(constants.%int_32) [template = constants.%i32]
51-
// CHECK:STDOUT: %I.ref.loc13: type = name_ref I, file.%I.decl [template = constants.%I.type]
48+
// CHECK:STDOUT: impl_decl @impl.1 [template] {} {
49+
// CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [template = constants.%int_32]
50+
// CHECK:STDOUT: %i32: type = class_type @Int, @Int(constants.%int_32) [template = constants.%i32]
51+
// CHECK:STDOUT: %I.ref: type = name_ref I, file.%I.decl [template = constants.%I.type]
5252
// CHECK:STDOUT: }
53-
// CHECK:STDOUT: %impl_witness: <witness> = impl_witness () [template = constants.%impl_witness]
54-
// CHECK:STDOUT: impl_decl @impl [template] {} {
55-
// CHECK:STDOUT: %int_32.loc21: Core.IntLiteral = int_value 32 [template = constants.%int_32]
56-
// CHECK:STDOUT: %i32.loc21: type = class_type @Int, @Int(constants.%int_32) [template = constants.%i32]
57-
// CHECK:STDOUT: %I.ref.loc21: type = name_ref I, file.%I.decl [template = constants.%I.type]
53+
// CHECK:STDOUT: %impl_witness.loc13: <witness> = impl_witness () [template = constants.%impl_witness]
54+
// CHECK:STDOUT: impl_decl @impl.2 [template] {} {
55+
// CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [template = constants.%int_32]
56+
// CHECK:STDOUT: %i32: type = class_type @Int, @Int(constants.%int_32) [template = constants.%i32]
57+
// CHECK:STDOUT: %I.ref: type = name_ref I, file.%I.decl [template = constants.%I.type]
5858
// CHECK:STDOUT: }
59+
// CHECK:STDOUT: %impl_witness.loc21: <witness> = impl_witness () [template = constants.%impl_witness]
5960
// CHECK:STDOUT: }
6061
// CHECK:STDOUT:
6162
// CHECK:STDOUT: interface @I {
@@ -66,8 +67,13 @@ impl i32 as I {}
6667
// CHECK:STDOUT: witness = ()
6768
// CHECK:STDOUT: }
6869
// CHECK:STDOUT:
69-
// CHECK:STDOUT: impl @impl: %i32.loc13 as %I.ref.loc13 {
70+
// CHECK:STDOUT: impl @impl.1: %i32 as %I.ref {
7071
// CHECK:STDOUT: !members:
71-
// CHECK:STDOUT: witness = file.%impl_witness
72+
// CHECK:STDOUT: witness = file.%impl_witness.loc13
73+
// CHECK:STDOUT: }
74+
// CHECK:STDOUT:
75+
// CHECK:STDOUT: impl @impl.2: %i32 as %I.ref {
76+
// CHECK:STDOUT: !members:
77+
// CHECK:STDOUT: witness = file.%impl_witness.loc21
7278
// CHECK:STDOUT: }
7379
// CHECK:STDOUT:

toolchain/check/testdata/impl/no_prelude/fail_alias.carbon

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -48,15 +48,16 @@ impl AC as AI {}
4848
// CHECK:STDOUT: %AI: type = bind_alias AI, %I.decl [template = constants.%I.type]
4949
// CHECK:STDOUT: %C.ref: type = name_ref C, %C.decl [template = constants.%C]
5050
// CHECK:STDOUT: %AC: type = bind_alias AC, %C.decl [template = constants.%C]
51-
// CHECK:STDOUT: impl_decl @impl [template] {} {
52-
// CHECK:STDOUT: %AC.ref.loc17: type = name_ref AC, file.%AC [template = constants.%C]
53-
// CHECK:STDOUT: %AI.ref.loc17: type = name_ref AI, file.%AI [template = constants.%I.type]
51+
// CHECK:STDOUT: impl_decl @impl.1 [template] {} {
52+
// CHECK:STDOUT: %AC.ref: type = name_ref AC, file.%AC [template = constants.%C]
53+
// CHECK:STDOUT: %AI.ref: type = name_ref AI, file.%AI [template = constants.%I.type]
5454
// CHECK:STDOUT: }
55-
// CHECK:STDOUT: %impl_witness: <witness> = impl_witness () [template = constants.%impl_witness]
56-
// CHECK:STDOUT: impl_decl @impl [template] {} {
57-
// CHECK:STDOUT: %AC.ref.loc25: type = name_ref AC, file.%AC [template = constants.%C]
58-
// CHECK:STDOUT: %AI.ref.loc25: type = name_ref AI, file.%AI [template = constants.%I.type]
55+
// CHECK:STDOUT: %impl_witness.loc17: <witness> = impl_witness () [template = constants.%impl_witness]
56+
// CHECK:STDOUT: impl_decl @impl.2 [template] {} {
57+
// CHECK:STDOUT: %AC.ref: type = name_ref AC, file.%AC [template = constants.%C]
58+
// CHECK:STDOUT: %AI.ref: type = name_ref AI, file.%AI [template = constants.%I.type]
5959
// CHECK:STDOUT: }
60+
// CHECK:STDOUT: %impl_witness.loc25: <witness> = impl_witness () [template = constants.%impl_witness]
6061
// CHECK:STDOUT: }
6162
// CHECK:STDOUT:
6263
// CHECK:STDOUT: interface @I {
@@ -67,9 +68,14 @@ impl AC as AI {}
6768
// CHECK:STDOUT: witness = ()
6869
// CHECK:STDOUT: }
6970
// CHECK:STDOUT:
70-
// CHECK:STDOUT: impl @impl: %AC.ref.loc17 as %AI.ref.loc17 {
71+
// CHECK:STDOUT: impl @impl.1: %AC.ref as %AI.ref {
7172
// CHECK:STDOUT: !members:
72-
// CHECK:STDOUT: witness = file.%impl_witness
73+
// CHECK:STDOUT: witness = file.%impl_witness.loc17
74+
// CHECK:STDOUT: }
75+
// CHECK:STDOUT:
76+
// CHECK:STDOUT: impl @impl.2: %AC.ref as %AI.ref {
77+
// CHECK:STDOUT: !members:
78+
// CHECK:STDOUT: witness = file.%impl_witness.loc25
7379
// CHECK:STDOUT: }
7480
// CHECK:STDOUT:
7581
// CHECK:STDOUT: class @C {

0 commit comments

Comments
 (0)