Merge pull request #7893 from DougGregor/sr-3917-3.1
[3.1] [SR-3917] Allow missing witnesses for optional and unavailable requirements
diff --git a/include/swift/AST/TypeMatcher.h b/include/swift/AST/TypeMatcher.h
index 5899d5b..113644e 100644
--- a/include/swift/AST/TypeMatcher.h
+++ b/include/swift/AST/TypeMatcher.h
@@ -213,20 +213,7 @@
TRIVIAL_CASE(DynamicSelfType)
TRIVIAL_CASE(ArchetypeType)
TRIVIAL_CASE(GenericTypeParamType)
-
- bool visitDependentMemberType(CanDependentMemberType firstDepMember,
- Type secondType) {
- if (auto secondDepMember = secondType->getAs<DependentMemberType>()) {
- if (firstDepMember->getAssocType() != secondDepMember->getAssocType() ||
- firstDepMember->getName() != secondDepMember->getName())
- return mismatch(firstDepMember.getPointer(), secondDepMember);
-
- return this->visit(firstDepMember.getBase(),
- secondDepMember->getBase());
- }
-
- return mismatch(firstDepMember.getPointer(), secondType);
- }
+ TRIVIAL_CASE(DependentMemberType)
/// FIXME: Split this out into cases?
bool visitAnyFunctionType(CanAnyFunctionType firstFunc, Type secondType) {
diff --git a/lib/AST/SubstitutionMap.cpp b/lib/AST/SubstitutionMap.cpp
index 7919fe0..80d87d7 100644
--- a/lib/AST/SubstitutionMap.cpp
+++ b/lib/AST/SubstitutionMap.cpp
@@ -17,6 +17,7 @@
#include "swift/AST/ASTContext.h"
#include "swift/AST/SubstitutionMap.h"
#include "swift/AST/Decl.h"
+#include "swift/AST/Module.h"
#include "swift/AST/ProtocolConformance.h"
#include "swift/AST/Types.h"
@@ -70,7 +71,8 @@
}
// Check if we have substitutions from one of our parent types.
- return forEachParent(type, [&](CanType parent, AssociatedTypeDecl *assocType)
+ auto result = forEachParent(type,
+ [&](CanType parent, AssociatedTypeDecl *assocType)
-> Optional<ProtocolConformanceRef> {
auto *parentProto = assocType->getProtocol();
@@ -87,6 +89,18 @@
return lookupConformance(proto, sub.getConformances());
});
+
+ // FIXME: Narrow fix for broken conformance lookup
+ if (!result || result->isAbstract()) {
+ auto substTy = type.subst(*this);
+ if (!substTy)
+ return result;
+
+ auto *M = proto->getParentModule();
+ return M->lookupConformance(substTy, proto, nullptr);
+ }
+
+ return result;
}
void SubstitutionMap::
diff --git a/lib/Sema/TypeCheckProtocol.cpp b/lib/Sema/TypeCheckProtocol.cpp
index e724eec..2652de0 100644
--- a/lib/Sema/TypeCheckProtocol.cpp
+++ b/lib/Sema/TypeCheckProtocol.cpp
@@ -37,6 +37,9 @@
#include "llvm/ADT/SmallString.h"
#include "llvm/Support/SaveAndRestore.h"
+#define DEBUG_TYPE "protocol-conformance-checking"
+#include "llvm/Support/Debug.h"
+
using namespace swift;
namespace {
@@ -3015,6 +3018,9 @@
};
for (auto witness : lookupValueWitnesses(req, /*ignoringNames=*/nullptr)) {
+ DEBUG(llvm::dbgs() << "Inferring associated types from decl:\n";
+ witness->dump(llvm::dbgs()));
+
// If the potential witness came from an extension, and our `Self`
// type can't use it regardless of what associated types we end up
// inferring, skip the witness.
@@ -3035,18 +3041,27 @@
}
auto &result = witnessResult.Inferred[i];
+ DEBUG(llvm::dbgs() << "Considering whether " << result.first->getName()
+ << " can infer to:\n";
+ result.second->dump(llvm::dbgs()));
+
// Filter out errors.
- if (result.second->hasError())
+ if (result.second->hasError()) {
+ DEBUG(llvm::dbgs() << "-- has error type\n");
REJECT;
+ }
// Filter out duplicates.
if (!known.insert({result.first, result.second->getCanonicalType()})
- .second)
+ .second) {
+ DEBUG(llvm::dbgs() << "-- duplicate\n");
REJECT;
+ }
// Filter out circular possibilities, e.g. that
// AssocType == S.AssocType or
// AssocType == Foo<S.AssocType>.
+ bool canInferFromOtherAssociatedType = false;
bool containsTautologicalType =
result.second.findIf([&](Type t) -> bool {
auto dmt = t->getAs<DependentMemberType>();
@@ -3058,11 +3073,63 @@
if (!dmt->getBase()->isEqual(Conformance->getType()))
return false;
+ // If this associated type is same-typed to another associated type
+ // on `Self`, then it may still be an interesting candidate if we find
+ // an answer for that other type.
+ auto witnessContext = witness->getDeclContext();
+ if (witnessContext->getAsProtocolExtensionContext()
+ && witnessContext->getGenericSignatureOfContext()) {
+ auto selfTy = witnessContext->getSelfInterfaceType();
+ auto selfAssocTy = DependentMemberType::get(selfTy,
+ dmt->getAssocType());
+ for (auto &reqt : witnessContext->getGenericSignatureOfContext()
+ ->getRequirements()) {
+ switch (reqt.getKind()) {
+ case RequirementKind::Conformance:
+ case RequirementKind::Superclass:
+ case RequirementKind::Layout:
+ break;
+
+ case RequirementKind::SameType:
+ Type other;
+ if (reqt.getFirstType()->isEqual(selfAssocTy)) {
+ other = reqt.getSecondType();
+ } else if (reqt.getSecondType()->isEqual(selfAssocTy)) {
+ other = reqt.getFirstType();
+ } else {
+ break;
+ }
+
+ if (auto otherAssoc = other->getAs<DependentMemberType>()) {
+ if (otherAssoc->getBase()->isEqual(selfTy)) {
+ auto otherDMT = DependentMemberType::get(dmt->getBase(),
+ otherAssoc->getAssocType());
+
+ // We may be able to infer one associated type from the
+ // other.
+ result.second = result.second.transform([&](Type t) -> Type{
+ if (t->isEqual(dmt))
+ return otherDMT;
+ return t;
+ });
+ canInferFromOtherAssociatedType = true;
+ DEBUG(llvm::dbgs() << "++ we can same-type to:\n";
+ result.second->dump(llvm::dbgs()));
+ return false;
+ }
+ }
+ break;
+ }
+ }
+ }
+
return true;
});
- if (containsTautologicalType)
+ if (containsTautologicalType) {
+ DEBUG(llvm::dbgs() << "-- tautological\n");
REJECT;
+ }
// Check that the type witness doesn't contradict an
// explicitly-given type witness. If it does contradict, throw out the
@@ -3079,19 +3146,27 @@
auto newWitness = result.second->getCanonicalType();
if (!newWitness->hasTypeParameter()
&& !existingWitness->isEqual(newWitness)) {
+ DEBUG(llvm::dbgs() << "** contradicts explicit type witness, "
+ "rejecting inference from this decl\n");
goto next_witness;
}
}
- // Check that the type witness meets the
- // requirements on the associated type.
- if (auto failed = checkTypeWitness(TC, DC, result.first,
- result.second)) {
- witnessResult.NonViable.push_back(
- std::make_tuple(result.first,result.second,failed));
- REJECT;
+ // If we same-typed to another unresolved associated type, we won't
+ // be able to check conformances yet.
+ if (!canInferFromOtherAssociatedType) {
+ // Check that the type witness meets the
+ // requirements on the associated type.
+ if (auto failed = checkTypeWitness(TC, DC, result.first,
+ result.second)) {
+ witnessResult.NonViable.push_back(
+ std::make_tuple(result.first,result.second,failed));
+ DEBUG(llvm::dbgs() << "-- doesn't fulfill requirements\n");
+ REJECT;
+ }
}
+ DEBUG(llvm::dbgs() << "++ seems legit\n");
++i;
}
#undef REJECT
@@ -3605,6 +3680,8 @@
// Infer type witnesses from value witnesses.
auto inferred = inferTypeWitnessesViaValueWitnesses(unresolvedAssocTypes);
+ DEBUG(llvm::dbgs() << "Candidates for inference:\n";
+ dumpInferredAssociatedTypes(inferred));
// Compute the set of solutions.
SmallVector<std::pair<ValueDecl *, ValueDecl *>, 4> valueWitnesses;
@@ -3794,6 +3871,9 @@
Type replaced = known->first.transform(foldDependentMemberTypes);
if (replaced.isNull())
return true;
+
+ if (checkTypeWitness(TC, DC, assocType, replaced))
+ return true;
known->first = replaced;
}
diff --git a/test/Constraints/associated_types.swift b/test/Constraints/associated_types.swift
index 2436e92..9fa2709 100644
--- a/test/Constraints/associated_types.swift
+++ b/test/Constraints/associated_types.swift
@@ -37,3 +37,50 @@
func spoon<S: Spoon>(_ s: S) {
let _: S.Runcee?
}
+
+// SR-4143
+
+protocol SameTypedDefault {
+ associatedtype X
+ associatedtype Y
+ static var x: X { get }
+ static var y: Y { get }
+}
+extension SameTypedDefault where Y == X {
+ static var x: X {
+ return y
+ }
+}
+
+struct UsesSameTypedDefault: SameTypedDefault {
+ static var y: Int {
+ return 0
+ }
+}
+
+protocol XReqt {}
+protocol YReqt {}
+
+protocol SameTypedDefaultWithReqts {
+ associatedtype X: XReqt // expected-note{{}}
+ associatedtype Y: YReqt // expected-note{{}}
+ static var x: X { get }
+ static var y: Y { get }
+}
+extension SameTypedDefaultWithReqts where Y == X {
+ static var x: X {
+ return y
+ }
+}
+
+struct XYType: XReqt, YReqt {}
+struct YType: YReqt {}
+
+struct UsesSameTypedDefaultWithReqts: SameTypedDefaultWithReqts {
+ static var y: XYType { return XYType() }
+}
+
+// expected-error@+1{{does not conform}}
+struct UsesSameTypedDefaultWithoutSatisfyingReqts: SameTypedDefaultWithReqts {
+ static var y: YType { return YType() }
+}
diff --git a/validation-test/compiler_crashers_2_fixed/0076-sr3500.swift b/validation-test/compiler_crashers_2_fixed/0076-sr3500.swift
new file mode 100644
index 0000000..f931112
--- /dev/null
+++ b/validation-test/compiler_crashers_2_fixed/0076-sr3500.swift
@@ -0,0 +1,12 @@
+// RUN: %target-swift-frontend %s -emit-ir
+
+protocol A {
+ associatedtype Coordinate: Strideable
+ func doSomething(_: Range<Coordinate>) -> Coordinate.Stride
+}
+
+extension A where Coordinate == Int {
+ func extensionFunc(_ range: Range<Coordinate>) {
+ _ = doSomething(range)
+ }
+}