Merge pull request #14322 from xedin/rdar-36838495-4.1
[4.1][CSSolver] Fix performance regression related to contraction of closure parameters
diff --git a/lib/Sema/CSSolver.cpp b/lib/Sema/CSSolver.cpp
index 66ac650..eb103ef 100644
--- a/lib/Sema/CSSolver.cpp
+++ b/lib/Sema/CSSolver.cpp
@@ -1787,9 +1787,11 @@
auto &score = bestNonGenericScore->Data;
// Let's skip generic overload choices only in case if
// non-generic score indicates that there were no forced
- // unwrappings of optional(s) and no unavailable overload
- // choices present in the solution.
- if (score[SK_ForceUnchecked] == 0 && score[SK_Unavailable] == 0)
+ // unwrappings of optional(s), no unavailable overload
+ // choices present in the solution, and there are no
+ // non-trivial function conversions.
+ if (score[SK_ForceUnchecked] == 0 && score[SK_Unavailable] == 0 &&
+ score[SK_FunctionConversion] == 0)
return true;
}
diff --git a/lib/Sema/ConstraintGraph.cpp b/lib/Sema/ConstraintGraph.cpp
index 3698f76..3ba0365 100644
--- a/lib/Sema/ConstraintGraph.cpp
+++ b/lib/Sema/ConstraintGraph.cpp
@@ -675,13 +675,39 @@
auto isParamBindingConstraint = kind == ConstraintKind::BindParam;
- // If the parameter is allowed to bind to `inout` let's not
- // try to contract the edge connecting parameter declaration to
- // it's use in the body. If parameter declaration is bound to
- // `inout` it's use has to be bound to `l-value`, which can't
- // happen once equivalence classes of parameter and argument are merged.
- if (isParamBindingConstraint && tyvar1->getImpl().canBindToInOut())
- continue;
+ // If the argument is allowed to bind to `inout`, in general,
+ // it's invalid to contract the edge between argument and parameter,
+ // but if we can prove that there are no possible bindings
+ // which result in attempt to bind `inout` type to argument
+ // type variable, we should go ahead and allow (temporary)
+ // contraction, because that greatly helps with performance.
+ // Such action is valid because argument type variable can
+ // only get its bindings from related overload, which gives
+ // us enough information to decided on l-valueness.
+ if (isParamBindingConstraint && tyvar1->getImpl().canBindToInOut()) {
+ bool isNotContractable = true;
+ if (auto bindings = CS.getPotentialBindings(tyvar1)) {
+ for (auto &binding : bindings.Bindings) {
+ auto type = binding.BindingType;
+ isNotContractable = type.findIf([&](Type nestedType) -> bool {
+ if (auto tv = nestedType->getAs<TypeVariableType>()) {
+ if (!tv->getImpl().mustBeMaterializable())
+ return true;
+ }
+
+ return nestedType->is<InOutType>();
+ });
+
+ // If there is at least one non-contractable binding, let's
+ // not risk contracting this edge.
+ if (isNotContractable)
+ break;
+ }
+ }
+
+ if (isNotContractable)
+ continue;
+ }
auto rep1 = CS.getRepresentative(tyvar1);
auto rep2 = CS.getRepresentative(tyvar2);
diff --git a/validation-test/Sema/type_checker_perf/fast/rdar36838495.swift b/validation-test/Sema/type_checker_perf/fast/rdar36838495.swift
new file mode 100644
index 0000000..a9f7cd5
--- /dev/null
+++ b/validation-test/Sema/type_checker_perf/fast/rdar36838495.swift
@@ -0,0 +1,36 @@
+// RUN: %target-typecheck-verify-swift -solver-expression-time-threshold=1
+// REQUIRES: tools-release,no_asserts
+
+struct T {
+ enum B {
+ case a
+ case b
+ }
+
+ let name: String
+ let b: B
+}
+
+struct A {
+ enum C {
+ case a
+ case b
+ case c
+ }
+
+ let name: String
+ let t: T
+ let c: C
+
+ var isX: Bool {
+ return self.t.b == .a
+ }
+}
+
+let x: [String: A] = [:]
+let _ = x.values.filter { $0.isX }
+ .filter { $0.t.b != .a }
+ .filter { $0.c == .a || $0.c == .b }
+ .filter { $0.isX }
+ .filter { $0.t.b != .a }
+ .sorted { $0.name < $1.name }