[Demangler] Handle invertible reqs for extensions in findDeclContext

If we didn't find an extension result, try again disregarding
invertible requirements since `demangleGenericSignature` won't
include them. This is just meant to be a quick low risk fix that we
can cherry-pick, the proper fix here is to delete all this logic and
just return the nominal along with the ABI module name to filter
lookup results.

rdar://165639044
This commit is contained in:
Hamish Knight
2025-12-08 00:12:03 +00:00
parent 8da3b62d7b
commit d8a867df86
3 changed files with 75 additions and 0 deletions

View File

@@ -1550,6 +1550,17 @@ ASTBuilder::findDeclContext(NodePointer node) {
}
}
// FIXME: We shouldn't be attempting to find an exact extension match,
// clients only need the nominal for qualified lookup. Additionally, the
// module in which the extension resides is currently used to filter the
// lookup results. This means when we have multiple matches, the particular
// extension we choose matters.
//
// We ought to refactor things such that we return a module ABI name +
// nominal decl which downstream logic can use to lookup and limit results
// to only those that appear in the ABI module. Then we can delete all this
// logic.
SmallVector<ExtensionDecl *, 4> genericExts;
for (auto *ext : nominalDecl->getExtensions()) {
bool found = false;
for (ModuleDecl *module : moduleDecls) {
@@ -1588,6 +1599,39 @@ ASTBuilder::findDeclContext(NodePointer node) {
if (requirements.empty())
return ext;
}
genericExts.push_back(ext);
}
if (!genericSig)
return nullptr;
SmallVector<Requirement, 2> requirements;
SmallVector<InverseRequirement, 2> inverses;
genericSig->getRequirementsWithInverses(requirements, inverses);
// If we didn't find a result yet, try again without invertible requirements
// since `demangleGenericSignature` won't include them, e.g won't include
// Copyable for:
//
// struct S<T: ~Copyable> {}
// protocol P: ~Copyable {}
// extension S where T: P/*, T: Copyable*/ {}
//
// We do this as a separate loop to avoid disturbing existing lookup
// behavior for cases where there's an extension with matching inverses,
// since the choice of extension matters (see above FIXME).
//
// FIXME: This is a complete hack, we ought to delete all this logic and
// just return the nominal + module ABI name.
for (auto *ext : genericExts) {
auto extSig = ext->getGenericSignature().getCanonicalSignature();
if (extSig.getGenericParams() != genericSig.getGenericParams())
continue;
SmallVector<Requirement, 2> extReqs;
SmallVector<InverseRequirement, 2> extInvs;
extSig->getRequirementsWithInverses(extReqs, extInvs);
if (extReqs == requirements)
return ext;
}
return nullptr;

View File

@@ -0,0 +1,15 @@
// RUN: %batch-code-completion
struct S<T: ~Copyable> {}
protocol P: ~Copyable {}
extension S where T: P {
var bar: Int { 0 }
}
struct R: P {}
// Make sure USR round-tripping works here.
func foo(_ x: S<R>) {
x.#^COMPLETE^#
// COMPLETE: Decl[InstanceVar]/CurrNominal: bar[#Int#]; name=bar
}

View File

@@ -58,3 +58,19 @@ extension Generic where T: AnyObject {
// DEMANGLE-DECL: $s10extensions7GenericVAARlzClE18NestedViaAnyObjectV
// CHECK-DECL: extensions.(file).Generic extension.NestedViaAnyObject
// Invertible Constraints
struct GenericNonCopyable<T: ~Copyable> {}
protocol ProtoNonCopyable: ~Copyable {}
extension GenericNonCopyable where T: ProtoNonCopyable/*, T: Copyable*/ {
struct Nested {}
}
struct NonCopyableType: ProtoNonCopyable {}
// DEMANGLE-DECL: $s10extensions18GenericNonCopyableVA2A05ProtocD0RzlE6NestedV
// CHECK-DECL: extensions.(file).GenericNonCopyable extension.Nested
// DEMANGLE-TYPE: $s10extensions18GenericNonCopyableVA2A05ProtocD0RzlE6NestedVyAA0cD4TypeV_GD
// CHECK-TYPE: GenericNonCopyable<NonCopyableType>.Nested