Skip to content

Commit 4830c48

Browse files
committed
[Constraint system] Support if let / if case in function builders.
Use the generalized constraint generation and binding for patterns to introduce support for if-let and if-case in function builders, handling arbitrary patterns. Part of function builder generalization, rdar://problem/50426203.
1 parent 3f2f79a commit 4830c48

File tree

5 files changed

+84
-39
lines changed

5 files changed

+84
-39
lines changed

lib/Sema/BuilderTransform.cpp

Lines changed: 18 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -376,10 +376,6 @@ class BuilderClosureVisitor
376376
static bool isBuildableIfChainRecursive(IfStmt *ifStmt,
377377
unsigned &numPayloads,
378378
bool &isOptional) {
379-
// Check whether we can handle the conditional.
380-
if (!ConstraintSystem::canGenerateConstraints(ifStmt->getCond()))
381-
return false;
382-
383379
// The 'then' clause contributes a payload.
384380
numPayloads++;
385381

@@ -447,6 +443,14 @@ class BuilderClosureVisitor
447443
unsigned numPayloads, bool isOptional,
448444
bool isTopLevel = false) {
449445
assert(payloadIndex < numPayloads);
446+
447+
// First generate constraints for the conditions. This can introduce
448+
// variable bindings that will be used within the "then" branch.
449+
if (cs && cs->generateConstraints(ifStmt->getCond(), dc)) {
450+
hadError = true;
451+
return nullptr;
452+
}
453+
450454
// Make sure we recursively visit both sides even if we're not
451455
// building expressions.
452456

@@ -503,12 +507,6 @@ class BuilderClosureVisitor
503507
payloadIndex + 1, numPayloads, isOptional);
504508
}
505509

506-
// Generate constraints for the conditions.
507-
if (cs->generateConstraints(ifStmt->getCond(), dc)) {
508-
hadError = true;
509-
return nullptr;
510-
}
511-
512510
// The operand should have optional type if we had optional results,
513511
// so we just need to call `buildIf` now, since we're at the top level.
514512
if (isOptional && isTopLevel) {
@@ -941,8 +939,16 @@ class BuilderClosureRewriter
941939
continue;
942940
}
943941

944-
case StmtConditionElement::CK_PatternBinding:
945-
llvm_unreachable("unhandled statement condition");
942+
case StmtConditionElement::CK_PatternBinding: {
943+
ConstraintSystem &cs = solution.getConstraintSystem();
944+
auto target = *cs.getStmtConditionTarget(&condElement);
945+
auto resolvedTarget = rewriteTarget(target);
946+
if (resolvedTarget) {
947+
condElement.setInitializer(resolvedTarget->getAsExpr());
948+
condElement.setPattern(resolvedTarget->getInitializationPattern());
949+
}
950+
continue;
951+
}
946952
}
947953
}
948954
ifStmt->setCond(condition);

lib/Sema/CSGen.cpp

Lines changed: 15 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -4166,21 +4166,6 @@ Type ConstraintSystem::generateConstraints(Pattern *pattern,
41664166
return cg.getTypeForPattern(pattern, locator, Type(), bindPatternVarsOneWay);
41674167
}
41684168

4169-
bool ConstraintSystem::canGenerateConstraints(StmtCondition condition) {
4170-
for (const auto &element : condition) {
4171-
switch (element.getKind()) {
4172-
case StmtConditionElement::CK_Availability:
4173-
case StmtConditionElement::CK_Boolean:
4174-
continue;
4175-
4176-
case StmtConditionElement::CK_PatternBinding:
4177-
return false;
4178-
}
4179-
}
4180-
4181-
return true;
4182-
}
4183-
41844169
bool ConstraintSystem::generateConstraints(StmtCondition condition,
41854170
DeclContext *dc) {
41864171
// FIXME: This should be folded into constraint generation for conditions.
@@ -4213,8 +4198,21 @@ bool ConstraintSystem::generateConstraints(StmtCondition condition,
42134198
continue;
42144199
}
42154200

4216-
case StmtConditionElement::CK_PatternBinding:
4217-
llvm_unreachable("unhandled statement condition");
4201+
case StmtConditionElement::CK_PatternBinding: {
4202+
auto *pattern = TypeChecker::resolvePattern(
4203+
condElement.getPattern(), dc, /*isStmtCondition*/true);
4204+
if (!pattern)
4205+
return true;
4206+
4207+
auto target = SolutionApplicationTarget::forInitialization(
4208+
condElement.getInitializer(), dc, Type(),
4209+
pattern, /*bindPatternVarsOneWay=*/true);
4210+
if (generateConstraints(target, FreeTypeVariableBinding::Disallow))
4211+
return true;
4212+
4213+
setStmtConditionTarget(&condElement, target);
4214+
continue;
4215+
}
42184216
}
42194217
}
42204218

