From efec4e4658556e4691d2ce107ee7ccd1ffb69df8 Mon Sep 17 00:00:00 2001 From: Dana Jansens Date: Tue, 16 Dec 2025 10:32:56 -0500 Subject: [PATCH] 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. --- toolchain/check/facet_type.cpp | 182 ----- toolchain/check/facet_type.h | 28 - toolchain/check/handle_impl.cpp | 91 ++- toolchain/check/impl.cpp | 696 +++++++++++------- toolchain/check/impl.h | 58 +- .../check/testdata/impl/extend_impl.carbon | 2 +- 6 files changed, 531 insertions(+), 526 deletions(-) diff --git a/toolchain/check/facet_type.cpp b/toolchain/check/facet_type.cpp index 910def377996..5f47f993c1e4 100644 --- a/toolchain/check/facet_type.cpp +++ b/toolchain/check/facet_type.cpp @@ -43,15 +43,6 @@ auto FacetTypeFromNamedConstraint(Context& context, return {.type_id = SemIR::TypeType::TypeId, .facet_type_id = facet_type_id}; } -// Returns whether the `LookupImplWitness` of `witness_id` matches `interface`. -static auto WitnessQueryMatchesInterface( - Context& context, SemIR::InstId witness_id, - const SemIR::SpecificInterface& interface) -> bool { - auto lookup = context.insts().GetAs(witness_id); - return interface == - context.specific_interfaces().Get(lookup.query_specific_interface_id); -} - auto GetImplWitnessAccessWithoutSubstitution(Context& context, SemIR::InstId inst_id) -> SemIR::InstId { @@ -62,179 +53,6 @@ auto GetImplWitnessAccessWithoutSubstitution(Context& context, return inst_id; } -auto InitialFacetTypeImplWitness( - Context& context, SemIR::LocId witness_loc_id, - SemIR::TypeInstId facet_type_inst_id, SemIR::TypeInstId self_type_inst_id, - const SemIR::SpecificInterface& interface_to_witness, - SemIR::SpecificId self_specific_id) -> SemIR::InstId { - auto facet_type_id = - context.types().GetTypeIdForTypeInstId(facet_type_inst_id); - CARBON_CHECK(facet_type_id != SemIR::ErrorInst::TypeId); - auto facet_type = context.types().GetAs(facet_type_id); - const auto& facet_type_info = - context.facet_types().Get(facet_type.facet_type_id); - - // An iterator over the rewrite_constraints where the LHS of the rewrite names - // a member of the `interface_to_witness`. This filters out rewrites of names - // from other interfaces, as they do not set values in the witness table. - auto rewrites_into_interface_to_witness = llvm::make_filter_range( - facet_type_info.rewrite_constraints, - [&](const SemIR::FacetTypeInfo::RewriteConstraint& rewrite) { - auto access = context.insts().GetAs( - GetImplWitnessAccessWithoutSubstitution(context, rewrite.lhs_id)); - return WitnessQueryMatchesInterface(context, access.witness_id, - interface_to_witness); - }); - - if (rewrites_into_interface_to_witness.empty()) { - // The witness table is not needed until the definition. Make a placeholder - // for the declaration. - auto witness_table_inst_id = AddInst( - context, witness_loc_id, - {.elements_id = context.inst_blocks().AddPlaceholder(), - .impl_id = SemIR::ImplId::None}); - return AddInst( - context, witness_loc_id, - {.type_id = GetSingletonType(context, SemIR::WitnessType::TypeInstId), - .witness_table_id = witness_table_inst_id, - .specific_id = self_specific_id}); - } - - const auto& interface = - context.interfaces().Get(interface_to_witness.interface_id); - if (!interface.is_complete()) { - // This is a declaration with rewrite constraints into `.Self`, but the - // interface is not complete. Those rewrites have already been diagnosed as - // an error in their member access. - return SemIR::ErrorInst::InstId; - } - - auto assoc_entities = - context.inst_blocks().Get(interface.associated_entities_id); - // TODO: When this function is used for things other than just impls, may want - // to only load the specific associated entities that are mentioned in rewrite - // rules. - for (auto decl_id : assoc_entities) { - LoadImportRef(context, decl_id); - } - - SemIR::InstId witness_inst_id = SemIR::InstId::None; - llvm::MutableArrayRef table; - { - auto elements_id = - context.inst_blocks().AddUninitialized(assoc_entities.size()); - table = context.inst_blocks().GetMutable(elements_id); - for (auto& uninit : table) { - uninit = SemIR::InstId::ImplWitnessTablePlaceholder; - } - - auto witness_table_inst_id = AddInst( - context, witness_loc_id, - {.elements_id = elements_id, .impl_id = SemIR::ImplId::None}); - - witness_inst_id = AddInst( - context, witness_loc_id, - {.type_id = GetSingletonType(context, SemIR::WitnessType::TypeInstId), - .witness_table_id = witness_table_inst_id, - .specific_id = self_specific_id}); - } - - for (auto rewrite : rewrites_into_interface_to_witness) { - auto access = context.insts().GetAs( - GetImplWitnessAccessWithoutSubstitution(context, rewrite.lhs_id)); - auto& table_entry = table[access.index.index]; - if (table_entry == SemIR::ErrorInst::InstId) { - // Don't overwrite an error value. This prioritizes not generating - // multiple errors for one associated constant over picking a value - // for it to use to attempt recovery. - continue; - } - auto rewrite_inst_id = rewrite.rhs_id; - if (rewrite_inst_id == SemIR::ErrorInst::InstId) { - table_entry = SemIR::ErrorInst::InstId; - continue; - } - - auto decl_id = context.constant_values().GetConstantInstId( - assoc_entities[access.index.index]); - CARBON_CHECK(decl_id.has_value(), "Non-constant associated entity"); - if (decl_id == SemIR::ErrorInst::InstId) { - table_entry = SemIR::ErrorInst::InstId; - continue; - } - - auto assoc_constant_decl = - context.insts().TryGetAs(decl_id); - if (!assoc_constant_decl) { - auto type_id = context.insts().Get(decl_id).type_id(); - auto type_inst = context.types().GetAsInst(type_id); - auto fn_type = type_inst.As(); - const auto& fn = context.functions().Get(fn_type.function_id); - CARBON_DIAGNOSTIC(RewriteForAssociatedFunction, Error, - "rewrite specified for associated function {0}", - SemIR::NameId); - context.emitter().Emit(facet_type_inst_id, RewriteForAssociatedFunction, - fn.name_id); - table_entry = SemIR::ErrorInst::InstId; - continue; - } - - // FacetTypes resolution disallows two rewrites to the same associated - // constant, so we won't ever have a facet write twice to the same position - // in the witness table. - CARBON_CHECK(table_entry == SemIR::InstId::ImplWitnessTablePlaceholder); - - // If the associated constant has a symbolic type, convert the rewrite - // value to that type now we know the value of `Self`. - SemIR::TypeId assoc_const_type_id = assoc_constant_decl->type_id; - if (assoc_const_type_id.is_symbolic()) { - // Get the type of the associated constant in this interface with this - // value for `Self`. - assoc_const_type_id = GetTypeForSpecificAssociatedEntity( - context, SemIR::LocId(facet_type_inst_id), - interface_to_witness.specific_id, decl_id, - context.types().GetTypeIdForTypeInstId(self_type_inst_id), - witness_inst_id); - // Perform the conversion of the value to the type. We skipped this when - // forming the facet type because the type of the associated constant - // was symbolic. - auto converted_inst_id = - ConvertToValueOfType(context, SemIR::LocId(facet_type_inst_id), - rewrite_inst_id, assoc_const_type_id); - // Canonicalize the converted constant value. - converted_inst_id = - context.constant_values().GetConstantInstId(converted_inst_id); - // The result of conversion can be non-constant even if the original - // value was constant. - if (converted_inst_id.has_value()) { - rewrite_inst_id = converted_inst_id; - } else { - const auto& assoc_const = context.associated_constants().Get( - assoc_constant_decl->assoc_const_id); - CARBON_DIAGNOSTIC( - AssociatedConstantNotConstantAfterConversion, Error, - "associated constant {0} given value {1} that is not constant " - "after conversion to {2}", - SemIR::NameId, InstIdAsConstant, SemIR::TypeId); - context.emitter().Emit( - facet_type_inst_id, AssociatedConstantNotConstantAfterConversion, - assoc_const.name_id, rewrite_inst_id, assoc_const_type_id); - rewrite_inst_id = SemIR::ErrorInst::InstId; - } - } - - CARBON_CHECK(rewrite_inst_id == context.constant_values().GetConstantInstId( - rewrite_inst_id), - "Rewritten value for associated constant is not canonical."); - - table_entry = AddInst( - context, witness_loc_id, - {.type_id = context.insts().Get(rewrite_inst_id).type_id(), - .inst_id = rewrite_inst_id}); - } - return witness_inst_id; -} - // A mapping of each associated constant (represented as `ImplWitnessAccess`) to // its value (represented as an `InstId`). Used to track rewrite constraints, // with the LHS mapping to the resolved value of the RHS. diff --git a/toolchain/check/facet_type.h b/toolchain/check/facet_type.h index b5ec699dc689..d5eb25a2d505 100644 --- a/toolchain/check/facet_type.h +++ b/toolchain/check/facet_type.h @@ -39,34 +39,6 @@ auto GetImplWitnessAccessWithoutSubstitution(Context& context, SemIR::InstId inst_id) -> SemIR::InstId; -// Creates and returns an impl witness instruction for an impl declaration. -// -// If there are no rewrites into a name of the interface being implemented, a -// placeholder witness table is created, to be replaced in the impl definition. -// -// Adds and returns an `ImplWitness` instruction (created with location set to -// `witness_loc_id`) that shows "`Self` type" of type "facet type" (the value of -// the `facet_type_inst_id` instruction) implements interface -// `interface_to_witness`, which must be an interface required by "facet type" -// (as determined by `RequireIdentifiedFacetType`). This witness reflects the -// values assigned to associated constant members of that interface by rewrite -// constraints in the facet type. `self_specific_id` will be the `specific_id` -// of the resulting witness. -// -// `self_type_inst_id` is an instruction that evaluates to the `Self` type of -// the facet type. For example, in `T:! X where ...`, we will bind the `.Self` -// of the `where` facet type to `T`, and in `(X where ...) where ...`, we will -// bind the inner `.Self` to the outer `.Self`. -// -// If the facet type contains a rewrite, we may have deferred converting the -// rewritten value to the type of the associated constant. That conversion will -// also be performed as part of resolution, and may depend on the `Self` type. -auto InitialFacetTypeImplWitness( - Context& context, SemIR::LocId witness_loc_id, - SemIR::TypeInstId facet_type_inst_id, SemIR::TypeInstId self_type_inst_id, - const SemIR::SpecificInterface& interface_to_witness, - SemIR::SpecificId self_specific_id) -> SemIR::InstId; - // Perform rewrite constraint resolution for a facet type. The rewrite // constraints resolution is described here: // https://docs.carbon-lang.dev/docs/design/generics/appendix-rewrite-constraints.html#rewrite-constraint-resolution diff --git a/toolchain/check/handle_impl.cpp b/toolchain/check/handle_impl.cpp index 26b2e1230ffe..fba77b6316fc 100644 --- a/toolchain/check/handle_impl.cpp +++ b/toolchain/check/handle_impl.cpp @@ -5,6 +5,7 @@ #include #include +#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(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(); - - FinishImplWitness(context, impl_id); - auto& impl = context.impls().Get(impl_id); + + FinishImplWitness(context, impl); + impl.defined = true; FinishGenericDefinition(context, impl.generic_id); diff --git a/toolchain/check/impl.cpp b/toolchain/check/impl.cpp index da5dfbd544ec..89e798d602d1 100644 --- a/toolchain/check/impl.cpp +++ b/toolchain/check/impl.cpp @@ -6,6 +6,7 @@ #include "toolchain/base/kind_switch.h" #include "toolchain/check/context.h" +#include "toolchain/check/convert.h" #include "toolchain/check/deduce.h" #include "toolchain/check/eval.h" #include "toolchain/check/facet_type.h" @@ -85,21 +86,421 @@ auto CheckAssociatedFunctionImplementation( defer_thunk_definition); } -// Builds an initial witness from the rewrites in the facet type, if any. -auto ImplWitnessForDeclaration(Context& context, const SemIR::Impl& impl) - -> SemIR::InstId { - CARBON_CHECK(!impl.has_definition_started()); +// Returns true if impl redeclaration parameters match. +static auto CheckImplRedeclParamsMatch(Context& context, + const SemIR::Impl& new_impl, + SemIR::ImplId prev_impl_id) -> bool { + auto& prev_impl = context.impls().Get(prev_impl_id); - auto self_type_id = context.types().GetTypeIdForTypeInstId(impl.self_id); - if (self_type_id == SemIR::ErrorInst::TypeId) { - // When 'impl as' is invalid, the self type is an error. + // 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 true; +} + +// Returns whether an impl can be redeclared. For example, defined impls +// cannot be redeclared. +static auto IsValidImplRedecl(Context& context, const SemIR::Impl& new_impl, + SemIR::ImplId prev_impl_id) -> bool { + auto& prev_impl = context.impls().Get(prev_impl_id); + + // TODO: Following #3763, disallow redeclarations in different scopes. + + // Following #4672, disallowing defining non-extern declarations in another + // file. + if (auto import_ref = + context.insts().TryGetAs(prev_impl.self_id)) { + // TODO: Handle extern. + CARBON_DIAGNOSTIC(RedeclImportedImpl, Error, + "redeclaration of imported impl"); + // TODO: Note imported declaration + context.emitter().Emit(new_impl.latest_decl_id(), RedeclImportedImpl); + return false; + } + + if (prev_impl.has_definition_started()) { + // Impls aren't merged in order to avoid generic region lookup into a + // mismatching table. + CARBON_DIAGNOSTIC(ImplRedefinition, Error, + "redefinition of `impl {0} as {1}`", InstIdAsRawType, + InstIdAsRawType); + CARBON_DIAGNOSTIC(ImplPreviousDefinition, Note, + "previous definition was here"); + context.emitter() + .Build(new_impl.latest_decl_id(), ImplRedefinition, new_impl.self_id, + new_impl.constraint_id) + .Note(prev_impl.definition_id, ImplPreviousDefinition) + .Emit(); + return false; + } + + // TODO: Only allow redeclaration in a match_first/impl_priority block. + + return true; +} + +// Looks for any unused generic bindings. If one is found, it is diagnosed and +// false is returned. +static auto VerifyAllGenericBindingsUsed(Context& context, SemIR::LocId loc_id, + SemIR::LocId implicit_params_loc_id, + SemIR::Impl& impl) -> bool { + if (impl.witness_id == SemIR::ErrorInst::InstId) { + return true; + } + if (!impl.generic_id.has_value()) { + return true; + } + + if (impl.implicit_param_patterns_id.has_value()) { + for (auto inst_id : + context.inst_blocks().Get(impl.implicit_param_patterns_id)) { + if (inst_id == SemIR::ErrorInst::InstId) { + // An error was already diagnosed for a generic binding. + return true; + } + } + } + + auto deduced_specific_id = DeduceImplArguments( + context, loc_id, impl, context.constant_values().Get(impl.self_id), + impl.interface.specific_id); + if (deduced_specific_id.has_value()) { + // Deduction succeeded, all bindings were used. + return true; + } + + CARBON_DIAGNOSTIC(ImplUnusedBinding, Error, + "`impl` with unused generic binding"); + // TODO: This location may be incorrect, the binding may be inherited + // from an outer declaration. It would be nice to get the particular + // binding that was undeducible back from DeduceImplArguments here and + // use that. + auto diag_loc_id = + implicit_params_loc_id.has_value() ? implicit_params_loc_id : loc_id; + context.emitter().Emit(diag_loc_id, ImplUnusedBinding); + return false; +} + +// Apply an `extend impl` declaration by extending the parent scope with the +// `impl`. If there's an error it is diagnosed and false is returned. +static auto ApplyExtendImplAs(Context& context, SemIR::LocId loc_id, + const SemIR::Impl& impl, + Parse::NodeId extend_node, + SemIR::LocId implicit_params_loc_id) -> bool { + auto parent_scope_id = context.decl_name_stack().PeekParentScopeId(); + + // TODO: Also handle the parent scope being a mixin. + auto class_scope = TryAsClassScope(context, parent_scope_id); + if (!class_scope) { + if (impl.witness_id != SemIR::ErrorInst::InstId) { + CARBON_DIAGNOSTIC( + ExtendImplOutsideClass, Error, + "`extend impl` can only be used in an interface or class"); + context.emitter().Emit(loc_id, ExtendImplOutsideClass); + } + return false; + } + + auto& parent_scope = *class_scope->name_scope; + + // An error was already diagnosed, but this is `extend impl as` inside a + // class, so propagate the error into the enclosing class scope. + if (impl.witness_id == SemIR::ErrorInst::InstId) { + parent_scope.set_has_error(); + return false; + } + + if (implicit_params_loc_id.has_value()) { + CARBON_DIAGNOSTIC(ExtendImplForall, Error, + "cannot `extend` a parameterized `impl`"); + context.emitter().Emit(extend_node, ExtendImplForall); + parent_scope.set_has_error(); + return false; + } + + if (!RequireCompleteType( + context, context.types().GetTypeIdForTypeInstId(impl.constraint_id), + SemIR::LocId(impl.constraint_id), [&] { + CARBON_DIAGNOSTIC(ExtendImplAsIncomplete, Error, + "`extend impl as` incomplete facet type {0}", + InstIdAsType); + return context.emitter().Build(impl.latest_decl_id(), + ExtendImplAsIncomplete, + impl.constraint_id); + })) { + parent_scope.set_has_error(); + return false; + } + + if (!impl.generic_id.has_value()) { + parent_scope.AddExtendedScope(impl.constraint_id); + } else { + auto constraint_id_in_self_specific = AddTypeInst( + context, SemIR::LocId(impl.constraint_id), + {.type_id = SemIR::TypeType::TypeId, + .inst_id = impl.constraint_id, + .specific_id = context.generics().GetSelfSpecific(impl.generic_id)}); + parent_scope.AddExtendedScope(constraint_id_in_self_specific); + } + return true; +} + +auto FindImplId(Context& context, const SemIR::Impl& query_impl) + -> std::variant { + // Look for an existing matching declaration. + auto lookup_bucket_ref = context.impls().GetOrAddLookupBucket(query_impl); + // TODO: Detect two impl declarations with the same self type and interface, + // and issue an error if they don't match. + for (auto prev_impl_id : lookup_bucket_ref) { + if (CheckImplRedeclParamsMatch(context, query_impl, prev_impl_id)) { + if (IsValidImplRedecl(context, query_impl, prev_impl_id)) { + return RedeclaredImpl{.prev_impl_id = prev_impl_id}; + } else { + // IsValidImplRedecl() has issued a diagnostic, take care to avoid + // generating more diagnostics for this declaration. + return NewImpl{.lookup_bucket = lookup_bucket_ref, + .find_had_error = true}; + } + break; + } + } + + return NewImpl{.lookup_bucket = lookup_bucket_ref, .find_had_error = false}; +} + +// Sets the `ImplId` in the `ImplWitnessTable`. +static auto AssignImplIdInWitness(Context& context, SemIR::ImplId impl_id, + SemIR::InstId witness_id) -> void { + if (witness_id == SemIR::ErrorInst::InstId) { + return; + } + auto witness = context.insts().GetAs(witness_id); + auto witness_table = + context.insts().GetAs(witness.witness_table_id); + witness_table.impl_id = impl_id; + // Note: The `ImplWitnessTable` instruction is `Unique`, so while this marks + // the instruction as being a dependent instruction of a generic impl, it will + // not be substituted into the eval block. + ReplaceInstBeforeConstantUse(context, witness.witness_table_id, + witness_table); +} + +auto AddImpl(Context& context, const SemIR::Impl& impl, + SemIR::ImplStore::LookupBucketRef lookup_bucket, + Parse::NodeId extend_node, SemIR::LocId implicit_params_loc_id) + -> SemIR::ImplId { + auto impl_decl_id = impl.latest_decl_id(); + + // From here on, use the `Impl` from the `ImplStore` instead of `impl` in + // order to make and see any changes to the `Impl`. + auto impl_id = context.impls().Add(impl); + lookup_bucket.push_back(impl_id); + AssignImplIdInWitness(context, impl_id, impl.witness_id); + + auto& stored_impl = context.impls().Get(impl_id); + + // Look to see if there are any generic bindings on the `impl` declaration + // that are not deducible. If so, and the `impl` does not actually use all + // its generic bindings, and will never be matched. This should be + // diagnossed to the user. + if (!VerifyAllGenericBindingsUsed(context, SemIR::LocId(impl_decl_id), + implicit_params_loc_id, stored_impl)) { + FillImplWitnessWithErrors(context, stored_impl); + } + + if (extend_node.has_value()) { + if (!ApplyExtendImplAs(context, SemIR::LocId(impl_decl_id), stored_impl, + extend_node, implicit_params_loc_id)) { + FillImplWitnessWithErrors(context, stored_impl); + } + } + + return impl_id; +} + +// Returns whether the `LookupImplWitness` of `witness_id` matches `interface`. +static auto WitnessQueryMatchesInterface( + Context& context, SemIR::InstId witness_id, + const SemIR::SpecificInterface& interface) -> bool { + auto lookup = context.insts().GetAs(witness_id); + return interface == + context.specific_interfaces().Get(lookup.query_specific_interface_id); +} + +auto AddImplWitnessForDeclaration(Context& context, SemIR::LocId loc_id, + const SemIR::Impl& impl, + SemIR::SpecificId self_specific_id) + -> SemIR::InstId { + auto facet_type_id = + context.types().GetTypeIdForTypeInstId(impl.constraint_id); + CARBON_CHECK(facet_type_id != SemIR::ErrorInst::TypeId); + auto facet_type = context.types().GetAs(facet_type_id); + const auto& facet_type_info = + context.facet_types().Get(facet_type.facet_type_id); + + // An iterator over the rewrite_constraints where the LHS of the rewrite names + // a member of the `impl.interface`. This filters out rewrites of names + // from other interfaces, as they do not set values in the witness table. + auto rewrites_into_interface_to_witness = llvm::make_filter_range( + facet_type_info.rewrite_constraints, + [&](const SemIR::FacetTypeInfo::RewriteConstraint& rewrite) { + auto access = context.insts().GetAs( + GetImplWitnessAccessWithoutSubstitution(context, rewrite.lhs_id)); + return WitnessQueryMatchesInterface(context, access.witness_id, + impl.interface); + }); + + if (rewrites_into_interface_to_witness.empty()) { + // The witness table is not needed until the definition. Make a placeholder + // for the declaration. + auto witness_table_inst_id = AddInst( + context, loc_id, + {.elements_id = context.inst_blocks().AddPlaceholder(), + .impl_id = SemIR::ImplId::None}); + return AddInst( + context, loc_id, + {.type_id = GetSingletonType(context, SemIR::WitnessType::TypeInstId), + .witness_table_id = witness_table_inst_id, + .specific_id = self_specific_id}); + } + + const auto& interface = context.interfaces().Get(impl.interface.interface_id); + if (!interface.is_complete()) { + // This is a declaration with rewrite constraints into `.Self`, but the + // interface is not complete. Those rewrites have already been diagnosed as + // an error in their member access. return SemIR::ErrorInst::InstId; } - return InitialFacetTypeImplWitness( - context, SemIR::LocId(impl.latest_decl_id()), impl.constraint_id, - impl.self_id, impl.interface, - context.generics().GetSelfSpecific(impl.generic_id)); + auto assoc_entities = + context.inst_blocks().Get(interface.associated_entities_id); + // TODO: When this function is used for things other than just impls, may want + // to only load the specific associated entities that are mentioned in rewrite + // rules. + for (auto decl_id : assoc_entities) { + LoadImportRef(context, decl_id); + } + + SemIR::InstId witness_inst_id = SemIR::InstId::None; + llvm::MutableArrayRef table; + { + auto elements_id = + context.inst_blocks().AddUninitialized(assoc_entities.size()); + table = context.inst_blocks().GetMutable(elements_id); + for (auto& uninit : table) { + uninit = SemIR::InstId::ImplWitnessTablePlaceholder; + } + + auto witness_table_inst_id = AddInst( + context, loc_id, + {.elements_id = elements_id, .impl_id = SemIR::ImplId::None}); + + witness_inst_id = AddInst( + context, loc_id, + {.type_id = GetSingletonType(context, SemIR::WitnessType::TypeInstId), + .witness_table_id = witness_table_inst_id, + .specific_id = self_specific_id}); + } + + for (auto rewrite : rewrites_into_interface_to_witness) { + auto access = context.insts().GetAs( + GetImplWitnessAccessWithoutSubstitution(context, rewrite.lhs_id)); + auto& table_entry = table[access.index.index]; + if (table_entry == SemIR::ErrorInst::InstId) { + // Don't overwrite an error value. This prioritizes not generating + // multiple errors for one associated constant over picking a value + // for it to use to attempt recovery. + continue; + } + auto rewrite_inst_id = rewrite.rhs_id; + if (rewrite_inst_id == SemIR::ErrorInst::InstId) { + table_entry = SemIR::ErrorInst::InstId; + continue; + } + + auto decl_id = context.constant_values().GetConstantInstId( + assoc_entities[access.index.index]); + CARBON_CHECK(decl_id.has_value(), "Non-constant associated entity"); + if (decl_id == SemIR::ErrorInst::InstId) { + table_entry = SemIR::ErrorInst::InstId; + continue; + } + + auto assoc_constant_decl = + context.insts().TryGetAs(decl_id); + if (!assoc_constant_decl) { + auto type_id = context.insts().Get(decl_id).type_id(); + auto type_inst = context.types().GetAsInst(type_id); + auto fn_type = type_inst.As(); + const auto& fn = context.functions().Get(fn_type.function_id); + CARBON_DIAGNOSTIC(RewriteForAssociatedFunction, Error, + "rewrite specified for associated function {0}", + SemIR::NameId); + context.emitter().Emit(impl.constraint_id, RewriteForAssociatedFunction, + fn.name_id); + table_entry = SemIR::ErrorInst::InstId; + continue; + } + + // FacetTypes resolution disallows two rewrites to the same associated + // constant, so we won't ever have a facet write twice to the same position + // in the witness table. + CARBON_CHECK(table_entry == SemIR::InstId::ImplWitnessTablePlaceholder); + + // If the associated constant has a symbolic type, convert the rewrite + // value to that type now we know the value of `Self`. + SemIR::TypeId assoc_const_type_id = assoc_constant_decl->type_id; + if (assoc_const_type_id.is_symbolic()) { + // Get the type of the associated constant in this interface with this + // value for `Self`. + assoc_const_type_id = GetTypeForSpecificAssociatedEntity( + context, SemIR::LocId(impl.constraint_id), impl.interface.specific_id, + decl_id, context.types().GetTypeIdForTypeInstId(impl.self_id), + witness_inst_id); + // Perform the conversion of the value to the type. We skipped this when + // forming the facet type because the type of the associated constant + // was symbolic. + auto converted_inst_id = + ConvertToValueOfType(context, SemIR::LocId(impl.constraint_id), + rewrite_inst_id, assoc_const_type_id); + // Canonicalize the converted constant value. + converted_inst_id = + context.constant_values().GetConstantInstId(converted_inst_id); + // The result of conversion can be non-constant even if the original + // value was constant. + if (converted_inst_id.has_value()) { + rewrite_inst_id = converted_inst_id; + } else { + const auto& assoc_const = context.associated_constants().Get( + assoc_constant_decl->assoc_const_id); + CARBON_DIAGNOSTIC( + AssociatedConstantNotConstantAfterConversion, Error, + "associated constant {0} given value {1} that is not constant " + "after conversion to {2}", + SemIR::NameId, InstIdAsConstant, SemIR::TypeId); + context.emitter().Emit( + impl.constraint_id, AssociatedConstantNotConstantAfterConversion, + assoc_const.name_id, rewrite_inst_id, assoc_const_type_id); + rewrite_inst_id = SemIR::ErrorInst::InstId; + } + } + + CARBON_CHECK(rewrite_inst_id == context.constant_values().GetConstantInstId( + rewrite_inst_id), + "Rewritten value for associated constant is not canonical."); + + table_entry = AddInst( + context, loc_id, + {.type_id = context.insts().Get(rewrite_inst_id).type_id(), + .inst_id = rewrite_inst_id}); + } + return witness_inst_id; } auto ImplWitnessStartDefinition(Context& context, SemIR::Impl& impl) -> void { @@ -188,9 +589,7 @@ auto ImplWitnessStartDefinition(Context& context, SemIR::Impl& impl) -> void { // Adds functions to the witness that the specified impl implements the given // interface. -auto FinishImplWitness(Context& context, SemIR::ImplId impl_id) -> void { - const auto& impl = context.impls().Get(impl_id); - +auto FinishImplWitness(Context& context, const SemIR::Impl& impl) -> void { CARBON_CHECK(impl.is_being_defined()); CARBON_CHECK(impl.witness_id.has_value()); if (impl.witness_id == SemIR::ErrorInst::InstId) { @@ -325,275 +724,6 @@ auto CheckConstraintIsInterface(Context& context, SemIR::InstId impl_decl_id, return identified.impl_as_target_interface(); } -// Returns true if impl redeclaration parameters match. -static auto CheckImplRedeclParamsMatch(Context& context, - const SemIR::Impl& new_impl, - SemIR::ImplId prev_impl_id) -> bool { - auto& prev_impl = context.impls().Get(prev_impl_id); - - // 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 true; -} - -// Returns whether an impl can be redeclared. For example, defined impls -// cannot be redeclared. -static auto IsValidImplRedecl(Context& context, const SemIR::Impl& new_impl, - SemIR::ImplId prev_impl_id) -> bool { - auto& prev_impl = context.impls().Get(prev_impl_id); - - // TODO: Following #3763, disallow redeclarations in different scopes. - - // Following #4672, disallowing defining non-extern declarations in another - // file. - if (auto import_ref = - context.insts().TryGetAs(prev_impl.self_id)) { - // TODO: Handle extern. - CARBON_DIAGNOSTIC(RedeclImportedImpl, Error, - "redeclaration of imported impl"); - // TODO: Note imported declaration - context.emitter().Emit(new_impl.latest_decl_id(), RedeclImportedImpl); - return false; - } - - if (prev_impl.has_definition_started()) { - // Impls aren't merged in order to avoid generic region lookup into a - // mismatching table. - CARBON_DIAGNOSTIC(ImplRedefinition, Error, - "redefinition of `impl {0} as {1}`", InstIdAsRawType, - InstIdAsRawType); - CARBON_DIAGNOSTIC(ImplPreviousDefinition, Note, - "previous definition was here"); - context.emitter() - .Build(new_impl.latest_decl_id(), ImplRedefinition, new_impl.self_id, - new_impl.constraint_id) - .Note(prev_impl.definition_id, ImplPreviousDefinition) - .Emit(); - return false; - } - - // TODO: Only allow redeclaration in a match_first/impl_priority block. - - return true; -} - -// Sets the `ImplId` in the `ImplWitnessTable`. -static auto AssignImplIdInWitness(Context& context, SemIR::ImplId impl_id, - SemIR::InstId witness_id) -> void { - if (witness_id == SemIR::ErrorInst::InstId) { - return; - } - auto witness = context.insts().GetAs(witness_id); - auto witness_table = - context.insts().GetAs(witness.witness_table_id); - witness_table.impl_id = impl_id; - // Note: The `ImplWitnessTable` instruction is `Unique`, so while this marks - // the instruction as being a dependent instruction of a generic impl, it will - // not be substituted into the eval block. - ReplaceInstBeforeConstantUse(context, witness.witness_table_id, - witness_table); -} - -// Looks for any unused generic bindings. If one is found, it is diagnosed and -// false is returned. -static auto VerifyAllGenericBindingsUsed(Context& context, SemIR::LocId loc_id, - SemIR::LocId implicit_params_loc_id, - SemIR::Impl& impl) -> bool { - if (impl.witness_id == SemIR::ErrorInst::InstId) { - return true; - } - if (!impl.generic_id.has_value()) { - return true; - } - - if (impl.implicit_param_patterns_id.has_value()) { - for (auto inst_id : - context.inst_blocks().Get(impl.implicit_param_patterns_id)) { - if (inst_id == SemIR::ErrorInst::InstId) { - // An error was already diagnosed for a generic binding. - return true; - } - } - } - - auto deduced_specific_id = DeduceImplArguments( - context, loc_id, impl, context.constant_values().Get(impl.self_id), - impl.interface.specific_id); - if (deduced_specific_id.has_value()) { - // Deduction succeeded, all bindings were used. - return true; - } - - CARBON_DIAGNOSTIC(ImplUnusedBinding, Error, - "`impl` with unused generic binding"); - // TODO: This location may be incorrect, the binding may be inherited - // from an outer declaration. It would be nice to get the particular - // binding that was undeducible back from DeduceImplArguments here and - // use that. - auto diag_loc_id = - implicit_params_loc_id.has_value() ? implicit_params_loc_id : loc_id; - context.emitter().Emit(diag_loc_id, ImplUnusedBinding); - return false; -} - -// Apply an `extend impl` declaration by extending the parent scope with the -// `impl`. If there's an error it is diagnosed and false is returned. -static auto ApplyExtendImplAs(Context& context, SemIR::LocId loc_id, - const SemIR::Impl& impl, - Parse::NodeId extend_node, - SemIR::LocId implicit_params_loc_id) -> bool { - auto parent_scope_id = context.decl_name_stack().PeekParentScopeId(); - - // TODO: Also handle the parent scope being a mixin or an interface. - auto class_scope = TryAsClassScope(context, parent_scope_id); - if (!class_scope) { - if (impl.witness_id != SemIR::ErrorInst::InstId) { - CARBON_DIAGNOSTIC( - ExtendImplOutsideClass, Error, - "`extend impl` can only be used in an interface or class"); - context.emitter().Emit(loc_id, ExtendImplOutsideClass); - } - return false; - } - - auto& parent_scope = *class_scope->name_scope; - - // An error was already diagnosed, but this is `extend impl as` inside a - // class, so propagate the error into the enclosing class scope. - if (impl.witness_id == SemIR::ErrorInst::InstId) { - parent_scope.set_has_error(); - return false; - } - - if (implicit_params_loc_id.has_value()) { - CARBON_DIAGNOSTIC(ExtendImplForall, Error, - "cannot `extend` a parameterized `impl`"); - context.emitter().Emit(extend_node, ExtendImplForall); - parent_scope.set_has_error(); - return false; - } - - if (!RequireCompleteType( - context, context.types().GetTypeIdForTypeInstId(impl.constraint_id), - SemIR::LocId(impl.constraint_id), [&] { - CARBON_DIAGNOSTIC(ExtendImplAsIncomplete, Error, - "`extend impl as` incomplete facet type {0}", - InstIdAsType); - return context.emitter().Build(impl.latest_decl_id(), - ExtendImplAsIncomplete, - impl.constraint_id); - })) { - parent_scope.set_has_error(); - return false; - } - - if (!impl.generic_id.has_value()) { - parent_scope.AddExtendedScope(impl.constraint_id); - } else { - auto constraint_id_in_self_specific = AddTypeInst( - context, SemIR::LocId(impl.constraint_id), - {.type_id = SemIR::TypeType::TypeId, - .inst_id = impl.constraint_id, - .specific_id = context.generics().GetSelfSpecific(impl.generic_id)}); - parent_scope.AddExtendedScope(constraint_id_in_self_specific); - } - return true; -} - -auto GetOrAddImpl(Context& context, SemIR::LocId loc_id, - SemIR::LocId implicit_params_loc_id, SemIR::Impl impl, - Parse::NodeId extend_node) -> SemIR::ImplId { - auto impl_id = SemIR::ImplId::None; - - // Look for an existing matching declaration. - auto lookup_bucket_ref = context.impls().GetOrAddLookupBucket(impl); - // TODO: Detect two impl declarations with the same self type and interface, - // and issue an error if they don't match. - for (auto prev_impl_id : lookup_bucket_ref) { - if (CheckImplRedeclParamsMatch(context, impl, prev_impl_id)) { - if (IsValidImplRedecl(context, impl, prev_impl_id)) { - impl_id = prev_impl_id; - } else { - // IsValidImplRedecl() has issued a diagnostic, take care to avoid - // generating more diagnostics for this declaration. - impl.witness_id = SemIR::ErrorInst::InstId; - } - break; - } - } - - if (impl_id.has_value()) { - // This is a redeclaration of another impl, now held in `impl_id`. - auto& prev_impl = context.impls().Get(impl_id); - FinishGenericRedecl(context, prev_impl.generic_id); - return impl_id; - } - - // This is a new declaration (possibly with an attached definition). Create a - // new `impl_id`, filling the missing generic and witness in the provided - // `Impl`. - - impl.generic_id = BuildGeneric(context, impl.latest_decl_id()); - - // 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. 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. - if (!impl.interface.interface_id.has_value() || - impl.self_id == SemIR::ErrorInst::TypeInstId || - impl.constraint_id == SemIR::ErrorInst::TypeInstId) { - impl.witness_id = SemIR::ErrorInst::InstId; - // TODO: We might also want to mark that the name scope for the impl has an - // error -- at least once we start making name lookups within the impl also - // look into the facet (eg, so you can name associated constants from within - // the impl). - } - - if (impl.witness_id != SemIR::ErrorInst::InstId) { - // This makes either a placeholder witness 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. - impl.witness_id = ImplWitnessForDeclaration(context, impl); - } - - FinishGenericDecl(context, SemIR::LocId(impl.latest_decl_id()), - impl.generic_id); - // From here on, use the `Impl` from the `ImplStore` instead of `impl` in - // order to make and see any changes to the `Impl`. - impl_id = context.impls().Add(impl); - lookup_bucket_ref.push_back(impl_id); - AssignImplIdInWitness(context, impl_id, impl.witness_id); - - auto& stored_impl = context.impls().Get(impl_id); - - // Look to see if there are any generic bindings on the `impl` declaration - // that are not deducible. If so, and the `impl` does not actually use all its - // generic bindings, and will never be matched. This should be diagnosed to - // the user. - if (!VerifyAllGenericBindingsUsed(context, loc_id, implicit_params_loc_id, - stored_impl)) { - FillImplWitnessWithErrors(context, stored_impl); - } - - if (extend_node.has_value()) { - if (!ApplyExtendImplAs(context, loc_id, stored_impl, extend_node, - implicit_params_loc_id)) { - FillImplWitnessWithErrors(context, stored_impl); - } - } - - return impl_id; -} - auto BuildCustomWitness(Context& context, SemIR::LocId loc_id, SemIR::TypeId self_type_id, SemIR::SpecificInterface specific_interface, diff --git a/toolchain/check/impl.h b/toolchain/check/impl.h index 3b4518a1a295..f178e89a9e3b 100644 --- a/toolchain/check/impl.h +++ b/toolchain/check/impl.h @@ -10,18 +10,56 @@ namespace Carbon::Check { -// Returns the initial witness value for a new `impl` declaration. +struct RedeclaredImpl { + // The previous Impl which the query Impl is redeclaring. + SemIR::ImplId prev_impl_id; +}; +struct NewImpl { + // The lookup bucket for the query Impl where it should be added once an + // ImplId is known. + SemIR::ImplStore::LookupBucketRef lookup_bucket; + // Indicates the query Impl is not a redeclaration but an error was diagnosed. + // The caller should avoid diagnosing more errors in the query impl. + bool find_had_error; +}; + +// Finds an existing impl if the `query_impl` is a redeclaration, and returns +// its `ImplId`. This ensures all (valid) redeclarations share the same +// `ImplId`. Otherwise, returns the bucket where a new `ImplId` should be added. +auto FindImplId(Context& context, const SemIR::Impl& query_impl) + -> std::variant; + +// Adds an impl to the ImplStore, and returns a new `ImplId`. // -// `has_definition` is whether this declaration is immediately followed by the -// opening of the definition. -auto ImplWitnessForDeclaration(Context& context, const SemIR::Impl& impl) +// If the impl is modified with `extend` then the parent's scope is extended +// with it. +auto AddImpl(Context& context, const SemIR::Impl& impl, + SemIR::ImplStore::LookupBucketRef lookup_bucket, + Parse::NodeId extend_node, SemIR::LocId implicit_params_loc_id) + -> SemIR::ImplId; + +// Creates and returns an impl witness instruction for an impl declaration. +// +// If there are no rewrites into a name of the interface being implemented, a +// placeholder witness table is created, to be replaced in the impl definition. +// +// Adds and returns an `ImplWitness` instruction (created with location set to +// `loc_id`) that shows the "`Self` type" (from a facet in `impl.self_id`) +// implements an identified interface (from a facet type in +// `impl.constraint_id`). This witness reflects the values assigned to +// associated constant members of that interface by rewrite constraints in the +// constraint facet type. `self_specific_id` will be the `specific_id` of the +// resulting witness. +auto AddImplWitnessForDeclaration(Context& context, SemIR::LocId loc_id, + const SemIR::Impl& impl, + SemIR::SpecificId self_specific_id) -> SemIR::InstId; // Update `impl`'s witness at the start of a definition. auto ImplWitnessStartDefinition(Context& context, SemIR::Impl& impl) -> void; // Adds the function members to the witness for `impl`. -auto FinishImplWitness(Context& context, SemIR::ImplId impl_id) -> void; +auto FinishImplWitness(Context& context, const SemIR::Impl& impl_id) -> void; // Sets all unset members of the witness for `impl` to the error instruction and // sets the witness id in the `Impl` to an error. @@ -48,16 +86,6 @@ auto CheckConstraintIsInterface(Context& context, SemIR::InstId impl_decl_id, SemIR::TypeInstId constraint_id) -> SemIR::SpecificInterface; -// Finds an existing `Impl` if the `impl` is a redeclaration. Otherwise, -// finishes construction of the `impl`, adds it to the ImplStore, and returns -// the new `ImplId`. This ensures all redeclarations share the same `ImplId`. -// -// If the impl is modified with `extend` then the parent's scope is extended -// with it. -auto GetOrAddImpl(Context& context, SemIR::LocId loc_id, - SemIR::LocId implicit_params_loc_id, SemIR::Impl impl, - Parse::NodeId extend_node) -> SemIR::ImplId; - // Builds a witness that the given type implements the given interface, // populating it with the specified set of values. Returns a corresponding // lookup result. Produces a diagnostic and returns `None` if the specified diff --git a/toolchain/check/testdata/impl/extend_impl.carbon b/toolchain/check/testdata/impl/extend_impl.carbon index 795de0a7e054..aa79d505b944 100644 --- a/toolchain/check/testdata/impl/extend_impl.carbon +++ b/toolchain/check/testdata/impl/extend_impl.carbon @@ -66,7 +66,7 @@ class C { impl nonexistent as I {} fn F() { - // The name lookup error still happens, since the `impl` is not `extend`. + // The name lookup error still happens, since the `require` is not `extend`. // CHECK:STDERR: fail_impl_nonexistent.carbon:[[@LINE+4]]:5: error: member name `A` not found in `C` [MemberNameNotFoundInInstScope] // CHECK:STDERR: Self.A; // CHECK:STDERR: ^~~~~~