In cases where matched concrete overload used a bridging, CF*, or
`AnyHashable` conversion, let's attempt generic overload choices
as well because one of them could produce a better solution e.g.
`RawRepresentable` for `==` where underlying type conforms to `Equatable`
has a better generic match than `(AnyHashable, AnyHashable) -> Bool`.
Resolves: rdar://95992916
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.
To avoid unnecessary fixes when solver discovers that closure type
is only partially resolved after conjunction failure, let's fix it
up by replacing type variables with placeholders after solution
application.
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.
Previously the "too complex" would be detected by `ComponentStep::take`
but binding steps would still proceed until the producer is exhausted
because it considers failure of the previous resume to be just a failed
choice.
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.
Successful conjunction should preseve a score set by a follow-up
solve with outer context. Failure should reset the score back to
original one pre-conjunction.
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.
The current IUO design always forms a disjunction
at the overload reference, for both:
- An IUO property `T!`, forming `$T := T? or T`
- An IUO-returning function `() -> T!`, forming `$T := () -> T? or () -> T`
This is simple in concept, however it's suboptimal
for the latter case of IUO-returning functions for
a couple of reasons:
- The arguments cannot be matched independently of
the disjunction
- There's some awkwardness when it comes e.g wrapping
the overload type in an outer layer of optionality
such as `(() -> T!)?`:
- The binding logic has to "adjust" the correct
reference type after forming the disjunction.
- The applicable fn solving logic needs a special
case to handle such functions.
- The CSApply logic needs various hacks such as
ImplicitlyUnwrappedFunctionConversionExpr to
make up for the fact that there's no function
conversion for IUO functions, we can only force
unwrap the function result.
- This lead to various crashes in cases where
we we'd fail to detect the expr and peephole
the force unwrap.
- This also lead to crashes where the solver
would have a different view of the world than
CSApply, as the former would consider an
unwrapped IUO function to be of type `() -> T`
whereas CSApply would correctly see the overload
as being of type `() -> T?`.
To remedy these issues, IUO-returning functions no
longer have their disjunction formed at the overload
reference. Instead, a disjunction is formed when
matching result types for the applicable fn
constraint, using the callee locator to determine
if there's an IUO return to consider. CSApply then
consults the callee locator when finishing up
applies, and inserts the force unwraps as needed,
eliminating ImplicitlyUnwrappedFunctionConversionExpr.
This means that now all IUO disjunctions are of the
form `$T := T? or T`. This will hopefully allow a
further refactoring away from using disjunctions
and instead using type variable binding logic to
apply the correct unwrapping.
Fixes SR-10492.
While solving a conjunction that represents a (multi-statement) closure
constraint system should use such closure as its declaration context,
otherwise member lookup would produce incorrect results.
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.
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.
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]
`PotentialBindings` lost most of its responsibilities,
and are no longer comparable. Their main purpose now
is binding and metadata tracking (introduction/retraction).
New `BindingSet` type is something that represents a set
of bindings at the current step of the solver.
Create a new namespace - `swift::constraints::inference` and associate
`PotentialBinding` with it. This way it would be possible for constraint
graph to operate on `PotentialBinding(s)` in the future.
disjunction choice that does not introduce conversions, check to see
if known argument types satisfy generic operator conformance requirements
early, and skip the overload choice if any requirements fail.
This helps the solver avoid exploring way too much search space when
the right solution involves a generic operator, but the argument types
are known up front, such as `collection + collection + collection`.
successfully finding a solution by favoring operators already bound
elsewhere.
Favoring existing operator bindings often lets the solver find a solution
fast, but it's not necessarily the best solution.