From 835a61c3858e7d563c4256a832bf72fa6697ff76 Mon Sep 17 00:00:00 2001 From: Dana Jansens Date: Fri, 15 May 2026 10:53:51 -0400 Subject: [PATCH] Gracefully handle a concrete ImplWitnessAccess in the type structure (#7214) While this can only happen when some other error is taking place, we should handle it gracefully and report a concrete (but unmatchable) value in the type structure instead of CHECK-failing. --- .../check/testdata/impl/lookup/access.carbon | 28 +++++++++++++++++++ toolchain/sem_ir/type_iterator.cpp | 6 ++-- 2 files changed, 30 insertions(+), 4 deletions(-) diff --git a/toolchain/check/testdata/impl/lookup/access.carbon b/toolchain/check/testdata/impl/lookup/access.carbon index bcac443b1c32..709294c32a1a 100644 --- a/toolchain/check/testdata/impl/lookup/access.carbon +++ b/toolchain/check/testdata/impl/lookup/access.carbon @@ -53,3 +53,31 @@ fn H(U:! type, V:! Z where .Z1 impls (Y where .Y1 = U)) { // query's. V as X(U); } + +// --- fail_concrete_impl_witness_access_in_type_structure.carbon +library "[[@TEST_NAME]]"; + +interface Z(T:! type) { + let Z1:! type; +} +interface Y {} + +class C; + +// Converting to this facet type requires a lookup for `C.(Z(C).Z1) as Y`. That +// has a type structure that contains a fully concrete ImplWitnessAccess. But if +// the lookup fails because there is no impl for `C as Z(C)`, then the access +// remains in the type structure as is. This test ensures the type structure can +// handle this edge case. +fn F(unused T:! Z(.Self) where C impls (Z(C) where .Z1 impls Y)) {} + +fn G(T:! Z(.Self)) { + // CHECK:STDERR: fail_concrete_impl_witness_access_in_type_structure.carbon:[[@LINE+7]]:3: error: cannot convert type `T` that implements `Z(.Self)` into type implementing `Z(.Self) where C impls Z(C) and C.(Z(C).Z1) impls Y` [ConversionFailureFacetToFacet] + // CHECK:STDERR: F(T); + // CHECK:STDERR: ^~~~ + // CHECK:STDERR: fail_concrete_impl_witness_access_in_type_structure.carbon:[[@LINE-6]]:1: note: while deducing parameters of generic declared here [DeductionGenericHere] + // CHECK:STDERR: fn F(unused T:! Z(.Self) where C impls (Z(C) where .Z1 impls Y)) {} + // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + // CHECK:STDERR: + F(T); +} diff --git a/toolchain/sem_ir/type_iterator.cpp b/toolchain/sem_ir/type_iterator.cpp index 75b6cdfc0880..f31c51523951 100644 --- a/toolchain/sem_ir/type_iterator.cpp +++ b/toolchain/sem_ir/type_iterator.cpp @@ -254,10 +254,8 @@ auto TypeIterator::ProcessType(InstId inst_id) -> std::optional { // We return FacetValues as a separate iterative step, then also recurse // into the the self value being accessed. // - // Witness access of a concrete value would have evaluated to the accessed - // value, so we only see ImplWitnessAccess in a type when it's a symbolic - // value, which implies it contains a LookupImplWitness. - CARBON_CHECK(sem_ir_->constant_values().Get(inst_id).is_symbolic()); + // If ImplWitnessAccess did not evaluate to some other value, it must + // contain a non-final witness. auto witness = sem_ir_->insts().GetAs(access.witness_id); // Recurse into symbolic ImplWitnessAccess, replacing it with the self