From 6776e2b804d5d18a288282fd272d560570453c6c Mon Sep 17 00:00:00 2001 From: Dana Jansens Date: Thu, 7 May 2026 09:44:40 -0400 Subject: [PATCH] Detect impl redecls in non-declarative scopes (#7170) Avoid crashing on the fact that block scopes have no related InstId. So we can't push the InstId of the current scope in the ImplIntroducer node, as it can be None. Instead we have to find the parent InstId when we're building the ImplDecl, because the ImplDecl has a node at the top of the scope stack for the DeclNameStack. Impls are allowed in sequential (non-declarative) scopes (functions, blocks), but [redeclarations are not allowed](https://github.com/carbon-language/carbon-lang/blob/db24042fe56d22275aa801696e2f8f5c4171e35b/proposals/p3763.md?plain=1#L279). We now diagnose these redecls as invalid. --- toolchain/check/handle_impl.cpp | 45 +-- toolchain/check/impl.cpp | 137 ++++--- toolchain/check/node_stack.h | 2 +- toolchain/check/scope_stack.cpp | 1 + toolchain/check/scope_stack.h | 11 +- .../check/testdata/impl/forward_decls.carbon | 345 ------------------ .../impl/impl_inside_interface.carbon | 56 +++ .../check/testdata/impl/redeclaration.carbon | 85 +++++ toolchain/diagnostics/kind.def | 1 + 9 files changed, 266 insertions(+), 417 deletions(-) create mode 100644 toolchain/check/testdata/impl/impl_inside_interface.carbon diff --git a/toolchain/check/handle_impl.cpp b/toolchain/check/handle_impl.cpp index 7f2a65d34268..7cbea20964de 100644 --- a/toolchain/check/handle_impl.cpp +++ b/toolchain/check/handle_impl.cpp @@ -48,7 +48,7 @@ auto HandleParseNode(Context& context, Parse::ImplIntroducerId node_id) context.inst_block_stack().Push(); // Push the bracketing node. - context.node_stack().Push(node_id, context.scope_stack().PeekInstId()); + context.node_stack().Push(node_id); // Optional modifiers follow. context.decl_introducer_state_stack().Push(); @@ -143,11 +143,10 @@ auto HandleParseNode(Context& context, Parse::ImplDefaultSelfAsId node_id) } // Pops the parameters of an `impl`, forming a `NameComponent` with no -// associated name that describes them. The ImplIntroducer node also records the -// parent scope, which is returned. +// associated name that describes them. static auto PopImplIntroducerAndParamsAsNameComponent( Context& context, Parse::AnyImplDeclId end_of_decl_node_id) - -> std::pair { + -> NameComponent { auto [implicit_params_loc_id, implicit_param_patterns_id] = context.node_stack() .PopWithNodeIdIf(); @@ -165,8 +164,8 @@ static auto PopImplIntroducerAndParamsAsNameComponent( SemIR::InstBlockId::Empty); } - auto [first_param_node_id, parent_scope_inst_id] = - context.node_stack().PopWithNodeId(); + auto first_param_node_id = + context.node_stack().PopForSoloNodeId(); // Subtracting 1 since we don't want to include the final `{` or `;` of the // declaration when performing syntactic match. Parse::Tree::PostorderIterator last_param_iter(end_of_decl_node_id); @@ -177,20 +176,19 @@ static auto PopImplIntroducerAndParamsAsNameComponent( pattern_block_id = context.pattern_block_stack().Pop(); context.full_pattern_stack().PopFullPattern(); } - return {{.name_loc_id = Parse::NodeId::None, - .name_id = SemIR::NameId::None, - .first_param_node_id = first_param_node_id, - .last_param_node_id = *last_param_iter, - .implicit_params_loc_id = implicit_params_loc_id, - .implicit_param_patterns_id = - implicit_param_patterns_id.value_or(SemIR::InstBlockId::None), - .params_loc_id = Parse::NodeId::None, - .param_patterns_id = SemIR::InstBlockId::None, - .call_param_patterns_id = SemIR::InstBlockId::None, - .call_params_id = SemIR::InstBlockId::None, - .param_ranges = SemIR::Function::CallParamIndexRanges::Empty, - .pattern_block_id = pattern_block_id}, - parent_scope_inst_id}; + return {.name_loc_id = Parse::NodeId::None, + .name_id = SemIR::NameId::None, + .first_param_node_id = first_param_node_id, + .last_param_node_id = *last_param_iter, + .implicit_params_loc_id = implicit_params_loc_id, + .implicit_param_patterns_id = + implicit_param_patterns_id.value_or(SemIR::InstBlockId::None), + .params_loc_id = Parse::NodeId::None, + .param_patterns_id = SemIR::InstBlockId::None, + .call_param_patterns_id = SemIR::InstBlockId::None, + .call_params_id = SemIR::InstBlockId::None, + .param_ranges = SemIR::Function::CallParamIndexRanges::Empty, + .pattern_block_id = pattern_block_id}; } // Build an ImplDecl describing the signature of an impl. This handles the @@ -202,8 +200,7 @@ static auto BuildImplDecl(Context& context, Parse::AnyImplDeclId node_id) auto [self_type_node, self_type_inst_id] = context.node_stack().PopWithNodeId(); // Pop the `impl` introducer and any `forall` parameters as a "name". - auto [name, parent_scope_inst_id] = - PopImplIntroducerAndParamsAsNameComponent(context, node_id); + auto name = PopImplIntroducerAndParamsAsNameComponent(context, node_id); auto decl_block_id = context.inst_block_stack().Pop(); // Convert the constraint expression to a type. @@ -236,6 +233,10 @@ static auto BuildImplDecl(Context& context, Parse::AnyImplDeclId node_id) auto specific_interface = CheckConstraintIsInterface( context, impl_decl_id, self_type_inst_id, constraint_type_inst_id); + // The impl decl has a scope stack entry for the DeclNameStack, so we look at + // the parent scope of that. + auto parent_scope_inst_id = context.scope_stack().PeekParentInstId(); + auto impl_id = SemIR::ImplId::None; { SemIR::Impl impl = {name_context.MakeEntityWithParamsBase( diff --git a/toolchain/check/impl.cpp b/toolchain/check/impl.cpp index c8fec451cdfa..22335bfaae69 100644 --- a/toolchain/check/impl.cpp +++ b/toolchain/check/impl.cpp @@ -82,58 +82,95 @@ auto CheckAssociatedFunctionImplementation( defer_thunk_definition); } +static auto GetScopeInstId(Context& context, SemIR::InstId scope_inst_id) + -> SemIR::InstId { + if (!scope_inst_id.has_value()) { + return SemIR::InstId::None; + } + auto inst_id = context.constant_values().GetConstantInstId(scope_inst_id); + if (auto struct_val = context.insts().TryGetAs(inst_id)) { + inst_id = context.types().GetTypeInstId(struct_val->type_id); + } + return inst_id; +} + +enum class ImplRedeclType { + ValidRedecl, + Mismatch, + DiagnosedInvalidRedecl, +}; + +// Returns whether the scope of the `new_impl` is the same as the scope of the +// `prev_impl`. If the `new_impl` is in an invalid scope for a redecl, that is +// diagnosed. +static auto ScopesMatch(Context& context, const SemIR::Impl& new_impl, + const SemIR::Impl& prev_impl) -> ImplRedeclType { + auto new_id = GetScopeInstId(context, new_impl.parent_scope_inst_id); + auto prev_id = GetScopeInstId(context, prev_impl.parent_scope_inst_id); + if (new_id.has_value()) { + auto new_scope_inst = context.insts().Get(new_id); + CARBON_KIND_SWITCH(new_scope_inst) { + case CARBON_KIND(SemIR::ClassType new_scope): { + if (auto prev_scope = + context.insts().TryGetAs(prev_id)) { + if (new_scope.class_id == prev_scope->class_id) { + return ImplRedeclType::ValidRedecl; + } + } + return ImplRedeclType::Mismatch; + } + case CARBON_KIND(SemIR::GenericClassType new_scope): { + if (auto prev_scope = + context.insts().TryGetAs(prev_id)) { + if (new_scope.class_id == prev_scope->class_id) { + return ImplRedeclType::ValidRedecl; + } + } + return ImplRedeclType::Mismatch; + } + case CARBON_KIND(SemIR::Namespace new_scope): { + if (auto prev_scope = + context.insts().TryGetAs(prev_id)) { + if (new_scope.name_scope_id == prev_scope->name_scope_id) { + return ImplRedeclType::ValidRedecl; + } + } + return ImplRedeclType::Mismatch; + } + default: + break; + } + } + + // The redecl is is an invalid scope. + CARBON_DIAGNOSTIC(ImplDeclInInvalidScope, Error, + "impl redeclation not in a declarative scope; " + "redeclaration is allowed only in a class or namespace"); + context.emitter().Emit(new_impl.latest_decl_id(), ImplDeclInInvalidScope); + return ImplRedeclType::DiagnosedInvalidRedecl; +} + // Returns true if impl redeclaration parameters and scopes match. -static auto ImplRedeclMatches(Context& context, const SemIR::Impl& new_impl, - const SemIR::Impl& prev_impl) -> bool { +// +// TODO: Generalize things to validate re-declarations of other entity types, +// which also have some similar rules such as sharing scopes. +static auto VerifyImplRedecl(Context& context, const SemIR::Impl& new_impl, + const SemIR::Impl& prev_impl) -> ImplRedeclType { // If the parameters aren't the same, then this is not a redeclaration of this // `impl`. Keep looking for a prior declaration without issuing a diagnostic. if (!CheckRedeclParamsMatch(context, DeclParams(new_impl), DeclParams(prev_impl), SemIR::SpecificId::None, /*diagnose=*/false, /*check_syntax=*/true, /*check_self=*/true)) { - // NOLINTNEXTLINE(readability-simplify-boolean-expr) - return false; + return ImplRedeclType::Mismatch; } // If the scopes are different, it is not treated as a redeclaration. - auto new_scope_inst = - context.insts().Get(context.constant_values().GetConstantInstId( - new_impl.parent_scope_inst_id)); - auto prev_scope_inst = - context.insts().Get(context.constant_values().GetConstantInstId( - prev_impl.parent_scope_inst_id)); - if (new_scope_inst.kind() != prev_scope_inst.kind()) { - return false; - } - if (new_scope_inst.Is()) { - auto new_class = new_scope_inst.As(); - auto prev_class = prev_scope_inst.As(); - if (new_class.class_id != prev_class.class_id) { - return false; - } - } else if (new_scope_inst.Is()) { - auto new_namespace = new_scope_inst.As(); - auto prev_namespace = prev_scope_inst.As(); - if (new_namespace.name_scope_id != prev_namespace.name_scope_id) { - return false; - } - } else if (new_scope_inst.Is()) { - // A functions's constant value is a StructValue. - auto new_struct = new_scope_inst.As(); - auto prev_struct = prev_scope_inst.As(); - if (new_struct.type_id != prev_struct.type_id) { - return false; - } - } else { - CARBON_FATAL("unexpected scope {0} for impl", new_scope_inst); + if (auto scope_result = ScopesMatch(context, new_impl, prev_impl); + scope_result != ImplRedeclType::ValidRedecl) { + return scope_result; } - return true; -} - -static auto VerifyImplRedeclIsValid(Context& context, - const SemIR::Impl& new_impl, - const SemIR::Impl& prev_impl) -> bool { // Following #4672, disallowing defining non-extern declarations in another // file. if (auto import_ref = @@ -143,7 +180,7 @@ static auto VerifyImplRedeclIsValid(Context& context, "redeclaration of imported impl"); // TODO: Note imported declaration context.emitter().Emit(new_impl.latest_decl_id(), RedeclImportedImpl); - return false; + return ImplRedeclType::DiagnosedInvalidRedecl; } if (prev_impl.has_definition_started()) { @@ -159,10 +196,10 @@ static auto VerifyImplRedeclIsValid(Context& context, new_impl.constraint_id) .Note(prev_impl.definition_id, ImplPreviousDefinition) .Emit(); - return false; + return ImplRedeclType::DiagnosedInvalidRedecl; } - return true; + return ImplRedeclType::ValidRedecl; } // Looks for any unused generic bindings. If one is found, it is diagnosed and @@ -284,15 +321,19 @@ auto FindImplId(Context& context, const SemIR::Impl& query_impl) for (auto prev_impl_id : lookup_bucket_ref) { auto& prev_impl = context.impls().Get(prev_impl_id); - if (ImplRedeclMatches(context, query_impl, prev_impl)) { - if (!VerifyImplRedeclIsValid(context, query_impl, prev_impl)) { + auto redecl_type = VerifyImplRedecl(context, query_impl, prev_impl); + switch (redecl_type) { + case ImplRedeclType::ValidRedecl: + // Found a valid redecl. + return RedeclaredImpl{.prev_impl_id = prev_impl_id}; + case ImplRedeclType::Mismatch: + // Did not match as a redecl, try again. + break; + case ImplRedeclType::DiagnosedInvalidRedecl: // Found an invalid redecl, which has been diagnosed as such. Treat it // as a new decl, with an error. return NewImpl{.lookup_bucket = lookup_bucket_ref, .find_had_error = true}; - } - // Found a valid redecl. - return RedeclaredImpl{.prev_impl_id = prev_impl_id}; } } diff --git a/toolchain/check/node_stack.h b/toolchain/check/node_stack.h index 32306dc817b9..91132c14e448 100644 --- a/toolchain/check/node_stack.h +++ b/toolchain/check/node_stack.h @@ -416,7 +416,6 @@ class NodeStack { case Parse::NodeKind::CallExprStart: case Parse::NodeKind::FieldNameAndType: case Parse::NodeKind::IfExprThen: - case Parse::NodeKind::ImplIntroducer: case Parse::NodeKind::RequireIntroducer: case Parse::NodeKind::ShortCircuitOperandAnd: case Parse::NodeKind::ShortCircuitOperandOr: @@ -460,6 +459,7 @@ class NodeStack { case Parse::NodeKind::ForHeaderStart: case Parse::NodeKind::FunctionIntroducer: case Parse::NodeKind::IfStatementElse: + case Parse::NodeKind::ImplIntroducer: case Parse::NodeKind::ImplicitParamListStart: case Parse::NodeKind::InterfaceIntroducer: case Parse::NodeKind::LambdaIntroducer: diff --git a/toolchain/check/scope_stack.cpp b/toolchain/check/scope_stack.cpp index d7cc2d58402d..8b757d7d22c3 100644 --- a/toolchain/check/scope_stack.cpp +++ b/toolchain/check/scope_stack.cpp @@ -7,6 +7,7 @@ #include #include "common/check.h" +#include "common/find.h" #include "toolchain/check/context.h" #include "toolchain/check/unused.h" #include "toolchain/sem_ir/ids.h" diff --git a/toolchain/check/scope_stack.h b/toolchain/check/scope_stack.h index 53479bfaebd3..5a9241d5e368 100644 --- a/toolchain/check/scope_stack.h +++ b/toolchain/check/scope_stack.h @@ -88,6 +88,12 @@ class ScopeStack { // there is no such instruction, such as for a block scope. auto PeekInstId() const -> SemIR::InstId { return Peek().scope_inst_id; } + // Returns the instruction associated with the parent scope, or `None` if + // there is no such instruction, such as for a block scope. + auto PeekParentInstId() const -> SemIR::InstId { + return Peek(1).scope_inst_id; + } + // Returns the specific associated with the innermost enclosing scope that is // associated with a specific. This will generally be the self specific of the // innermost enclosing generic, as there is no way to enter any other specific @@ -280,7 +286,10 @@ class ScopeStack { SemIR::SpecificId specific_id, bool lexical_lookup_has_load_error) -> void; - auto Peek() const -> const ScopeStackEntry& { return scope_stack_.back(); } + auto Peek(int drop = 0) const -> const ScopeStackEntry& { + CARBON_DCHECK(drop < static_cast(scope_stack_.size())); + return scope_stack_[scope_stack_.size() - 1 - drop]; + } // Returns whether lexical lookup currently has any load errors. auto LexicalLookupHasLoadError() const -> bool { diff --git a/toolchain/check/testdata/impl/forward_decls.carbon b/toolchain/check/testdata/impl/forward_decls.carbon index e574bd965002..385f94118371 100644 --- a/toolchain/check/testdata/impl/forward_decls.carbon +++ b/toolchain/check/testdata/impl/forward_decls.carbon @@ -230,55 +230,6 @@ impl C as Y; impl C as X {} impl C as Y {} -// --- fail_todo_impl_in_interface_definition.carbon -library "[[@TEST_NAME]]"; - -// This test uses many unsupported features, and is expected to change. -interface I { - // CHECK:STDERR: fail_todo_impl_in_interface_definition.carbon:[[@LINE+4]]:3: error: semantics TODO: `interface modifier` [SemanticsTodo] - // CHECK:STDERR: default fn F() { - // CHECK:STDERR: ^~~~~~~ - // CHECK:STDERR: - default fn F() { - class C {} - impl C as I; - - // CHECK:STDERR: fail_todo_impl_in_interface_definition.carbon:[[@LINE+7]]:5: error: missing implementation of F in impl of interface I [ImplMissingFunction] - // CHECK:STDERR: impl C as I {} - // CHECK:STDERR: ^~~~~~~~~~~~~ - // CHECK:STDERR: fail_todo_impl_in_interface_definition.carbon:[[@LINE-7]]:3: note: associated function F declared here [AssociatedFunctionHere] - // CHECK:STDERR: default fn F() { - // CHECK:STDERR: ^~~~~~~~~~~~~~~~ - // CHECK:STDERR: - impl C as I {} - } -} - -// --- fail_todo_impl_in_interface_definition_with_associated.carbon -library "[[@TEST_NAME]]"; - -// This test uses many unsupported features, and is expected to change. -interface I { - let U:! type; - // CHECK:STDERR: fail_todo_impl_in_interface_definition_with_associated.carbon:[[@LINE+4]]:3: error: semantics TODO: `interface modifier` [SemanticsTodo] - // CHECK:STDERR: default fn F() { - // CHECK:STDERR: ^~~~~~~ - // CHECK:STDERR: - default fn F() { - class C {} - impl C as I where .U = C; - - // CHECK:STDERR: fail_todo_impl_in_interface_definition_with_associated.carbon:[[@LINE+7]]:5: error: missing implementation of F in impl of interface I [ImplMissingFunction] - // CHECK:STDERR: impl C as I where .U = C {} - // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~ - // CHECK:STDERR: fail_todo_impl_in_interface_definition_with_associated.carbon:[[@LINE-7]]:3: note: associated function F declared here [AssociatedFunctionHere] - // CHECK:STDERR: default fn F() { - // CHECK:STDERR: ^~~~~~~~~~~~~~~~ - // CHECK:STDERR: - impl C as I where .U = C {} - } -} - // CHECK:STDOUT: --- empty.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { @@ -1946,299 +1897,3 @@ interface I { // CHECK:STDOUT: !definition: // CHECK:STDOUT: } // CHECK:STDOUT: -// CHECK:STDOUT: --- fail_todo_impl_in_interface_definition.carbon -// CHECK:STDOUT: -// CHECK:STDOUT: constants { -// CHECK:STDOUT: %I.type: type = facet_type <@I> [concrete] -// CHECK:STDOUT: %Self: %I.type = symbolic_binding Self, 0 [symbolic] -// CHECK:STDOUT: %I.WithSelf.F.type.08c: type = fn_type @I.WithSelf.F, @I.WithSelf(%Self) [symbolic] -// CHECK:STDOUT: %I.WithSelf.F.705: %I.WithSelf.F.type.08c = struct_value () [symbolic] -// CHECK:STDOUT: %I.assoc_type: type = assoc_entity_type @I [concrete] -// CHECK:STDOUT: %assoc0: %I.assoc_type = assoc_entity element0, @I.WithSelf.%I.WithSelf.F.decl [concrete] -// CHECK:STDOUT: %C: type = class_type @C, @C(%Self) [symbolic] -// CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] -// CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] -// CHECK:STDOUT: %I.impl_witness: = impl_witness @C.as.I.impl.%I.impl_witness_table, @C.as.I.impl(%Self) [symbolic] -// CHECK:STDOUT: %I.facet: %I.type = facet_value %C, (%I.impl_witness) [symbolic] -// CHECK:STDOUT: %I.WithSelf.F.type.5dd: type = fn_type @I.WithSelf.F, @I.WithSelf(%I.facet) [symbolic] -// CHECK:STDOUT: %I.WithSelf.F.f5e: %I.WithSelf.F.type.5dd = struct_value () [symbolic] -// CHECK:STDOUT: } -// CHECK:STDOUT: -// CHECK:STDOUT: imports { -// CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { -// CHECK:STDOUT: import Core//prelude -// CHECK:STDOUT: import Core//prelude/... -// CHECK:STDOUT: } -// CHECK:STDOUT: } -// CHECK:STDOUT: -// CHECK:STDOUT: file { -// CHECK:STDOUT: package: = namespace [concrete] { -// CHECK:STDOUT: .Core = imports.%Core -// CHECK:STDOUT: .I = %I.decl -// CHECK:STDOUT: } -// CHECK:STDOUT: %Core.import = import Core -// CHECK:STDOUT: %I.decl: type = interface_decl @I [concrete = constants.%I.type] {} {} -// CHECK:STDOUT: } -// CHECK:STDOUT: -// CHECK:STDOUT: interface @I { -// CHECK:STDOUT: %Self: %I.type = symbolic_binding Self, 0 [symbolic = constants.%Self] -// CHECK:STDOUT: %I.WithSelf.decl = interface_with_self_decl @I [concrete] -// CHECK:STDOUT: -// CHECK:STDOUT: !with Self: -// CHECK:STDOUT: %I.WithSelf.F.decl: @I.WithSelf.%I.WithSelf.F.type (%I.WithSelf.F.type.08c) = fn_decl @I.WithSelf.F [symbolic = @I.WithSelf.%I.WithSelf.F (constants.%I.WithSelf.F.705)] {} {} -// CHECK:STDOUT: %assoc0: %I.assoc_type = assoc_entity element0, %I.WithSelf.F.decl [concrete = constants.%assoc0] -// CHECK:STDOUT: -// CHECK:STDOUT: !members: -// CHECK:STDOUT: .Self = %Self -// CHECK:STDOUT: .I = -// CHECK:STDOUT: .F = @I.WithSelf.%assoc0 -// CHECK:STDOUT: .I = -// CHECK:STDOUT: witness = (@I.WithSelf.%I.WithSelf.F.decl) -// CHECK:STDOUT: -// CHECK:STDOUT: !requires: -// CHECK:STDOUT: } -// CHECK:STDOUT: -// CHECK:STDOUT: generic impl @C.as.I.impl(@I.%Self: %I.type) { -// CHECK:STDOUT: %Self: %I.type = symbolic_binding Self, 0 [symbolic = %Self (constants.%Self)] -// CHECK:STDOUT: %C: type = class_type @C, @C(%Self) [symbolic = %C (constants.%C)] -// CHECK:STDOUT: %I.impl_witness.loc11_16.2: = impl_witness %I.impl_witness_table, @C.as.I.impl(%Self) [symbolic = %I.impl_witness.loc11_16.2 (constants.%I.impl_witness)] -// CHECK:STDOUT: -// CHECK:STDOUT: !definition: -// CHECK:STDOUT: -// CHECK:STDOUT: impl: %C.ref.loc11 as %I.ref.loc11 { -// CHECK:STDOUT: %I.impl_witness_table = impl_witness_table (), @C.as.I.impl [concrete] -// CHECK:STDOUT: %I.impl_witness.loc11_16.1: = impl_witness %I.impl_witness_table, @C.as.I.impl(constants.%Self) [symbolic = %I.impl_witness.loc11_16.2 (constants.%I.impl_witness)] -// CHECK:STDOUT: -// CHECK:STDOUT: !members: -// CHECK:STDOUT: .F = -// CHECK:STDOUT: witness = %I.impl_witness.loc11_16.1 -// CHECK:STDOUT: } -// CHECK:STDOUT: } -// CHECK:STDOUT: -// CHECK:STDOUT: generic class @C(@I.%Self: %I.type) { -// CHECK:STDOUT: !definition: -// CHECK:STDOUT: -// CHECK:STDOUT: class { -// CHECK:STDOUT: %complete_type: = complete_type_witness constants.%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: } -// CHECK:STDOUT: } -// CHECK:STDOUT: -// CHECK:STDOUT: generic fn @I.WithSelf.F(@I.%Self: %I.type) { -// CHECK:STDOUT: !definition: -// CHECK:STDOUT: %Self: %I.type = symbolic_binding Self, 0 [symbolic = %Self (constants.%Self)] -// CHECK:STDOUT: %C: type = class_type @C, @C(%Self) [symbolic = %C (constants.%C)] -// CHECK:STDOUT: -// CHECK:STDOUT: fn() { -// CHECK:STDOUT: !entry: -// CHECK:STDOUT: %C.decl: type = class_decl @C [symbolic = @I.WithSelf.F.%C (constants.%C)] {} {} -// CHECK:STDOUT: impl_decl @C.as.I.impl [concrete] {} { -// CHECK:STDOUT: %C.ref.loc11: type = name_ref C, @I.WithSelf.F.%C.decl [symbolic = %C (constants.%C)] -// CHECK:STDOUT: %I.ref.loc11: type = name_ref I, file.%I.decl [concrete = constants.%I.type] -// CHECK:STDOUT: } -// CHECK:STDOUT: impl_decl @C.as.I.impl [concrete] {} { -// CHECK:STDOUT: %C.ref.loc20: type = name_ref C, @I.WithSelf.F.%C.decl [symbolic = %C (constants.%C)] -// CHECK:STDOUT: %I.ref.loc20: type = name_ref I, file.%I.decl [concrete = constants.%I.type] -// CHECK:STDOUT: } -// CHECK:STDOUT: return -// CHECK:STDOUT: } -// CHECK:STDOUT: } -// CHECK:STDOUT: -// CHECK:STDOUT: specific @I.WithSelf(constants.%Self) { -// CHECK:STDOUT: !definition: -// CHECK:STDOUT: %Self => constants.%Self -// CHECK:STDOUT: %I.WithSelf.F.type => constants.%I.WithSelf.F.type.08c -// CHECK:STDOUT: %I.WithSelf.F => constants.%I.WithSelf.F.705 -// CHECK:STDOUT: } -// CHECK:STDOUT: -// CHECK:STDOUT: specific @I.WithSelf.F(constants.%Self) {} -// CHECK:STDOUT: -// CHECK:STDOUT: specific @C(constants.%Self) {} -// CHECK:STDOUT: -// CHECK:STDOUT: specific @C.as.I.impl(constants.%Self) { -// CHECK:STDOUT: %Self => constants.%Self -// CHECK:STDOUT: %C => constants.%C -// CHECK:STDOUT: %I.impl_witness.loc11_16.2 => constants.%I.impl_witness -// CHECK:STDOUT: } -// CHECK:STDOUT: -// CHECK:STDOUT: specific @I.WithSelf(constants.%I.facet) { -// CHECK:STDOUT: !definition: -// CHECK:STDOUT: %Self => constants.%I.facet -// CHECK:STDOUT: %I.WithSelf.F.type => constants.%I.WithSelf.F.type.5dd -// CHECK:STDOUT: %I.WithSelf.F => constants.%I.WithSelf.F.f5e -// CHECK:STDOUT: } -// CHECK:STDOUT: -// CHECK:STDOUT: --- fail_todo_impl_in_interface_definition_with_associated.carbon -// CHECK:STDOUT: -// CHECK:STDOUT: constants { -// CHECK:STDOUT: %I.type: type = facet_type <@I> [concrete] -// CHECK:STDOUT: %Self: %I.type = symbolic_binding Self, 0 [symbolic] -// CHECK:STDOUT: %I.assoc_type: type = assoc_entity_type @I [concrete] -// CHECK:STDOUT: %assoc0: %I.assoc_type = assoc_entity element0, @I.WithSelf.%U [concrete] -// CHECK:STDOUT: %I.WithSelf.F.type.08c: type = fn_type @I.WithSelf.F, @I.WithSelf(%Self) [symbolic] -// CHECK:STDOUT: %I.WithSelf.F.705: %I.WithSelf.F.type.08c = struct_value () [symbolic] -// CHECK:STDOUT: %assoc1: %I.assoc_type = assoc_entity element1, @I.WithSelf.%I.WithSelf.F.decl [concrete] -// CHECK:STDOUT: %C: type = class_type @C, @C(%Self) [symbolic] -// CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] -// CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] -// CHECK:STDOUT: %.Self: %I.type = symbolic_binding .Self [symbolic_self] -// CHECK:STDOUT: %I.WithSelf.F.type.29d: type = fn_type @I.WithSelf.F, @I.WithSelf(%.Self) [symbolic_self] -// CHECK:STDOUT: %I.WithSelf.F.8ec: %I.WithSelf.F.type.29d = struct_value () [symbolic_self] -// CHECK:STDOUT: %.Self.as_type: type = facet_access_type %.Self [symbolic_self] -// CHECK:STDOUT: %I.lookup_impl_witness: = lookup_impl_witness %.Self, @I [symbolic_self] -// CHECK:STDOUT: %impl.elem0: type = impl_witness_access %I.lookup_impl_witness, element0 [symbolic_self] -// CHECK:STDOUT: %I_where.type: type = facet_type <@I where %impl.elem0 = %C> [symbolic] -// CHECK:STDOUT: %I.impl_witness: = impl_witness @C.as.I.impl.%I.impl_witness_table, @C.as.I.impl(%Self) [symbolic] -// CHECK:STDOUT: %require_complete: = require_complete_type %I_where.type [symbolic] -// CHECK:STDOUT: %I.facet: %I.type = facet_value %C, (%I.impl_witness) [symbolic] -// CHECK:STDOUT: %I.WithSelf.F.type.000: type = fn_type @I.WithSelf.F, @I.WithSelf(%I.facet) [symbolic] -// CHECK:STDOUT: %I.WithSelf.F.a8c: %I.WithSelf.F.type.000 = struct_value () [symbolic] -// CHECK:STDOUT: } -// CHECK:STDOUT: -// CHECK:STDOUT: imports { -// CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { -// CHECK:STDOUT: import Core//prelude -// CHECK:STDOUT: import Core//prelude/... -// CHECK:STDOUT: } -// CHECK:STDOUT: } -// CHECK:STDOUT: -// CHECK:STDOUT: file { -// CHECK:STDOUT: package: = namespace [concrete] { -// CHECK:STDOUT: .Core = imports.%Core -// CHECK:STDOUT: .I = %I.decl -// CHECK:STDOUT: } -// CHECK:STDOUT: %Core.import = import Core -// CHECK:STDOUT: %I.decl: type = interface_decl @I [concrete = constants.%I.type] {} {} -// CHECK:STDOUT: } -// CHECK:STDOUT: -// CHECK:STDOUT: interface @I { -// CHECK:STDOUT: %Self: %I.type = symbolic_binding Self, 0 [symbolic = constants.%Self] -// CHECK:STDOUT: %I.WithSelf.decl = interface_with_self_decl @I [concrete] -// CHECK:STDOUT: -// CHECK:STDOUT: !with Self: -// CHECK:STDOUT: %U: type = assoc_const_decl @U [concrete] { -// CHECK:STDOUT: %assoc0: %I.assoc_type = assoc_entity element0, @I.WithSelf.%U [concrete = constants.%assoc0] -// CHECK:STDOUT: } -// CHECK:STDOUT: %I.WithSelf.F.decl: @I.WithSelf.%I.WithSelf.F.type (%I.WithSelf.F.type.08c) = fn_decl @I.WithSelf.F [symbolic = @I.WithSelf.%I.WithSelf.F (constants.%I.WithSelf.F.705)] {} {} -// CHECK:STDOUT: %assoc1: %I.assoc_type = assoc_entity element1, %I.WithSelf.F.decl [concrete = constants.%assoc1] -// CHECK:STDOUT: -// CHECK:STDOUT: !members: -// CHECK:STDOUT: .Self = %Self -// CHECK:STDOUT: .I = -// CHECK:STDOUT: .U = @U.%assoc0 -// CHECK:STDOUT: .F = @I.WithSelf.%assoc1 -// CHECK:STDOUT: .I = -// CHECK:STDOUT: witness = (@I.WithSelf.%U, @I.WithSelf.%I.WithSelf.F.decl) -// CHECK:STDOUT: -// CHECK:STDOUT: !requires: -// CHECK:STDOUT: } -// CHECK:STDOUT: -// CHECK:STDOUT: generic impl @C.as.I.impl(@I.%Self: %I.type) { -// CHECK:STDOUT: %Self: %I.type = symbolic_binding Self, 0 [symbolic = %Self (constants.%Self)] -// CHECK:STDOUT: %C: type = class_type @C, @C(%Self) [symbolic = %C (constants.%C)] -// CHECK:STDOUT: %I_where.type: type = facet_type <@I where constants.%impl.elem0 = %C> [symbolic = %I_where.type (constants.%I_where.type)] -// CHECK:STDOUT: %I.impl_witness.loc12_29.2: = impl_witness %I.impl_witness_table, @C.as.I.impl(%Self) [symbolic = %I.impl_witness.loc12_29.2 (constants.%I.impl_witness)] -// CHECK:STDOUT: -// CHECK:STDOUT: !definition: -// CHECK:STDOUT: %require_complete: = require_complete_type %I_where.type [symbolic = %require_complete (constants.%require_complete)] -// CHECK:STDOUT: -// CHECK:STDOUT: impl: %C.ref.loc12_10 as %.loc12_17 { -// CHECK:STDOUT: %I.impl_witness_table = impl_witness_table (%impl_witness_assoc_constant, ), @C.as.I.impl [concrete] -// CHECK:STDOUT: %I.impl_witness.loc12_29.1: = impl_witness %I.impl_witness_table, @C.as.I.impl(constants.%Self) [symbolic = %I.impl_witness.loc12_29.2 (constants.%I.impl_witness)] -// CHECK:STDOUT: %impl_witness_assoc_constant: type = impl_witness_assoc_constant constants.%C [symbolic = %C (constants.%C)] -// CHECK:STDOUT: -// CHECK:STDOUT: !members: -// CHECK:STDOUT: .F = -// CHECK:STDOUT: witness = %I.impl_witness.loc12_29.1 -// CHECK:STDOUT: } -// CHECK:STDOUT: } -// CHECK:STDOUT: -// CHECK:STDOUT: generic class @C(@I.%Self: %I.type) { -// CHECK:STDOUT: !definition: -// CHECK:STDOUT: -// CHECK:STDOUT: class { -// CHECK:STDOUT: %complete_type: = complete_type_witness constants.%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: } -// CHECK:STDOUT: } -// CHECK:STDOUT: -// CHECK:STDOUT: generic fn @I.WithSelf.F(@I.%Self: %I.type) { -// CHECK:STDOUT: !definition: -// CHECK:STDOUT: %Self: %I.type = symbolic_binding Self, 0 [symbolic = %Self (constants.%Self)] -// CHECK:STDOUT: %C: type = class_type @C, @C(%Self) [symbolic = %C (constants.%C)] -// CHECK:STDOUT: -// CHECK:STDOUT: fn() { -// CHECK:STDOUT: !entry: -// CHECK:STDOUT: %C.decl: type = class_decl @C [symbolic = @I.WithSelf.F.%C (constants.%C)] {} {} -// CHECK:STDOUT: impl_decl @C.as.I.impl [concrete] {} { -// CHECK:STDOUT: %C.ref.loc12_10: type = name_ref C, @I.WithSelf.F.%C.decl [symbolic = %C (constants.%C)] -// CHECK:STDOUT: %I.ref.loc12: type = name_ref I, file.%I.decl [concrete = constants.%I.type] -// CHECK:STDOUT: %.Self.loc12: %I.type = symbolic_binding .Self [symbolic_self = constants.%.Self] -// CHECK:STDOUT: %.Self.ref.loc12: %I.type = name_ref .Self, %.Self.loc12 [symbolic_self = constants.%.Self] -// CHECK:STDOUT: %.Self.as_type.loc12: type = facet_access_type %.Self.ref.loc12 [symbolic_self = constants.%.Self.as_type] -// CHECK:STDOUT: %.loc12_23: type = converted %.Self.ref.loc12, %.Self.as_type.loc12 [symbolic_self = constants.%.Self.as_type] -// CHECK:STDOUT: %U.ref.loc12: %I.assoc_type = name_ref U, @U.%assoc0 [concrete = constants.%assoc0] -// CHECK:STDOUT: %impl.elem0.loc12: type = impl_witness_access constants.%I.lookup_impl_witness, element0 [symbolic_self = constants.%impl.elem0] -// CHECK:STDOUT: %C.ref.loc12_28: type = name_ref C, @I.WithSelf.F.%C.decl [symbolic = %C (constants.%C)] -// CHECK:STDOUT: %.loc12_17: type = where_expr [symbolic = %I_where.type (constants.%I_where.type)] { -// CHECK:STDOUT: requirement_base_facet_type %I.ref.loc12 -// CHECK:STDOUT: requirement_rewrite %impl.elem0.loc12, %C.ref.loc12_28 -// CHECK:STDOUT: } -// CHECK:STDOUT: } -// CHECK:STDOUT: impl_decl @C.as.I.impl [concrete] {} { -// CHECK:STDOUT: %C.ref.loc21_10: type = name_ref C, @I.WithSelf.F.%C.decl [symbolic = %C (constants.%C)] -// CHECK:STDOUT: %I.ref.loc21: type = name_ref I, file.%I.decl [concrete = constants.%I.type] -// CHECK:STDOUT: %.Self.loc21: %I.type = symbolic_binding .Self [symbolic_self = constants.%.Self] -// CHECK:STDOUT: %.Self.ref.loc21: %I.type = name_ref .Self, %.Self.loc21 [symbolic_self = constants.%.Self] -// CHECK:STDOUT: %.Self.as_type.loc21: type = facet_access_type %.Self.ref.loc21 [symbolic_self = constants.%.Self.as_type] -// CHECK:STDOUT: %.loc21_23: type = converted %.Self.ref.loc21, %.Self.as_type.loc21 [symbolic_self = constants.%.Self.as_type] -// CHECK:STDOUT: %U.ref.loc21: %I.assoc_type = name_ref U, @U.%assoc0 [concrete = constants.%assoc0] -// CHECK:STDOUT: %impl.elem0.loc21: type = impl_witness_access constants.%I.lookup_impl_witness, element0 [symbolic_self = constants.%impl.elem0] -// CHECK:STDOUT: %C.ref.loc21_28: type = name_ref C, @I.WithSelf.F.%C.decl [symbolic = %C (constants.%C)] -// CHECK:STDOUT: %.loc21_17: type = where_expr [symbolic = %I_where.type (constants.%I_where.type)] { -// CHECK:STDOUT: requirement_base_facet_type %I.ref.loc21 -// CHECK:STDOUT: requirement_rewrite %impl.elem0.loc21, %C.ref.loc21_28 -// CHECK:STDOUT: } -// CHECK:STDOUT: } -// CHECK:STDOUT: return -// CHECK:STDOUT: } -// CHECK:STDOUT: } -// CHECK:STDOUT: -// CHECK:STDOUT: specific @I.WithSelf(constants.%Self) { -// CHECK:STDOUT: !definition: -// CHECK:STDOUT: %Self => constants.%Self -// CHECK:STDOUT: %I.WithSelf.F.type => constants.%I.WithSelf.F.type.08c -// CHECK:STDOUT: %I.WithSelf.F => constants.%I.WithSelf.F.705 -// CHECK:STDOUT: } -// CHECK:STDOUT: -// CHECK:STDOUT: specific @I.WithSelf.F(constants.%Self) {} -// CHECK:STDOUT: -// CHECK:STDOUT: specific @C(constants.%Self) {} -// CHECK:STDOUT: -// CHECK:STDOUT: specific @I.WithSelf(constants.%.Self) { -// CHECK:STDOUT: !definition: -// CHECK:STDOUT: %Self => constants.%.Self -// CHECK:STDOUT: %I.WithSelf.F.type => constants.%I.WithSelf.F.type.29d -// CHECK:STDOUT: %I.WithSelf.F => constants.%I.WithSelf.F.8ec -// CHECK:STDOUT: } -// CHECK:STDOUT: -// CHECK:STDOUT: specific @C.as.I.impl(constants.%Self) { -// CHECK:STDOUT: %Self => constants.%Self -// CHECK:STDOUT: %C => constants.%C -// CHECK:STDOUT: %I_where.type => constants.%I_where.type -// CHECK:STDOUT: %I.impl_witness.loc12_29.2 => constants.%I.impl_witness -// CHECK:STDOUT: } -// CHECK:STDOUT: -// CHECK:STDOUT: specific @I.WithSelf(constants.%I.facet) { -// CHECK:STDOUT: !definition: -// CHECK:STDOUT: %Self => constants.%I.facet -// CHECK:STDOUT: %I.WithSelf.F.type => constants.%I.WithSelf.F.type.000 -// CHECK:STDOUT: %I.WithSelf.F => constants.%I.WithSelf.F.a8c -// CHECK:STDOUT: } -// CHECK:STDOUT: diff --git a/toolchain/check/testdata/impl/impl_inside_interface.carbon b/toolchain/check/testdata/impl/impl_inside_interface.carbon new file mode 100644 index 000000000000..8e15ce24dcba --- /dev/null +++ b/toolchain/check/testdata/impl/impl_inside_interface.carbon @@ -0,0 +1,56 @@ +// 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/testdata/min_prelude/none.carbon +// +// AUTOUPDATE +// TIP: To test this file alone, run: +// TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/impl/impl_inside_interface.carbon +// TIP: To dump output, run: +// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/impl/impl_inside_interface.carbon + +// --- fail_todo_impl_in_interface_definition.carbon +library "[[@TEST_NAME]]"; + +// This test uses many unsupported features, and is expected to change. +interface I { + // CHECK:STDERR: fail_todo_impl_in_interface_definition.carbon:[[@LINE+4]]:3: error: semantics TODO: `interface modifier` [SemanticsTodo] + // CHECK:STDERR: default fn F() { + // CHECK:STDERR: ^~~~~~~ + // CHECK:STDERR: + default fn F() { + class C {} + // CHECK:STDERR: fail_todo_impl_in_interface_definition.carbon:[[@LINE+7]]:5: error: missing implementation of F in impl of interface I [ImplMissingFunction] + // CHECK:STDERR: impl C as I {} + // CHECK:STDERR: ^~~~~~~~~~~~~ + // CHECK:STDERR: fail_todo_impl_in_interface_definition.carbon:[[@LINE-5]]:3: note: associated function F declared here [AssociatedFunctionHere] + // CHECK:STDERR: default fn F() { + // CHECK:STDERR: ^~~~~~~~~~~~~~~~ + // CHECK:STDERR: + impl C as I {} + } +} + +// --- fail_todo_impl_in_interface_definition_with_associated.carbon +library "[[@TEST_NAME]]"; + +// This test uses many unsupported features, and is expected to change. +interface I { + let U:! type; + // CHECK:STDERR: fail_todo_impl_in_interface_definition_with_associated.carbon:[[@LINE+4]]:3: error: semantics TODO: `interface modifier` [SemanticsTodo] + // CHECK:STDERR: default fn F() { + // CHECK:STDERR: ^~~~~~~ + // CHECK:STDERR: + default fn F() { + class C {} + // CHECK:STDERR: fail_todo_impl_in_interface_definition_with_associated.carbon:[[@LINE+7]]:5: error: missing implementation of F in impl of interface I [ImplMissingFunction] + // CHECK:STDERR: impl C as I where .U = C {} + // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~ + // CHECK:STDERR: fail_todo_impl_in_interface_definition_with_associated.carbon:[[@LINE-5]]:3: note: associated function F declared here [AssociatedFunctionHere] + // CHECK:STDERR: default fn F() { + // CHECK:STDERR: ^~~~~~~~~~~~~~~~ + // CHECK:STDERR: + impl C as I where .U = C {} + } +} diff --git a/toolchain/check/testdata/impl/redeclaration.carbon b/toolchain/check/testdata/impl/redeclaration.carbon index 1539baf53e9d..f046bd9d5e8c 100644 --- a/toolchain/check/testdata/impl/redeclaration.carbon +++ b/toolchain/check/testdata/impl/redeclaration.carbon @@ -237,3 +237,88 @@ class D { // CHECK:STDERR: impl C as Z {} } + +// --- fail_redecl_in_fn_scope.carbon +library "[[@TEST_NAME]]"; + +interface Z {} + +fn F() { + class C {} + impl C as Z; + // CHECK:STDERR: fail_redecl_in_fn_scope.carbon:[[@LINE+8]]:3: error: impl redeclation not in a declarative scope; redeclaration is allowed only in a class or namespace [ImplDeclInInvalidScope] + // CHECK:STDERR: impl C as Z {} + // CHECK:STDERR: ^~~~~~~~~~~~~ + // CHECK:STDERR: + // CHECK:STDERR: fail_redecl_in_fn_scope.carbon:[[@LINE-5]]:3: error: impl declared but not defined [ImplMissingDefinition] + // CHECK:STDERR: impl C as Z; + // CHECK:STDERR: ^~~~~~~~~~~~ + // CHECK:STDERR: + impl C as Z {} +} + +// --- fail_redecl_in_different_fn_scope.carbon +library "[[@TEST_NAME]]"; + +interface Z {} +class C {} + +fn F() { + impl C as Z; +} + +fn G() { + // CHECK:STDERR: fail_redecl_in_different_fn_scope.carbon:[[@LINE+8]]:3: error: impl redeclation not in a declarative scope; redeclaration is allowed only in a class or namespace [ImplDeclInInvalidScope] + // CHECK:STDERR: impl C as Z {} + // CHECK:STDERR: ^~~~~~~~~~~~~ + // CHECK:STDERR: + // CHECK:STDERR: fail_redecl_in_different_fn_scope.carbon:[[@LINE-8]]:3: error: impl declared but not defined [ImplMissingDefinition] + // CHECK:STDERR: impl C as Z; + // CHECK:STDERR: ^~~~~~~~~~~~ + // CHECK:STDERR: + impl C as Z {} +} + +// --- fail_redecl_in_block_scope.carbon +library "[[@TEST_NAME]]"; + +interface Z {} + +fn F() { + if (true) { + class C {} + impl C as Z; + // CHECK:STDERR: fail_redecl_in_block_scope.carbon:[[@LINE+8]]:5: error: impl redeclation not in a declarative scope; redeclaration is allowed only in a class or namespace [ImplDeclInInvalidScope] + // CHECK:STDERR: impl C as Z {} + // CHECK:STDERR: ^~~~~~~~~~~~~ + // CHECK:STDERR: + // CHECK:STDERR: fail_redecl_in_block_scope.carbon:[[@LINE-5]]:5: error: impl declared but not defined [ImplMissingDefinition] + // CHECK:STDERR: impl C as Z; + // CHECK:STDERR: ^~~~~~~~~~~~ + // CHECK:STDERR: + impl C as Z {} + } +} + +// --- fail_redecl_in_different_block_scope.carbon +library "[[@TEST_NAME]]"; + +interface Z {} +class C {} + +fn F() { + if (true) { + impl C as Z; + } + if (true) { + // CHECK:STDERR: fail_redecl_in_different_block_scope.carbon:[[@LINE+8]]:5: error: impl redeclation not in a declarative scope; redeclaration is allowed only in a class or namespace [ImplDeclInInvalidScope] + // CHECK:STDERR: impl C as Z {} + // CHECK:STDERR: ^~~~~~~~~~~~~ + // CHECK:STDERR: + // CHECK:STDERR: fail_redecl_in_different_block_scope.carbon:[[@LINE-7]]:5: error: impl declared but not defined [ImplMissingDefinition] + // CHECK:STDERR: impl C as Z; + // CHECK:STDERR: ^~~~~~~~~~~~ + // CHECK:STDERR: + impl C as Z {} + } +} diff --git a/toolchain/diagnostics/kind.def b/toolchain/diagnostics/kind.def index 9e4f1cf11158..f227971de3a3 100644 --- a/toolchain/diagnostics/kind.def +++ b/toolchain/diagnostics/kind.def @@ -352,6 +352,7 @@ CARBON_DIAGNOSTIC_KIND(ImplAsIncompleteFacetTypeDefinition) CARBON_DIAGNOSTIC_KIND(ImplAsNonFacetType) CARBON_DIAGNOSTIC_KIND(ImplAsOutsideClass) CARBON_DIAGNOSTIC_KIND(ImplAssociatedConstantNeedsValue) +CARBON_DIAGNOSTIC_KIND(ImplDeclInInvalidScope) CARBON_DIAGNOSTIC_KIND(ImplFunctionWithNonFunction) CARBON_DIAGNOSTIC_KIND(ImplIsOrphan) CARBON_DIAGNOSTIC_KIND(ImplMissingDefinition)