Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 44 additions & 0 deletions lib/AST/ASTDemangler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down Expand Up @@ -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;
Expand Down
15 changes: 15 additions & 0 deletions test/IDE/rdar165639044.swift
Original file line number Diff line number Diff line change
@@ -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
}
16 changes: 16 additions & 0 deletions test/TypeDecoder/extensions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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