Some of the unary operators, i.e. prefix `-`, don't have
CGFloat variants and expect generic `FloatingPoint` overload
to match CGFloat type. Let's not attempt `CGFloat` -> `Double`
conversion for unary operators because it always leads
to a worse solutions vs. generic overloads.
If a disjunction doesn't have an application, let's prefer
it if it's passed as an argument to an operator application
or involved in a member chain, in such situations attempting
a disjunction could provide context to parent call/member chain.
If disjunction is passed as an unapplied reference to some
parameter i.e. `<base>.map(String.init(describing:))` we don't
favor it for now because optimizer cannot do enough checking
to determine whether preferring such disjunction would help
make forward progress in solving by pruning some space or
providing additional context.
Mitigation for a historic incorrect type-checker behavior
caused by one of the performance hacks that used to favor
sync constructor overload over async one in async context
if initializers take a single unlabeled argument.
```swift
struct S {
init(_: Int) {}
init(_: Int) async {}
}
func test(v: Int) async { S(v) }
```
The type-checker should use `init(_: Int) async` in `test` context
but used to select the sync overload. The hack is now gone but we
need to downgrade an error to a warning to give the developers time
to fix their code.
This would make sure that any same-type requirements to a concrete
type are substituted with such types which is especially important
for SIMD operators like `&{+, -}` because they have `Scalar == (U)Int*`
requirements.
Let's not perform $T? -> $T for closure result types to avoid having
to re-discover solutions that differ only in location of optional
injection.
The pattern with such type variables is:
```
$T_body <conv/subtype> $T_result <conv/subtype> $T_contextual_result
```
When `$T_contextual_result` is `Optional<$U>`, the optional injection
can either happen from `$T_body` or from `$T_result` (if `return`
expression is non-optional), if we allow both the solver would
find two solutions that differ only in location of optional
injection.
Prioritize `build{Block, Expression, ...}` and any chained
members that are connected to individual builder elements
i.e. `ForEach(...) { ... }.padding(...)`, once `ForEach`
is resolved, `padding` should be prioritized because its
requirements can help prune the solution space before the
body is checked.
Allow CGFloat -> Double widening conversions between
candidate argument types and parameter types. This would
make sure that Double is always preferred over CGFloat
when using literals and ranking supported disjunction
choices. Narrowing conversion (Double -> CGFloat) should
be delayed as much as possible.
If result of `CGFloat` -> `Double` conversion is injected into an optional
it should be ranked based on depth just like when locator is fully simplified.
For example:
```swift
func test(v: CGFloat?) {
_ = v ?? 2.0 / 3.0
}
```
In this expression division should be performed on `Double` and result
narrowed down (based on the rule that narrowing conversion should always
be delayed) but `Double` -> `CGFloat?` was given an incorrect score and
instead of picking `?? (_: T?, _: T) -> T` overload, the solver would
use `?? (_: T?, _: T?) -> T?`.
The original attempt to do this was reverted by https://github.com/swiftlang/swift/pull/77653
The problem is that the fix was too broad, I narrowed it down to
non-exact uses of stdlib collections that support upcasts.
If disjunction represents a member reference that has no arguments
applied, let's score that as `1` to indicate that it should be priorized.
This helps in situations like `a.b + 1` where resolving `a.b` member
chain helps to establish context for `+`.
If a disjunction has favored choices, let's mark it as optimized
with a high score to make sure that such disjunctions are prioritized
since only disjunctions that could have their choices fovored
independently from the optimization algorithm are compiler synthesized
ones for things like IUO references, explicit coercions etc.
If some of the requirements of a generic overload reference other
generic parameters, the optimizer won't be able to satisfy them
because it only has candidates for one (current) parameter. In
cases like that, let's fallback to a light-weight protocol conformance
check instead of skipping an overload choice altogether.
Some disjunctions e.g. explicit coercions, compositions of restrictions,
and implicit CGFloat initializers have favored choices set independently
from optimization algorithm, `selectDisjunction` should account for
such situations.
`scoreCandidateMatch` needs to reflect the fact that literal can
assume any type that conforms to `ExpressibleBy{Integer, Float}Literal`
protocol but since such bindings are non-default and alway produce
a worse solution vs. default literal types if all of the arguments
are literals let's use only exact matches otherwise there is a chance
of "over-favoring" and creating more work for ranking.
It is not always possible to match candidate types against corresponding
parameter types for certain overloads i.e. when parameter is an associated type.
`scoreCandidateMatch` needs to be able to indicate that to the caller
to allow it to move to the next parameter (if any) without failing the
overload choice when the match cannot be established.
Use `checkGenericRequirements` to check whether all of the requirements
placed on a particular parameter match, that gives us a lot of confidence
that the overload should be favored.
This maintains an "old hack" behavior where overloads of some
`OverloadedDeclRef` calls were favored purely based on number of
argument and (non-defaulted) parameters matching.
This is important to maintain source compatibility.
Don't attempt this optimization if call has number literals.
This is intended to narrowly fix situations like:
```swift
func test<T: FloatingPoint>(_: T) { ... }
func test<T: Numeric>(_: T) { ... }
test(42)
```
The call should use `<T: Numeric>` overload even though the
`<T: FloatingPoint>` is a more specialized version because
selecting `<T: Numeric>` doesn't introduce non-default literal
types.
Helps situations like `1 + {Double, CGFloat}(...)` by inferring
a type for the second operand of `+` based on a type being constructed.
Currently limited to Double and CGFloat only since we need to
support implicit `Double<->CGFloat` conversion.
Helps situations like `1 + CGFloat(...)` by inferring a type for
the second operand of `+` based on a type being constructed.
Currently limited to known integer, floating-point and CGFloat types
since we know how they are structured.
Regardless of whether an overload choice matched on one or more
literal candidate types assign it a fixed score to make sure that
we always prefer outmost disjunction in cases like `test(1 + 2 + 3)`
since it gives us a better chance to prune once there is a contextual
type.
When operators are chained it's possible that we don't know anything
about parameter(s) but result is known from the context, we should
use that information.
`containsIDEInspectionTarget` doesn't work for implicit argument
lists, but luckily implicit code completion expressions cannot
be injected into arguments, so we can check whether whether they
appear at an argument position and prevent optimization.
Preserves old behavior where for unary calls to members
the solver would not consider choices that didn't match on
the number of parameters (regardless of defaults) and only
exact matches were favored.
This is already accounted for by `determineBestChoicesInContext`
and reflected in the overall score, which means that we no longer
need to use this in vacuum.
Since erasure of a concrete type into an existential value yields
score of 1, we need to bump the score in cases where a type
passed to a generic parameter satisfies all of its protocol
conformance requirements and that parameter represents an
opaque type.
Dependent members just like generic parameter types could
have associated protocol conformances which give us a glimpse
into whether arguments could possibly match.
Candidate is viable (with some score) if:
- Candidate is exactly equal to a parameter type
- Candidate type differs from a parameter type only in optionality
- Parameter is a generic parameter type and all conformances are matched by a candidate type
- Candidate tuples matches a parameter tuple on arity
- Candidate is an `Array<T>` and parameter is an `Unsafe*Pointer`
- Candidate is a subclass of a parameter class type
- Candidate is a concrete type and parameter is its existential value (except Any)