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