Skip to content

Commit ce2abf1

Browse files
authored
Merge pull request #9130 from rudkx/disjunction-selection
2 parents fa583e3 + ecfa406 commit ce2abf1

File tree

4 files changed

+189
-17
lines changed

4 files changed

+189
-17
lines changed

lib/Sema/CSSolver.cpp

Lines changed: 161 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -823,13 +823,24 @@ static PotentialBindings getPotentialBindings(ConstraintSystem &cs,
823823
// Relational constraints: break out to look for types above/below.
824824
break;
825825

826+
case ConstraintKind::DynamicTypeOf: {
827+
auto otherTy = constraint->getSecondType();
828+
if (constraint->getSecondType()->isEqual(typeVar))
829+
otherTy = constraint->getFirstType();
830+
831+
auto simplified = cs.simplifyType(otherTy);
832+
if (simplified->hasTypeVariable())
833+
result.InvolvesTypeVariables = true;
834+
continue;
835+
}
836+
826837
case ConstraintKind::BridgingConversion:
827838
case ConstraintKind::CheckedCast:
828-
case ConstraintKind::DynamicTypeOf:
829839
case ConstraintKind::EscapableFunctionOf:
830840
case ConstraintKind::OpenedExistentialOf:
831841
case ConstraintKind::KeyPath:
832842
case ConstraintKind::KeyPathApplication:
843+
// FIXME: check for other type variables?
833844
// Constraints from which we can't do anything.
834845
continue;
835846

@@ -1027,6 +1038,17 @@ static PotentialBindings getPotentialBindings(ConstraintSystem &cs,
10271038
continue;
10281039
}
10291040

1041+
// If the other side of an argument conversion is also a type
1042+
// variable, we shouldn't allow that to block us from attempting
1043+
// bindings if it is the only constraint involving other type
1044+
// variables. Doing this allows us to attempt a binding much
1045+
// earlier in solving which can result in backing out of
1046+
// solutions that cannot work due to conformance constraints.
1047+
if (constraint->getKind() ==
1048+
ConstraintKind::OperatorArgumentTupleConversion ||
1049+
constraint->getKind() == ConstraintKind::OperatorArgumentConversion)
1050+
continue;
1051+
10301052
result.InvolvesTypeVariables = true;
10311053
continue;
10321054
}
@@ -2389,6 +2411,81 @@ determineBestBindings(ConstraintSystem &CS) {
23892411
return std::make_pair(bestBindings, bestTypeVar);
23902412
}
23912413

