Generalize the representation of contextual types so we can store
contextual types for more than one expression. Allow these to be
added/rolled back in solver scopes. Nothing uses this functionality
yet.
When requesting information about the contextual type of a constraint
system, do so using a given expression rather than treating it like
the global state that it is.
This constraint connects type variable representing a closure
expression to its inferred type and behaves just like regular
`Defaultable` constraint expect for type inference where it's
used only if there are no contextual bindings available.
In preparation to change type-checking behavior of closures
we need to introduce a special mapping from closure expressions
to their inferred types (based on parameters/result and body).
Chip away at the expression-centric nature of the constraint system by
generalizing the "target" of solution application from a single
expression to a a "SolutionApplicationTarget", which can be an
expression or a function body. The latter will be used for applying
function builders to an entire function body.
Introduce a new kind of constraint, the "value witness" constraint,
which captures a reference to a witness for a specific protocol
conformance. It otherwise acts like a more restricted form of a "value
member" constraint, where the specific member is known (as a
ValueDecl*) in advance.
The constraint is effectively dependent on the protocol
conformance itself; if that conformance fails, mark the type variables
in the resolved member type as "holes", so that the conformance
failure does not cascade.
Note that the resolved overload for this constraint always refers to
the requirement, rather than the witness, so we will end up recording
witness-method references in the AST rather than concrete references,
and leave it up to the optimizers to perform devirtualization. This is
demonstrated by the SIL changes needed in tests, and is part of the
wider resilience issue with conformances described by
rdar://problem/22708391.
Rather than maintaining a linked list of overload
choices, which must be linearly searched each time
we need to lookup an overload at a given callee
locator, use a MapVector which can be rolled back
at the end of a scope.
Remove ResolvedOverloadSetListItem in favor of
using SelectedOverload, which avoids the need to
convert between them when moving from
ConstraintSystem to Solution.
Some constraint transformations require knowledge about what state
constraint system is currently in e.g. `constraint generation`,
`solving` or `diagnostics` to make a decision whether simplication
is possible. Notable example is `keypath dynamic member lookup`
which requires a presence of `applicable fn` constraint to retrieve
some contextual information.
Currently presence or absence of solver state is used to determine
whether constraint system is in `constraint generation` or `solving`
phase, but it's incorrect in case of `diagnoseFailureForExpr` which
tries to simplify leftover "active" constraints before it can attempt
type-check based diagnostics.
To make this more robust let's introduce (maybe temporarily until
type-check based diagnostics are completely obsoleted) a proper
notion of "phase" to constraint system so it is always clear what
transitions are allowed and what state constraint system is
currently in.
Resolves: rdar://problem/57201781
Rework the interface to ConstraintSystem::salvage() to (a) not require
an existing set of solutions, which it overwrites anyway, (b) not
depend on having a single expression as input, and (c) be clear with
its client about whether the operation has already emitted a
diagnostic vs. the client being expected to produce a diagnostic.
Rather than setting up the constraint solver with a single expression
(that gets recorded for parents/depths), record each expression that
goes through constraint generation.
solve() is a bit too overloaded, so rename the version that does the
core "evaluate all of the steps to produce a set of solutions"
functionality to solveImpl().
ProtocolConformanceRef already has an invalid state. Drop all of the
uses of Optional<ProtocolConformanceRef> and just use
ProtocolConformanceRef::forInvalid() to represent it. Mechanically
translate all of the callers and callsites to use this new
representation.
A "hole" is a type variable which type couldn't be determined
due to an inference failure e.g. missing member, ambiguous generic
parameter which hasn't been explicitly specified.
It is used to propagate information about failures and avoid
recording fixes which are a consequence of earlier failures e.g.
```swift
func foo<T: BinaryInteger>(_: T) {}
struct S {}
foo(S.bar) // Actual failure here is that `S` doesn't have a member
// `bar` but a consequence of that failure is that generic
// parameter `T` doesn't conform to `BinaryInteger`.
```
Such tracking makes it easier to ignore already "fixed" requirements
which have been recorded in the constraint system multiple times e.g.
a call to initializer would open both base type and initializer
method which have shared (if not the same) requirements.
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.
When we transform each expression or statement in a function builder,
introduce a one-way constraint so that type information does not flow
backwards from the context into that statement or expression. This
more closely mimics the behavior of normal code, where type inference
is per-statement, flowing from top to bottom.
This also allows us to isolate different expressions and statements
within a closure that's passed into a function builder parameter,
reducing the search space and (hopefully) improving compile times for
large function builder closures.
For now, put this functionality behind the compiler flag
`-enable-function-builder-one-way-constraints` for testing purposes;
we still have both optimization and correctness work to do to turn
this on by default.
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.
Simplify the interface to gatherConstraints() by performing the
uniquing within the function itself and returning only the resulting
(uniqued) vector of constraints.
If assignment expression is not considered as a top-level candidate
it would mean that other candidates would be allowed to produce
types inconsistent with destination type of the assignment.
Resolves: rdar://problem/51413254
It's only needed in one place in the constraint solver to allow
`() -> T` to `() -> ()` and `() -> Never` to `() -> T` for expressions
representing return of a single expression functions, so to simplify
contextual type handling in diagnostic and other places it would be
better to replace dedicated locator kind with a flag on existing `ContextualType`.
Resolves: rdar://problem/51641323
Merging partial solutions can end up assigning the same type to a
particular typed node (expression, parameter, etc.), which can lead to
unbalanced set/clear when exploring the solution space (and later on,
crashes). Don't re-insert such information.
This is the same approach taken for type variable bindings, but it's
all pretty unfortunate: partial solutions should only record
information relative to their part of the constraint system, which
would save time and memory during solving. Howver, that's too big a
change for right now.
Fixes rdar://problem/50853028.
When we perform constraint generation while solving, capture the
type mappings we generate as part of the solver scope and solutions,
rolling them back and replaying them as necessary. Otherwise, we’ll
end up with uses of stale type variables, expression/parameter/type-loc
types set twice, etc.
Fixes rdar://problem/50390449 and rdar://problem/50150314.