Skip to content

Commit c614c01

Browse files
committed
Sema: Refine the 'tautological witness' condition
1 parent ba174ae commit c614c01

File tree

2 files changed

+174
-75
lines changed

2 files changed

+174
-75
lines changed

lib/Sema/AssociatedTypeInference.cpp

Lines changed: 124 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -1321,6 +1321,108 @@ static bool isExtensionUsableForInference(const ExtensionDecl *extension,
13211321
return true;
13221322
}
13231323

1324+
namespace {
1325+
1326+
enum class InferenceCandidateKind {
1327+
/// Nothing weird going on.
1328+
Good,
1329+
1330+
/// T := T. Always satisfied.
1331+
Tautological,
1332+
1333+
/// T := G<T>. Cannot be satisfied.
1334+
Infinite
1335+
};
1336+
1337+
}
1338+
1339+
static InferenceCandidateKind checkInferenceCandidate(
1340+
std::pair<AssociatedTypeDecl *, Type> *result,
1341+
bool *canInferFromOtherAssociatedType,
1342+
NormalProtocolConformance *conformance,
1343+
ValueDecl *witness) {
1344+
auto isTautological = [&](Type t) -> bool {
1345+
auto dmt = t->getAs<DependentMemberType>();
1346+
if (!dmt)
1347+
return false;
1348+
if (!associatedTypesAreSameEquivalenceClass(dmt->getAssocType(),
1349+
result->first))
1350+
return false;
1351+
1352+
auto typeInContext =
1353+
conformance->getDeclContext()->mapTypeIntoContext(conformance->getType());
1354+
1355+
if (!dmt->getBase()->isEqual(typeInContext))
1356+
return false;
1357+
1358+
return true;
1359+
};
1360+
1361+
if (isTautological(result->second)) {
1362+
auto *dmt = result->second->castTo<DependentMemberType>();
1363+
1364+
// If this associated type is same-typed to another associated type
1365+
// on `Self`, then it may still be an interesting candidate if we find
1366+
// an answer for that other type.
1367+
auto witnessContext = witness->getDeclContext();
1368+
if (witnessContext->getExtendedProtocolDecl()
1369+
&& witnessContext->getGenericSignatureOfContext()) {
1370+
auto selfTy = witnessContext->getSelfInterfaceType();
1371+
auto selfAssocTy = DependentMemberType::get(selfTy,
1372+
dmt->getAssocType());
1373+
for (auto &reqt : witnessContext->getGenericSignatureOfContext()
1374+
.getRequirements()) {
1375+
switch (reqt.getKind()) {
1376+
case RequirementKind::SameShape:
1377+
llvm_unreachable("Same-shape requirement not supported here");
1378+
1379+
case RequirementKind::Conformance:
1380+
case RequirementKind::Superclass:
1381+
case RequirementKind::Layout:
1382+
break;
1383+
1384+
case RequirementKind::SameType:
1385+
Type other;
1386+
if (reqt.getFirstType()->isEqual(selfAssocTy)) {
1387+
other = reqt.getSecondType();
1388+
} else if (reqt.getSecondType()->isEqual(selfAssocTy)) {
1389+
other = reqt.getFirstType();
1390+
} else {
1391+
break;
1392+
}
1393+
1394+
if (auto otherAssoc = other->getAs<DependentMemberType>()) {
1395+
if (otherAssoc->getBase()->isEqual(selfTy)) {
1396+
auto otherDMT = DependentMemberType::get(dmt->getBase(),
1397+
otherAssoc->getAssocType());
1398+
1399+
// We may be able to infer one associated type from the
1400+
// other.
1401+
result->second = result->second.transform([&](Type t) -> Type{
1402+
if (t->isEqual(dmt))
1403+
return otherDMT;
1404+
return t;
1405+
});
1406+
*canInferFromOtherAssociatedType = true;
1407+
LLVM_DEBUG(llvm::dbgs() << "++ we can same-type to:\n";
1408+
result->second->dump(llvm::dbgs()));
1409+
return InferenceCandidateKind::Good;
1410+
}
1411+
}
1412+
break;
1413+
}
1414+
}
1415+
}
1416+
1417+
return InferenceCandidateKind::Tautological;
1418+
}
1419+
1420+
if (result->second.findIf(isTautological))
1421+
return InferenceCandidateKind::Infinite;
1422+
1423+
return InferenceCandidateKind::Good;
1424+
}
1425+
13241426
InferredAssociatedTypesByWitnesses
13251427
AssociatedTypeInference::inferTypeWitnessesViaValueWitnesses(
13261428
const llvm::SetVector<AssociatedTypeDecl *> &allUnresolved,
@@ -1397,82 +1499,25 @@ AssociatedTypeInference::inferTypeWitnessesViaValueWitnesses(
13971499
// AssocType == S.AssocType or
13981500
// AssocType == Foo<S.AssocType>.
13991501
bool canInferFromOtherAssociatedType = false;
1400-
bool containsTautologicalType =
1401-
result.second.findIf([&](Type t) -> bool {
1402-
auto dmt = t->getAs<DependentMemberType>();
1403-
if (!dmt)
1404-
return false;
1405-
if (!associatedTypesAreSameEquivalenceClass(dmt->getAssocType(),
1406-
result.first))
1407-
return false;
1408-
1409-
auto typeInContext =
1410-
conformance->getDeclContext()->mapTypeIntoContext(conformance->getType());
1411-
1412-
if (!dmt->getBase()->isEqual(typeInContext))
1413-
return false;
1414-
1415-
// If this associated type is same-typed to another associated type
1416-
// on `Self`, then it may still be an interesting candidate if we find
1417-
// an answer for that other type.
1418-
auto witnessContext = witness->getDeclContext();
1419-
if (witnessContext->getExtendedProtocolDecl()
1420-
&& witnessContext->getGenericSignatureOfContext()) {
1421-
auto selfTy = witnessContext->getSelfInterfaceType();
1422-
auto selfAssocTy = DependentMemberType::get(selfTy,
1423-
dmt->getAssocType());
1424-
for (auto &reqt : witnessContext->getGenericSignatureOfContext()
1425-
.getRequirements()) {
1426-
switch (reqt.getKind()) {
1427-
case RequirementKind::SameShape:
1428-
llvm_unreachable("Same-shape requirement not supported here");
1429-
1430-
case RequirementKind::Conformance:
1431-
case RequirementKind::Superclass:
1432-
case RequirementKind::Layout:
1433-
break;
1434-
1435-
case RequirementKind::SameType:
1436-
Type other;
1437-
if (reqt.getFirstType()->isEqual(selfAssocTy)) {
1438-
other = reqt.getSecondType();
1439-
} else if (reqt.getSecondType()->isEqual(selfAssocTy)) {
1440-
other = reqt.getFirstType();
1441-
} else {
1442-
break;
1443-
}
1444-
1445-
if (auto otherAssoc = other->getAs<DependentMemberType>()) {
1446-
if (otherAssoc->getBase()->isEqual(selfTy)) {
1447-
auto otherDMT = DependentMemberType::get(dmt->getBase(),
1448-
otherAssoc->getAssocType());
1449-
1450-
// We may be able to infer one associated type from the
1451-
// other.
1452-
result.second = result.second.transform([&](Type t) -> Type{
1453-
if (t->isEqual(dmt))
1454-
return otherDMT;
1455-
return t;
1456-
});
1457-
canInferFromOtherAssociatedType = true;
1458-
LLVM_DEBUG(llvm::dbgs() << "++ we can same-type to:\n";
1459-
result.second->dump(llvm::dbgs()));
1460-
return false;
1461-
}
1462-
}
1463-
break;
1464-
}
1465-
}
1466-
}
14671502

1468-
return true;
1469-
});
1503+
switch (checkInferenceCandidate(&result,
1504+
&canInferFromOtherAssociatedType,
1505+
conformance, witness)) {
1506+
case InferenceCandidateKind::Good:
1507+
// Continued below.
1508+
break;
14701509

1471-
if (containsTautologicalType) {
1510+
case InferenceCandidateKind::Tautological: {
14721511
LLVM_DEBUG(llvm::dbgs() << "-- tautological\n");
14731512
REJECT;
14741513
}
14751514

1515+
case InferenceCandidateKind::Infinite: {
1516+
LLVM_DEBUG(llvm::dbgs() << "-- infinite\n");
1517+
goto next_witness;
1518+
}
1519+
}
1520+
14761521
// Check that the type witness doesn't contradict an
14771522
// explicitly-given type witness. If it does contradict, throw out the
14781523
// witness completely.
@@ -1868,13 +1913,17 @@ AssociatedTypeInference::inferTypeWitnessesViaValueWitness(ValueDecl *req,
18681913
if (!inferredType->isMaterializable())
18691914
return true;
18701915

1871-
// If the type contains a type parameter, there is nothing we can infer
1872-
// from it.
1873-
// FIXME: This is a weird state introduced by associated type inference
1874-
// that should not exist.
1875-
if (inferredType->hasTypeParameter())
1916+
// Type parameters of the conforming type become archetypes here, so
1917+
// any remaining type parameters correspond to the innermost generic
1918+
// parameter list of the witness. A generic parameter gives us a
1919+
// tautological match.
1920+
if (inferredType->is<GenericTypeParamType>())
18761921
return true;
18771922

1923+
// A type containing a type parameter cannot match.
1924+
if (inferredType->hasTypeParameter())
1925+
return false;
1926+
18781927
auto proto = Conformance->getProtocol();
18791928
if (auto assocType = getReferencedAssocTypeOfProtocol(firstDepMember,
18801929
proto)) {
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
// RUN: %target-typecheck-verify-swift -disable-experimental-associated-type-inference
2+
// RUN: %target-typecheck-verify-swift -enable-experimental-associated-type-inference
3+
4+
protocol P1 {
5+
associatedtype A
6+
associatedtype B
7+
8+
func f(_: A, _: B)
9+
func g(_: A, _: B)
10+
}
11+
12+
struct G<T> {}
13+
14+
extension P1 {
15+
// These two are not candidate witnesses at all!
16+
func f(_: G<A>, _: Int) {}
17+
func g(_: Float, _: G<B>) {}
18+
19+
// We can infer A and B from these two:
20+
func f(_: A, _: String) {}
21+
func g(_: String, _: B) {}
22+
}
23+
24+
struct S1: P1 {}
25+
26+
let x1: String.Type = S1.A.self
27+
let y1: String.Type = S1.B.self
28+
29+
protocol P2 {
30+
associatedtype A
31+
associatedtype B
32+
33+
func f(_: A, _: B)
34+
func g(_: A, _: B)
35+
}
36+
37+
extension P2 {
38+
// These two are not candidate witnesses at all!
39+
func f<T>(_: G<T>, _: Int) {}
40+
func g<T>(_: Float, _: G<T>) {}
41+
42+
// We can infer A and B from these two:
43+
func f<T>(_: T, _: String) {}
44+
func g<T>(_: String, _: T) {}
45+
}
46+
47+
struct S2: P2 {}
48+
49+
let x2: String.Type = S2.A.self
50+
let y2: String.Type = S2.B.self

0 commit comments

Comments
 (0)