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)