18
18
#include " swift/AST/ConformanceLookup.h"
19
19
#include " swift/AST/ExistentialLayout.h"
20
20
#include " swift/AST/GenericSignature.h"
21
+ #include " swift/Basic/Defer.h"
21
22
#include " swift/Basic/OptionSet.h"
22
23
#include " swift/Sema/ConstraintGraph.h"
23
24
#include " swift/Sema/ConstraintSystem.h"
@@ -49,6 +50,103 @@ struct DisjunctionInfo {
49
50
: Score(score), FavoredChoices(favoredChoices) {}
50
51
};
51
52
53
+ static DeclContext *getDisjunctionDC (Constraint *disjunction) {
54
+ auto *choice = disjunction->getNestedConstraints ()[0 ];
55
+ switch (choice->getKind ()) {
56
+ case ConstraintKind::BindOverload:
57
+ return choice->getOverloadUseDC ();
58
+ case ConstraintKind::ValueMember:
59
+ case ConstraintKind::UnresolvedValueMember:
60
+ case ConstraintKind::ValueWitness:
61
+ return choice->getMemberUseDC ();
62
+ default :
63
+ return nullptr ;
64
+ }
65
+ }
66
+
67
+ // / Determine whether the given disjunction appears in a context
68
+ // / transformed by a result builder.
69
+ static bool isInResultBuilderContext (ConstraintSystem &cs,
70
+ Constraint *disjunction) {
71
+ auto *DC = getDisjunctionDC (disjunction);
72
+ if (!DC)
73
+ return false ;
74
+
75
+ do {
76
+ auto fnContext = AnyFunctionRef::fromDeclContext (DC);
77
+ if (!fnContext)
78
+ return false ;
79
+
80
+ if (cs.getAppliedResultBuilderTransform (*fnContext))
81
+ return true ;
82
+
83
+ } while ((DC = DC->getParent ()));
84
+
85
+ return false ;
86
+ }
87
+
88
+ // / If the given operator disjunction appears in some position
89
+ // inside of a not yet resolved call i.e. `a.b(1 + c(4) - 1)`
90
+ // both `+` and `-` are "in" argument context of `b`.
91
+ static bool isOperatorPassedToUnresolvedCall (ConstraintSystem &cs,
92
+ Constraint *disjunction) {
93
+ ASSERT (isOperatorDisjunction (disjunction));
94
+
95
+ auto *curr = castToExpr (disjunction->getLocator ()->getAnchor ());
96
+ while (auto *parent = cs.getParentExpr (curr)) {
97
+ SWIFT_DEFER { curr = parent; };
98
+
99
+ switch (parent->getKind ()) {
100
+ case ExprKind::OptionalEvaluation:
101
+ case ExprKind::Paren:
102
+ case ExprKind::Binary:
103
+ case ExprKind::PrefixUnary:
104
+ case ExprKind::PostfixUnary:
105
+ continue ;
106
+
107
+ // a.b(<<cond>> ? <<operator chain>> : <<...>>)
108
+ case ExprKind::Ternary: {
109
+ auto *T = cast<TernaryExpr>(parent);
110
+ // If the operator is located in the condition it's
111
+ // not tied to the context.
112
+ if (T->getCondExpr () == curr)
113
+ return false ;
114
+
115
+ // But the branches are connected to the context.
116
+ continue ;
117
+ }
118
+
119
+ // Handles `a(<<operator chain>>), `a[<<operator chain>>]`,
120
+ // `.a(<<operator chain>>)` etc.
121
+ case ExprKind::Call: {
122
+ auto *call = cast<CallExpr>(parent);
123
+
124
+ // Type(...)
125
+ if (isa<TypeExpr>(call->getFn ())) {
126
+ auto *ctorLoc = cs.getConstraintLocator (
127
+ call, {LocatorPathElt::ApplyFunction (),
128
+ LocatorPathElt::ConstructorMember ()});
129
+ return !cs.findSelectedOverloadFor (ctorLoc);
130
+ }
131
+
132
+ // Ignore injected result builder methods like `buildExpression`
133
+ // and `buildBlock`.
134
+ if (auto *UDE = dyn_cast<UnresolvedDotExpr>(call->getFn ())) {
135
+ if (isResultBuilderMethodReference (cs.getASTContext (), UDE))
136
+ return false ;
137
+ }
138
+
139
+ return !cs.findSelectedOverloadFor (call->getFn ());
140
+ }
141
+
142
+ default :
143
+ return false ;
144
+ }
145
+ }
146
+
147
+ return false ;
148
+ }
149
+
52
150
// TODO: both `isIntegerType` and `isFloatType` should be available on Type
53
151
// as `isStdlib{Integer, Float}Type`.
54
152
@@ -1542,77 +1640,30 @@ static void determineBestChoicesInContext(
1542
1640
}
1543
1641
}
1544
1642
1545
- // / Prioritize `build{Block, Expression, ...}` and any chained
1546
- // / members that are connected to individual builder elements
1547
- // / i.e. `ForEach(...) { ... }.padding(...)`, once `ForEach`
1548
- // / is resolved, `padding` should be prioritized because its
1549
- // / requirements can help prune the solution space before the
1550
- // / body is checked.
1551
- static Constraint *
1552
- selectDisjunctionInResultBuilderContext (ConstraintSystem &cs,
1553
- ArrayRef<Constraint *> disjunctions) {
1554
- auto context = AnyFunctionRef::fromDeclContext (cs.DC );
1555
- if (!context)
1556
- return nullptr ;
1557
-
1558
- if (!cs.getAppliedResultBuilderTransform (context.value ()))
1559
- return nullptr ;
1560
-
1561
- std::pair<Constraint *, unsigned > best{nullptr , 0 };
1562
- for (auto *disjunction : disjunctions) {
1563
- auto *member =
1564
- getAsExpr<UnresolvedDotExpr>(disjunction->getLocator ()->getAnchor ());
1565
- if (!member)
1566
- continue ;
1567
-
1568
- // Attempt `build{Block, Expression, ...} first because they
1569
- // provide contextual information for the inner calls.
1570
- if (isResultBuilderMethodReference (cs.getASTContext (), member))
1571
- return disjunction;
1572
-
1573
- Expr *curr = member;
1574
- bool disqualified = false ;
1575
- // Walk up the parent expression chain and check whether this
1576
- // disjunction represents one of the members in a chain that
1577
- // leads up to `buildExpression` (if defined by the builder)
1578
- // or to a pattern binding for `$__builderN` (the walk won't
1579
- // find any argument position locations in that case).
1580
- while (auto parent = cs.getParentExpr (curr)) {
1581
- if (!(isExpr<CallExpr>(parent) || isExpr<UnresolvedDotExpr>(parent))) {
1582
- disqualified = true ;
1583
- break ;
1584
- }
1585
-
1586
- if (auto *call = getAsExpr<CallExpr>(parent)) {
1587
- // The current parent appears in an argument position.
1588
- if (call->getFn () != curr) {
1589
- // Allow expressions that appear in a argument position to
1590
- // `build{Expression, Block, ...} methods.
1591
- if (auto *UDE = getAsExpr<UnresolvedDotExpr>(call->getFn ())) {
1592
- disqualified =
1593
- !isResultBuilderMethodReference (cs.getASTContext (), UDE);
1594
- } else {
1595
- disqualified = true ;
1596
- }
1597
- }
1598
- }
1599
-
1600
- if (disqualified)
1601
- break ;
1602
-
1603
- curr = parent;
1604
- }
1605
-
1606
- if (disqualified)
1607
- continue ;
1643
+ static std::optional<bool > isPreferable (ConstraintSystem &cs,
1644
+ Constraint *disjunctionA,
1645
+ Constraint *disjunctionB) {
1646
+ // Consider only operator vs. non-operator situations.
1647
+ if (isOperatorDisjunction (disjunctionA) ==
1648
+ isOperatorDisjunction (disjunctionB))
1649
+ return std::nullopt;
1608
1650
1609
- if (auto depth = cs.getExprDepth (member)) {
1610
- if (!best.first || best.second > depth)
1611
- best = std::make_pair (disjunction, depth.value ());
1651
+ // Prevent operator selection if its passed as an argument
1652
+ // to not-yet resolved call. This helps to make sure that
1653
+ // in result builder context chained members and other
1654
+ // non-operator disjunctions are always selected first,
1655
+ // because they provide the context and help to prune the system.
1656
+ if (isInResultBuilderContext (cs, disjunctionA)) {
1657
+ if (isOperatorDisjunction (disjunctionA)) {
1658
+ if (isOperatorPassedToUnresolvedCall (cs, disjunctionA))
1659
+ return false ;
1660
+ } else {
1661
+ if (isOperatorPassedToUnresolvedCall (cs, disjunctionB))
1662
+ return true ;
1612
1663
}
1613
1664
}
1614
1665
1615
- return best. first ;
1666
+ return std::nullopt ;
1616
1667
}
1617
1668
1618
1669
std::optional<std::pair<Constraint *, llvm::TinyPtrVector<Constraint *>>>
@@ -1626,11 +1677,6 @@ ConstraintSystem::selectDisjunction() {
1626
1677
llvm::DenseMap<Constraint *, DisjunctionInfo> favorings;
1627
1678
determineBestChoicesInContext (*this , disjunctions, favorings);
1628
1679
1629
- if (auto *disjunction =
1630
- selectDisjunctionInResultBuilderContext (*this , disjunctions)) {
1631
- return std::make_pair (disjunction, favorings[disjunction].FavoredChoices );
1632
- }
1633
-
1634
1680
// Pick the disjunction with the smallest number of favored, then active
1635
1681
// choices.
1636
1682
auto bestDisjunction = std::min_element (
@@ -1642,6 +1688,9 @@ ConstraintSystem::selectDisjunction() {
1642
1688
if (firstActive == 1 || secondActive == 1 )
1643
1689
return secondActive != 1 ;
1644
1690
1691
+ if (auto preference = isPreferable (*this , first, second))
1692
+ return preference.value ();
1693
+
1645
1694
auto &[firstScore, firstFavoredChoices] = favorings[first];
1646
1695
auto &[secondScore, secondFavoredChoices] = favorings[second];
1647
1696
0 commit comments