Skip to content

Commit 132d2ee

Browse files
committed
[Sema] Type check multi-statement closures inside result builders
1 parent 3de97d6 commit 132d2ee

File tree

11 files changed

+46
-22
lines changed

11 files changed

+46
-22
lines changed

include/swift/Sema/ConstraintSystem.h

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -73,8 +73,9 @@ class SolutionApplicationTarget;
7373
// so they could be made friends of ConstraintSystem.
7474
namespace TypeChecker {
7575

76-
Optional<BraceStmt *> applyResultBuilderBodyTransform(FuncDecl *func,
77-
Type builderType);
76+
Optional<BraceStmt *> applyResultBuilderBodyTransform(
77+
FuncDecl *func, Type builderType,
78+
bool ClosuresInResultBuilderDontParticipateInInference);
7879

7980
Optional<constraints::SolutionApplicationTarget>
8081
typeCheckExpression(constraints::SolutionApplicationTarget &target,
@@ -1774,6 +1775,12 @@ enum class ConstraintSystemFlags {
17741775

17751776
/// When set, ignore async/sync mismatches
17761777
IgnoreAsyncSyncMismatch = 0x80,
1778+
1779+
/// Non solver-based code completion expects that closures inside result
1780+
/// builders don't participate in inference.
1781+
/// Once all code completion kinds are migrated to solver-based we should be
1782+
/// able to remove this flag.
1783+
ClosuresInResultBuildersDontParticipateInInference = 0x100,
17771784
};
17781785

17791786
/// Options that affect the constraint system as a whole.
@@ -3576,8 +3583,9 @@ class ConstraintSystem {
35763583

35773584
// FIXME: Perhaps these belong on ConstraintSystem itself.
35783585
friend Optional<BraceStmt *>
3579-
swift::TypeChecker::applyResultBuilderBodyTransform(FuncDecl *func,
3580-
Type builderType);
3586+
swift::TypeChecker::applyResultBuilderBodyTransform(
3587+
FuncDecl *func, Type builderType,
3588+
bool ClosuresInResultBuilderDontParticipateInInference);
35813589

35823590
friend Optional<SolutionApplicationTarget>
35833591
swift::TypeChecker::typeCheckExpression(

lib/Sema/BuilderTransform.cpp

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -922,7 +922,8 @@ class ResultBuilderTransform
922922
} // end anonymous namespace
923923

924924
Optional<BraceStmt *> TypeChecker::applyResultBuilderBodyTransform(
925-
FuncDecl *func, Type builderType) {
925+
FuncDecl *func, Type builderType,
926+
bool ClosuresInResultBuilderDontParticipateInInference) {
926927
// Pre-check the body: pre-check any expressions in it and look
927928
// for return statements.
928929
//
@@ -978,6 +979,10 @@ Optional<BraceStmt *> TypeChecker::applyResultBuilderBodyTransform(
978979
}
979980

980981
ConstraintSystemOptions options = ConstraintSystemFlags::AllowFixes;
982+
if (ClosuresInResultBuilderDontParticipateInInference) {
983+
options |= ConstraintSystemFlags::
984+
ClosuresInResultBuildersDontParticipateInInference;
985+
}
981986
auto resultInterfaceTy = func->getResultInterfaceType();
982987
auto resultContextType = func->mapTypeIntoContext(resultInterfaceTy);
983988

lib/Sema/CSDiagnostics.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4874,7 +4874,9 @@ bool MissingArgumentsFailure::diagnoseClosure(const ClosureExpr *closure) {
48744874

48754875
auto *locator = getLocator();
48764876
if (locator->isForContextualType()) {
4877-
funcType = getContextualType(locator->getAnchor())->getAs<FunctionType>();
4877+
if (auto type = getContextualType(locator->getAnchor())) {
4878+
funcType = type->getAs<FunctionType>();
4879+
}
48784880
} else if (auto info = getFunctionArgApplyInfo(locator)) {
48794881
auto paramType = info->getParamType();
48804882
// Drop a single layer of optionality because argument could get injected

lib/Sema/CSFix.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -602,6 +602,10 @@ bool AllowTupleTypeMismatch::coalesceAndDiagnose(
602602
purpose = cs.getContextualTypePurpose(locator->getAnchor());
603603
}
604604

605+
if (!getFromType()->is<TupleType>() || !getToType()->is<TupleType>()) {
606+
return false;
607+
}
608+
605609
TupleContextualFailure failure(solution, purpose, getFromType(), getToType(),
606610
indices, locator);
607611
return failure.diagnose(asNote);

lib/Sema/ConstraintSystem.cpp

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6892,7 +6892,13 @@ bool ConstraintSystem::participatesInInference(ClosureExpr *closure) const {
68926892

68936893
// If body is nested in a parent that has a function builder applied,
68946894
// let's prevent inference until result builders.
6895-
return !isInResultBuilderContext(closure);
6895+
if (Options.contains(
6896+
ConstraintSystemFlags::
6897+
ClosuresInResultBuildersDontParticipateInInference)) {
6898+
return !isInResultBuilderContext(closure);
6899+
} else {
6900+
return true;
6901+
}
68966902
}
68976903

68986904
TypeVarBindingProducer::TypeVarBindingProducer(BindingSet &bindings)

lib/Sema/TypeCheckStmt.cpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1946,8 +1946,10 @@ bool TypeCheckASTNodeAtLocRequest::evaluate(
19461946
// Function builder function doesn't support partial type checking.
19471947
if (auto *func = dyn_cast<FuncDecl>(DC)) {
19481948
if (Type builderType = getResultBuilderType(func)) {
1949-
auto optBody =
1950-
TypeChecker::applyResultBuilderBodyTransform(func, builderType);
1949+
auto optBody = TypeChecker::applyResultBuilderBodyTransform(
1950+
func, builderType,
1951+
/*ClosuresInResultBuilderDontParticipateInInference=*/
1952+
ctx.CompletionCallback == nullptr);
19511953
if (optBody && *optBody) {
19521954
// Wire up the function body now.
19531955
func->setBody(*optBody, AbstractFunctionDecl::BodyKind::TypeChecked);

lib/Sema/TypeChecker.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -451,8 +451,9 @@ void typeCheckASTNode(ASTNode &node, DeclContext *DC,
451451
/// e.g., because of a \c return statement. Otherwise, returns either the
452452
/// fully type-checked body of the function (on success) or a \c nullptr
453453
/// value if an error occurred while type checking the transformed body.
454-
Optional<BraceStmt *> applyResultBuilderBodyTransform(FuncDecl *func,
455-
Type builderType);
454+
Optional<BraceStmt *> applyResultBuilderBodyTransform(
455+
FuncDecl *func, Type builderType,
456+
bool ClosuresInResultBuilderDontParticipateInInference = true);
456457

457458
/// Find the return statements within the body of the given function.
458459
std::vector<ReturnStmt *> findReturnStatements(AnyFunctionRef fn);

test/Constraints/result_builder_diags.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ struct TupleBuilderWithoutIf { // expected-note 3{{struct 'TupleBuilderWithoutIf
7878
static func buildDo<T>(_ value: T) -> T { return value }
7979
}
8080

81-
func tuplify<T>(_ cond: Bool, @TupleBuilder body: (Bool) -> T) {
81+
func tuplify<T>(_ cond: Bool, @TupleBuilder body: (Bool) -> T) { // expected-note 3 {{in call to function 'tuplify(_:body:)'}}
8282
print(body(cond))
8383
}
8484

@@ -88,7 +88,7 @@ func tuplifyWithoutIf<T>(_ cond: Bool, @TupleBuilderWithoutIf body: (Bool) -> T)
8888

8989
func testDiags() {
9090
// For loop
91-
tuplify(true) { _ in
91+
tuplify(true) { _ in // expected-error {{generic parameter 'T' could not be inferred}}
9292
17
9393
for c in name {
9494
// expected-error@-1 {{cannot find 'name' in scope}}
@@ -466,7 +466,7 @@ struct TestConstraintGenerationErrors {
466466
}
467467

468468
func buildTupleClosure() {
469-
tuplify(true) { _ in
469+
tuplify(true) { _ in // expected-error {{generic parameter 'T' could not be inferred}}
470470
let a = nothing // expected-error {{cannot find 'nothing' in scope}}
471471
String(nothing) // expected-error {{cannot find 'nothing' in scope}}
472472
}
@@ -733,7 +733,7 @@ struct TuplifiedStructWithInvalidClosure {
733733
}
734734

735735
@TupleBuilder var nestedErrorsDiagnosedByParser: some Any {
736-
tuplify(true) { _ in
736+
tuplify(true) { _ in // expected-error {{generic parameter 'T' could not be inferred}}
737737
tuplify { _ in
738738
self. // expected-error {{expected member name following '.'}}
739739
}

test/Parse/recovery.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -773,7 +773,7 @@ class r22240342 {
773773
lazy var xx: Int = {
774774
foo { // expected-error {{cannot find 'foo' in scope}}
775775
let issueView = 42
776-
issueView.delegate = 12
776+
issueView.delegate = 12 // expected-error {{value of type 'Int' has no member 'delegate'}}
777777

778778
}
779779
return 42

test/StringProcessing/Sema/regex_builder_fix_import_top_level.swift

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,7 @@ Regex { // expected-error {{regex builder requires the 'RegexBuilder' module be
1919
/g(h)(i)/
2020
}
2121

22-
// FIXME: Unfortunately we bail from CSGen if we end up with an ErrorExpr, so
23-
// don't get a chance to diagnose. We ought to try solving with holes.
24-
// For now at least, this error should at least hopefully nudge users into
25-
// realizing they have a missing import.
26-
Regex {
22+
Regex { // expected-error {{regex builder requires the 'RegexBuilder' module be imported'}}
2723
Capture { // expected-error {{cannot find 'Capture' in scope}}
2824
/abc/
2925
}

0 commit comments

Comments
 (0)