Skip to content

[5.0][CSSolver] Fix performance regression related to contraction of closu… #14318

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Feb 1, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions lib/Sema/CSSolver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1788,9 +1788,10 @@ static bool shouldSkipDisjunctionChoice(ConstraintSystem &cs,
// Let's skip generic overload choices only in case if
// non-generic score indicates that there were no forced
// unwrappings of optional(s), no unavailable overload
// choices present in the solution, and no fixes required.
if (score[SK_ForceUnchecked] == 0 && score[SK_Unavailable] == 0
&& score[SK_Fix] == 0)
// choices present in the solution, no fixes required,
// and there are no non-trivial function conversions.
if (score[SK_ForceUnchecked] == 0 && score[SK_Unavailable] == 0 &&
score[SK_Fix] == 0 && score[SK_FunctionConversion] == 0)
return true;
}

Expand Down
40 changes: 33 additions & 7 deletions lib/Sema/ConstraintGraph.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -675,13 +675,39 @@ bool ConstraintGraph::contractEdges() {

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);
Expand Down
36 changes: 36 additions & 0 deletions validation-test/Sema/type_checker_perf/fast/rdar36838495.swift
Original file line number Diff line number Diff line change
@@ -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 }