From 6326bbdbe18abcd04f21af32a1060f07240fe5ae Mon Sep 17 00:00:00 2001 From: Dana Jansens Date: Wed, 13 May 2026 12:11:20 -0400 Subject: [PATCH] Resolve cycles in `.Self` replacement in nested designators (#7183) A nested designator like `.(X.X1).(Y.Y1)` results in nested ImplWitnessAccess instructions, which can produce cycles in the toolchain easily when replacing `.Self`. First, when constructing a facet type like `V:! Z where .Z1 impls (Y where .Y1 = U)` we substitute replace `.Self` in the nested facet type, and in this case we replace `.Self` with `.Z1` which contains a `.Self` of its own. This was coming from us being lazy about replacing `.Self` in an `impl as` declaration, such as `impl C as Z where .Z1 = .Self`. The self type is known there, so we can more eagerly replace `.Self` as we do in a `require impls` declaration. Then the replacement for `.Self` never comes with a `.Self` that needs to also be replaced. Any resulting `.Self` would always be the top-level one. Second, when evaluating ImplWitnessAccess, we were replacing .Self in the LHS of rewrite constraints, but the `.Self` may itself have a type that contains rewrite constraints. If one of those rewrite constraints has nested ImplWitnessAccess instructions, we evaluate the new ImplWitnessAccess, which again finds rewrite constraints to replace `.Self` in, and we repeat forever. For this one we just stop replacing .Self in the LHS of rewrite constraints. Since they are always against .Self, we can always look in the access facet's type for a value. While fixing ImplWitness access, also correct the lookup to search through the types of nested ImplWitnessAccess instructions to find a rewrite value, since it may find it at any level up to the eventual `.Self`. --------- Co-authored-by: Richard Smith --- toolchain/check/eval.cpp | 33 +- toolchain/check/eval_inst.cpp | 192 +++--- toolchain/check/handle_impl.cpp | 31 +- toolchain/check/handle_require.cpp | 81 +-- toolchain/check/impl.cpp | 45 +- toolchain/check/impl.h | 13 +- toolchain/check/impl_lookup.cpp | 67 +- toolchain/check/period_self.cpp | 605 +++++++++++++----- toolchain/check/period_self.h | 92 +-- toolchain/check/testdata/facet/access.carbon | 71 +- .../testdata/impl/assoc_const_self.carbon | 2 +- .../impl/fail_extend_non_interface.carbon | 2 +- .../impl/fail_impl_bad_interface.carbon | 6 +- .../check/testdata/impl/forward_decls.carbon | 4 +- .../testdata/impl/impl_assoc_const.carbon | 4 +- .../impl/impl_assoc_const_with_prelude.carbon | 2 +- .../impl/import_interface_assoc_const.carbon | 12 +- .../check/testdata/impl/incomplete.carbon | 4 +- .../check/testdata/impl/lookup/access.carbon | 35 +- .../testdata/interface/incomplete.carbon | 2 +- .../cpp/operators/arithmetic_operators.carbon | 28 +- toolchain/check/type_completion.cpp | 11 +- toolchain/sem_ir/impl.cpp | 12 +- 23 files changed, 820 insertions(+), 534 deletions(-) diff --git a/toolchain/check/eval.cpp b/toolchain/check/eval.cpp index dabf8fac1a02..f06a0d65c563 100644 --- a/toolchain/check/eval.cpp +++ b/toolchain/check/eval.cpp @@ -2642,32 +2642,37 @@ static auto AddRequirementImpls(Context& context, SemIR::LocId loc_id, // references are to the same top-level value for `.Self` and can all be // replaced together later. - SubstPeriodSelfCallbacks callbacks( - &context, loc_id, context.constant_values().Get(lhs_facet_or_type), - SubstPeriodSelfCallbacks::Behaviour::ImplicitOnly); + auto period_self_replacement_id = + context.constant_values().Get(lhs_facet_or_type); auto self_impls_interface = [&](SemIR::SpecificInterface si) { - return SubstPeriodSelf(context, callbacks, si); + return SubstPeriodSelf(context, loc_id, si, period_self_replacement_id, + SubstPeriodSelfBehaviour::ImplicitOnly); }; auto self_impls_constraint = [&](SemIR::SpecificNamedConstraint sc) { - return SubstPeriodSelf(context, callbacks, sc); + return SubstPeriodSelf(context, loc_id, sc, period_self_replacement_id, + SubstPeriodSelfBehaviour::ImplicitOnly); }; auto type_impls_interface = [&](SemIR::FacetTypeInfo::TypeImplsInterface impls) -> SemIR::FacetTypeInfo::TypeImplsInterface { auto self = SubstPeriodSelf( - context, callbacks, context.constant_values().Get(impls.self_type)); - auto interface = - SubstPeriodSelf(context, callbacks, impls.specific_interface); + context, loc_id, context.constant_values().Get(impls.self_type), + period_self_replacement_id, SubstPeriodSelfBehaviour::ImplicitOnly); + auto interface = SubstPeriodSelf( + context, loc_id, impls.specific_interface, period_self_replacement_id, + SubstPeriodSelfBehaviour::ImplicitOnly); return {context.constant_values().GetInstId(self), interface}; }; auto type_impls_constraint = [&](SemIR::FacetTypeInfo::TypeImplsNamedConstraint impls) -> SemIR::FacetTypeInfo::TypeImplsNamedConstraint { auto self = SubstPeriodSelf( - context, callbacks, context.constant_values().Get(impls.self_type)); - auto constraint = - SubstPeriodSelf(context, callbacks, impls.specific_named_constraint); + context, loc_id, context.constant_values().Get(impls.self_type), + period_self_replacement_id, SubstPeriodSelfBehaviour::ImplicitOnly); + auto constraint = SubstPeriodSelf( + context, loc_id, impls.specific_named_constraint, + period_self_replacement_id, SubstPeriodSelfBehaviour::ImplicitOnly); return {context.constant_values().GetInstId(self), constraint}; }; @@ -2688,9 +2693,11 @@ static auto AddRequirementImpls(Context& context, SemIR::LocId loc_id, [&](SemIR::FacetTypeInfo::RewriteConstraint rewrite) -> SemIR::FacetTypeInfo::RewriteConstraint { auto lhs_id = SubstPeriodSelf( - context, callbacks, context.constant_values().Get(rewrite.lhs_id)); + context, loc_id, context.constant_values().Get(rewrite.lhs_id), + period_self_replacement_id, SubstPeriodSelfBehaviour::ImplicitOnly); auto rhs_id = SubstPeriodSelf( - context, callbacks, context.constant_values().Get(rewrite.rhs_id)); + context, loc_id, context.constant_values().Get(rewrite.rhs_id), + period_self_replacement_id, SubstPeriodSelfBehaviour::ImplicitOnly); return {context.constant_values().GetInstId(lhs_id), context.constant_values().GetInstId(rhs_id)}; }; diff --git a/toolchain/check/eval_inst.cpp b/toolchain/check/eval_inst.cpp index bdd344a164f7..9a58f6d19e24 100644 --- a/toolchain/check/eval_inst.cpp +++ b/toolchain/check/eval_inst.cpp @@ -285,6 +285,94 @@ auto EvalConstantInst(Context& context, SemIR::InstId inst_id, return ConstantEvalResult::NewSamePhase(inst); } +// Given a SpecificInterface and an index of an associated constant in that +// interface, try find a value for that constant in the rewrite constraints of +// the type of `search_facet`. +static auto TryFindValueInRewriteConstraints( + Context& context, SemIR::LocId loc_id, + SemIR::SpecificInterfaceId specific_interface_id, + SemIR::ElementIndex interface_index, SemIR::InstId search_facet) + -> SemIR::ConstantId { + auto access_self_type_id = context.insts().Get(search_facet).type_id(); + if (context.types().Is(access_self_type_id)) { + // A self facet of type `type` has no rewrite constraints to look in. + return SemIR::ConstantId::None; + } + + // The `ImplWitnessAccess` is accessing a value, by index, for this `self + // impls interface` combination. + auto access_interface = + context.specific_interfaces().Get(specific_interface_id); + + auto access_self_facet_type_id = + context.types() + .GetAs(access_self_type_id) + .facet_type_id; + // TODO: We could consider something better than linear search here, such as a + // map. However that would probably require heap allocations which may be + // worse overall since the number of rewrite constraints is generally low. If + // the `rewrite_constraints` were sorted so that associated constants are + // grouped together, as in ResolveFacetTypeRewriteConstraints(), and limited + // to just the `ImplWitnessAccess` entries, then a binary search may work + // here. + for (const auto& rewrite : context.facet_types() + .Get(access_self_facet_type_id) + .rewrite_constraints) { + // Look at each rewrite constraint in the self facet's type. If the LHS is + // an `ImplWitnessAccess` into the same interface that `inst` is indexing + // into, then we can use its RHS as the value. + auto rewrite_lhs_access = + context.insts().TryGetAs(rewrite.lhs_id); + if (!rewrite_lhs_access) { + continue; + } + if (rewrite_lhs_access->index != interface_index) { + continue; + } + + // Witnesses come from impl lookup, and the operands are from + // IdentifiedFacetTypes, so `.Self` is replaced. However rewrite constraints + // are not part of an IdentifiedFacetType, so they are not replaced. We have + // to do the same replacement in the rewrite's LHS witness in order to + // compare it with the access witness. + // + // However we don't substitute the witness directly as that would + // re-evaluate it and cause us to do an impl lookup. Instead we substitute + // and compare its operands. + auto rewrite_lhs_witness = context.insts().GetAs( + rewrite_lhs_access->witness_id); + + auto self_const_id = context.constant_values().Get(search_facet); + + // The LHS of the rewrite might be `.Self` or it could be one or more nested + // ImplWitnessAccess instructions that eventually bottom out in `.Self`. + // Rewrite constraints must modify `.Self` so we know the target of the + // rewrite is ultimately always `.Self` which refers to the `search_facet`. + // So we don't have to substitute the `.Self` and do any comparison. + + auto rewrite_lhs_interface = + SubstPeriodSelf(context, loc_id, + context.specific_interfaces().Get( + rewrite_lhs_witness.query_specific_interface_id), + self_const_id); + + if (rewrite_lhs_interface != access_interface) { + // This rewrite is into a different interface than the access query. + continue; + } + + // The `ImplWitnessAccess` evaluates to the RHS from the witness self facet + // value's type. Any `.Self` references in the RHS are also replaced with + // the self type of the access. + auto rewrite_rhs = SubstPeriodSelf( + context, loc_id, context.constant_values().Get(rewrite.rhs_id), + self_const_id); + return rewrite_rhs; + } + + return SemIR::ConstantId::None; +} + auto EvalConstantInst(Context& context, SemIR::InstId inst_id, SemIR::ImplWitnessAccess inst) -> ConstantEvalResult { CARBON_DIAGNOSTIC(ImplAccessMemberBeforeSet, Error, @@ -332,94 +420,26 @@ auto EvalConstantInst(Context& context, SemIR::InstId inst_id, // If the witness is symbolic but has a self type that is a FacetType, it // can pull rewrite values from the self's facet type. If the access is // for one of those rewrites, evaluate to the RHS of the rewrite. - - // The type of the query self type (a FacetType or TypeType). - auto access_self_type_id = - context.insts().Get(witness.query_self_inst_id).type_id(); - if (context.types().Is(access_self_type_id)) { - // A self facet of type `type` has no rewrite constraints to look in. - return ConstantEvalResult::NewSamePhase(inst); - } - - // The `ImplWitnessAccess` is accessing a value, by index, for this - // `self impls interface` combination. - auto access_self = - context.constant_values().Get(witness.query_self_inst_id); - auto access_interface = context.specific_interfaces().Get( - witness.query_specific_interface_id); - - auto access_self_facet_type_id = - context.types() - .GetAs(access_self_type_id) - .facet_type_id; - // TODO: We could consider something better than linear search here, such - // as a map. However that would probably require heap allocations which - // may be worse overall since the number of rewrite constraints is - // generally low. If the `rewrite_constraints` were sorted so that - // associated constants are grouped together, as in - // ResolveFacetTypeRewriteConstraints(), and limited to just the - // `ImplWitnessAccess` entries, then a binary search may work here. - for (const auto& rewrite : context.facet_types() - .Get(access_self_facet_type_id) - .rewrite_constraints) { - // Look at each rewrite constraint in the self facet's type. If the LHS - // is an `ImplWitnessAccess` into the same interface that `inst` is - // indexing into, then we can use its RHS as the value. - auto rewrite_lhs_access = - context.insts().TryGetAs(rewrite.lhs_id); - if (!rewrite_lhs_access) { - continue; + // + // If we have a nested `.X1.Y1.Z1` we start with the facet type of .Y1 to + // look for a rewrite constraint that provides the value for .Z1. But if + // we don't find it, we try .X1 and .Self. + auto search_facet = witness.query_self_inst_id; + while (true) { + auto const_id = TryFindValueInRewriteConstraints( + context, SemIR::LocId(inst_id), witness.query_specific_interface_id, + inst.index, search_facet); + if (const_id.has_value()) { + return ConstantEvalResult::Existing(const_id); } - if (rewrite_lhs_access->index != inst.index) { - continue; + if (auto access = context.insts().TryGetAs( + search_facet)) { + auto witness = context.insts().GetAs( + access->witness_id); + search_facet = witness.query_self_inst_id; + } else { + break; } - - // Witnesses come from impl lookup, and the operands are from - // IdentifiedFacetTypes, so `.Self` is replaced. However rewrite - // constraints are not part of an IdentifiedFacetType, so they are not - // replaced. We have to do the same replacement in the rewrite's LHS - // witness in order to compare it with the access witness. - // - // However we don't substitute the witness directly as that would - // re-evaluate it and cause us to do an impl lookup. Instead we - // substitute and compare its operands. - auto rewrite_lhs_witness = - context.insts().GetAs( - rewrite_lhs_access->witness_id); - - SubstPeriodSelfCallbacks callbacks(&context, SemIR::LocId(inst_id), - access_self); - - auto rewrite_lhs_self = context.constant_values().Get( - rewrite_lhs_witness.query_self_inst_id); - rewrite_lhs_self = - SubstPeriodSelf(context, callbacks, rewrite_lhs_self); - // Witnesses have a canonicalized self value. Perform the same - // canonicalization here so that we can compare them. - rewrite_lhs_self = GetCanonicalQuerySelfForLookupImplWitness( - context, rewrite_lhs_self); - - if (rewrite_lhs_self != access_self) { - // This rewrite is into a different self type than the access query. - continue; - } - - auto rewrite_lhs_interface = SubstPeriodSelf( - context, callbacks, - context.specific_interfaces().Get( - rewrite_lhs_witness.query_specific_interface_id)); - - if (rewrite_lhs_interface != access_interface) { - // This rewrite is into a different interface than the access query. - continue; - } - - // The `ImplWitnessAccess` evaluates to the RHS from the witness self - // facet value's type. Any `.Self` references in the RHS are also - // replaced with the self type of the access. - auto rewrite_rhs = SubstPeriodSelf( - context, callbacks, context.constant_values().Get(rewrite.rhs_id)); - return ConstantEvalResult::Existing(rewrite_rhs); } break; } diff --git a/toolchain/check/handle_impl.cpp b/toolchain/check/handle_impl.cpp index 7cbea20964de..8bf7e8ae28f8 100644 --- a/toolchain/check/handle_impl.cpp +++ b/toolchain/check/handle_impl.cpp @@ -17,6 +17,7 @@ #include "toolchain/check/name_lookup.h" #include "toolchain/check/name_scope.h" #include "toolchain/check/pattern_match.h" +#include "toolchain/check/period_self.h" #include "toolchain/check/type.h" #include "toolchain/check/type_completion.h" #include "toolchain/parse/node_ids.h" @@ -228,10 +229,31 @@ static auto BuildImplDecl(Context& context, Parse::AnyImplDeclId node_id) SemIR::ImplDecl{.impl_id = SemIR::ImplId::None, .decl_block_id = decl_block_id}); - // This requires that the facet type is identified. It returns None if an - // error was diagnosed. + if (!CheckConstraintIsFacetType(context, node_id, constraint_type_inst_id)) { + constraint_type_inst_id = SemIR::ErrorInst::TypeInstId; + } + + // The identified facet type will also replace `.Self` references in the + // specific interface, but we want to store the full facet type not just the + // identified one. So we have to replace `.Self` references explicitly here in + // the constraint. + // + // We do this after `CheckConstraintIsFacetType()` which has ensured the + // constraint is in fact a FacetType. We do this before identifying the facet + // type in `CheckConstraintIsInterface()` so that the identified facet type is + // for the substituted facet type instruction that will be stored in the Impl. + // This ensures the impl bucket finds the identified facet type. + constraint_type_inst_id = SubstPeriodSelfInFacetType( + context, constraint_node, self_type_inst_id, constraint_type_inst_id); + + // This requires that the facet type is identified, and returns the single + // interface from the identified facet type. It returns None if an error was + // diagnosed. auto specific_interface = CheckConstraintIsInterface( - context, impl_decl_id, self_type_inst_id, constraint_type_inst_id); + context, node_id, self_type_inst_id, constraint_type_inst_id); + if (!specific_interface.interface_id.has_value()) { + constraint_type_inst_id = SemIR::ErrorInst::TypeInstId; + } // The impl decl has a scope stack entry for the DeclNameStack, so we look at // the parent scope of that. @@ -256,8 +278,7 @@ static auto BuildImplDecl(Context& context, Parse::AnyImplDeclId node_id) context.types().GetTypeIdForTypeInstId(impl.self_id) == SemIR::ErrorInst::TypeId || context.types().GetTypeIdForTypeInstId(impl.constraint_id) == - SemIR::ErrorInst::TypeId || - !impl.interface.interface_id.has_value(); + SemIR::ErrorInst::TypeId; CARBON_KIND_SWITCH(FindImplId(context, impl)) { case CARBON_KIND(RedeclaredImpl redeclared_impl): { diff --git a/toolchain/check/handle_require.cpp b/toolchain/check/handle_require.cpp index fb39aff79a48..5fd9931cf6b0 100644 --- a/toolchain/check/handle_require.cpp +++ b/toolchain/check/handle_require.cpp @@ -268,85 +268,6 @@ static auto ValidateRequire(Context& context, SemIR::LocId full_require_loc_id, return ValidateRequireResult{.identified_facet_type = &identified}; } -// Replace all `.Self` references with the self-type. -static auto SubstPeriodSelfInConstraint(Context& context, SemIR::LocId loc_id, - SemIR::TypeInstId self_type_inst_id, - SemIR::TypeInstId constraint_inst_id) - -> SemIR::TypeInstId { - auto orig_facet_type = context.insts().GetAs( - context.constant_values().GetConstantInstId(constraint_inst_id)); - const auto& orig_info = - context.facet_types().Get(orig_facet_type.facet_type_id); - - SubstPeriodSelfCallbacks callbacks( - &context, loc_id, context.constant_values().Get(self_type_inst_id)); - - auto replace_interface = [&](SemIR::SpecificInterface si) { - return SubstPeriodSelf(context, callbacks, si); - }; - auto replace_constraint = [&](SemIR::SpecificNamedConstraint sc) { - return SubstPeriodSelf(context, callbacks, sc); - }; - auto replace_type_impls_interface = - [&](SemIR::FacetTypeInfo::TypeImplsInterface impls) - -> SemIR::FacetTypeInfo::TypeImplsInterface { - auto self = SubstPeriodSelf(context, callbacks, - context.constant_values().Get(impls.self_type)); - auto interface = - SubstPeriodSelf(context, callbacks, impls.specific_interface); - return {context.constant_values().GetInstId(self), interface}; - }; - auto replace_type_impls_constraint = - [&](SemIR::FacetTypeInfo::TypeImplsNamedConstraint impls) - -> SemIR::FacetTypeInfo::TypeImplsNamedConstraint { - auto self = SubstPeriodSelf(context, callbacks, - context.constant_values().Get(impls.self_type)); - auto constraint = - SubstPeriodSelf(context, callbacks, impls.specific_named_constraint); - return {context.constant_values().GetInstId(self), constraint}; - }; - - SemIR::FacetTypeInfo info; - llvm::append_range( - info.extend_constraints, - llvm::map_range(orig_info.extend_constraints, replace_interface)); - llvm::append_range( - info.extend_named_constraints, - llvm::map_range(orig_info.extend_named_constraints, replace_constraint)); - llvm::append_range( - info.self_impls_constraints, - llvm::map_range(orig_info.self_impls_constraints, replace_interface)); - llvm::append_range(info.self_impls_named_constraints, - llvm::map_range(orig_info.self_impls_named_constraints, - replace_constraint)); - llvm::append_range(info.type_impls_interfaces, - llvm::map_range(orig_info.type_impls_interfaces, - replace_type_impls_interface)); - llvm::append_range(info.type_impls_named_constraints, - llvm::map_range(orig_info.type_impls_named_constraints, - replace_type_impls_constraint)); - // TODO: Replace .Self in rewrites too. We need to actually validate rewrite - // constraints from named constraints in impl lookup (see - // todo_fail_require_with_mismatching_rewrite_constraint.carbon). - llvm::append_range(info.rewrite_constraints, orig_info.rewrite_constraints); - - info.Canonicalize(); - if (info == orig_info) { - // Nothing was substituted, keep the original instruction. - // - // It is noteworthy that we keep the non-canonical instruction here, since - // it may have a symbolic value (which is attached to a generic, and can be - // updated by specifics). Returning the canonical constraint instruction - // would lose the attachment to the generic which would be incorrect. - return constraint_inst_id; - } - - return AddTypeInst( - context, loc_id, - {.type_id = SemIR::TypeType::TypeId, - .facet_type_id = context.facet_types().Add(info)}); -} - auto HandleParseNode(Context& context, Parse::RequireDeclId node_id) -> bool { auto [constraint_node_id, constraint_inst_id] = context.node_stack().PopExprWithNodeId(); @@ -391,7 +312,7 @@ auto HandleParseNode(Context& context, Parse::RequireDeclId node_id) -> bool { // replace `.Self` references explicitly here in the canonical constraint. We // do this after `ValidateRequire()` which has ensured the constraint is in // fact a FacetType. - auto constraint_type_inst_id = SubstPeriodSelfInConstraint( + auto constraint_type_inst_id = SubstPeriodSelfInFacetType( context, constraint_node_id, self_inst_id, context.types().GetAsTypeInstId(constraint_inst_id)); // The replacement of `.Self` can create a new FacetType instruction which we diff --git a/toolchain/check/impl.cpp b/toolchain/check/impl.cpp index 22335bfaae69..afdad3ff74d2 100644 --- a/toolchain/check/impl.cpp +++ b/toolchain/check/impl.cpp @@ -405,10 +405,10 @@ static auto WitnessQueryMatchesInterface( // `.Self` replaced, so we need to do that here. // // TODO: Do this more eagerly as soon as we know the full decl before we - // construct the witness table from it? - SubstPeriodSelfCallbacks callbacks(&context, loc_id, + // construct the witness table from it? We do replace `.Self` in the facet + // type, but we don't replace the designators. + access_interface = SubstPeriodSelf(context, loc_id, access_interface, context.constant_values().Get(impl_self)); - access_interface = SubstPeriodSelf(context, callbacks, access_interface); return access_interface == impl_interface; } @@ -854,32 +854,39 @@ auto IsImplEffectivelyFinal(Context& context, const SemIR::Impl& impl) -> bool { context.constant_values().Get(impl.constraint_id).is_concrete()); } -auto CheckConstraintIsInterface(Context& context, SemIR::InstId impl_decl_id, +auto CheckConstraintIsFacetType(Context& context, SemIR::LocId loc_id, + SemIR::TypeInstId constraint_id) -> bool { + auto facet_type_const_inst_id = + context.constant_values().GetConstantInstId(constraint_id); + auto facet_type = + context.insts().TryGetAs(facet_type_const_inst_id); + if (!facet_type && facet_type_const_inst_id != SemIR::ErrorInst::InstId) { + CARBON_DIAGNOSTIC(ImplAsNonFacetType, Error, "impl as non-facet type {0}", + InstIdAsType); + context.emitter().Emit(loc_id, ImplAsNonFacetType, constraint_id); + return false; + } + return true; +} + +auto CheckConstraintIsInterface(Context& context, SemIR::LocId loc_id, SemIR::InstId self_id, SemIR::TypeInstId constraint_id) -> SemIR::SpecificInterface { - auto facet_type_as_type_id = - context.types().GetTypeIdForTypeInstId(constraint_id); - if (facet_type_as_type_id == SemIR::ErrorInst::TypeId) { + auto canon_constraint_id = + context.constant_values().GetConstantInstId(constraint_id); + if (canon_constraint_id == SemIR::ErrorInst::TypeInstId) { return SemIR::SpecificInterface::None; } auto facet_type = - context.types().TryGetAs(facet_type_as_type_id); - if (!facet_type) { - CARBON_DIAGNOSTIC(ImplAsNonFacetType, Error, "impl as non-facet type {0}", - InstIdAsType); - context.emitter().Emit(impl_decl_id, ImplAsNonFacetType, constraint_id); - return SemIR::SpecificInterface::None; - } - + context.insts().GetAs(canon_constraint_id); auto identified_id = RequireIdentifiedFacetType( context, SemIR::LocId(constraint_id), - context.constant_values().Get(self_id), *facet_type, [&](auto& builder) { + context.constant_values().Get(self_id), facet_type, [&](auto& builder) { CARBON_DIAGNOSTIC(ImplOfUnidentifiedFacetType, Context, "facet type {0} cannot be identified in `impl as`", InstIdAsType); - builder.Context(impl_decl_id, ImplOfUnidentifiedFacetType, - constraint_id); + builder.Context(loc_id, ImplOfUnidentifiedFacetType, constraint_id); }); if (!identified_id.has_value()) { return SemIR::SpecificInterface::None; @@ -888,7 +895,7 @@ auto CheckConstraintIsInterface(Context& context, SemIR::InstId impl_decl_id, if (!identified.is_valid_impl_as_target()) { CARBON_DIAGNOSTIC(ImplOfNotOneInterface, Error, "impl as {0} interfaces, expected 1", int); - context.emitter().Emit(impl_decl_id, ImplOfNotOneInterface, + context.emitter().Emit(loc_id, ImplOfNotOneInterface, identified.num_interfaces_to_impl()); return SemIR::SpecificInterface::None; } diff --git a/toolchain/check/impl.h b/toolchain/check/impl.h index ba90737af804..b81b1433a891 100644 --- a/toolchain/check/impl.h +++ b/toolchain/check/impl.h @@ -84,10 +84,15 @@ auto CheckAssociatedFunctionImplementation( SemIR::SpecificId enclosing_specific_id, SemIR::InstId impl_decl_id, bool defer_thunk_definition) -> SemIR::InstId; -// Checks that the constraint specified for the impl is valid and identified. -// Returns the interface that the impl implements. On error, issues a diagnostic -// and returns `None`. -auto CheckConstraintIsInterface(Context& context, SemIR::InstId impl_decl_id, +// Checks that the constraint specified for the impl is a facet type. Returns +// false if an error was diagnosed. +auto CheckConstraintIsFacetType(Context& context, SemIR::LocId loc_id, + SemIR::TypeInstId constraint_id) -> bool; + +// Checks that the constraint specified for the impl is a valid, identified +// facet type that extends a single interface. Returns the interface that the +// impl implements. On error, issues a diagnostic and returns `None`. +auto CheckConstraintIsInterface(Context& context, SemIR::LocId loc_id, SemIR::InstId self_id, SemIR::TypeInstId constraint_id) -> SemIR::SpecificInterface; diff --git a/toolchain/check/impl_lookup.cpp b/toolchain/check/impl_lookup.cpp index 2e1e8c424a17..8594461e8c6e 100644 --- a/toolchain/check/impl_lookup.cpp +++ b/toolchain/check/impl_lookup.cpp @@ -450,10 +450,10 @@ static auto TryFindMatchingWitnessFromImplLookup( // The `req_impls` come from an IdentifiedFacetType so they have `.Self` // replaced. We need to do the same for the self and interface in the // `orig_witness` for comparing with them. - SubstPeriodSelfCallbacks callbacks(&context, loc_id, - canonical_query_self_const_id); - orig_const_self = SubstPeriodSelf(context, callbacks, orig_const_self); - orig_interface = SubstPeriodSelf(context, callbacks, orig_interface); + orig_const_self = SubstPeriodSelf(context, loc_id, orig_const_self, + canonical_query_self_const_id); + orig_interface = SubstPeriodSelf(context, loc_id, orig_interface, + canonical_query_self_const_id); // Witnesses have a canonicalized self value. Perform the same // canonicalization here so that we can compare them. @@ -470,41 +470,6 @@ static auto TryFindMatchingWitnessFromImplLookup( return SemIR::InstId::None; } -class SubstPeriodSelfInRewriteCallbacks : public SubstPeriodSelfCallbacks { - public: - explicit SubstPeriodSelfInRewriteCallbacks( - Context* context, SemIR::LocId loc_id, - SemIR::ConstantId period_self_replacement_id, - llvm::ArrayRef req_impls, - llvm::ArrayRef witness_inst_ids) - : SubstPeriodSelfCallbacks(context, loc_id, period_self_replacement_id), - req_impls_(req_impls), - witness_inst_ids_(witness_inst_ids) {} - - auto Rebuild(SemIR::InstId orig_inst_id, SemIR::Inst new_inst) - -> SemIR::InstId override { - // When rebuilding a witness where `.Self` was replaced, use a witness we - // found in impl lookup instead of performing impl lookup again. - if (auto lookup = new_inst.TryAs()) { - auto witness = TryFindMatchingWitnessFromImplLookup( - context(), loc_id(), period_self_replacement_id(), req_impls_, - witness_inst_ids_, - context().constant_values().Get(lookup->query_self_inst_id), - context().specific_interfaces().Get( - lookup->query_specific_interface_id)); - if (witness.has_value()) { - return witness; - } - } - - return SubstPeriodSelfCallbacks::Rebuild(orig_inst_id, new_inst); - } - - private: - llvm::ArrayRef req_impls_; - llvm::ArrayRef witness_inst_ids_; -}; - static auto VerifyQueryFacetTypeConstraints( Context& context, SemIR::LocId loc_id, SemIR::ConstantId query_self_const_id, @@ -517,8 +482,22 @@ static auto VerifyQueryFacetTypeConstraints( .facet_type_id); if (!facet_type_info.rewrite_constraints.empty()) { - SubstPeriodSelfInRewriteCallbacks callbacks( - &context, loc_id, query_self_const_id, req_impls, witness_inst_ids); + auto rebuild = [&](SemIR::Inst new_inst) -> SemIR::InstId { + // When rebuilding a witness where `.Self` was replaced, use a witness we + // found in impl lookup instead of performing impl lookup again. + if (auto lookup = new_inst.TryAs()) { + auto witness = TryFindMatchingWitnessFromImplLookup( + context, loc_id, query_self_const_id, req_impls, witness_inst_ids, + context.constant_values().Get(lookup->query_self_inst_id), + context.specific_interfaces().Get( + lookup->query_specific_interface_id)); + if (witness.has_value()) { + return witness; + } + } + + return SemIR::InstId::None; + }; for (const auto& rewrite : facet_type_info.rewrite_constraints) { // Replace `.Self` in rewrite constraints with the query self in order to @@ -531,9 +510,11 @@ static auto VerifyQueryFacetTypeConstraints( // execute another impl lookup. auto lhs_id = context.constant_values().GetInstId(SubstPeriodSelf( - context, callbacks, context.constant_values().Get(rewrite.lhs_id))); + context, loc_id, context.constant_values().Get(rewrite.lhs_id), + query_self_const_id, SubstPeriodSelfBehaviour::All, rebuild)); auto rhs_id = context.constant_values().GetInstId(SubstPeriodSelf( - context, callbacks, context.constant_values().Get(rewrite.rhs_id))); + context, loc_id, context.constant_values().Get(rewrite.rhs_id), + query_self_const_id, SubstPeriodSelfBehaviour::All, rebuild)); if (lhs_id != rhs_id) { // TODO: Provide a diagnostic note and location for which rewrite diff --git a/toolchain/check/period_self.cpp b/toolchain/check/period_self.cpp index 25c6222ef508..aba77da09bf5 100644 --- a/toolchain/check/period_self.cpp +++ b/toolchain/check/period_self.cpp @@ -10,6 +10,7 @@ #include "toolchain/check/facet_type.h" #include "toolchain/check/generic.h" #include "toolchain/check/inst.h" +#include "toolchain/check/subst.h" #include "toolchain/check/type.h" #include "toolchain/check/type_completion.h" #include "toolchain/sem_ir/typed_insts.h" @@ -40,199 +41,332 @@ auto MakePeriodSelfFacetValue(Context& context, SemIR::LocId loc_id, return inst_id; } -SubstPeriodSelfCallbacks::SubstPeriodSelfCallbacks( - Context* context, SemIR::LocId loc_id, - SemIR::ConstantId period_self_replacement_id, Behaviour behaviour) - : SubstInstCallbacks(context), - loc_id_(loc_id), - period_self_replacement_id_(period_self_replacement_id), - behaviour_(behaviour) {} +class SubstPeriodSelfCallbacks : public SubstInstCallbacks { + public: + explicit SubstPeriodSelfCallbacks( + Context* context, SemIR::LocId loc_id, + SemIR::ConstantId period_self_replacement_id, + SubstPeriodSelfRebuildInst rebuild) + : SubstInstCallbacks(context), + loc_id_(loc_id), + period_self_replacement_id_(period_self_replacement_id), + rebuild_callback_(rebuild) {} -auto SubstPeriodSelfCallbacks::Subst(SemIR::InstId& inst_id) -> SubstResult { - // FacetTypes are concrete even if they have `.Self` inside them, but we - // don't recurse into FacetTypes, so we can use this as a base case. This - // avoids infinite recursion on TypeType and ErrorInst. - if (context().constant_values().Get(inst_id).is_concrete()) { - return FullySubstituted; - } - // Don't recurse into nested facet types, even if they are symbolic. Leave - // their `.Self` as is. - if (context().insts().Is(inst_id)) { - return FullySubstituted; + virtual ~SubstPeriodSelfCallbacks() = default; + + auto Rebuild(SemIR::InstId orig_inst_id, SemIR::Inst new_inst) + -> SemIR::InstId override { + if (rebuild_callback_) { + if (auto inst_id = rebuild_callback_(new_inst); inst_id.has_value()) { + return inst_id; + } + } + return RebuildNewInst(SemIR::LocId(orig_inst_id), new_inst); } - // Look for implicit use of `.Self` in designators: `.X` is really `.Self.X`. - if (auto access = - context().insts().TryGetAs(inst_id)) { - if (auto witness = context().insts().TryGetAs( - access->witness_id)) { - // Canonicalization not necessary; We are working with the constant + protected: + auto GetReplacement(SemIR::InstId period_self) -> SemIR::InstId { + auto period_self_type_id = context().insts().Get(period_self).type_id(); + CARBON_CHECK(context().types().Is(period_self_type_id)); + + auto replacement_self_inst_id = + context().constant_values().GetInstId(period_self_replacement_id_); + auto replacement_type_id = + context().insts().Get(replacement_self_inst_id).type_id(); + CARBON_CHECK(context().types().IsFacetType(replacement_type_id)); + + // If the replacement has the same type as `.Self`, use it directly. + if (replacement_type_id == period_self_type_id) { + return replacement_self_inst_id; + } + + // If we have already converted the replacement to the type of `.Self`, use + // our previous conversion. + if (period_self_type_id == cached_replacement_type_id_) { + return cached_replacement_id_; + } + + // Convert the replacement facet to the type of `.Self`. + cached_replacement_id_ = ConvertReplacement( + replacement_self_inst_id, replacement_type_id, period_self_type_id); + cached_replacement_type_id_ = period_self_type_id; + return cached_replacement_id_; + } + + private: + auto ConvertReplacement(SemIR::InstId replacement_self_inst_id, + SemIR::TypeId replacement_type_id, + SemIR::TypeId period_self_type_id) -> SemIR::InstId { + // TODO: Replace all empty facet types with TypeType. + if (period_self_type_id == GetEmptyFacetType(context())) { + // Convert to an empty facet type (representing TypeType); we don't need + // any witnesses. + return ConvertToValueOfType(context(), loc_id_, replacement_self_inst_id, + period_self_type_id); + } + + // We have a facet or a type, but we need more interfaces in the facet type. + // We will have to synthesize a symbolic witness for each interface. + // + // Why is this okay? The type of `.Self` comes from interfaces that are + // before it (to the left of it) in the facet type. The replacement for + // `.Self` will have to impl those interfaces in order to match the facet + // type, so we know that it is valid to construct these witnesses. + + // Make the replacement into a type, which we will need for the FacetValue. + if (context().types().Is(replacement_type_id)) { + replacement_self_inst_id = context().constant_values().GetInstId( + EvalOrAddInst( + context(), loc_id_, + {.type_id = SemIR::TypeType::TypeId, + .facet_value_inst_id = replacement_self_inst_id})); + } + + auto period_self_facet_type = + context().types().GetAs(period_self_type_id); + auto identified_period_self_type_id = RequireIdentifiedFacetType( + context(), loc_id_, + context().constant_values().Get(replacement_self_inst_id), + period_self_facet_type, [&](auto& /*builder*/) { + // The facet type containing this `.Self` should have already been + // identified, which would ensure that the type of `.Self` can be + // identified since it can only depend on things to the left of it + // inside the same facet type. + CARBON_FATAL("could not identify type of `.Self`"); + }); + const auto& identified_period_self_type = + context().identified_facet_types().Get(identified_period_self_type_id); + auto required_impls = identified_period_self_type.required_impls(); + llvm::SmallVector witnesses; + witnesses.reserve(required_impls.size()); + for (const auto& req : required_impls) { + witnesses.push_back(context().constant_values().GetInstId( + EvalOrAddInst( + context(), loc_id_, + {.type_id = + GetSingletonType(context(), SemIR::WitnessType::TypeInstId), + .query_self_inst_id = + context().constant_values().GetInstId(req.self_facet_value), + .query_specific_interface_id = + context().specific_interfaces().Add( + req.specific_interface)}))); + } + return context().constant_values().GetInstId( + EvalOrAddInst( + context(), loc_id_, + { + .type_id = period_self_type_id, + .type_inst_id = + context().types().GetAsTypeInstId(replacement_self_inst_id), + .witnesses_block_id = context().inst_blocks().Add(witnesses), + })); + } + + SemIR::LocId loc_id_; + SemIR::ConstantId period_self_replacement_id_; + SubstPeriodSelfRebuildInst rebuild_callback_; + + // The last output of GetReplacement(). + SemIR::InstId cached_replacement_id_ = SemIR::InstId::None; + // The type of the last output of GetReplacement(). If the type of `.Self` + // matches, we can reuse the `cached_replacement_id_`. + SemIR::TypeId cached_replacement_type_id_ = SemIR::TypeId::None; +}; + +class SubstDesignatorPeriodSelfCallbacks : public SubstPeriodSelfCallbacks { + public: + explicit SubstDesignatorPeriodSelfCallbacks( + Context* context, SemIR::LocId loc_id, + SemIR::ConstantId period_self_replacement_id, + SubstPeriodSelfRebuildInst rebuild) + : SubstPeriodSelfCallbacks(context, loc_id, period_self_replacement_id, + rebuild) {} + + ~SubstDesignatorPeriodSelfCallbacks() override { + CARBON_CHECK(designator_states_.empty()); + } + + auto Subst(SemIR::InstId& inst_id) -> SubstResult override { + // FacetTypes are concrete even if they have `.Self` inside them, but we + // don't recurse into FacetTypes, so we can use this as a base case. This + // avoids infinite recursion on TypeType and ErrorInst. + if (context().constant_values().Get(inst_id).is_concrete()) { + return FullySubstituted; + } + // Don't recurse into nested facet types, even if they are symbolic. Leave + // their `.Self` as is. + if (context().insts().Is(inst_id)) { + return FullySubstituted; + } + + // Look for implicit use of `.Self` in designators: `.X` is really + // `.Self.X`. + if (auto access = + context().insts().TryGetAs(inst_id)) { + if (auto witness = context().insts().TryGetAs( + access->witness_id)) { + // Canonicalization not necessary; we are working with the constant + // value already, and the query self in a witness is already + // canonicalized. + if (IsPeriodSelf(context(), witness->query_self_inst_id, + /*canonicalize=*/false)) { + // We are entering a designator. We watch for the witness next. + designator_states_.push_back(WitnessNext); + return SubstOperands; + } + } + } + + if (GetDesignatorState() == WitnessNext) { + if (auto witness = + context().insts().TryGetAs(inst_id)) { + // The query self comes next, before the specific interface. This + // deepnds on the order of the operands in the LookupImplWitness, and + // that Subst visits them in top to bottom order. + designator_states_.back() = WitnessSelfNext; + return SubstOperands; + } + } + + if (GetDesignatorState() == WitnessSelfNext) { + // Canonicalization not necessary; we are working with the constant // value already, and the query self in a witness is already // canonicalized. - if (IsPeriodSelf(context(), witness->query_self_inst_id, + if (IsPeriodSelf(context(), inst_id, /*canonicalize=*/false)) { - auto replacement_id = GetReplacement(witness->query_self_inst_id); - auto new_witness = - Rebuild(access->witness_id, - SemIR::LookupImplWitness{ - .type_id = witness->type_id, - .query_self_inst_id = replacement_id, - // Don't replace `.Self` in the interface specific - // here. That is an explicit `.Self` use. We'll - // revisit the instruction for that. - .query_specific_interface_id = - witness->query_specific_interface_id, - }); - auto new_access = Rebuild(inst_id, SemIR::ImplWitnessAccess{ - .type_id = access->type_id, - .witness_id = new_witness, - .index = access->index, - }); - inst_id = new_access; - return SubstAgain; + // Found the implicit use of `.Self` in a designator. + inst_id = GetReplacement(inst_id); + designator_states_.back() = RebuildNext; + return FullySubstituted; + } + } + + return SubstOperands; + } + + auto Rebuild(SemIR::InstId orig_inst_id, SemIR::Inst new_inst) + -> SemIR::InstId override { + TryPopDesignatorState(orig_inst_id); + return SubstPeriodSelfCallbacks::Rebuild(orig_inst_id, new_inst); + } + + auto ReuseUnchanged(SemIR::InstId orig_inst_id) -> SemIR::InstId override { + TryPopDesignatorState(orig_inst_id); + return SubstPeriodSelfCallbacks::ReuseUnchanged(orig_inst_id); + } + + private: + enum DesignatorState { + // We are not inside a designator. + None, + // We are looking for the LookupImplWitness of a designator next. + WitnessNext, + // We are looking for `.Self`, the next one will be the query self of the + // LookupImplWitness of a designator. This is the implicit use of `.Self`. + WitnessSelfNext, + // We have seen the query self of the designator, and are now looking for + // the designator to be rebuilt with the replacement. Any other `.Self` that + // we find are explicit uses of `.Self`, such as in the designator's + // specific interface. + RebuildNext, + }; + + auto GetDesignatorState() const -> DesignatorState { + return designator_states_.empty() ? DesignatorState::None + : designator_states_.back(); + } + + auto TryPopDesignatorState(SemIR::InstId orig_inst_id) -> void { + if (GetDesignatorState() == RebuildNext) { + if (auto access = context().insts().TryGetAs( + orig_inst_id)) { + if (auto witness = context().insts().TryGetAs( + access->witness_id)) { + // Canonicalization not necessary; we are working with the constant + // value already, and the query self in a witness is already + // canonicalized. + if (IsPeriodSelf(context(), witness->query_self_inst_id, + /*canonicalize=*/false)) { + designator_states_.pop_back(); + } + } } } } - // Look for explicit use of `.Self`. - if (behaviour_ == Behaviour::All) { - // Canonicalization not necessary; Subst will recurse anyway, so avoid - // extra work for non-matches. - if (IsPeriodSelf(context(), inst_id, /*canonicalize=*/false)) { + llvm::SmallVector designator_states_; +}; + +class SubstAllPeriodSelfCallbacks : public SubstPeriodSelfCallbacks { + public: + explicit SubstAllPeriodSelfCallbacks( + Context* context, SemIR::LocId loc_id, + SemIR::ConstantId period_self_replacement_id, + SubstPeriodSelfRebuildInst rebuild) + : SubstPeriodSelfCallbacks(context, loc_id, period_self_replacement_id, + rebuild) {} + + auto Subst(SemIR::InstId& inst_id) -> SubstResult override { + // FacetTypes are concrete even if they have `.Self` inside them, but we + // don't recurse into FacetTypes, so we can use this as a base case. This + // avoids infinite recursion on TypeType and ErrorInst. + if (context().constant_values().Get(inst_id).is_concrete()) { + return FullySubstituted; + } + // Don't recurse into nested facet types, even if they are symbolic. Leave + // their `.Self` as is. + if (context().insts().Is(inst_id)) { + return FullySubstituted; + } + + if (IsPeriodSelf(context(), inst_id, + /*canonicalize=*/false)) { inst_id = GetReplacement(inst_id); return FullySubstituted; } + + return SubstOperands; } +}; - return SubstOperands; -} - -auto SubstPeriodSelfCallbacks::Rebuild(SemIR::InstId orig_inst_id, - SemIR::Inst new_inst) -> SemIR::InstId { - return RebuildNewInst(SemIR::LocId(orig_inst_id), new_inst); -} - -auto SubstPeriodSelfCallbacks::GetReplacement(SemIR::InstId period_self) - -> SemIR::InstId { - auto period_self_type_id = context().insts().Get(period_self).type_id(); - CARBON_CHECK(context().types().Is(period_self_type_id)); - - auto replacement_self_inst_id = - context().constant_values().GetInstId(period_self_replacement_id_); - auto replacement_type_id = - context().insts().Get(replacement_self_inst_id).type_id(); - CARBON_CHECK(context().types().IsFacetType(replacement_type_id)); - - // If the replacement has the same type as `.Self`, use it directly. - if (replacement_type_id == period_self_type_id) { - return replacement_self_inst_id; - } - - // If we have already converted the replacement to the type of `.Self`, use - // our previous conversion. - if (period_self_type_id == cached_replacement_type_id_) { - return cached_replacement_id_; - } - - // Convert the replacement facet to the type of `.Self`. - cached_replacement_id_ = ConvertReplacement( - replacement_self_inst_id, replacement_type_id, period_self_type_id); - cached_replacement_type_id_ = period_self_type_id; - return cached_replacement_id_; -} - -auto SubstPeriodSelfCallbacks::ConvertReplacement( - SemIR::InstId replacement_self_inst_id, SemIR::TypeId replacement_type_id, - SemIR::TypeId period_self_type_id) -> SemIR::InstId { - // TODO: Replace all empty facet types with TypeType. - if (period_self_type_id == GetEmptyFacetType(context())) { - // Convert to an empty facet type (representing TypeType); we don't need - // any witnesses. - return ConvertToValueOfType(context(), loc_id_, replacement_self_inst_id, - period_self_type_id); - } - - // We have a facet or a type, but we need more interfaces in the facet type. - // We will have to synthesize a symbolic witness for each interface. - // - // Why is this okay? The type of `.Self` comes from interfaces that are - // before it (to the left of it) in the facet type. The replacement for - // `.Self` will have to impl those interfaces in order to match the facet - // type, so we know that it is valid to construct these witnesses. - - // Make the replacement into a type, which we will need for the FacetValue. - if (context().types().Is(replacement_type_id)) { - replacement_self_inst_id = context().constant_values().GetInstId( - EvalOrAddInst( - context(), loc_id_, - {.type_id = SemIR::TypeType::TypeId, - .facet_value_inst_id = replacement_self_inst_id})); - } - - auto period_self_facet_type = - context().types().GetAs(period_self_type_id); - auto identified_period_self_type_id = RequireIdentifiedFacetType( - context(), loc_id_, - context().constant_values().Get(replacement_self_inst_id), - period_self_facet_type, [&](auto& /*builder*/) { - // The facet type containing this `.Self` should have already been - // identified, which would ensure that the type of `.Self` can be - // identified since it can only depend on things to the left of it - // inside the same facet type. - CARBON_FATAL("could not identify type of `.Self`"); - }); - const auto& identified_period_self_type = - context().identified_facet_types().Get(identified_period_self_type_id); - auto required_impls = identified_period_self_type.required_impls(); - llvm::SmallVector witnesses; - witnesses.reserve(required_impls.size()); - for (const auto& req : required_impls) { - witnesses.push_back(context().constant_values().GetInstId( - EvalOrAddInst( - context(), loc_id_, - {.type_id = - GetSingletonType(context(), SemIR::WitnessType::TypeInstId), - .query_self_inst_id = - context().constant_values().GetInstId(req.self_facet_value), - .query_specific_interface_id = context().specific_interfaces().Add( - req.specific_interface)}))); - } - return context().constant_values().GetInstId(EvalOrAddInst( - context(), loc_id_, - { - .type_id = period_self_type_id, - .type_inst_id = - context().types().GetAsTypeInstId(replacement_self_inst_id), - .witnesses_block_id = context().inst_blocks().Add(witnesses), - })); -} - -auto SubstPeriodSelf(Context& context, SubstPeriodSelfCallbacks& callbacks, - SemIR::ConstantId const_id) -> SemIR::ConstantId { +auto SubstPeriodSelf(Context& context, SemIR::LocId loc_id, + SemIR::ConstantId const_id, + SemIR::ConstantId period_self_replacement_id, + SubstPeriodSelfBehaviour behaviour, + SubstPeriodSelfRebuildInst rebuild) -> SemIR::ConstantId { // Don't replace `.Self` with itself; that is cyclical. // // If the types differ, we would try to convert the replacement to a `.Self` // of the desired type in `const_id`, which is what we already have, so // there's nothing we need to do. But trying to do that conversion recurses // when the type of the `.Self` contains a `.Self`. - if (auto bind_type = - context.constant_values().TryGetInstAs( - GetCanonicalFacetOrTypeValue( - context, callbacks.period_self_replacement_id()))) { - if (context.entity_names().Get(bind_type->entity_name_id).name_id == - SemIR::NameId::PeriodSelf) { - return const_id; - } + if (IsPeriodSelf(context, context.constant_values().GetInstId( + period_self_replacement_id))) { + return const_id; } - auto subst_id = SubstInst( - context, context.constant_values().GetInstId(const_id), callbacks); - return context.constant_values().Get(subst_id); + switch (behaviour) { + case SubstPeriodSelfBehaviour::All: { + SubstAllPeriodSelfCallbacks callbacks( + &context, loc_id, period_self_replacement_id, rebuild); + auto subst_id = SubstInst( + context, context.constant_values().GetInstId(const_id), callbacks); + return context.constant_values().Get(subst_id); + } + case SubstPeriodSelfBehaviour::ImplicitOnly: { + SubstDesignatorPeriodSelfCallbacks callbacks( + &context, loc_id, period_self_replacement_id, rebuild); + auto subst_id = SubstInst( + context, context.constant_values().GetInstId(const_id), callbacks); + return context.constant_values().Get(subst_id); + } + } } -static auto SubstPeriodSelfInSpecific(Context& context, - SubstPeriodSelfCallbacks& callbacks, - SemIR::SpecificId specific_id) +static auto SubstPeriodSelfInSpecific( + Context& context, SemIR::LocId loc_id, SemIR::SpecificId specific_id, + SemIR::ConstantId period_self_replacement_id, + SubstPeriodSelfBehaviour behaviour, SubstPeriodSelfRebuildInst rebuild) -> SemIR::SpecificId { if (!specific_id.has_value()) { return specific_id; @@ -246,27 +380,144 @@ static auto SubstPeriodSelfInSpecific(Context& context, context.inst_blocks().Get(specific.args_id)); for (auto& arg_id : args) { auto const_id = context.constant_values().Get(arg_id); - const_id = SubstPeriodSelf(context, callbacks, const_id); + const_id = SubstPeriodSelf(context, loc_id, const_id, + period_self_replacement_id, behaviour, rebuild); arg_id = context.constant_values().GetInstId(const_id); } - return MakeSpecific(context, callbacks.loc_id(), specific.generic_id, args); + return MakeSpecific(context, loc_id, specific.generic_id, args); } -auto SubstPeriodSelf(Context& context, SubstPeriodSelfCallbacks& callbacks, - SemIR::SpecificInterface interface) +auto SubstPeriodSelf(Context& context, SemIR::LocId loc_id, + SemIR::SpecificInterface interface, + SemIR::ConstantId period_self_replacement_id, + SubstPeriodSelfBehaviour behaviour, + SubstPeriodSelfRebuildInst rebuild) -> SemIR::SpecificInterface { interface.specific_id = - SubstPeriodSelfInSpecific(context, callbacks, interface.specific_id); + SubstPeriodSelfInSpecific(context, loc_id, interface.specific_id, + period_self_replacement_id, behaviour, rebuild); return interface; } -auto SubstPeriodSelf(Context& context, SubstPeriodSelfCallbacks& callbacks, - SemIR::SpecificNamedConstraint constraint) +auto SubstPeriodSelf(Context& context, SemIR::LocId loc_id, + SemIR::SpecificNamedConstraint constraint, + SemIR::ConstantId period_self_replacement_id, + SubstPeriodSelfBehaviour behaviour, + SubstPeriodSelfRebuildInst rebuild) -> SemIR::SpecificNamedConstraint { constraint.specific_id = - SubstPeriodSelfInSpecific(context, callbacks, constraint.specific_id); + SubstPeriodSelfInSpecific(context, loc_id, constraint.specific_id, + period_self_replacement_id, behaviour, rebuild); return constraint; } +auto SubstPeriodSelfInFacetType(Context& context, SemIR::LocId loc_id, + SemIR::TypeInstId self_type_inst_id, + SemIR::TypeInstId facet_type_inst_id) + -> SemIR::TypeInstId { + auto canon_facet_type_inst_id = + context.constant_values().GetConstantInstId(facet_type_inst_id); + if (canon_facet_type_inst_id == SemIR::ErrorInst::TypeInstId) { + return SemIR::ErrorInst::TypeInstId; + } + + auto period_self_replacement_id = + context.constant_values().Get(self_type_inst_id); + + auto orig_facet_type = + context.insts().GetAs(canon_facet_type_inst_id); + const auto& orig_info = + context.facet_types().Get(orig_facet_type.facet_type_id); + + auto replace_interface = [&](SemIR::SpecificInterface si) { + return SubstPeriodSelf(context, loc_id, si, period_self_replacement_id, + SubstPeriodSelfBehaviour::All); + }; + auto replace_constraint = [&](SemIR::SpecificNamedConstraint sc) { + return SubstPeriodSelf(context, loc_id, sc, period_self_replacement_id, + SubstPeriodSelfBehaviour::All); + }; + auto replace_type_impls_interface = + [&](SemIR::FacetTypeInfo::TypeImplsInterface impls) + -> SemIR::FacetTypeInfo::TypeImplsInterface { + auto self = SubstPeriodSelf( + context, loc_id, context.constant_values().Get(impls.self_type), + period_self_replacement_id, SubstPeriodSelfBehaviour::All); + auto interface = SubstPeriodSelf(context, loc_id, impls.specific_interface, + period_self_replacement_id, + SubstPeriodSelfBehaviour::All); + return {context.constant_values().GetInstId(self), interface}; + }; + auto replace_type_impls_constraint = + [&](SemIR::FacetTypeInfo::TypeImplsNamedConstraint impls) + -> SemIR::FacetTypeInfo::TypeImplsNamedConstraint { + auto self = SubstPeriodSelf( + context, loc_id, context.constant_values().Get(impls.self_type), + period_self_replacement_id, SubstPeriodSelfBehaviour::All); + auto constraint = SubstPeriodSelf( + context, loc_id, impls.specific_named_constraint, + period_self_replacement_id, SubstPeriodSelfBehaviour::All); + return {context.constant_values().GetInstId(self), constraint}; + }; + auto replace_rewrite = [&](SemIR::FacetTypeInfo::RewriteConstraint r) + -> SemIR::FacetTypeInfo::RewriteConstraint { + // Replace `.Self` only on the RHS, and only if's not a simple designator. + // The LHS is always a simple designator since only that is allowed. + if (auto access = + context.insts().TryGetAs(r.rhs_id)) { + if (auto lookup = context.insts().TryGetAs( + access->witness_id)) { + if (IsPeriodSelf(context, lookup->query_self_inst_id)) { + // Found a simple designator. Leave it alone. + return r; + } + } + } + auto rhs = SubstPeriodSelf( + context, loc_id, context.constant_values().Get(r.rhs_id), + period_self_replacement_id, SubstPeriodSelfBehaviour::All); + return {r.lhs_id, context.constant_values().GetInstId(rhs)}; + }; + + SemIR::FacetTypeInfo info; + llvm::append_range( + info.extend_constraints, + llvm::map_range(orig_info.extend_constraints, replace_interface)); + llvm::append_range( + info.extend_named_constraints, + llvm::map_range(orig_info.extend_named_constraints, replace_constraint)); + llvm::append_range( + info.self_impls_constraints, + llvm::map_range(orig_info.self_impls_constraints, replace_interface)); + llvm::append_range(info.self_impls_named_constraints, + llvm::map_range(orig_info.self_impls_named_constraints, + replace_constraint)); + llvm::append_range(info.type_impls_interfaces, + llvm::map_range(orig_info.type_impls_interfaces, + replace_type_impls_interface)); + llvm::append_range(info.type_impls_named_constraints, + llvm::map_range(orig_info.type_impls_named_constraints, + replace_type_impls_constraint)); + llvm::append_range( + info.rewrite_constraints, + llvm::map_range(orig_info.rewrite_constraints, replace_rewrite)); + + info.Canonicalize(); + if (info == orig_info) { + // Nothing was substituted, keep the original instruction. + // + // It is noteworthy that we keep the non-canonical instruction here, since + // it may have a symbolic value (which is attached to a generic, and can be + // updated by specifics). Returning the canonical facet type instruction + // would lose the attachment to the generic which would be incorrect. + return facet_type_inst_id; + } + + return AddTypeInst( + context, loc_id, + {.type_id = SemIR::TypeType::TypeId, + .facet_type_id = context.facet_types().Add(info)}); +} + auto IsPeriodSelf(Context& context, SemIR::InstId inst_id, bool canonicalize) -> bool { auto const_inst_id = context.constant_values().GetConstantInstId(inst_id); @@ -356,7 +607,7 @@ class SearchCanonicalForExplicitPeriodSelf : public SubstInstCallbacks { const_inst_id)) { if (auto lookup = context().insts().TryGetAs( access->witness_id)) { - // Canonicalization not necessary; We are working with the constant + // Canonicalization not necessary; we are working with the constant // value already, and the query self in a witness is already // canonicalized. if (IsPeriodSelf(context(), lookup->query_self_inst_id, diff --git a/toolchain/check/period_self.h b/toolchain/check/period_self.h index f72e6671d635..b2de3c03666d 100644 --- a/toolchain/check/period_self.h +++ b/toolchain/check/period_self.h @@ -21,57 +21,57 @@ namespace Carbon::Check { auto MakePeriodSelfFacetValue(Context& context, SemIR::LocId loc_id, SemIR::TypeId self_type_id) -> SemIR::InstId; -class SubstPeriodSelfCallbacks : public SubstInstCallbacks { - public: - enum Behaviour { - ImplicitOnly, - All, - }; - - explicit SubstPeriodSelfCallbacks( - Context* context, SemIR::LocId loc_id, - SemIR::ConstantId period_self_replacement_id, - Behaviour behaviour = Behaviour::All); - auto Subst(SemIR::InstId& inst_id) -> SubstResult override; - auto Rebuild(SemIR::InstId orig_inst_id, SemIR::Inst new_inst) - -> SemIR::InstId override; - - auto loc_id() const -> SemIR::LocId { return loc_id_; } - auto period_self_replacement_id() const -> SemIR::ConstantId { - return period_self_replacement_id_; - } - - private: - auto GetReplacement(SemIR::InstId period_self) -> SemIR::InstId; - auto ConvertReplacement(SemIR::InstId replacement_self_inst_id, - SemIR::TypeId replacement_type_id, - SemIR::TypeId period_self_type_id) -> SemIR::InstId; - - SemIR::LocId loc_id_; - SemIR::ConstantId period_self_replacement_id_; - Behaviour behaviour_; - - // The last output of GetReplacement(). - SemIR::InstId cached_replacement_id_ = SemIR::InstId::None; - // The type of the last output of GetReplacement(). If the type of `.Self` - // matches, we can reuse the `cached_replacement_id_`. - SemIR::TypeId cached_replacement_type_id_ = SemIR::TypeId::None; +enum class SubstPeriodSelfBehaviour { + ImplicitOnly, + All, }; -// Replace all `.Self` references in `const_id`. The `callbacks` specifies the -// facet to replace them with. -auto SubstPeriodSelf(Context& context, SubstPeriodSelfCallbacks& callbacks, - SemIR::ConstantId const_id) -> SemIR::ConstantId; +using SubstPeriodSelfRebuildInst = + llvm::function_refSemIR::InstId>; -// Replace all `.Self` references in the specific of the interface or named -// constraint. The `callbacks` specifies the facet to replace them with. -auto SubstPeriodSelf(Context& context, SubstPeriodSelfCallbacks& callbacks, - SemIR::SpecificInterface interface) - -> SemIR::SpecificInterface; -auto SubstPeriodSelf(Context& context, SubstPeriodSelfCallbacks& callbacks, - SemIR::SpecificNamedConstraint constraint) +// Replace `.Self` references in `const_id` with `period_self_replacement_id`. +// The `behaviour` specifies if all `.Self` are replaced or just implicit use in +// designators. The `rebuild` callback can optionally be specified to override +// how an instruction is re-constructed to form an InstId after replacement. It +// can return None to fall back to the default of evaluating the inst. +auto SubstPeriodSelf( + Context& context, SemIR::LocId loc_id, SemIR::ConstantId const_id, + SemIR::ConstantId period_self_replacement_id, + SubstPeriodSelfBehaviour behaviour = SubstPeriodSelfBehaviour::All, + SubstPeriodSelfRebuildInst rebuild = nullptr) -> SemIR::ConstantId; + +// Replace `.Self` references in the specific of the interface or named +// constraint. +// +// The `behaviour` specifies if all `.Self` are replaced or just implicit use in +// designators. The `rebuild` callback can optionally be specified to override +// how an instruction is re-constructed to form an InstId after replacement. It +// can return None to fall back to the default of evaluating the inst. +auto SubstPeriodSelf( + Context& context, SemIR::LocId loc_id, SemIR::SpecificInterface interface, + SemIR::ConstantId period_self_replacement_id, + SubstPeriodSelfBehaviour behaviour = SubstPeriodSelfBehaviour::All, + SubstPeriodSelfRebuildInst rebuild = nullptr) -> SemIR::SpecificInterface; +auto SubstPeriodSelf( + Context& context, SemIR::LocId loc_id, + SemIR::SpecificNamedConstraint constraint, + SemIR::ConstantId period_self_replacement_id, + SubstPeriodSelfBehaviour behaviour = SubstPeriodSelfBehaviour::All, + SubstPeriodSelfRebuildInst rebuild = nullptr) -> SemIR::SpecificNamedConstraint; +// Replace all `.Self` references with the self-type. The `facet_type_inst_id` +// must be a `FacetType` instruction (or error). +// +// Unlike SubstPeriodSelf, which works with constant values and thus canonical +// instructions, this operation can be done for non-canonical facet types. A new +// instruction is added for the output FacetType if anything does get replaced, +// and the original instruction id is preserved otherwise. +auto SubstPeriodSelfInFacetType(Context& context, SemIR::LocId loc_id, + SemIR::TypeInstId self_type_inst_id, + SemIR::TypeInstId facet_type_inst_id) + -> SemIR::TypeInstId; + // Returns whether the constant value of `inst_id` is a reference to `.Self`. // // If `canonicalize` is true, look at the constant value of `inst_id` and get diff --git a/toolchain/check/testdata/facet/access.carbon b/toolchain/check/testdata/facet/access.carbon index 1cd526a2ac99..b17bc935c88e 100644 --- a/toolchain/check/testdata/facet/access.carbon +++ b/toolchain/check/testdata/facet/access.carbon @@ -347,6 +347,75 @@ fn F2(U:! Z) { F1(U.Y1, U.G()); } +// --- find_access_value_in_first_nested_access.carbon +library "[[@TEST_NAME]]"; + +interface W {} +interface X { + let X1:! type; +} +interface Y(U:! type) { + let Y1:! X where .X1 = U; +} +interface Z(T:! type) { + let Z1:! type; +} + +fn F(U:! W, V:! Z(.Self) where .Z1 impls Y(U)) { + // This has to search for a value for `.Z1.Y1.X1` in V. To do so it needs to + // look for a rewrite of .X1. First it looks in `V.Z1.Y1`, where + // it finds the rewrite of `.X1 = U`. + // + // Only U impls W so we use `as W` to test that the LHS is U. + V.(Z(V).Z1).(Y(U).Y1).(X.X1) as W; +} + +// --- find_access_value_in_second_nested_access.carbon +library "[[@TEST_NAME]]"; + +interface W {} +interface X { + let X1:! type; +} +interface Y(T:! type) { + let Y1:! type; +} +interface Z(U:! type, PeriodSelf:! type) { + let Z1:! Y(PeriodSelf) where .Y1 impls (X where .X1 = U); +} + +fn F(U:! W, V:! Z(U, .Self)) { + // This has to search for a value for `.Z1.Y1.X1` in V. To do so it needs to + // look for a rewrite of .X1. First it looks in `V.Z1.Y1`, then `V.Z1`, where + // it finds the rewrite of `.X1 = U`. + // + // Only U impls W so we use `as W` to test that the LHS is U. + V.(Z(U, V).Z1).(Y(V).Y1).(X.X1) as W; +} + +// --- find_access_value_in_third_nested_access.carbon +library "[[@TEST_NAME]]"; + +interface W {} +interface X { + let X1:! type; +} +interface Y { + let Y1:! type; +} +interface Z(T:! type) { + let Z1:! type; +} + +fn F(U:! W, V:! Z(.Self) where .Z1 impls (Y where .Y1 impls (X where .X1 = U))) { + // This has to search for a value for `.Z1.Y1.X1` in V. To do so it needs to + // look for a rewrite of .X1. First it looks in `V.Z1.Y1`, then `V.Z1`, then + // `V` where it finds the rewrite of `.X1 = U`. + // + // Only U impls W so we use `as W` to test that the LHS is U. + V.(Z(V).Z1).(Y.Y1).(X.X1) as W; +} + // CHECK:STDOUT: --- access_assoc_fn.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { @@ -774,11 +843,11 @@ fn F2(U:! Z) { // CHECK:STDOUT: %AB: %facet_type.82c = symbolic_binding AB, 0 [symbolic] // CHECK:STDOUT: %AB.as_type: type = facet_access_type %AB [symbolic] // CHECK:STDOUT: %A.lookup_impl_witness.1b9: = lookup_impl_witness %AB, @A [symbolic] -// CHECK:STDOUT: %B.lookup_impl_witness.97b: = lookup_impl_witness %AB, @B [symbolic] // CHECK:STDOUT: %.262: Core.Form = init_form %empty_tuple.type [concrete] // CHECK:STDOUT: %pattern_type.cb1: type = pattern_type %empty_tuple.type [concrete] // CHECK:STDOUT: %F.type: type = fn_type @F [concrete] // CHECK:STDOUT: %F: %F.type = struct_value () [concrete] +// CHECK:STDOUT: %B.lookup_impl_witness.97b: = lookup_impl_witness %AB, @B [symbolic] // CHECK:STDOUT: %.469: Core.Form = init_form %empty_struct_type [concrete] // CHECK:STDOUT: %pattern_type.a96: type = pattern_type %empty_struct_type [concrete] // CHECK:STDOUT: %G.type: type = fn_type @G [concrete] diff --git a/toolchain/check/testdata/impl/assoc_const_self.carbon b/toolchain/check/testdata/impl/assoc_const_self.carbon index e47f75c760d0..269c6811fcc4 100644 --- a/toolchain/check/testdata/impl/assoc_const_self.carbon +++ b/toolchain/check/testdata/impl/assoc_const_self.carbon @@ -695,7 +695,7 @@ fn CallF() { // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: -// CHECK:STDOUT: impl @empty_struct_type.as..impl: %.loc15_7.2 as %.loc15_18 { +// CHECK:STDOUT: impl @empty_struct_type.as..impl: %.loc15_7.2 as { // CHECK:STDOUT: !members: // CHECK:STDOUT: witness = // CHECK:STDOUT: } diff --git a/toolchain/check/testdata/impl/fail_extend_non_interface.carbon b/toolchain/check/testdata/impl/fail_extend_non_interface.carbon index 375dd22f8bca..24ff9d937653 100644 --- a/toolchain/check/testdata/impl/fail_extend_non_interface.carbon +++ b/toolchain/check/testdata/impl/fail_extend_non_interface.carbon @@ -50,7 +50,7 @@ class C { // CHECK:STDOUT: %C.decl: type = class_decl @C [concrete = constants.%C] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: -// CHECK:STDOUT: impl @C.as..impl: %Self.ref as %i32; +// CHECK:STDOUT: impl @C.as..impl: %Self.ref as ; // CHECK:STDOUT: // CHECK:STDOUT: class @C { // CHECK:STDOUT: impl_decl @C.as..impl [concrete] {} { diff --git a/toolchain/check/testdata/impl/fail_impl_bad_interface.carbon b/toolchain/check/testdata/impl/fail_impl_bad_interface.carbon index 79b5e8cfec5a..a53209907ffe 100644 --- a/toolchain/check/testdata/impl/fail_impl_bad_interface.carbon +++ b/toolchain/check/testdata/impl/fail_impl_bad_interface.carbon @@ -124,7 +124,7 @@ impl {.a: bool} as type where .Self impls I {} // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: -// CHECK:STDOUT: impl @bool.as..impl: %.loc8_6 as %.loc8_14 { +// CHECK:STDOUT: impl @bool.as..impl: %.loc8_6 as { // CHECK:STDOUT: !members: // CHECK:STDOUT: witness = // CHECK:STDOUT: } @@ -170,7 +170,7 @@ impl {.a: bool} as type where .Self impls I {} // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: -// CHECK:STDOUT: impl @f64.as..impl: %f64 as %.loc8_18 { +// CHECK:STDOUT: impl @f64.as..impl: %f64 as { // CHECK:STDOUT: !members: // CHECK:STDOUT: witness = // CHECK:STDOUT: } @@ -232,7 +232,7 @@ impl {.a: bool} as type where .Self impls I {} // CHECK:STDOUT: !requires: // CHECK:STDOUT: } // CHECK:STDOUT: -// CHECK:STDOUT: impl @struct_type.a.as..impl: %struct_type.a as %.loc10_25 { +// CHECK:STDOUT: impl @struct_type.a.as..impl: %struct_type.a as { // CHECK:STDOUT: !members: // CHECK:STDOUT: witness = // CHECK:STDOUT: } diff --git a/toolchain/check/testdata/impl/forward_decls.carbon b/toolchain/check/testdata/impl/forward_decls.carbon index 385f94118371..7ade658de026 100644 --- a/toolchain/check/testdata/impl/forward_decls.carbon +++ b/toolchain/check/testdata/impl/forward_decls.carbon @@ -1052,7 +1052,7 @@ impl C as Y {} // CHECK:STDOUT: !requires: // CHECK:STDOUT: } // CHECK:STDOUT: -// CHECK:STDOUT: impl @D.as..impl: %D.ref.loc13 as %.loc13_13 { +// CHECK:STDOUT: impl @D.as..impl: %D.ref.loc13 as { // CHECK:STDOUT: !members: // CHECK:STDOUT: witness = // CHECK:STDOUT: } @@ -1502,7 +1502,7 @@ impl C as Y {} // CHECK:STDOUT: !requires: // CHECK:STDOUT: } // CHECK:STDOUT: -// CHECK:STDOUT: impl @empty_struct_type.as..impl: %.loc10_7.2 as file.%.loc10_14.2; +// CHECK:STDOUT: impl @empty_struct_type.as..impl: %.loc10_7.2 as ; // CHECK:STDOUT: // CHECK:STDOUT: specific @I.WithSelf(constants.%Self.ab9) {} // CHECK:STDOUT: diff --git a/toolchain/check/testdata/impl/impl_assoc_const.carbon b/toolchain/check/testdata/impl/impl_assoc_const.carbon index c59cdfb62206..43e4eb6bca88 100644 --- a/toolchain/check/testdata/impl/impl_assoc_const.carbon +++ b/toolchain/check/testdata/impl/impl_assoc_const.carbon @@ -492,7 +492,7 @@ fn G() { // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: -// CHECK:STDOUT: impl @empty_tuple.type.as..impl: %.loc13_7.2 as %.loc13_14 { +// CHECK:STDOUT: impl @empty_tuple.type.as..impl: %.loc13_7.2 as { // CHECK:STDOUT: !members: // CHECK:STDOUT: witness = // CHECK:STDOUT: } @@ -558,7 +558,7 @@ fn G() { // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: -// CHECK:STDOUT: impl @empty_tuple.type.as..impl: %.loc13_7.2 as %.loc13_14 { +// CHECK:STDOUT: impl @empty_tuple.type.as..impl: %.loc13_7.2 as { // CHECK:STDOUT: !members: // CHECK:STDOUT: witness = // CHECK:STDOUT: } diff --git a/toolchain/check/testdata/impl/impl_assoc_const_with_prelude.carbon b/toolchain/check/testdata/impl/impl_assoc_const_with_prelude.carbon index 68f6af126647..d11f11f97392 100644 --- a/toolchain/check/testdata/impl/impl_assoc_const_with_prelude.carbon +++ b/toolchain/check/testdata/impl/impl_assoc_const_with_prelude.carbon @@ -435,7 +435,7 @@ impl () as I where .X = {.a = true, .b = (1, 2)} and .X = {.a = false, .b = (3, // CHECK:STDOUT: !requires: // CHECK:STDOUT: } // CHECK:STDOUT: -// CHECK:STDOUT: impl @empty_tuple.type.as..impl: %.loc10_7.2 as %.loc10_14 { +// CHECK:STDOUT: impl @empty_tuple.type.as..impl: %.loc10_7.2 as { // CHECK:STDOUT: !members: // CHECK:STDOUT: witness = // CHECK:STDOUT: } diff --git a/toolchain/check/testdata/impl/import_interface_assoc_const.carbon b/toolchain/check/testdata/impl/import_interface_assoc_const.carbon index 0aa8dd0ff0fd..ef9c23d26ec1 100644 --- a/toolchain/check/testdata/impl/import_interface_assoc_const.carbon +++ b/toolchain/check/testdata/impl/import_interface_assoc_const.carbon @@ -856,9 +856,9 @@ impl CD as IF where .F = 0 { // CHECK:STDOUT: !requires: // CHECK:STDOUT: } // CHECK:STDOUT: -// CHECK:STDOUT: impl @C5.as..impl.bcbfb8.1: %C5.ref as %.loc17_15; +// CHECK:STDOUT: impl @C5.as..impl.bcbfb8.1: %C5.ref as ; // CHECK:STDOUT: -// CHECK:STDOUT: impl @C5.as..impl.bcbfb8.2: %C5.ref as %.loc27_15 { +// CHECK:STDOUT: impl @C5.as..impl.bcbfb8.2: %C5.ref as { // CHECK:STDOUT: !members: // CHECK:STDOUT: witness = // CHECK:STDOUT: } @@ -1055,7 +1055,7 @@ impl CD as IF where .F = 0 { // CHECK:STDOUT: !requires: // CHECK:STDOUT: } // CHECK:STDOUT: -// CHECK:STDOUT: impl @C7.as..impl: %C7.ref as %.loc10_14 { +// CHECK:STDOUT: impl @C7.as..impl: %C7.ref as { // CHECK:STDOUT: !members: // CHECK:STDOUT: witness = // CHECK:STDOUT: } @@ -1150,7 +1150,7 @@ impl CD as IF where .F = 0 { // CHECK:STDOUT: !requires: // CHECK:STDOUT: } // CHECK:STDOUT: -// CHECK:STDOUT: impl @C8.as..impl: %C8.ref as %.loc10_14 { +// CHECK:STDOUT: impl @C8.as..impl: %C8.ref as { // CHECK:STDOUT: !members: // CHECK:STDOUT: witness = // CHECK:STDOUT: } @@ -1244,7 +1244,7 @@ impl CD as IF where .F = 0 { // CHECK:STDOUT: !requires: // CHECK:STDOUT: } // CHECK:STDOUT: -// CHECK:STDOUT: impl @C9.as..impl: %C9.ref as %.loc10_14 { +// CHECK:STDOUT: impl @C9.as..impl: %C9.ref as { // CHECK:STDOUT: !members: // CHECK:STDOUT: witness = // CHECK:STDOUT: } @@ -1337,7 +1337,7 @@ impl CD as IF where .F = 0 { // CHECK:STDOUT: !requires: // CHECK:STDOUT: } // CHECK:STDOUT: -// CHECK:STDOUT: impl @CA.as..impl: %CA.ref as %.loc14_14 { +// CHECK:STDOUT: impl @CA.as..impl: %CA.ref as { // CHECK:STDOUT: !members: // CHECK:STDOUT: witness = // CHECK:STDOUT: } diff --git a/toolchain/check/testdata/impl/incomplete.carbon b/toolchain/check/testdata/impl/incomplete.carbon index 1b415de4bfc0..e177f838af2b 100644 --- a/toolchain/check/testdata/impl/incomplete.carbon +++ b/toolchain/check/testdata/impl/incomplete.carbon @@ -387,7 +387,7 @@ interface B { // CHECK:STDOUT: // CHECK:STDOUT: interface @J; // CHECK:STDOUT: -// CHECK:STDOUT: impl @C.as..impl: %C.ref.loc13 as %.loc13_13 { +// CHECK:STDOUT: impl @C.as..impl: %C.ref.loc13 as { // CHECK:STDOUT: !members: // CHECK:STDOUT: witness = // CHECK:STDOUT: } @@ -756,7 +756,7 @@ interface B { // CHECK:STDOUT: // CHECK:STDOUT: constraint @X; // CHECK:STDOUT: -// CHECK:STDOUT: impl @C.as..impl: %C.ref as %X.ref; +// CHECK:STDOUT: impl @C.as..impl: %C.ref as ; // CHECK:STDOUT: // CHECK:STDOUT: class @C { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type] diff --git a/toolchain/check/testdata/impl/lookup/access.carbon b/toolchain/check/testdata/impl/lookup/access.carbon index 97997b91601f..bcac443b1c32 100644 --- a/toolchain/check/testdata/impl/lookup/access.carbon +++ b/toolchain/check/testdata/impl/lookup/access.carbon @@ -22,33 +22,34 @@ interface Z { } // There's 2 ImplWitnessAccess instructions in the type, but they together -// resolve to a symbolic type value, so the type structure is: `?C(?)` -impl forall [U:! Z where .Z1 impls Y] U as X(U.Z1.(Y.Y1)) {} +// resolve to a symbolic type value, so the type structure is: `? as X(?)` +impl forall [T:! Z where .Z1 impls Y] T as X(T.Z1.(Y.Y1)) {} class C { impl as Y where .Y1 = {} {} } fn F(V:! Z where .Z1 = C) { - // The type stucture is `?C(C)` which will match the impl's less specific - // `?C(?)`, then the impl will deduce the parameter of `X` to be `{}` from the - // type of `V`. This would fail if ImplWitnessAccess instructions were treated - // as Concrete in the type structure, since the impl would have a different - // concrete value (an ImplWitnessAccess) than the query (a StructValue). + // The type stucture is `? as X({})` which will match the impl's less specific + // `? as X(?)`, then the impl will deduce the parameter of `X` to be `{}` from + // the type of `V`. This would fail if ImplWitnessAccess instructions were + // treated as Concrete in the type structure, since the impl would have a + // different concrete value (an ImplWitnessAccess) than the query (a + // StructValue). V as X({}); } fn G(V:! Z where .Z1 impls Y) { - // The type structure is `?C(?)`, also built from ImplWitnessAccess insts, - // which will match the impl's `?C(?)`. + // The type structure is `? as X(?)`, also built from ImplWitnessAccess insts, + // which will match the impl's `? as X(?)`. V as X(V.(Z.Z1).(Y.Y1)); } -// TODO: This creates an infinite loop in `.Self` substitution. -// fn H(U:! type, V:! Z where .Z1 impls (Y where .Y1 = U)) { -// // The type structure is `?C(?)`, without using an ImplWitnessAccess, which -// // will match the impl's `?C(?)`. This would fail if ImplWitnessAccess -// // instructions were treated as Concrete in the type structure, since the -// // impl's type structure would be more specific than the query's. -// V as X(U); -// } +fn H(U:! type, V:! Z where .Z1 impls (Y where .Y1 = U)) { + // The type structure is `? as X(?)`, without using an ImplWitnessAccess, + // which will match the impl's `? as X(?)`. This would fail if + // ImplWitnessAccess instructions were treated as Concrete in the type + // structure, since the impl's type structure would be more specific than the + // query's. + V as X(U); +} diff --git a/toolchain/check/testdata/interface/incomplete.carbon b/toolchain/check/testdata/interface/incomplete.carbon index 574e106a9200..02a311c90cba 100644 --- a/toolchain/check/testdata/interface/incomplete.carbon +++ b/toolchain/check/testdata/interface/incomplete.carbon @@ -200,7 +200,7 @@ interface A(T:! type) { // CHECK:STDOUT: !requires: // CHECK:STDOUT: } // CHECK:STDOUT: -// CHECK:STDOUT: impl @empty_struct_type.as..impl: %.loc16_7.2 as %.loc16_14 { +// CHECK:STDOUT: impl @empty_struct_type.as..impl: %.loc16_7.2 as { // CHECK:STDOUT: !members: // CHECK:STDOUT: witness = // CHECK:STDOUT: } diff --git a/toolchain/check/testdata/interop/cpp/operators/arithmetic_operators.carbon b/toolchain/check/testdata/interop/cpp/operators/arithmetic_operators.carbon index 1d6acb6a8069..93f7ea582124 100644 --- a/toolchain/check/testdata/interop/cpp/operators/arithmetic_operators.carbon +++ b/toolchain/check/testdata/interop/cpp/operators/arithmetic_operators.carbon @@ -345,10 +345,10 @@ fn TestUnaryOperators(a: Cpp.Int16, b: Cpp.Int32) { // CHECK:STDOUT: %.97d: type = fn_type_with_self_type %AddWith.WithSelf.Op.type.82f, %AddWith.facet.570 [symbolic] // CHECK:STDOUT: %impl.elem1.07a: %.97d = impl_witness_access %AddWith.lookup_impl_witness.3c7, element1 [symbolic] // CHECK:STDOUT: %specific_impl_fn.dbc: = specific_impl_function %impl.elem1.07a, @AddWith.WithSelf.Op(%U.as_type.2b8, %AddWith.facet.570) [symbolic] -// CHECK:STDOUT: %Destroy.lookup_impl_witness.d86: = lookup_impl_witness %Result, @Destroy [symbolic] +// CHECK:STDOUT: %Destroy.lookup_impl_witness: = lookup_impl_witness %Result, @Destroy [symbolic] // CHECK:STDOUT: %Destroy.WithSelf.Op.type.cb2e47.2: type = fn_type @Destroy.WithSelf.Op, @Destroy.WithSelf(%Result) [symbolic] // CHECK:STDOUT: %.d5f: type = fn_type_with_self_type %Destroy.WithSelf.Op.type.cb2e47.2, %Result [symbolic] -// CHECK:STDOUT: %impl.elem0.a6b: %.d5f = impl_witness_access %Destroy.lookup_impl_witness.d86, element0 [symbolic] +// CHECK:STDOUT: %impl.elem0.a6b: %.d5f = impl_witness_access %Destroy.lookup_impl_witness, element0 [symbolic] // CHECK:STDOUT: %specific_impl_fn.761: = specific_impl_function %impl.elem0.a6b, @Destroy.WithSelf.Op(%Result) [symbolic] // CHECK:STDOUT: %SubWith.type.99c: type = facet_type <@SubWith.1, @SubWith.1(%Other)> [symbolic] // CHECK:STDOUT: %Self.ddf: %SubWith.type.99c = symbolic_binding Self, 1 [symbolic] @@ -1383,7 +1383,7 @@ fn TestUnaryOperators(a: Cpp.Int16, b: Cpp.Int32) { // CHECK:STDOUT: %specific_impl_fn.loc126_5.3: = specific_impl_function %impl.elem1.loc126_5.2, @AddWith.WithSelf.Op(%U.as_type.loc123_36.1, %AddWith.facet) [symbolic = %specific_impl_fn.loc126_5.3 (constants.%specific_impl_fn.dbc)] // CHECK:STDOUT: %Destroy.WithSelf.Op.type: type = fn_type @Destroy.WithSelf.Op, @Destroy.WithSelf(%Result.loc121_9.1) [symbolic = %Destroy.WithSelf.Op.type (constants.%Destroy.WithSelf.Op.type.cb2e47.2)] // CHECK:STDOUT: %.loc126_5.5: type = fn_type_with_self_type %Destroy.WithSelf.Op.type, %Result.loc121_9.1 [symbolic = %.loc126_5.5 (constants.%.d5f)] -// CHECK:STDOUT: %Destroy.lookup_impl_witness: = lookup_impl_witness %Result.loc121_9.1, @Destroy [symbolic = %Destroy.lookup_impl_witness (constants.%Destroy.lookup_impl_witness.d86)] +// CHECK:STDOUT: %Destroy.lookup_impl_witness: = lookup_impl_witness %Result.loc121_9.1, @Destroy [symbolic = %Destroy.lookup_impl_witness (constants.%Destroy.lookup_impl_witness)] // CHECK:STDOUT: %impl.elem0.loc126_5.2: @AddWith.loc124.%.loc126_5.5 (%.d5f) = impl_witness_access %Destroy.lookup_impl_witness, element0 [symbolic = %impl.elem0.loc126_5.2 (constants.%impl.elem0.a6b)] // CHECK:STDOUT: %specific_impl_fn.loc126_5.4: = specific_impl_function %impl.elem0.loc126_5.2, @Destroy.WithSelf.Op(%Result.loc121_9.1) [symbolic = %specific_impl_fn.loc126_5.4 (constants.%specific_impl_fn.761)] // CHECK:STDOUT: @@ -1401,7 +1401,7 @@ fn TestUnaryOperators(a: Cpp.Int16, b: Cpp.Int32) { // CHECK:STDOUT: %.loc126_5.2: ref @AddWith.loc124.%Result.as_type.loc123_54.1 (%Result.as_type) = temporary_storage // CHECK:STDOUT: %AddWith.WithSelf.Op.call: init @AddWith.loc124.%Result.as_type.loc123_54.1 (%Result.as_type) to %.loc126_5.2 = call %bound_method.loc126_5.2(%x.ref, %y.ref) // CHECK:STDOUT: %.loc126_5.3: ref @AddWith.loc124.%Result.as_type.loc123_54.1 (%Result.as_type) = temporary %.loc126_5.2, %AddWith.WithSelf.Op.call -// CHECK:STDOUT: %impl.elem0.loc126_5.1: @AddWith.loc124.%.loc126_5.5 (%.d5f) = impl_witness_access constants.%Destroy.lookup_impl_witness.d86, element0 [symbolic = %impl.elem0.loc126_5.2 (constants.%impl.elem0.a6b)] +// CHECK:STDOUT: %impl.elem0.loc126_5.1: @AddWith.loc124.%.loc126_5.5 (%.d5f) = impl_witness_access constants.%Destroy.lookup_impl_witness, element0 [symbolic = %impl.elem0.loc126_5.2 (constants.%impl.elem0.a6b)] // CHECK:STDOUT: %bound_method.loc126_5.3: = bound_method %.loc126_5.3, %impl.elem0.loc126_5.1 // CHECK:STDOUT: %specific_impl_fn.loc126_5.2: = specific_impl_function %impl.elem0.loc126_5.1, @Destroy.WithSelf.Op(constants.%Result) [symbolic = %specific_impl_fn.loc126_5.4 (constants.%specific_impl_fn.761)] // CHECK:STDOUT: %bound_method.loc126_5.4: = bound_method %.loc126_5.3, %specific_impl_fn.loc126_5.2 @@ -1425,7 +1425,7 @@ fn TestUnaryOperators(a: Cpp.Int16, b: Cpp.Int32) { // CHECK:STDOUT: %specific_impl_fn.loc137_5.3: = specific_impl_function %impl.elem1.loc137_5.2, @SubWith.WithSelf.Op(%U.as_type.loc134_36.1, %SubWith.facet) [symbolic = %specific_impl_fn.loc137_5.3 (constants.%specific_impl_fn.0d3)] // CHECK:STDOUT: %Destroy.WithSelf.Op.type: type = fn_type @Destroy.WithSelf.Op, @Destroy.WithSelf(%Result.loc132_9.1) [symbolic = %Destroy.WithSelf.Op.type (constants.%Destroy.WithSelf.Op.type.cb2e47.2)] // CHECK:STDOUT: %.loc137_5.5: type = fn_type_with_self_type %Destroy.WithSelf.Op.type, %Result.loc132_9.1 [symbolic = %.loc137_5.5 (constants.%.d5f)] -// CHECK:STDOUT: %Destroy.lookup_impl_witness: = lookup_impl_witness %Result.loc132_9.1, @Destroy [symbolic = %Destroy.lookup_impl_witness (constants.%Destroy.lookup_impl_witness.d86)] +// CHECK:STDOUT: %Destroy.lookup_impl_witness: = lookup_impl_witness %Result.loc132_9.1, @Destroy [symbolic = %Destroy.lookup_impl_witness (constants.%Destroy.lookup_impl_witness)] // CHECK:STDOUT: %impl.elem0.loc137_5.2: @SubWith.loc135.%.loc137_5.5 (%.d5f) = impl_witness_access %Destroy.lookup_impl_witness, element0 [symbolic = %impl.elem0.loc137_5.2 (constants.%impl.elem0.a6b)] // CHECK:STDOUT: %specific_impl_fn.loc137_5.4: = specific_impl_function %impl.elem0.loc137_5.2, @Destroy.WithSelf.Op(%Result.loc132_9.1) [symbolic = %specific_impl_fn.loc137_5.4 (constants.%specific_impl_fn.761)] // CHECK:STDOUT: @@ -1443,7 +1443,7 @@ fn TestUnaryOperators(a: Cpp.Int16, b: Cpp.Int32) { // CHECK:STDOUT: %.loc137_5.2: ref @SubWith.loc135.%Result.as_type.loc134_54.1 (%Result.as_type) = temporary_storage // CHECK:STDOUT: %SubWith.WithSelf.Op.call: init @SubWith.loc135.%Result.as_type.loc134_54.1 (%Result.as_type) to %.loc137_5.2 = call %bound_method.loc137_5.2(%x.ref, %y.ref) // CHECK:STDOUT: %.loc137_5.3: ref @SubWith.loc135.%Result.as_type.loc134_54.1 (%Result.as_type) = temporary %.loc137_5.2, %SubWith.WithSelf.Op.call -// CHECK:STDOUT: %impl.elem0.loc137_5.1: @SubWith.loc135.%.loc137_5.5 (%.d5f) = impl_witness_access constants.%Destroy.lookup_impl_witness.d86, element0 [symbolic = %impl.elem0.loc137_5.2 (constants.%impl.elem0.a6b)] +// CHECK:STDOUT: %impl.elem0.loc137_5.1: @SubWith.loc135.%.loc137_5.5 (%.d5f) = impl_witness_access constants.%Destroy.lookup_impl_witness, element0 [symbolic = %impl.elem0.loc137_5.2 (constants.%impl.elem0.a6b)] // CHECK:STDOUT: %bound_method.loc137_5.3: = bound_method %.loc137_5.3, %impl.elem0.loc137_5.1 // CHECK:STDOUT: %specific_impl_fn.loc137_5.2: = specific_impl_function %impl.elem0.loc137_5.1, @Destroy.WithSelf.Op(constants.%Result) [symbolic = %specific_impl_fn.loc137_5.4 (constants.%specific_impl_fn.761)] // CHECK:STDOUT: %bound_method.loc137_5.4: = bound_method %.loc137_5.3, %specific_impl_fn.loc137_5.2 @@ -1467,7 +1467,7 @@ fn TestUnaryOperators(a: Cpp.Int16, b: Cpp.Int32) { // CHECK:STDOUT: %specific_impl_fn.loc148_5.3: = specific_impl_function %impl.elem1.loc148_5.2, @MulWith.WithSelf.Op(%U.as_type.loc145_36.1, %MulWith.facet) [symbolic = %specific_impl_fn.loc148_5.3 (constants.%specific_impl_fn.889)] // CHECK:STDOUT: %Destroy.WithSelf.Op.type: type = fn_type @Destroy.WithSelf.Op, @Destroy.WithSelf(%Result.loc143_9.1) [symbolic = %Destroy.WithSelf.Op.type (constants.%Destroy.WithSelf.Op.type.cb2e47.2)] // CHECK:STDOUT: %.loc148_5.5: type = fn_type_with_self_type %Destroy.WithSelf.Op.type, %Result.loc143_9.1 [symbolic = %.loc148_5.5 (constants.%.d5f)] -// CHECK:STDOUT: %Destroy.lookup_impl_witness: = lookup_impl_witness %Result.loc143_9.1, @Destroy [symbolic = %Destroy.lookup_impl_witness (constants.%Destroy.lookup_impl_witness.d86)] +// CHECK:STDOUT: %Destroy.lookup_impl_witness: = lookup_impl_witness %Result.loc143_9.1, @Destroy [symbolic = %Destroy.lookup_impl_witness (constants.%Destroy.lookup_impl_witness)] // CHECK:STDOUT: %impl.elem0.loc148_5.2: @MulWith.loc146.%.loc148_5.5 (%.d5f) = impl_witness_access %Destroy.lookup_impl_witness, element0 [symbolic = %impl.elem0.loc148_5.2 (constants.%impl.elem0.a6b)] // CHECK:STDOUT: %specific_impl_fn.loc148_5.4: = specific_impl_function %impl.elem0.loc148_5.2, @Destroy.WithSelf.Op(%Result.loc143_9.1) [symbolic = %specific_impl_fn.loc148_5.4 (constants.%specific_impl_fn.761)] // CHECK:STDOUT: @@ -1485,7 +1485,7 @@ fn TestUnaryOperators(a: Cpp.Int16, b: Cpp.Int32) { // CHECK:STDOUT: %.loc148_5.2: ref @MulWith.loc146.%Result.as_type.loc145_54.1 (%Result.as_type) = temporary_storage // CHECK:STDOUT: %MulWith.WithSelf.Op.call: init @MulWith.loc146.%Result.as_type.loc145_54.1 (%Result.as_type) to %.loc148_5.2 = call %bound_method.loc148_5.2(%x.ref, %y.ref) // CHECK:STDOUT: %.loc148_5.3: ref @MulWith.loc146.%Result.as_type.loc145_54.1 (%Result.as_type) = temporary %.loc148_5.2, %MulWith.WithSelf.Op.call -// CHECK:STDOUT: %impl.elem0.loc148_5.1: @MulWith.loc146.%.loc148_5.5 (%.d5f) = impl_witness_access constants.%Destroy.lookup_impl_witness.d86, element0 [symbolic = %impl.elem0.loc148_5.2 (constants.%impl.elem0.a6b)] +// CHECK:STDOUT: %impl.elem0.loc148_5.1: @MulWith.loc146.%.loc148_5.5 (%.d5f) = impl_witness_access constants.%Destroy.lookup_impl_witness, element0 [symbolic = %impl.elem0.loc148_5.2 (constants.%impl.elem0.a6b)] // CHECK:STDOUT: %bound_method.loc148_5.3: = bound_method %.loc148_5.3, %impl.elem0.loc148_5.1 // CHECK:STDOUT: %specific_impl_fn.loc148_5.2: = specific_impl_function %impl.elem0.loc148_5.1, @Destroy.WithSelf.Op(constants.%Result) [symbolic = %specific_impl_fn.loc148_5.4 (constants.%specific_impl_fn.761)] // CHECK:STDOUT: %bound_method.loc148_5.4: = bound_method %.loc148_5.3, %specific_impl_fn.loc148_5.2 @@ -1509,7 +1509,7 @@ fn TestUnaryOperators(a: Cpp.Int16, b: Cpp.Int32) { // CHECK:STDOUT: %specific_impl_fn.loc159_5.3: = specific_impl_function %impl.elem1.loc159_5.2, @DivWith.WithSelf.Op(%U.as_type.loc156_36.1, %DivWith.facet) [symbolic = %specific_impl_fn.loc159_5.3 (constants.%specific_impl_fn.dbd)] // CHECK:STDOUT: %Destroy.WithSelf.Op.type: type = fn_type @Destroy.WithSelf.Op, @Destroy.WithSelf(%Result.loc154_9.1) [symbolic = %Destroy.WithSelf.Op.type (constants.%Destroy.WithSelf.Op.type.cb2e47.2)] // CHECK:STDOUT: %.loc159_5.5: type = fn_type_with_self_type %Destroy.WithSelf.Op.type, %Result.loc154_9.1 [symbolic = %.loc159_5.5 (constants.%.d5f)] -// CHECK:STDOUT: %Destroy.lookup_impl_witness: = lookup_impl_witness %Result.loc154_9.1, @Destroy [symbolic = %Destroy.lookup_impl_witness (constants.%Destroy.lookup_impl_witness.d86)] +// CHECK:STDOUT: %Destroy.lookup_impl_witness: = lookup_impl_witness %Result.loc154_9.1, @Destroy [symbolic = %Destroy.lookup_impl_witness (constants.%Destroy.lookup_impl_witness)] // CHECK:STDOUT: %impl.elem0.loc159_5.2: @DivWith.loc157.%.loc159_5.5 (%.d5f) = impl_witness_access %Destroy.lookup_impl_witness, element0 [symbolic = %impl.elem0.loc159_5.2 (constants.%impl.elem0.a6b)] // CHECK:STDOUT: %specific_impl_fn.loc159_5.4: = specific_impl_function %impl.elem0.loc159_5.2, @Destroy.WithSelf.Op(%Result.loc154_9.1) [symbolic = %specific_impl_fn.loc159_5.4 (constants.%specific_impl_fn.761)] // CHECK:STDOUT: @@ -1527,7 +1527,7 @@ fn TestUnaryOperators(a: Cpp.Int16, b: Cpp.Int32) { // CHECK:STDOUT: %.loc159_5.2: ref @DivWith.loc157.%Result.as_type.loc156_54.1 (%Result.as_type) = temporary_storage // CHECK:STDOUT: %DivWith.WithSelf.Op.call: init @DivWith.loc157.%Result.as_type.loc156_54.1 (%Result.as_type) to %.loc159_5.2 = call %bound_method.loc159_5.2(%x.ref, %y.ref) // CHECK:STDOUT: %.loc159_5.3: ref @DivWith.loc157.%Result.as_type.loc156_54.1 (%Result.as_type) = temporary %.loc159_5.2, %DivWith.WithSelf.Op.call -// CHECK:STDOUT: %impl.elem0.loc159_5.1: @DivWith.loc157.%.loc159_5.5 (%.d5f) = impl_witness_access constants.%Destroy.lookup_impl_witness.d86, element0 [symbolic = %impl.elem0.loc159_5.2 (constants.%impl.elem0.a6b)] +// CHECK:STDOUT: %impl.elem0.loc159_5.1: @DivWith.loc157.%.loc159_5.5 (%.d5f) = impl_witness_access constants.%Destroy.lookup_impl_witness, element0 [symbolic = %impl.elem0.loc159_5.2 (constants.%impl.elem0.a6b)] // CHECK:STDOUT: %bound_method.loc159_5.3: = bound_method %.loc159_5.3, %impl.elem0.loc159_5.1 // CHECK:STDOUT: %specific_impl_fn.loc159_5.2: = specific_impl_function %impl.elem0.loc159_5.1, @Destroy.WithSelf.Op(constants.%Result) [symbolic = %specific_impl_fn.loc159_5.4 (constants.%specific_impl_fn.761)] // CHECK:STDOUT: %bound_method.loc159_5.4: = bound_method %.loc159_5.3, %specific_impl_fn.loc159_5.2 @@ -1551,7 +1551,7 @@ fn TestUnaryOperators(a: Cpp.Int16, b: Cpp.Int32) { // CHECK:STDOUT: %specific_impl_fn.loc170_5.3: = specific_impl_function %impl.elem1.loc170_5.2, @ModWith.WithSelf.Op(%U.as_type.loc167_36.1, %ModWith.facet) [symbolic = %specific_impl_fn.loc170_5.3 (constants.%specific_impl_fn.772)] // CHECK:STDOUT: %Destroy.WithSelf.Op.type: type = fn_type @Destroy.WithSelf.Op, @Destroy.WithSelf(%Result.loc165_9.1) [symbolic = %Destroy.WithSelf.Op.type (constants.%Destroy.WithSelf.Op.type.cb2e47.2)] // CHECK:STDOUT: %.loc170_5.5: type = fn_type_with_self_type %Destroy.WithSelf.Op.type, %Result.loc165_9.1 [symbolic = %.loc170_5.5 (constants.%.d5f)] -// CHECK:STDOUT: %Destroy.lookup_impl_witness: = lookup_impl_witness %Result.loc165_9.1, @Destroy [symbolic = %Destroy.lookup_impl_witness (constants.%Destroy.lookup_impl_witness.d86)] +// CHECK:STDOUT: %Destroy.lookup_impl_witness: = lookup_impl_witness %Result.loc165_9.1, @Destroy [symbolic = %Destroy.lookup_impl_witness (constants.%Destroy.lookup_impl_witness)] // CHECK:STDOUT: %impl.elem0.loc170_5.2: @ModWith.loc168.%.loc170_5.5 (%.d5f) = impl_witness_access %Destroy.lookup_impl_witness, element0 [symbolic = %impl.elem0.loc170_5.2 (constants.%impl.elem0.a6b)] // CHECK:STDOUT: %specific_impl_fn.loc170_5.4: = specific_impl_function %impl.elem0.loc170_5.2, @Destroy.WithSelf.Op(%Result.loc165_9.1) [symbolic = %specific_impl_fn.loc170_5.4 (constants.%specific_impl_fn.761)] // CHECK:STDOUT: @@ -1569,7 +1569,7 @@ fn TestUnaryOperators(a: Cpp.Int16, b: Cpp.Int32) { // CHECK:STDOUT: %.loc170_5.2: ref @ModWith.loc168.%Result.as_type.loc167_54.1 (%Result.as_type) = temporary_storage // CHECK:STDOUT: %ModWith.WithSelf.Op.call: init @ModWith.loc168.%Result.as_type.loc167_54.1 (%Result.as_type) to %.loc170_5.2 = call %bound_method.loc170_5.2(%x.ref, %y.ref) // CHECK:STDOUT: %.loc170_5.3: ref @ModWith.loc168.%Result.as_type.loc167_54.1 (%Result.as_type) = temporary %.loc170_5.2, %ModWith.WithSelf.Op.call -// CHECK:STDOUT: %impl.elem0.loc170_5.1: @ModWith.loc168.%.loc170_5.5 (%.d5f) = impl_witness_access constants.%Destroy.lookup_impl_witness.d86, element0 [symbolic = %impl.elem0.loc170_5.2 (constants.%impl.elem0.a6b)] +// CHECK:STDOUT: %impl.elem0.loc170_5.1: @ModWith.loc168.%.loc170_5.5 (%.d5f) = impl_witness_access constants.%Destroy.lookup_impl_witness, element0 [symbolic = %impl.elem0.loc170_5.2 (constants.%impl.elem0.a6b)] // CHECK:STDOUT: %bound_method.loc170_5.3: = bound_method %.loc170_5.3, %impl.elem0.loc170_5.1 // CHECK:STDOUT: %specific_impl_fn.loc170_5.2: = specific_impl_function %impl.elem0.loc170_5.1, @Destroy.WithSelf.Op(constants.%Result) [symbolic = %specific_impl_fn.loc170_5.4 (constants.%specific_impl_fn.761)] // CHECK:STDOUT: %bound_method.loc170_5.4: = bound_method %.loc170_5.3, %specific_impl_fn.loc170_5.2 @@ -1787,7 +1787,7 @@ fn TestUnaryOperators(a: Cpp.Int16, b: Cpp.Int32) { // CHECK:STDOUT: %specific_impl_fn.loc296_3.3: = specific_impl_function %impl.elem1.loc296_3.2, @Negate.WithSelf.Op(%Negate.facet) [symbolic = %specific_impl_fn.loc296_3.3 (constants.%specific_impl_fn.f22)] // CHECK:STDOUT: %Destroy.WithSelf.Op.type: type = fn_type @Destroy.WithSelf.Op, @Destroy.WithSelf(%Result.loc292_9.1) [symbolic = %Destroy.WithSelf.Op.type (constants.%Destroy.WithSelf.Op.type.cb2e47.2)] // CHECK:STDOUT: %.loc296_3.4: type = fn_type_with_self_type %Destroy.WithSelf.Op.type, %Result.loc292_9.1 [symbolic = %.loc296_3.4 (constants.%.d5f)] -// CHECK:STDOUT: %Destroy.lookup_impl_witness: = lookup_impl_witness %Result.loc292_9.1, @Destroy [symbolic = %Destroy.lookup_impl_witness (constants.%Destroy.lookup_impl_witness.d86)] +// CHECK:STDOUT: %Destroy.lookup_impl_witness: = lookup_impl_witness %Result.loc292_9.1, @Destroy [symbolic = %Destroy.lookup_impl_witness (constants.%Destroy.lookup_impl_witness)] // CHECK:STDOUT: %impl.elem0.loc296_3.2: @TestNegate.%.loc296_3.4 (%.d5f) = impl_witness_access %Destroy.lookup_impl_witness, element0 [symbolic = %impl.elem0.loc296_3.2 (constants.%impl.elem0.a6b)] // CHECK:STDOUT: %specific_impl_fn.loc296_3.4: = specific_impl_function %impl.elem0.loc296_3.2, @Destroy.WithSelf.Op(%Result.loc292_9.1) [symbolic = %specific_impl_fn.loc296_3.4 (constants.%specific_impl_fn.761)] // CHECK:STDOUT: @@ -1801,7 +1801,7 @@ fn TestUnaryOperators(a: Cpp.Int16, b: Cpp.Int32) { // CHECK:STDOUT: %.loc296_3.1: ref @TestNegate.%Result.as_type.loc293_50.1 (%Result.as_type) = temporary_storage // CHECK:STDOUT: %Negate.WithSelf.Op.call: init @TestNegate.%Result.as_type.loc293_50.1 (%Result.as_type) to %.loc296_3.1 = call %bound_method.loc296_3.2(%x.ref) // CHECK:STDOUT: %.loc296_3.2: ref @TestNegate.%Result.as_type.loc293_50.1 (%Result.as_type) = temporary %.loc296_3.1, %Negate.WithSelf.Op.call -// CHECK:STDOUT: %impl.elem0.loc296_3.1: @TestNegate.%.loc296_3.4 (%.d5f) = impl_witness_access constants.%Destroy.lookup_impl_witness.d86, element0 [symbolic = %impl.elem0.loc296_3.2 (constants.%impl.elem0.a6b)] +// CHECK:STDOUT: %impl.elem0.loc296_3.1: @TestNegate.%.loc296_3.4 (%.d5f) = impl_witness_access constants.%Destroy.lookup_impl_witness, element0 [symbolic = %impl.elem0.loc296_3.2 (constants.%impl.elem0.a6b)] // CHECK:STDOUT: %bound_method.loc296_3.3: = bound_method %.loc296_3.2, %impl.elem0.loc296_3.1 // CHECK:STDOUT: %specific_impl_fn.loc296_3.2: = specific_impl_function %impl.elem0.loc296_3.1, @Destroy.WithSelf.Op(constants.%Result) [symbolic = %specific_impl_fn.loc296_3.4 (constants.%specific_impl_fn.761)] // CHECK:STDOUT: %bound_method.loc296_3.4: = bound_method %.loc296_3.2, %specific_impl_fn.loc296_3.2 diff --git a/toolchain/check/type_completion.cpp b/toolchain/check/type_completion.cpp index 271d5609e675..18abb9a7e872 100644 --- a/toolchain/check/type_completion.cpp +++ b/toolchain/check/type_completion.cpp @@ -998,7 +998,7 @@ static auto IdentifyFacetType(Context& context, SemIR::LocId loc_id, llvm::SmallVector impls; // `.Self` is always replaced with the top-level self type. - SubstPeriodSelfCallbacks callbacks(&context, loc_id, initial_self_const_id); + auto period_self_replacement_id = initial_self_const_id; while (!work.empty()) { SelfImplsFacetType next_impls = work.pop_back_val(); @@ -1012,15 +1012,18 @@ static auto IdentifyFacetType(Context& context, SemIR::LocId loc_id, // Note that we subst `.Self` in the interface, but we do not in the // self type here, as that would be cyclical, replacing part of the self // type with itself. - return {self_const_id, SubstPeriodSelf(context, callbacks, interface)}; + return {self_const_id, SubstPeriodSelf(context, loc_id, interface, + period_self_replacement_id)}; }; auto type_and_interface = [&](SemIR::FacetTypeInfo::TypeImplsInterface impls) -> SemIR::IdentifiedFacetType::RequiredImpl { auto self = SubstPeriodSelf( - context, callbacks, context.constant_values().Get(impls.self_type)); + context, loc_id, context.constant_values().Get(impls.self_type), + period_self_replacement_id); auto interface = - SubstPeriodSelf(context, callbacks, impls.specific_interface); + SubstPeriodSelf(context, loc_id, impls.specific_interface, + period_self_replacement_id); return {self, interface}; }; diff --git a/toolchain/sem_ir/impl.cpp b/toolchain/sem_ir/impl.cpp index edae3eb5b802..79fbc6e68824 100644 --- a/toolchain/sem_ir/impl.cpp +++ b/toolchain/sem_ir/impl.cpp @@ -24,12 +24,12 @@ auto ImplStore::GetOrAddLookupBucket(const Impl& impl) -> LookupBucketRef { sem_ir_.types().TryGetAs(facet_type_type_id)) { auto identified_id = sem_ir_.identified_facet_types().Lookup( {facet_type->facet_type_id, self_const_id}); - if (identified_id.has_value()) { - const auto& identified = - sem_ir_.identified_facet_types().Get(identified_id); - if (identified.is_valid_impl_as_target()) { - impl_as_interface = identified.impl_as_target_interface(); - } + CARBON_CHECK(identified_id.has_value(), + "impl with unidentitied facet type"); + const auto& identified = + sem_ir_.identified_facet_types().Get(identified_id); + if (identified.is_valid_impl_as_target()) { + impl_as_interface = identified.impl_as_target_interface(); } } return LookupBucketRef(