While producing a combined solution, let's reflect the number of
fixes and holes discovered in the conjunction, that way it would
be possible to filter solutions and keep track of the fact that
there were issues in the conjunction.
Since conjunction is not going to continue current solver path
after discovering an ambiguity, let's just mark all of the
unbound outer variables as placeholders to produce a complete solution.
Solver can now handle multiple different targets e.g. multi-statement
closures, result builders etc. So it's more appropriate to say that
the constraint system is too complex.
Each conjunction step should be executed in isolation from outer
constraint system state, which should also include scope counter
because otherwise, e.g. for large closures, solver might stop
prematurely since all of the previous statements/expressions would
contribute to the scope total.
Closure result type or generic parameter associated with such a location
could bw inferred from a body of a multi-statement closure (when inference
is enabled), so we need to give closure a chance to run before attemtping
a hole for such positions in diagnostic mode.
Closure result type or generic parameter associated with such a location
could bw inferred from a body of a multi-statement closure (when inference
is enabled), so we need to give closure a chance to run before attemtping
a hole for such positions in diagnostic mode.
Stop a conjunction after the first invalid element in diagnostic mode.
No need to run the inference for other elements once the problem has been
found. All ambiguities and solutions with fixes are propagated back to the
outer context to be diagnosed.
In preparation to handle ambiguities in the elements, it's useful
to extract the logic dealing with constraint system state restoration
into a separate logical entity.
Attempting to pre-compute a set of referenced type variables
upfront is incorrect because parameter(s) and/or result type
could be bound before conjunction is attempted. Let's compute
a set of referenced variables before each element gets attempted.
It helps to simply handling of outer constrants because they have
to be added to the constraint system before scope is created but
constraint graph have to get updated after to make sure that
incremental binding inference already knows about types inferred
from conjunction.
`SplitterStep` doesn't filter solutions if it results in a single
component (which is does for conjunction elements), so the solutions
have to be filtered manually after each element is done.
Iterate over all of the elements one-by-one and make sure that
each results in a single solution, otherwise fail the conjunction step.
Once all of the elements are handled either stop or,
if conjunction step has been performed in isolation,
return all of the outer constraints back to the system
and attempt to solve for outer context - that should
produce one or more solutions for conjunction to be
considered successfully solved.
Instead of crashing in release builds, let's simplify fail the
step and let type-checker produce a fallback diagnostic. Maintain
a trap in debug builds to make it easier to investigate the root
cause of the issue.
Resolves: rdar://56167233
Start treating the null {Can}GenericSignature as a regular signature
with no requirements and no parameters. This not only makes for a much
safer abstraction, but allows us to simplify a lot of the clients of
GenericSignature that would previously have to check for null before
using the abstraction.
Currently all `ComponentSteps` created by `DependentComponentSplitterStep` share the same `Solutions` vector. Because of this, the `ComponentStep`s might modify solutions created by previous `ComponentStep`s. Use different `Solutions` vectors for each `ComponentStep` to avoid sharing information between the `ComponentStep`s.
The concrete manifestation in the added test case is that the `Bar` overload gets added to `Solutions`, it’s score gets reduced by its `ComponentStep` original score, then the `Foo` overload gets added to `Solutions` and both solutions have their score decreased by the `OriginalScore` of `Foo`’s `ComponentStep`, causing `Bar`’s score to underflow.
Fixes rdar://78780840 [SR-14692]
Not all of the unary operators have `CGFloat` overloads,
so in order to preserve previous behavior (and overall
best solution) with implicit Double<->CGFloat conversion
we need to allow attempting generic operators for such cases.
```swift
let _: CGFloat = -.pi / 2
```
`-` doesn't have `CGFloat` overload (which might be an oversight),
so in order to preserve type-checking behavior solver can't be
allowed to pick `-(Double) -> Double` based on overload of `/`,
the best possible solution would be with `/` as `(CGFloat, CGFloat) -> CGFloat`
and `-` on a `FloatingPoint` protocol.
- Prefer CGFloat -> Double over the other way around to avoid
ambiguities;
- Every new conversion impacts the score by factor of number of
previously applied conversions to make it possible to select
solutions that require the least such conversions.
- Prefer concrete overloads with Double <-> CGFloat conversion
over generic ones.
Augment `DisjunctionChoice::{shouldSkip, shouldStopAt}` to check
for presence of implicit value conversions, and if score indicates
that there is at least one of those was attempted - downgrade
importance of a last successful choice.
The existing overloading rules strongly prefer async functions within
async contexts, and synchronous functions in synchronous contexts.
However, when there are other differences in the
signature, particularly parameters of function type that differ in
async vs. synchronous, the overloading rule would force the use of the
synchronous function even in cases where the synchronous function
would be better. An example:
func f(_: (Int) -> Int) { }
func f(_: (Int) async -> Int) async { }
func g(_ x: Int) -> Int { -x }
func h() async {
f(g) // currently selects async f, want to select synchronous f
}
Effect the semantics change by splitting the "sync/async mismatch"
score in the constraint system into an "async in sync mismatch" score
that is mostly disqualifying (because the call will always fail) and a
less-important score for "sync used in an async context", which also
includes conversion from a synchronous function to an asynchronous
one. This way, only synchronous functions are still considered within
a synchronous context, but we get more natural overloading behavior
within an asynchronous context. The end result is intended to be
equivalent to what one would get with reasync:
func f(_: (Int) async -> Int) async { ... }
Addresses rdar://74289867.