For operators with default implementations in an extension, we don't
want to typecheck it both with overloads from the protocol type and
the ones from the extension.
For now this is only used for the same purpose as the previous type
constraint walking utility function.
Eventually we can incorporate this into our selection of overload
choices.
Use the type designated in the operator declaration as a mechanism to
limit the exploration we do in constraint solving. If we can find a
solution with an overload defined within that type or an extension of
that type, we do not look at any of the other options in the
disjunction.
This is disabled by default, and enabled with
> `-swift-version 5 -Xfrontend -solver-enable-operator-designated-protocols`
This will minimize the chance of breaking code. Currently SwiftLint
has one "too complex" expression with this change. Further changes to
the solver may improve that situation, and potentially allow us to
move this out from -swift-version 5 if we're willing to take the risk
of breaking some code as a result.
Because binding producer is going to attempt to unwrap optionals
and try the type, which would lead to infinite recursion because
dependent member types aren't bindable.
Resolves: rdar://problem/44770297
Attempt to visit disjunctions that are associated with applies where
we have at least some useful information about the types of all of the
arguments before visiting other disjunctions.
Two tests here got faster, and one slightly slower. One of the
faster tests is actually moving from test/ to the slow/ directory in
validation-test because despite going from 16s to less than 1s, it was
still borderline for what we consider the slow threshold, so I made
the test more complex. The one that got a little slower is
rdar22022980, which I also made more complex so that it is clearly
"slow" by the way we are testing it.
slower:
rdar22022980.swift
faster:
rdar33688063.swift
expression_too_complex_4.swift
Instead of asserting in the solver loop, let's move all of that
logic into `SolverStep::transitionTo(StepState)` and verify state
transition validity there.
Instead of using `TypeChecker` and related APIs directly every time,
let's just abstract it all away to easily check if solver is running
in debug mode and to produce indented logger.
First of all, let the splitter transfer constraints from inactive
work-list to associated components. Also start tracking components
to reinstate constraints back to constraint system when everything
is done, this allows to execute component steps in any desirable order.
Introduce a notion of `StepState` with some valid transitions,
and split monolithic `take(bool)` into three phases - `setup`, `take`, and
`resume`.
* `setup` - is preliminary state before the step has been taken
for the first time.
* `take` - represents actual logic of the step, results in either
step being done or suspended.
* `resume` - `take` might split steps into smaller ones to make
incremental progress towards the target, which means
that the main step has to be suspended. `resume` is
triggered when all of the "follow-up" steps have been
executed and are "done".
`DisjunctionStep` attempts all of the viable choices,
at least one of the choices has to succeed for disjunction
to be considered a success.
`DisjunctionStep` attempts each choice and generates followup
`SplitterStep` to see if applying choice to constraint system
resulted in any simplification.
Failure propagation is crucial for splitter and component steps
because that's the only signal for them to figure out if they
could be completely solved or have to fail.
For example, if one of the component steps created by "split"
fails it would cascade to the rest of the pending component steps
receiving "fail" signal and ultimately result in failure of the
"split" when it's re-taken.
* `SplitterStep` is responsible for running connected components
algorithm to determine how many independent sub-systems there are.
Once that's done it would create one `ComponentStep` per such
sub-system, and move to try to solve each and then merge partial
solutions produced by components into complete solution(s).
* `ComponentStep` represents a set of type variables and related
constraints which could be solved independently. It's further
simplified into "binding" steps which attempt type variable and
disjunction choices.
* "Binding" steps such as `TypeVariableStep` and `DisjunctionStep`
are responsible for trying type binding choices in attempt to
simplify the system and produce a solution. After attempting each
choice they introduce a new "split" step to compute more work.
The idea so to split solving into non-recursive steps,
represented by `SolverStep`, each of the steps is resposible
for a unit of work e.g. attempting type variable or
disjunction bindings/choices.
Each step could produce more work via "follow-up" steps,
complete "partial" solution when it's done, or error which
terminates solver loop.
* Extract logic for attempting individual choices into `solveForDisjunctionChoice`
* Remove all of the unnecessary information from `DisjunctionChoice`
* Convert auxiliary logic to operate on `TypeBinding` instead of `DisjunctionChoice`
Introduce `TypeBinding` interface with a single `attempt` method
which is useful to encapsulate specific binding logic used for
attempting disjunction choices and type variable bindings under
a single interface. This makes it easier to unify top-level logic
responsible for binding enumeration.
It should also make it possible to introduce unified binding producer
interface in the future.
Currently `allowFreeTypeVariableBindings` flag has to be passed all
the way down from top-level `solve` call to `finalize` that forms
(partial and complete) solutions. Instead of doing that, let's just
make it a part of the solver state, which is already present
throughout whole solver run.
Encapsulate most of the logic related to new binding generation
and iteration into `TypeVarBindingGenerator`. `tryTypeVariableBindings`
is only responsible for attempting bindings and recording results.
One more step towards iterative solver.
* Move logic to ensure that r-value type var would get r-value type to `PotentialBindings`;
* Strip uncessary parens directly when creating `PotentialBinding`;
* Check if the binding is viable before inclusion in the set, instead of filtering it later;
* Assert that bindings don't have types with errors included in the set.
Instead of passing a set of choices, locator and other flags to
`solveForDisjunctionChoices` directly, let's wrap all that information
into "disjunction" iterator which returns `DisjunctionChoice`s
directly.
Since this logic is tightly coupled to constraint, it makes sense
to move just there, also it's easier to re-use it elsewhere since
it doesn't have to be `private` anymore.
Replace the GenericTypeResolver type hierarchy with TypeResolution,
which more simply encapsulates the information needed to resolve
dependent types and makes explicit those places where we are
using resolution to contextual types.
Add an assert which verifies that all of the disjunction
choices has the same type variable on the left-hand side.
Based on that fact, refactor `selectBestBindingDisjunction`
to only check first choice to find suitable "bind" disjunctions
to favor.