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
@@ -1534,77 +1632,30 @@ static void determineBestChoicesInContext(
1534
1632
}
1535
1633
}
1536
1634
1537
- // / Prioritize `build{Block, Expression, ...}` and any chained
1538
- // / members that are connected to individual builder elements
1539
- // / i.e. `ForEach(...) { ... }.padding(...)`, once `ForEach`
1540
- // / is resolved, `padding` should be prioritized because its
1541
- // / requirements can help prune the solution space before the
1542
- // / body is checked.
1543
- static Constraint *
1544
- selectDisjunctionInResultBuilderContext (ConstraintSystem &cs,
1545
- ArrayRef<Constraint *> disjunctions) {
1546
- auto context = AnyFunctionRef::fromDeclContext (cs.DC );
1547
- if (!context)
1548
- return nullptr ;
1549
-
1550
- if (!cs.getAppliedResultBuilderTransform (context.value ()))
1551
- return nullptr ;
1552
-
1553
- std::pair<Constraint *, unsigned > best{nullptr , 0 };
1554
- for (auto *disjunction : disjunctions) {
1555
- auto *member =
1556
- getAsExpr<UnresolvedDotExpr>(disjunction->getLocator ()->getAnchor ());
1557
- if (!member)
1558
- continue ;
1559
-
1560
- // Attempt `build{Block, Expression, ...} first because they
1561
- // provide contextual information for the inner calls.
1562
- if (isResultBuilderMethodReference (cs.getASTContext (), member))
1563
- return disjunction;
1564
-
1565
- Expr *curr = member;
1566
- bool disqualified = false ;
1567
- // Walk up the parent expression chain and check whether this
1568
- // disjunction represents one of the members in a chain that
1569
- // leads up to `buildExpression` (if defined by the builder)
1570
- // or to a pattern binding for `$__builderN` (the walk won't
1571
- // find any argument position locations in that case).
1572
- while (auto parent = cs.getParentExpr (curr)) {
1573
- if (!(isExpr<CallExpr>(parent) || isExpr<UnresolvedDotExpr>(parent))) {
1574
- disqualified = true ;
1575
- break ;
1576
- }
1577
-
1578
- if (auto *call = getAsExpr<CallExpr>(parent)) {
1579
- // The current parent appears in an argument position.
1580
- if (call->getFn () != curr) {
1581
- // Allow expressions that appear in a argument position to
1582
- // `build{Expression, Block, ...} methods.
1583
- if (auto *UDE = getAsExpr<UnresolvedDotExpr>(call->getFn ())) {
1584
- disqualified =
1585
- !isResultBuilderMethodReference (cs.getASTContext (), UDE);
1586
- } else {
1587
- disqualified = true ;
1588
- }
1589
- }
1590
- }
1591
-
1592
- if (disqualified)
1593
- break ;
1594
-
1595
- curr = parent;
1596
- }
1597
-
1598
- if (disqualified)
1599
- continue ;
1635
+ static std::optional<bool > isPreferable (ConstraintSystem &cs,
1636
+ Constraint *disjunctionA,
1637
+ Constraint *disjunctionB) {
1638
+ // Consider only operator vs. non-operator situations.
1639
+ if (isOperatorDisjunction (disjunctionA) ==
1640
+ isOperatorDisjunction (disjunctionB))
1641
+ return std::nullopt;
1600
1642
1601
- if (auto depth = cs.getExprDepth (member)) {
1602
- if (!best.first || best.second > depth)
1603
- best = std::make_pair (disjunction, depth.value ());
1643
+ // Prevent operator selection if its passed as an argument
1644
+ // to not-yet resolved call. This helps to make sure that
1645
+ // in result builder context chained members and other
1646
+ // non-operator disjunctions are always selected first,
1647
+ // because they provide the context and help to prune the system.
1648
+ if (isInResultBuilderContext (cs, disjunctionA)) {
1649
+ if (isOperatorDisjunction (disjunctionA)) {
1650
+ if (isOperatorPassedToUnresolvedCall (cs, disjunctionA))
1651
+ return false ;
1652
+ } else {
1653
+ if (isOperatorPassedToUnresolvedCall (cs, disjunctionB))
1654
+ return true ;
1604
1655
}
1605
1656
}
1606
1657
1607
- return best. first ;
1658
+ return std::nullopt ;
1608
1659
}
1609
1660
1610
1661
std::optional<std::pair<Constraint *, llvm::TinyPtrVector<Constraint *>>>
@@ -1618,11 +1669,6 @@ ConstraintSystem::selectDisjunction() {
1618
1669
llvm::DenseMap<Constraint *, DisjunctionInfo> favorings;
1619
1670
determineBestChoicesInContext (*this , disjunctions, favorings);
1620
1671
1621
- if (auto *disjunction =
1622
- selectDisjunctionInResultBuilderContext (*this , disjunctions)) {
1623
- return std::make_pair (disjunction, favorings[disjunction].FavoredChoices );
1624
- }
1625
-
1626
1672
// Pick the disjunction with the smallest number of favored, then active
1627
1673
// choices.
1628
1674
auto bestDisjunction = std::min_element (
@@ -1634,6 +1680,9 @@ ConstraintSystem::selectDisjunction() {
1634
1680
if (firstActive == 1 || secondActive == 1 )
1635
1681
return secondActive != 1 ;
1636
1682
1683
+ if (auto preference = isPreferable (*this , first, second))
1684
+ return preference.value ();
1685
+
1637
1686
auto &[firstScore, firstFavoredChoices] = favorings[first];
1638
1687
auto &[secondScore, secondFavoredChoices] = favorings[second];
1639
1688
0 commit comments