Merge pull request #11242 from slavapestov/ambiguous-requirement-witness-subclass-existential-4.0
Sema: Fix requirement/witness disambiguation for subclass existentials [4.0]
diff --git a/lib/Sema/TypeCheckNameLookup.cpp b/lib/Sema/TypeCheckNameLookup.cpp
index 8f73302..b37f2f9 100644
--- a/lib/Sema/TypeCheckNameLookup.cpp
+++ b/lib/Sema/TypeCheckNameLookup.cpp
@@ -16,6 +16,7 @@
//
//===----------------------------------------------------------------------===//
#include "TypeChecker.h"
+#include "swift/AST/ExistentialLayout.h"
#include "swift/AST/Initializer.h"
#include "swift/AST/NameLookup.h"
#include "swift/AST/ProtocolConformance.h"
@@ -120,27 +121,48 @@
if (!Options.contains(NameLookupFlags::ProtocolMembers) ||
!isa<ProtocolDecl>(foundDC) ||
isa<GenericTypeParamDecl>(found) ||
- (isa<FuncDecl>(found) && cast<FuncDecl>(found)->isOperator()) ||
- foundInType->isAnyExistentialType()) {
+ (isa<FuncDecl>(found) && cast<FuncDecl>(found)->isOperator())) {
addResult(found);
return;
}
assert(isa<ProtocolDecl>(foundDC));
+ auto conformingType = foundInType;
+
+ // When performing a lookup on a subclass existential, we might
+ // find a member of the class that witnesses a requirement on a
+ // protocol that the class conforms to.
+ //
+ // Since subclass existentials don't normally conform to protocols,
+ // pull out the superclass instead, and use that below.
+ if (foundInType->isExistentialType()) {
+ auto layout = foundInType->getExistentialLayout();
+ if (layout.superclass)
+ conformingType = layout.superclass;
+ }
+
// If we found something within the protocol itself, and our
// search began somewhere that is not in a protocol or extension
// thereof, remap this declaration to the witness.
if (foundInType->is<ArchetypeType>() ||
+ foundInType->isExistentialType() ||
Options.contains(NameLookupFlags::PerformConformanceCheck)) {
// Dig out the protocol conformance.
- auto conformance = TC.conformsToProtocol(foundInType, foundProto, DC,
+ auto conformance = TC.conformsToProtocol(conformingType, foundProto, DC,
conformanceOptions);
- if (!conformance)
+ if (!conformance) {
+ // If there's no conformance, we have an existential
+ // and we found a member from one of the protocols, and
+ // not a class constraint if any.
+ assert(foundInType->isExistentialType());
+ addResult(found);
return;
+ }
if (conformance->isAbstract()) {
- assert(foundInType->is<ArchetypeType>());
+ assert(foundInType->is<ArchetypeType>() ||
+ foundInType->isExistentialType());
addResult(found);
return;
}
@@ -161,6 +183,10 @@
// FIXME: the "isa<ProtocolDecl>()" check will be wrong for
// default implementations in protocols.
+ //
+ // If we have an imported conformance or the witness could
+ // not be deserialized, getWitnessDecl() will just return
+ // the requirement, so just drop the lookup result here.
if (witness && !isa<ProtocolDecl>(witness->getDeclContext()))
addResult(witness);
diff --git a/test/ClangImporter/subclass_existentials.swift b/test/ClangImporter/subclass_existentials.swift
index 3050b5b..690d30c 100644
--- a/test/ClangImporter/subclass_existentials.swift
+++ b/test/ClangImporter/subclass_existentials.swift
@@ -35,3 +35,7 @@
return g!
}
}
+
+// Make sure the method lookup is not ambiguous
+
+_ = Coat.fashionStatement.wear()
diff --git a/test/Inputs/clang-importer-sdk/usr/include/Foundation.h b/test/Inputs/clang-importer-sdk/usr/include/Foundation.h
index ffd5deb..1ad3a5c 100644
--- a/test/Inputs/clang-importer-sdk/usr/include/Foundation.h
+++ b/test/Inputs/clang-importer-sdk/usr/include/Foundation.h
@@ -1086,13 +1086,21 @@
FictionalServerErrorMeltedDown = 1
} FictionalServerErrorCode;
+@protocol Wearable
+- (void)wear;
+@end
+
@protocol Garment
@end
@protocol Cotton
@end
-@interface Coat
+@interface Coat : NSObject<Wearable>
+
+- (void)wear;
+@property (class) Coat <Wearable> *fashionStatement;
+
@end
@protocol NSLaundry