lib/Sema/ConstraintSystem.h

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3283,10 +3283,6 @@ class ConstraintSystem {
32833283
Type generateConstraints(Pattern *P, ConstraintLocatorBuilder locator,
32843284
bool bindPatternVarsOneWay);
32853285

3286-
/// Determines whether we can generate constraints for this statement
3287-
/// condition.
3288-
static bool canGenerateConstraints(StmtCondition condition);
3289-
32903286
/// Generate constraints for a statement condition.
32913287
///
32923288
/// \returns true if there was an error in constraint generation, false

test/Constraints/function_builder.swift

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -491,3 +491,48 @@ tuplify(true) { c in
491491
// CHECK-SAME: hello
492492
// CHECK-SAME: true
493493

494+
// Use if let / if case with various forms of decomposition.
495+
func getOptionalInt(_: Bool) -> Int? { return 25 }
496+
497+
enum E {
498+
case a
499+
case b(Int, String?)
500+
}
501+
502+
func getE(_ i: Int) -> E {
503+
switch i {
504+
case 0:
505+
return .a
506+
case 1:
507+
return .b(17, "hello")
508+
case 2:
509+
return .b(42, nil)
510+
default:
511+
fatalError("Unhandled case")
512+
}
513+
}
514+
515+
tuplify(true) { c in
516+
"testIfLetMatching"
517+
if let theValue = getOptionalInt(c) {
518+
theValue + 17
519+
}
520+
if case let .a = getE(0) {
521+
"matched without payload"
522+
}
523+
if case let .b(i, s?) = getE(1) {
524+
"matched with payload"
525+
s + "!"
526+
i + 17
527+
}
528+
if case let .b(i, s?) = getE(2) {
529+
fatalError("cannot match this")
530+
} else {
531+
"intentional mismatch"
532+
}
533+
}
534+
// CHECK: testIfLetMatching
535+
// CHECK-SAME: Optional(42)
536+
// CHECK-SAME: Optional("matched without payload")
537+
// CHECK-SAME: "matched with payload", "hello!", 34
538+
// CHECK-SAME: "intentional mismatch"

test/Constraints/function_builder_one_way.swift

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -50,12 +50,12 @@ func tuplify<C: Collection, T>(_ collection: C, @TupleBuilder body: (C.Element)
5050

5151
// CHECK: ---Connected components---
5252
// CHECK-NEXT: 0: $T1 $T2 $T3 $T5 $T6 $T7 $T8 $T10 $T11 $T78 $T79 depends on 2
53-
// CHECK-NEXT: 2: $T13 $T18 $T29 $T42 $T53 $T55 $T56 $T57 $T58 $T59 $T60 $T61 $T62 $T63 $T64 $T65 $T66 $T67 $T69 $T70 $T71 $T72 $T73 $T74 $T75 $T76 $T77 depends on 1, 3, 4, 6, 9
54-
// CHECK-NEXT: 9: $T48 $T49 $T50 $T51 $T52 depends on 8
55-
// CHECK-NEXT: 8: $T43 $T44 $T45 $T46 $T47
56-
// CHECK-NEXT: 6: $T31 $T35 $T36 $T37 $T38 $T39 $T40 $T41 depends on 5, 7
57-
// CHECK-NEXT: 7: $T32 $T33 $T34
58-
// CHECK-NEXT: 5: $T30
53+
// CHECK-NEXT: 2: $T13 $T18 $T29 $T43 $T54 $T55 $T56 $T57 $T58 $T59 $T60 $T61 $T62 $T63 $T64 $T65 $T66 $T67 $T69 $T70 $T71 $T72 $T73 $T74 $T75 $T76 $T77 depends on 1, 3, 4, 6, 9
54+
// CHECK-NEXT: 9: $T49 $T50 $T51 $T52 $T53 depends on 8
55+
// CHECK-NEXT: 8: $T44 $T45 $T46 $T47 $T48
56+
// CHECK-NEXT: 6: $T32 $T36 $T37 $T38 $T39 $T40 $T41 $T42 depends on 5, 7
57+
// CHECK-NEXT: 7: $T33 $T34 $T35
58+
// CHECK-NEXT: 5: $T31
5959
// CHECK-NEXT: 4: $T19 $T20 $T21 $T22 $T23 $T24 $T25 $T26 $T27 $T28
6060
// CHECK-NEXT: 3: $T16 $T17
6161
// CHECK-NEXT: 1: $T12

0 commit comments

Comments
 (0)