diff --git a/lib/AST/ASTDemangler.cpp b/lib/AST/ASTDemangler.cpp index 340b41bc5d0..58064b56a15 100644 --- a/lib/AST/ASTDemangler.cpp +++ b/lib/AST/ASTDemangler.cpp @@ -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 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 requirements; + SmallVector 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 {} + // 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 extReqs; + SmallVector extInvs; + extSig->getRequirementsWithInverses(extReqs, extInvs); + if (extReqs == requirements) + return ext; } return nullptr; diff --git a/test/IDE/rdar165639044.swift b/test/IDE/rdar165639044.swift new file mode 100644 index 00000000000..62603fab15c --- /dev/null +++ b/test/IDE/rdar165639044.swift @@ -0,0 +1,15 @@ +// RUN: %batch-code-completion + +struct S {} +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) { + x.#^COMPLETE^# + // COMPLETE: Decl[InstanceVar]/CurrNominal: bar[#Int#]; name=bar +} diff --git a/test/TypeDecoder/extensions.swift b/test/TypeDecoder/extensions.swift index a0fbc4c44ea..114e4af8982 100644 --- a/test/TypeDecoder/extensions.swift +++ b/test/TypeDecoder/extensions.swift @@ -58,3 +58,19 @@ extension Generic where T: AnyObject { // DEMANGLE-DECL: $s10extensions7GenericVAARlzClE18NestedViaAnyObjectV // CHECK-DECL: extensions.(file).Generic extension.NestedViaAnyObject +// Invertible Constraints + +struct GenericNonCopyable {} +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.Nested