in the constraint system over a different binding or disjunction.
In other words, we will only choose to bind a hole to `Any` if there
are no other bindings and no disjunctions.
all cases of missing generic parameters.
In `ComponentStep::take` when there are no bindings or disjunctions, use hole
propagation to default remaining free type variables that aren't for generic
parameters and continue solving. Rather than using a defaultable constraint for
holes, assign a fixed type directly when we have no bindings to try.
One-way constraint expressions, which are the only things that
introduce one-way constraints at this point, want to look through
lvalue types to produce values. Rename OneWayBind to OneWayEqual, map
it down to an Equal constraint when it is simplified (to drop
lvalue-ness), and apply that coercion during constraint application.
Part of rdar://problem/50150793.
There were a few places where we wanted fast testing to see whether a
particular type variable is currently of interest. Instead of building
local hash tables in those places, keep type variables in a SetVector
for efficient testing.
Introduce the notion of "one-way" binding constraints of the form
$T0 one-way bind to $T1
which treats the type variables $T0 and $T1 as independent up until
the point where $T1 simplifies down to a concrete type, at which point
$T0 will be bound to that concrete type. $T0 won't be bound in any
other way, so type information ends up being propagated right-to-left,
only. This allows a constraint system to be broken up in more
components that are solved independently. Specifically, the connected
components algorithm now proceeds as follows:
1. Compute connected components, excluding one-way constraints from
consideration.
2. Compute a directed graph amongst the components using only the
one-way constraints, where an edge A -> B indicates that the type
variables in component A need to be solved before those in component
B.
3. Using the directed graph, compute the set of components that need
to be solved before a given component.
To utilize this, implement a new kind of solver step that handles the
propagation of partial solutions across one-way constraints. This
introduces a new kind of "split" within a connected component, where
we collect each combination of partial solutions for the input
components and (separately) try to solve the constraints in this
component. Any correct solution from any of these attempts will then
be recorded as a (partial) solution for this component.
For example, consider:
let _: Int8 = b ? Builtin.one_way(int8Or16(17)) :
Builtin.one_way(int8Or16(42\
))
where int8Or16 is overloaded with types `(Int8) -> Int8` and
`(Int16) -> Int16`. There are two one-way components (`int8Or16(17)`)
and (`int8Or16(42)`), each of which can produce a value of type `Int8`
or `Int16`. Those two components will be solved independently, and the
partial solutions for each will be fed into the component that
evaluates the ternary operator. There are four ways to attempt that
evaluation:
```
[Int8, Int8]
[Int8, Int16]
[Int16, Int8]
[Int16, Int16]
To test this, introduce a new expression builtin `Builtin.one_way(x)` that
introduces a one-way expression constraint binding the result of the
expression 'x'. The builtin is meant to be used for testing purposes,
and the one-way constraint expression itself can be synthesized by the
type checker to introduce one-way constraints later on.
Of these two, there are only two (partial) solutions that can work at
all, because the types in the ternary operator need a common
supertype:
[Int8, Int8]
[Int16, Int16]
Therefore, these are the partial solutions that will be considered the
results of the component containing the ternary expression. Note that
only one of them meets the final constraint (convertibility to
`Int8`), so the expression is well-formed.
Part of rdar://problem/50150793.
Move the logic for creating connected components of orphaned
constraints into the connected-components algorithm code, rather than
making it a special part of SplitterStep.
Have the constraint graph's connected-component implementation be more
self-contained, producing a vector containing each of the actual
components (where each is defined by a list of type variables and a list
of constraints). This simplifies the contract with the client
(SplitterStep) and eliminates a bunch of separate mapping steps to
interpret the results.
It also lets us enrich the Component data structure in the future.
The connected components computation was not forming components when all of
the type variables in a component were already bound. Any remaining
constraints involving those type variables would then arbitrarily end up
in component 0, due to the the default-construction behavior of a map’s
operator[] with missing keys, artificially increasing the size of that
component with (typically) additional disjunctions.
Ensure that all constraints get into a component, creating one to hold
the a constraint even when all of the type variables are already bound.
Then, assert the invariant that every constraint is associated with a
component.
In time, we should probably eliminate this notion of disjunctions that
remain even when the type variable has been bound. For now, strengthen the
invariant to at least ensure that they get solved in isolation.
When we are associating type variables with components in a “split” step,
there is no need to record already-bound type variables with every component.
Currently finalization e.g. scope reset and solution minimization
is only done if component step had follow-up e.g. type variable or
disjunction step(s), but it should be done if `take` generated any
fixes as well, or component changed score in any way, otherwise
we might miss some solutions with fixes because "best score" haven't
been reset properly.
Introduce a fix to detect and diagnose situations when omitted
generic arguments couldn't be deduced by the solver based on
the enclosing context.
Example:
```swift
struct S<T> {
}
_ = S() // There is not enough context to deduce `T`
```
Resolves: rdar://problem/51203824
The ConstraintSystem class is on the order of 1000s of bytes in size on
the stacka nd is causing issues with dispatch's 64k stack limit.
This changes most Small data types which store data on the stack to non
small heap based data types.
This draws a distinction between disabled choices with and without
fixes - former is disabled because it's unrelated, latter because
it only makes sense to attempt it during "salvage" mode while trying
to produce diagnostics.
We originally planned to use this to hide some simd operators from the typechecker unless the user explicitly opted into having them but that scheme turned out to be ill-conceived, so we moved them
back into the stdlib. This change simply cleans up the empty vestigial module.
The new SIMD proposal introduced a number of new operators, the presence of
which causes more "expression too complex" failures. Route around the
problem by de-prioritizing those operators, visiting them only if no
other operator could be chosen. This should limit the type checker
performance cost of said operators to only those expressions that need
them OR that already failed to type-check.
Fixes rdar://problem/46541800.
Allow a couple of the tests in shortCircuitDisjunctionAt() to run even
when hacks are disabled.
The first of these tests is imperfect since it assumes that "favored"
disjunction choices all come before the others (which may be the case
now but is pretty brittle).
Similarly, the second of these tests assumes that choices that are
fixes always come after ones that are not.
What we really want to do is skip these choices rather than stopping
at these points.
If sibling components failed, there is no reason to establish new
scope since remaining containers are not really going to be taken
but fail fast instead.
Instead of printing `trying` for type variable and `assuming`
for disjunction choices, unify it so `BindingStep` logs
`attempting {type variable, disjunction choice}`.
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.
There are situations when solver goes exponential, before
(when solver was recursive) it would fail relatively quickly by running
out of stack space, now we really need to make sure that emergency
break is in place in main solver loop, otherwise we risk it running for
a really long time if doesn't consume too much memory since default
timeout is ~10 mins.
Eagerly check if optimal solution has already been found even if
current binding attempt has failed, because otherwise it might
check too many bindings which would result in ambiguity.
Before this change some of the type variables, especially the ones
that have been previously bound haven't been associated with any
of the component steps, so it had to be done by scope once per step,
but now such recording is done once per split.
Orphaned constraints can be re-introduced to the system by `setup`
of the component, and all of them could be returned back by
`SplitterStep::resume` there so no need to use destructor for that.
Also, orphan constraints have to be registered as regular constraints
by components as well as marked as "orphaned".
Extract choice attempting logic into `DisjunctionStep::attemptChoice`
and start recording taken choices in constraint system when
requested by disjunction.
Execute components with fewer disjunction constraints first,
to make sure that if one of the components fails we do the
smallest amount of work possible.
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".
If "split" step produced a single component, there is no reason
to allocate a scope for it and modify constraint system,
because all of the existing type variables and constraints
are related to it anyway, so scope would just end up doing
useless work.
Similar to `DisjunctionStep` `TypeVariableStep` attempts
its choices one-by-one, and if any of the choices match
it makes type variable as a whole successfully solved.
Since type variable attempts choices it means that it
can only produce "split" steps as a follow-up.
`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.