Skip to content

Commit 98b8473

Browse files
authored
Merge pull request #19428 from rudkx/ordering
[ConstraintSystem] Change the order in which we visit disjunctions.
2 parents 8c94b38 + 9cc817d commit 98b8473

File tree

7 files changed

+230
-12
lines changed

7 files changed

+230
-12
lines changed

lib/Sema/CSSolver.cpp

Lines changed: 213 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1361,6 +1361,215 @@ static Constraint *selectBestBindingDisjunction(
13611361
return firstBindDisjunction;
13621362
}
13631363

1364+
// For a given type, determine if it's either a concrete type, the
1365+
// result of converting from a concrete type, or a type variable known
1366+
// to conform to other types (or the result of converting from such a
1367+
// type variable).
1368+
static bool havePotentialTypesOrLiteralConformances(Type ty,
1369+
ConstraintSystem &cs) {
1370+
llvm::SmallSet<TypeVariableType *, 4> visited;
1371+
llvm::SmallVector<Type, 4> worklist;
1372+
worklist.push_back(ty);
1373+
1374+
while (!worklist.empty()) {
1375+
auto itemTy = worklist.pop_back_val()->getRValueType();
1376+
1377+
if (!itemTy->is<TypeVariableType>())
1378+
return true;
1379+
1380+
auto tyvar = itemTy->castTo<TypeVariableType>();
1381+
if (cs.getFixedType(tyvar))
1382+
return true;
1383+
1384+
auto *rep = cs.getRepresentative(tyvar);
1385+
1386+
// FIXME: This can happen when we have two type variables that are
1387+
// subtypes of each other. We would ideally merge those type
1388+
// variables somewhere.
1389+
if (visited.count(rep))
1390+
continue;
1391+
1392+
visited.insert(rep);
1393+
1394+
// Gather all the constraints involving this type variable, and
1395+
// then attempt to trace back through each constraint to see if we
1396+
// can reach a concrete type or a literal.
1397+
1398+
llvm::SetVector<Constraint *> constraints;
1399+
cs.getConstraintGraph().gatherConstraints(
1400+
rep, constraints, ConstraintGraph::GatheringKind::EquivalenceClass);
1401+
1402+
for (auto *constraint : constraints) {
1403+
switch (constraint->getKind()) {
1404+
case ConstraintKind::LiteralConformsTo:
1405+
return true;
1406+
1407+
case ConstraintKind::Defaultable:
1408+
assert(!constraint->getSecondType()->is<TypeVariableType>());
1409+
return true;
1410+
1411+
case ConstraintKind::Bind:
1412+
case ConstraintKind::Equal: {
1413+
auto firstTy = constraint->getFirstType();
1414+
auto secondTy = constraint->getSecondType();
1415+
if (firstTy->is<TypeVariableType>()) {
1416+
auto otherRep =
1417+
cs.getRepresentative(firstTy->castTo<TypeVariableType>());
1418+
if (otherRep->isEqual(rep))
1419+
worklist.push_back(secondTy);
1420+
}
1421+
if (secondTy->is<TypeVariableType>()) {
1422+
auto otherRep =
1423+
cs.getRepresentative(secondTy->castTo<TypeVariableType>());
1424+
if (otherRep->isEqual(rep))
1425+
worklist.push_back(constraint->getFirstType());
1426+
}
1427+
break;
1428+
}
1429+
1430+
case ConstraintKind::Subtype:
1431+
case ConstraintKind::OperatorArgumentConversion:
1432+
case ConstraintKind::ArgumentConversion:
1433+
case ConstraintKind::Conversion:
1434+
case ConstraintKind::BridgingConversion:
1435+
case ConstraintKind::BindParam: {
1436+
auto secondTy = constraint->getSecondType();
1437+
if (secondTy->is<TypeVariableType>()) {
1438+
auto otherRep =
1439+
cs.getRepresentative(secondTy->castTo<TypeVariableType>());
1440+
if (otherRep->isEqual(rep))
1441+
worklist.push_back(constraint->getFirstType());
1442+
}
1443+
break;
1444+
}
1445+
1446+
case ConstraintKind::DynamicTypeOf:
1447+
case ConstraintKind::EscapableFunctionOf: {
1448+
auto firstTy = constraint->getFirstType();
1449+
if (firstTy->is<TypeVariableType>()) {
1450+
auto otherRep =
1451+
cs.getRepresentative(firstTy->castTo<TypeVariableType>());
1452+
if (otherRep->isEqual(rep))
1453+
worklist.push_back(constraint->getSecondType());
1454+
}
1455+
break;
1456+
}
1457+
1458+
case ConstraintKind::OptionalObject: {
1459+
// Get the underlying object type.
1460+
auto secondTy = constraint->getSecondType();
1461+
if (secondTy->is<TypeVariableType>()) {
1462+
auto otherRep =
1463+
cs.getRepresentative(secondTy->castTo<TypeVariableType>());
1464+
if (otherRep->isEqual(rep)) {
1465+
// See if we can actually determine what the underlying
1466+
// type is.
1467+
Type fixedTy;
1468+
auto firstTy = constraint->getFirstType();
1469+
if (!firstTy->is<TypeVariableType>()) {
1470+
fixedTy = firstTy;
1471+
} else {
1472+
fixedTy =
1473+
cs.getFixedType(firstTy->castTo<TypeVariableType>());
1474+
}
1475+
if (fixedTy && fixedTy->getOptionalObjectType())
1476+
worklist.push_back(fixedTy->getOptionalObjectType());
1477+
}
1478+
}
1479+
break;
1480+
}
1481+
1482+
case ConstraintKind::KeyPathApplication:
1483+
case ConstraintKind::KeyPath: {
1484+
auto firstTy = constraint->getFirstType();
1485+
if (firstTy->is<TypeVariableType>()) {
1486+
auto otherRep =
1487+
cs.getRepresentative(firstTy->castTo<TypeVariableType>());
1488+
if (otherRep->isEqual(rep))
1489+
worklist.push_back(constraint->getThirdType());
1490+
}
1491+
break;
1492+
}
1493+
1494+
case ConstraintKind::BindToPointerType:
1495+
case ConstraintKind::ValueMember:
1496+
case ConstraintKind::UnresolvedValueMember:
1497+
case ConstraintKind::Disjunction:
1498+
case ConstraintKind::CheckedCast:
1499+
case ConstraintKind::OpenedExistentialOf:
1500+
case ConstraintKind::ApplicableFunction:
1501+
case ConstraintKind::BindOverload:
1502+
case ConstraintKind::FunctionInput:
1503+
case ConstraintKind::FunctionResult:
1504+
case ConstraintKind::SelfObjectOfProtocol:
1505+
case ConstraintKind::ConformsTo:
1506+
break;
1507+
}
1508+
}
1509+
}
1510+
1511+
return false;
1512+
}
1513+
1514+
// Check to see if we know something about the types of all arguments
1515+
// in the given function type.
1516+
static bool haveTypeInformationForAllArguments(AnyFunctionType *fnType,
1517+
ConstraintSystem &cs) {
1518+
return llvm::all_of(fnType->getParams(), [&](AnyFunctionType::Param param) {
1519+
return havePotentialTypesOrLiteralConformances(param.getPlainType(), cs);
1520+
});
1521+
}
1522+
1523+
// Given a type variable representing the RHS of an ApplicableFunction
1524+
// constraint, attempt to find the disjunction of bind overloads
1525+
// associated with it. This may return null in cases where have not
1526+
// yet created a disjunction because we need to resolve a base type,
1527+
// e.g.: [1].map{ ... } does not have a disjunction until we decide on
1528+
// a type for [1].
1529+
static Constraint *getUnboundBindOverloadDisjunction(TypeVariableType *tyvar,
1530+
ConstraintSystem &cs) {
1531+
auto *rep = cs.getRepresentative(tyvar);
1532+
assert(!cs.getFixedType(rep));
1533+
1534+
llvm::SetVector<Constraint *> disjunctions;
1535+
cs.getConstraintGraph().gatherConstraints(
1536+
rep, disjunctions, ConstraintGraph::GatheringKind::EquivalenceClass,
1537+
[](Constraint *match) {
1538+
return match->getKind() == ConstraintKind::Disjunction &&
1539+
match->getNestedConstraints().front()->getKind() ==
1540+
ConstraintKind::BindOverload;
1541+
});
1542+
1543+
if (disjunctions.empty())
1544+
return nullptr;
1545+
1546+
return disjunctions[0];
1547+
}
1548+
1549+
// Find a disjunction associated with an ApplicableFunction constraint
1550+
// where we have some information about all of the types of in the
1551+
// function application (even if we only know something about what the
1552+
// types conform to and not actually a concrete type).
1553+
Constraint *ConstraintSystem::selectApplyDisjunction() {
1554+
for (auto &constraint : InactiveConstraints) {
1555+
if (constraint.getKind() != ConstraintKind::ApplicableFunction)
1556+
continue;
1557+
1558+
auto *applicable = &constraint;
1559+
if (haveTypeInformationForAllArguments(
1560+
applicable->getFirstType()->castTo<AnyFunctionType>(), *this)) {
1561+
auto *tyvar = applicable->getSecondType()->castTo<TypeVariableType>();
1562+
1563+
// If we have created the disjunction for this apply, find it.
1564+
auto *disjunction = getUnboundBindOverloadDisjunction(tyvar, *this);
1565+
if (disjunction)
1566+
return disjunction;
1567+
}
1568+
}
1569+
1570+
return nullptr;
1571+
}
1572+
13641573
void ConstraintSystem::partitionDisjunction(
13651574
ArrayRef<Constraint *> Choices, SmallVectorImpl<unsigned> &Ordering,
13661575
SmallVectorImpl<unsigned> &PartitionBeginning) {
@@ -1386,6 +1595,10 @@ Constraint *ConstraintSystem::selectDisjunction() {
13861595
if (auto *disjunction = selectBestBindingDisjunction(*this, disjunctions))
13871596
return disjunction;
13881597

1598+
if (getASTContext().isSwiftVersionAtLeast(5))
1599+
if (auto *disjunction = selectApplyDisjunction())
1600+
return disjunction;
1601+
13891602
// Pick the disjunction with the smallest number of active choices.
13901603
auto minDisjunction =
13911604
std::min_element(disjunctions.begin(), disjunctions.end(),

lib/Sema/ConstraintSystem.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2986,6 +2986,8 @@ class ConstraintSystem {
29862986
/// \returns The selected disjunction.
29872987
Constraint *selectDisjunction();
29882988

2989+
Constraint *selectApplyDisjunction();
2990+
29892991
bool simplifyForConstraintPropagation();
29902992
void collectNeighboringBindOverloadDisjunctions(
29912993
llvm::SetVector<Constraint *> &neighbors);

test/Misc/expression_too_complex_4.swift

Lines changed: 0 additions & 6 deletions
This file was deleted.

validation-test/Sema/type_checker_perf/fast/rdar22022980.swift

Lines changed: 0 additions & 4 deletions
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
// RUN: %target-typecheck-verify-swift -solver-expression-time-threshold=1 -swift-version 4
1+
// RUN: %target-typecheck-verify-swift -solver-expression-time-threshold=1 -swift-version 5
22
// REQUIRES: tools-release,no_asserts
33

44
let _ = 1 | UInt32(0) << 0 | UInt32(1) << 1 | UInt32(2) << 2 | UInt32(3) << 3 | UInt32(4) << 4
5-
// expected-error@-1 {{reasonable time}}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
// RUN: %target-typecheck-verify-swift -swift-version 4 -solver-expression-time-threshold=1 -swift-version 5
2+
// REQUIRES: tools-release,no_asserts
3+
4+
func test(_ i: Int, _ j: Int) -> Int {
5+
return 1 + (((i >> 1) + (i >> 2) + (i >> 3) + (i >> 4) << 1) << 1) & 0x40 +
6+
1 + (((i >> 1) + (i >> 2) + (i >> 3) + (i >> 4) << 1) << 1) & 0x40 +
7+
1 + (((i >> 1) + (i >> 2) + (i >> 3) + (i >> 4) << 1) << 1) & 0x40
8+
// expected-error@-1 {{reasonable time}}
9+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
// RUN: %target-typecheck-verify-swift -solver-expression-time-threshold=1 -swift-version 5
2+
// REQUIRES: tools-release,no_asserts
3+
4+
_ = [1, 3, 5, 7, 11].filter{ $0 == 1 || $0 == 3 || $0 == 11 || $0 == 1 || $0 == 3 || $0 == 11 } == [ 1, 3, 11 ]
5+
// expected-error@-1 {{reasonable time}}

0 commit comments

Comments
 (0)