From 417531f484e63955d00353407fa53e8e1e1f4893 Mon Sep 17 00:00:00 2001 From: Dana Jansens Date: Thu, 14 May 2026 12:37:50 -0400 Subject: [PATCH] Use early rewrites in compound member access (#7207) We replace the non-canonical ImplWitnessAccess instruction with an ImplWitnessAccessSubstituted instruction containing the RHS of a prior rewrite constraint in the same facet type when possible, in order to access the value of the prior constraint. We were doing this only in the designator access path though, not in the compound member access. Join these two code paths when they construct the ImplWitnessAccess, and perform the early access there for both. --- toolchain/check/member_access.cpp | 52 +++++++++++-------- .../testdata/facet/early_rewrites.carbon | 21 ++++++++ 2 files changed, 51 insertions(+), 22 deletions(-) diff --git a/toolchain/check/member_access.cpp b/toolchain/check/member_access.cpp index 3ff6cf55159f..94b62ec6f079 100644 --- a/toolchain/check/member_access.cpp +++ b/toolchain/check/member_access.cpp @@ -178,6 +178,26 @@ static auto ScopeNeedsImplLookup(Context& context, return true; } +static auto PerformImplWitnessAccessAndSubstitute( + Context& context, SemIR::LocId loc_id, SemIR::ImplWitnessAccess access) + -> SemIR::InstId { + auto access_id = + GetOrAddInst(context, loc_id, access); + + if (!context.rewrites_stack().empty()) { + if (auto result = context.rewrites_stack().back().Lookup( + context.constant_values().Get(access_id))) { + return GetOrAddInst( + context, loc_id, + {.type_id = access.type_id, + .impl_witness_access_id = access_id, + .value_id = result.value()}); + } + } + + return access_id; +} + static auto AccessMemberOfImplWitness( Context& context, SemIR::LocId loc_id, SemIR::InstId witness_id, SemIR::SpecificId interface_with_self_specific_id, SemIR::InstId member_id) @@ -203,10 +223,11 @@ static auto AccessMemberOfImplWitness( auto assoc_type_id = GetTypeForSpecificAssociatedEntity( context, interface_with_self_specific_id, assoc_entity->decl_id); - return GetOrAddInst(context, loc_id, - {.type_id = assoc_type_id, - .witness_id = witness_id, - .index = assoc_entity->index}); + return PerformImplWitnessAccessAndSubstitute( + context, loc_id, + SemIR::ImplWitnessAccess{.type_id = assoc_type_id, + .witness_id = witness_id, + .index = assoc_entity->index}); } // For an impl lookup query with a single interface in it, we can convert the @@ -356,20 +377,6 @@ static auto LookupMemberNameInScope(Context& context, SemIR::LocId loc_id, } } - if (!context.rewrites_stack().empty()) { - if (auto access = - context.insts().TryGetAs(member_id)) { - if (auto result = context.rewrites_stack().back().Lookup( - context.constant_values().Get(member_id))) { - return GetOrAddInst( - context, loc_id, - {.type_id = access->type_id, - .impl_witness_access_id = member_id, - .value_id = result.value()}); - } - } - } - return member_id; } @@ -715,10 +722,11 @@ static auto GetAssociatedValueImpl(Context& context, SemIR::LocId loc_id, context, interface_with_self_specific_id, assoc_entity.decl_id); // Now that we have the witness, an index into it, and the type of the // result, return the element of the witness. - return GetOrAddInst(context, loc_id, - {.type_id = assoc_type_id, - .witness_id = witness_id, - .index = assoc_entity.index}); + return PerformImplWitnessAccessAndSubstitute( + context, loc_id, + SemIR::ImplWitnessAccess{.type_id = assoc_type_id, + .witness_id = witness_id, + .index = assoc_entity.index}); } auto GetAssociatedValue(Context& context, SemIR::LocId loc_id, diff --git a/toolchain/check/testdata/facet/early_rewrites.carbon b/toolchain/check/testdata/facet/early_rewrites.carbon index 8729e78a79ae..1fd7dfc2d313 100644 --- a/toolchain/check/testdata/facet/early_rewrites.carbon +++ b/toolchain/check/testdata/facet/early_rewrites.carbon @@ -323,6 +323,27 @@ fn G(T:! J where .J1 = D) { F(T); } +// --- early_rewrite_of_compound_access.carbon +library "[[@TEST_NAME]]"; + +interface Z { + let Z1:! type; + let Z2:! type; +} + +interface Y {} +class NeedY(T:! Y) {} + +impl () as Y {} + +// These are written differently but should behave the same. + +// Designator access of `.Z1`. +fn F(_:! Z where .Z1 = () and .Z2 = NeedY(.Z1)) {} + +// Compound member access of `.Z1`. +fn G(_:! Z where .Z1 = () and .Z2 = NeedY(.Self.(Z.Z1))) {} + // --- fail_todo_resolved_constraint_visible_in_type_parameter.carbon library "[[@TEST_NAME]]";