2414+
Constraint *getApplicableFunctionConstraint(ConstraintSystem &CS,
2415+
Constraint *disjunction) {
2416+
auto *nested = disjunction->getNestedConstraints().front();
2417+
auto *firstTy = nested->getFirstType()->castTo<TypeVariableType>();
2418+
2419+
// FIXME: Is there something else we should be doing when a member
2420+
// of a disjunction is already bound because it's in an
2421+
// equivalence class?
2422+
if (CS.getFixedType(firstTy))
2423+
return nullptr;
2424+
2425+
Constraint *found = nullptr;
2426+
for (auto *constraint : CS.getConstraintGraph()[firstTy].getConstraints()) {
2427+
if (constraint->getKind() != ConstraintKind::ApplicableFunction)
2428+
continue;
2429+
2430+
// Unapplied function reference, e.g. a.map(String.init)
2431+
if (!constraint->getSecondType()->isEqual(firstTy))
2432+
return nullptr;
2433+
2434+
found = constraint;
2435+
break;
2436+
}
2437+
2438+
if (!found)
2439+
return nullptr;
2440+
2441+
#if !defined(NDEBUG)
2442+
for (auto *constraint : CS.getConstraintGraph()[firstTy].getConstraints()) {
2443+
if (constraint == found)
2444+
continue;
2445+
2446+
assert(constraint->getKind() != ConstraintKind::ApplicableFunction &&
2447+
"Type variable is involved in more than one applicable!");
2448+
}
2449+
#endif
2450+
2451+
return found;
2452+
}
2453+
2454+
static unsigned countUnboundTypes(ConstraintSystem &CS,
2455+
Constraint *disjunction) {
2456+
auto *nested = disjunction->getNestedConstraints().front();
2457+
assert(nested->getKind() == ConstraintKind::BindOverload &&
2458+
"Expected bind overload constraint!");
2459+
2460+
auto firstTy = nested->getFirstType();
2461+
2462+
if (!firstTy->is<FunctionType>()) {
2463+
// If the disjunction is of the form "$T1 bound to..." we should have
2464+
// an associated applicable function constraint.
2465+
auto *applicableFn = getApplicableFunctionConstraint(CS, disjunction);
2466+
2467+
// If we have an unapplied function, we should be able to leave
2468+
// that disjunction for last without any downside.
2469+
if (!applicableFn)
2470+
return 100;
2471+
2472+
firstTy = applicableFn->getFirstType();
2473+
}
2474+
2475+
auto *fnTy = firstTy->getAs<FunctionType>();
2476+
assert(fnTy && "Expected function type!");
2477+
auto simplifiedTy = CS.simplifyType(fnTy->getInput());
2478+
2479+
SmallPtrSet<TypeVariableType *, 4> typeVars;
2480+
findInferableTypeVars(simplifiedTy, typeVars);
2481+
unsigned count = 0;
2482+
for (auto *tv : typeVars)
2483+
if (!CS.getFixedType(tv))
2484+
++count;
2485+
2486+
return count;
2487+
}
2488+
23922489
bool ConstraintSystem::solveSimplified(
23932490
SmallVectorImpl<Solution> &solutions,
23942491
FreeTypeVariableBinding allowFreeTypeVariables) {
@@ -2445,29 +2542,78 @@ bool ConstraintSystem::solveSimplified(
24452542
return false;
24462543
}
24472544

2448-
// Pick the smallest disjunction.
2449-
// FIXME: This heuristic isn't great, but it helped somewhat for
2450-
// overload sets.
2451-
auto disjunction = disjunctions[0];
2452-
auto bestSize = disjunction->countActiveNestedConstraints();
2453-
if (bestSize > 2) {
2454-
for (auto contender : llvm::makeArrayRef(disjunctions).slice(1)) {
2455-
unsigned newSize = contender->countActiveNestedConstraints();
2456-
if (newSize < bestSize) {
2457-
bestSize = newSize;
2458-
disjunction = contender;
2459-
2460-
if (bestSize == 2)
2545+
// Select a disjunction based on a few factors, prioritizing
2546+
// coercions and initializer disjunctions, followed by bind overload
2547+
// disjunctions with with fewest unbound argument types.
2548+
Optional<Constraint *> selection;
2549+
Optional<unsigned> lowestUnbound;
2550+
Optional<unsigned> lowestActive;
2551+
2552+
for (auto candidate : disjunctions) {
2553+
unsigned countActive = candidate->countActiveNestedConstraints();
2554+
// Skip disjunctions with no active constraints.
2555+
if (!countActive)
2556+
continue;
2557+
2558+
// If this is the first disjunction with an active constraint,
2559+
// consider this our selection until we find a better one.
2560+
if (!selection)
2561+
selection = candidate;
2562+
2563+
if (auto loc = candidate->getLocator()) {
2564+
// If we have a coercion disjunction remaining, select that as
2565+
// they tend to constraint type variables maximally and give us
2566+
// information about potential bindings.
2567+
if (auto anchor = loc->getAnchor()) {
2568+
if (isa<CoerceExpr>(anchor)) {
2569+
selection = candidate;
24612570
break;
2571+
}
2572+
}
2573+
2574+
// Initializers also provide a lot of information in the form of
2575+
// the result type and tend to split the system into independent
2576+
// pieces.
2577+
auto path = loc->getPath();
2578+
if (!path.empty() &&
2579+
path.back().getKind() ==
2580+
ConstraintLocator::PathElementKind::ConstructorMember) {
2581+
selection = candidate;
2582+
break;
24622583
}
24632584
}
2585+
2586+
// Attempt to find a bind overload disjunction before considering
2587+
// this one.
2588+
assert(!candidate->getNestedConstraints().empty() &&
2589+
"Expected non-empty disjunction!");
2590+
if (candidate->getNestedConstraints().front()->getKind() !=
2591+
ConstraintKind::BindOverload)
2592+
continue;
2593+
2594+
// Otherwise, attempt to count the number of unbound types used
2595+
// for bind overload disjunctions.
2596+
unsigned countUnbound = countUnboundTypes(*this, candidate);
2597+
2598+
// If the number of unbound arguments is smaller than previous
2599+
// candidate, or if we had no previous candidate, this is our
2600+
// newest best option.
2601+
if (!lowestUnbound || countUnbound < lowestUnbound.getValue() ||
2602+
(countUnbound == lowestUnbound.getValue() &&
2603+
countActive < lowestActive.getValue())) {
2604+
selection = candidate;
2605+
lowestUnbound = countUnbound;
2606+
lowestActive = countActive;
2607+
}
24642608
}
24652609

24662610
// If there are no active constraints in the disjunction, there is
24672611
// no solution.
2468-
if (bestSize == 0)
2612+
if (!selection)
24692613
return true;
24702614

2615+
auto *disjunction = selection.getValue();
2616+
24712617
// Remove this disjunction constraint from the list.
24722618
auto afterDisjunction = InactiveConstraints.erase(disjunction);
24732619
CG.removeConstraint(disjunction);
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// RUN: %scale-test --begin 1 --end 5 --step 1 --select incrementScopeCounter %s --polynomial-threshold=2.3
2+
// REQUIRES: OS=macosx
3+
// REQUIRES: asserts
4+
5+
infix operator +|+ : AdditionPrecedence
6+
7+
func +|+ <T: FloatingPoint>(lhs: T, rhs: T) -> T {
8+
return lhs
9+
}
10+
11+
func +|+ <T: SignedInteger>(lhs: T, rhs: T) -> T {
12+
return lhs
13+
}
14+
15+
func test(i: Int, j: Int, k: Int, l: Int) -> Int {
16+
return i +|+ j +|+ k +|+ l
17+
%for i in range(1, N):
18+
+|+ i +|+ j +|+ k +|+ l
19+
%end
20+
}

validation-test/stdlib/Data.swift

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,10 @@ DataTestSuite.test("Data.Iterator semantics") {
2828
ptr[i] = UInt8(i % 23)
2929
}
3030
}
31-
checkSequence((0..<65535).lazy.map({ UInt8($0 % 23) }), data)
31+
// SR-4724: Should not need to be split out but if it is not it
32+
// is considered ambiguous.
33+
let temp = (0..<65535).lazy.map({ UInt8($0 % 23) })
34+
checkSequence(temp, data)
3235
}
3336

3437
DataTestSuite.test("associated types") {

validation-test/stdlib/Set.swift

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -203,7 +203,10 @@ func getBridgedVerbatimSet(_ members: [Int] = [1010, 2020, 3030])
203203
/// Get a Set<NSObject> (Set<TestObjCKeyTy>) backed by native storage
204204
func getNativeBridgedVerbatimSet(_ members: [Int] = [1010, 2020, 3030]) ->
205205
Set<NSObject> {
206-
let result: Set<NSObject> = Set(members.map({ TestObjCKeyTy($0) }))
206+
// SR-4724: Should not need to be split out but if it is not it
207+
// is considered ambiguous.
208+
let temp = members.map({ TestObjCKeyTy($0) })
209+
let result: Set<NSObject> = Set(temp)
207210
expectTrue(isNativeSet(result))
208211
return result
209212
}

0 commit comments

Comments
 (0)