-
Notifications
You must be signed in to change notification settings - Fork 10.5k
[WIP] [TypeChecker] Score disjunctions and constraint components to prune search space #9220
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
Conversation
@swift-ci please test |
@swift-ci Please test source compatibility |
1 similar comment
@swift-ci Please test source compatibility |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Interesting approach!
lib/Sema/CSSolver.cpp
Outdated
typeVar, constraints, ConstraintGraph::GatheringKind::EquivalenceClass); | ||
|
||
for (auto constraint : constraints) { | ||
if (constraint->getKind() != ConstraintKind::Conversion) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There are other constraints like SubType constraints that have similar properties. I suggest switching on constraint->getKind()
so it's clear that we've thought about all of these.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks, will do!
lib/Sema/CSSolver.cpp
Outdated
// that helps to avoid invalid disjunction choices. | ||
if (LHS->isEqual(typeVar) && !RHS->hasUnresolvedType()) { | ||
// No type variables, means pretty good chance that | ||
// this is conversion to some concerete type, which |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Typo "concerete".
d61b753
to
52bede3
Compare
@DougGregor @rudkx I've rebased everything with the latest master and made conversion propagation a part of |
@swift-ci please test |
52bede3
to
c0db10b
Compare
Looks like one of the expressions is no longer too complex :) |
@swift-ci please test |
@swift-ci please test source compatibility |
for (unsigned component = 0; component != numComponents; ++component) | ||
componentOrdering.push_back(component); | ||
// Now sort the components in the ordering based on the scores of the buckets. | ||
std::sort( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You could sort the buckets directly, so you don't have to separately maintain the componentOrdering
. std::sort
will std::move
the ConstraintBucket
instances, so it shouldn't be any more expensive.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've separated ordered set from the original one because components
is a vector and some of the code down the line relies on the original indexes the way they are produced by constraint graph e.g. associating constraints into their buckets and orphan handling logic. I think that should be fixed down the road but I just didn't want to tackle it right away...
lib/Sema/CSSolver.cpp
Outdated
}; | ||
|
||
auto evaluateDisjunction = [&](Constraint *contender, float score) { | ||
// Score has to be strictly greather than best because that would |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
typo "greather"
lib/Sema/CSSolver.cpp
Outdated
auto evaluateDisjunction = [&](Constraint *contender, float score) { | ||
// Score has to be strictly greather than best because that would | ||
// allow us to apply disjunctions in their semantic order, which gives | ||
// better changes of avoiding of the the obviously wrong choices with |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"the the"
lib/Sema/CSSolver.cpp
Outdated
// everything else being equal. | ||
if (score > bestScore || | ||
(score == bestScore && | ||
getDisjunctionId(bestDisjunction) > getDisjunctionId(contender))) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Rather than doing a bit of work to dig out type variable IDs to compare (which isn't always going to give an ordering), why not keep track of the index of the best option within the disjunction? It'll be more predictable and eliminate the need for getDisjunctionId
.
lib/Sema/ConstraintSystem.h
Outdated
@@ -1900,6 +1901,10 @@ class ConstraintSystem { | |||
/// reads the type from the ConstraintSystem expression type map. | |||
Type getResultType(const AbstractClosureExpr *E); | |||
|
|||
/// Determine if the given constraint represents explicit conversion, | |||
/// e.g. coercion constraint "as X" which forms a disjunction. | |||
bool isExplicitConversionConstraint(Constraint *constraint); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd love to have "disjunction" in the name, too.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sounds good, thanks!
lib/Sema/ConstraintSystem.h
Outdated
@@ -2728,6 +2748,48 @@ class DisjunctionChoice { | |||
return decl->isOperator() ? decl : nullptr; | |||
} | |||
}; | |||
|
|||
class ConstraintBucket { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm very glad to have this functionality split out into a separate class, but please document what this class does via comments.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Will do!
@swift-ci please test source compatibility |
c0db10b
to
56327c9
Compare
@swift-ci please test |
@swift-ci please test source compatibility |
56327c9
to
a290307
Compare
@swift-ci please smoke test |
a290307
to
80edd2b
Compare
@swift-ci please smoke test OS X platform |
80edd2b
to
804aa60
Compare
@swift-ci please smoke test OS X platform |
804aa60
to
906820a
Compare
@swift-ci please clean smoke test OS X platform |
@swift-ci please smoke test OS X platform |
5e41cb7
to
14458b2
Compare
@swift-ci please smoke test OS X platform |
14458b2
to
d0bbed4
Compare
@swift-ci please smoke test OS X platform |
d0bbed4
to
ec31877
Compare
@swift-ci please smoke test |
…une search space Before trying to solve constraint system components: 1. Score constraint components/buckets based on how many disjunctions they have, that helps to prune some of the branches with incorrect solutions early which limits overall depth of the search. 2. In each of the constraint components, score disjunctions based on how much information do we have about them, the more information we have the easier it is to prune search space of incorrect branches earlier. 3. When dealing with explicit coercions apply their conversions early to avoid searching for solutions with incorrect types.
… selection logic Move disjunction selection logic one level up from the `solveSimplified` which allows to simplify its logic and avoid collecting disjunctions multiple times for each solver step.
ec31877
to
42b17ab
Compare
@swift-ci please smoke test OS X platform |
I can’t look at the code, but I’d like to ask about the algorithm per se. I believe you’re solving a CSP here, right? By doing some variant of backtracking. Picking the variables with the least amount of options, so that you can branch less early and reduce the search space quickly. Am I right, there? Um... if I am, I was thinking that maybe you could use a Heap (heapify your array/vector of variables). That way, the ordering step (O(nlogn)) is ommited, and then the whole solving process might become less costly. I don’t know if it’s the best option, but in many heuristic algorithms that’s a classic :). In this case, CSP isn’t a “search” like the problems heuristics usually are used for... but the “score” you’re using fits the classic definition of “heuristic”. And therefore maybe it calls for similar tools 🤷🏻♂️🙂 I’d like to know the details of this problem. It looks really interesting :) |
@felix91gr It's close but not really, the algorithm/heuristic is actually trying to figure out which disjunction (with N choices) to use first to possibly reduce the depth of the search, the problem we have is that type variable domains are not known until all disjunction choices are all picked, only after that we can go ahead and pick bindings for type variables and check their satisfiability to constraints we have, which makes it more complicated comparing to classing CSP where most of the algorithms are based on the fixed variable domains. |
Ahh. I see. Then, it’s like a search for trying “easy disjunctions” first. That search happens inside one step of assigning variables and checking constraint satisfiability. And I guess you try with every disjunction in the list? (Do we have a documentation on this CS algorithm? I’d like to learn about it and work on it) |
@felix91gr Kind of yes, but it doesn't actually assign anything, it's impossible really until all disjunction choices are set, but if something else did assign type to one of the type variables related to the disjunction it adds to it's score because it's more likely to filter out some of the branches. Unfortunately we don't really have much documentation when it comes to expression type checker but we are working on it... |
We could talk on twitter (or anything). I could write an outline of the problem and algorithm after I understand it. It’ll be a pleasure. |
(And maybe add some drawings) |
@felix91gr Thanks, I'll figure out if it works at all or not and let you know! I've actually made it a separate PR for it #11723 |
Bah. Pardon me. I’m very tired... I want this algorithm to be written somewhere, that way people (like me) who aren’t that llvm/cpp savvy, can help from the algorithmic side of it. It would also help polishing the implementation, if need be. My point is, you have my |
Oh, I see. Cool! Best of luck... and if you need me, just @ me somewhere ;) |
Will do, thanks! |
Before trying to solve constraint system components:
Score constraint components/buckets based on how many disjunctions
they have, that helps to prune some of the branches with incorrect
solutions early which limits overall depth of the search.
In each of the constraint component score disjunctions
based on how much information do we have about them, the
more information we have the easier it is to prune search
space of incorrect branches earlier.
When dealing with explicit coercions apply their conversions
early to avoid searching for solutions with incorrect types.