Instead of setting empty closure (`{}`) result type to be `Void`
while generating constraints, let's allocate a new type variable
instead and let it be bound to `Void` once the body is opened.
This way we can support an interaction with function builders which
would return a type different from `Void` even when applied to empty closure.
Resolves: rdar://problem/61347993
When present in a function builder, buildFinalResult() will be called on
the value of the outermost block to form the final result of the closure.
This allows one to collapse the full function builder computation into
a single result without having to do it in each buildBlock() call.
Wherever we have constraints that involve pattern matching, use the
PatternMatch locator element. Additionally, don't use the TupleElement
locator element for tuple patterns, because it violates assumptions used
for diagnostics.
The new test was crashing; now it has a terrible diagnostic for which I
need to think harder about a fix.
The normal type checking of switch statements checks the switch subject
first, without context, then evaluates the cases. Introduce a one-way
constraint into the type checking of switch statements within function
builders to provide this same behavior. The difference can be observed
in code such as:
enum E {
case a
case b(Int, String?)
}
enum E2 {
case b(Int, String?)
}
func getSomeEnumOverloaded(_: Double) -> E { return .a }
func getSomeEnumOverloaded(_: Int) -> E2 { return .b(0, nil) }
func f() {
switch getSomeEnumOverloaded(17) {
case .a: // error: no member named "a" in E2
print("a")
default:
print("default")
}
}
When the subject expression `getSomeEnumOverloaded(17)` is resolved
without consider cases, it will select the second
`getSomeEnumOverloaded(_:)`, because the literal 17 prefers to be
an `Int`. The type checking of the first case would then fail because E2
does not contain a member named "a".
Prior to this change, the same expression within a function
builder would succeed in type checking, because the lack of case named
"a" within "E2" would make the second getSomeEnumOverloaded() unusable.
Making this code work by considering the cases along with the subject
expression is not unreasonable, and may be the right long term
direction for the language. However, it's a feature that should be
discussed separately, and the semantics should agree between function
builders and normal statements.
Big thanks to John McCall for noting the missing one-way constraint!
Implement support for switch statements within function builders. Cases can
perform arbitrary pattern matches, e.g.,
tuplify(true) { c in
"testSwitchCombined"
switch e {
case .a:
"a"
case .b(let i, _?), .b(let i, nil):
i + 17
}
}
subject to the normal rules of switch statements. Cases within function
builders cannot, however, include “fallthrough” statements, because those
(like “break” and “continue”) are control flow.
The translation of performed for `switch` statements is similar to that of
`if` statements, using `buildEither(first:)` and `buildEither(second:)` on
the function builder type.
This is the bulk of switch support, tracked by rdar://problem/50426203.
Introduce support for initialized let/var declarations within function
builder closures, e.g.,
let (a, b) = c()
We generate constraints for the declarations as elsewhere, but the types of
the declared variables (a and b in this case) are bound to the type of the
pattern by one-way constraints, to describe the flow of type information
through the closure.
Implements rdar://problem/57330696.
if the closure had a function builder transform applied.
This way, function builder closures can have syntactic restrictions
diagnosed the same way as other expressions.
Fix a few related issues involving the interaction with
single-expression closures:
* A single-expression closure can have a "return" in it; in such
cases, disable the function-builder transform.
* Have the function builder constraint generator look through the
"return" statement in a single-expression closure the same way as
solution application does
Fixes rdar://problem/59045763, where we rejected some well-formed code
involving a single-expression closure with a "return" keyword.
Set the contextual type of conditional expressions (to Bool) with the
"condition" purpose so we get customized diagnostics for mistakes such
as
if x.property = y { }
Prior to this, we'd get generic "() isn't convertible to Bool"
diagnostic. Now we get
use of '=' in a boolean context, did you mean '=='?
Fix `getArgumentExpr` to not assume that constraint system
always asks for index that exists in an argument tuple,
because sometimes arguments could be synthesized (such
arguments do not have corresponding expression).
Resolves: rdar://problem/56221372
When type checking the body of a function declaration that has a function
builder on it (e.g., `@ViewBuilder var body: some View { ... }`), create a
constraint system that is responsible for constraint generation and
application, sending function declarations through the same code paths
used by closures.
automatically.
This commit also renames `ConstraintSystem::recordHole/isHole` to
`recordPotentialHole` and `isPotentialHole` to make it clear that
we don't know for sure whether a type variable is a hole until it's
bound to unresolved.
When diagnosing failures in a function builder closure, we were
unnecessarily replacing the closure expression with its body,
destroying the AST and resulting in assertions due to DeclContext
mismatches. Fixes SR-11350 / rdar://problem/54590425.
Now that we associate argument labels for key path
subscript components, remove the special logic for
it. In addition, we can now search for callees
directly by using `getCalleeLocator`, as it should
now be able to find all the correct callees that
`getCalleeDeclAndArgs` does.
By using `getCalleeLocator`, we now also correctly
resolve callees for operator calls, meaning that
we can now use them with function builders. In
addition, we no longer incorrectly resolve callees
for calls made on SubscriptExprs.
Resolves SR-11439 & SR-11440.
Prior to this patch, we pre-checked the result of applying the function-builder transformation, but only when we hadn't already pre-checked the closure before. This causes two problems to arise when the transformation is applied to the same closure along multiple branches of a disjunction. The first is that any expressions that are synthesized by the transformation will not be pre-checked the second time through, which is a problem if we try to apply different builder types to the same closure (we do cache expressions for identical builder types). The second is that the pre-check will rewrite sub-expressions in place *in the synthesized expression*, which means that top-level expressions in the original closure body (including `if` conditions) that are now nested in the synthesized expression will not be rewritten in the original closure and therefore will be encountered in their raw state the second time through.
This patch causes all expressions in the original closure body to be pre-checked before doing any other work. We then pre-check the synthesized expression immediately before generating constraints for it in order to set up the AST appropriately for CSGen; this could be skipped if we just synthesized expressions the way that CSGen wants them, but that seems to be somewhat involved.
Pre-checking is safe to apply to an expression multiple times, so it's
fine if we take this path and then decide not to use a function builder.
I've also merged the check for `return` statements into this same walk, which was convenient.
Fixes rdar://53325810 at least, and probably also some bugs with applying different function builders to the same closure.
Current logic in `applySolutionFixes` didn't actually look inside
of single-expression closures (because of brace statement) or
closures which are transformed by function builders.
Resolves: rdar://problem/51167632
The implicit TypeExprs for function builders were getting inconsistently set
types, which would sometimes be the metatype and sometimes not be the
metatype, leading to a crash in the new test code.
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.