Skip to content

Commit 56d6635

Browse files
committed
[CSOptimizer] Implement special prioritization rules for result builder contexts
Prioritize `build{Block, Expression, ...}` and any chained members that are connected to individual builder elements i.e. `ForEach(...) { ... }.padding(...)`, once `ForEach` is resolved, `padding` should be prioritized because its requirements can help prune the solution space before the body is checked.
1 parent bf8ae3b commit 56d6635

File tree

2 files changed

+95
-0
lines changed

2 files changed

+95
-0
lines changed

lib/Sema/CSOptimizer.cpp

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1097,6 +1097,79 @@ selectBestBindingDisjunction(ConstraintSystem &cs,
10971097
return firstBindDisjunction;
10981098
}
10991099

1100+
/// Prioritize `build{Block, Expression, ...}` and any chained
1101+
/// members that are connected to individual builder elements
1102+
/// i.e. `ForEach(...) { ... }.padding(...)`, once `ForEach`
1103+
/// is resolved, `padding` should be prioritized because its
1104+
/// requirements can help prune the solution space before the
1105+
/// body is checked.
1106+
static Constraint *
1107+
selectDisjunctionInResultBuilderContext(ConstraintSystem &cs,
1108+
ArrayRef<Constraint *> disjunctions) {
1109+
auto context = AnyFunctionRef::fromDeclContext(cs.DC);
1110+
if (!context)
1111+
return nullptr;
1112+
1113+
if (!cs.getAppliedResultBuilderTransform(context.value()))
1114+
return nullptr;
1115+
1116+
std::pair<Constraint *, unsigned> best{nullptr, 0};
1117+
for (auto *disjunction : disjunctions) {
1118+
auto *member =
1119+
getAsExpr<UnresolvedDotExpr>(disjunction->getLocator()->getAnchor());
1120+
if (!member)
1121+
continue;
1122+
1123+
// Attempt `build{Block, Expression, ...} first because they
1124+
// provide contextual information for the inner calls.
1125+
if (isResultBuilderMethodReference(cs.getASTContext(), member))
1126+
return disjunction;
1127+
1128+
Expr *curr = member;
1129+
bool disqualified = false;
1130+
// Walk up the parent expression chain and check whether this
1131+
// disjunction represents one of the members in a chain that
1132+
// leads up to `buildExpression` (if defined by the builder)
1133+
// or to a pattern binding for `$__builderN` (the walk won't
1134+
// find any argument position locations in that case).
1135+
while (auto parent = cs.getParentExpr(curr)) {
1136+
if (!(isExpr<CallExpr>(parent) || isExpr<UnresolvedDotExpr>(parent))) {
1137+
disqualified = true;
1138+
break;
1139+
}
1140+
1141+
if (auto *call = getAsExpr<CallExpr>(parent)) {
1142+
// The current parent appears in an argument position.
1143+
if (call->getFn() != curr) {
1144+
// Allow expressions that appear in a argument position to
1145+
// `build{Expression, Block, ...} methods.
1146+
if (auto *UDE = getAsExpr<UnresolvedDotExpr>(call->getFn())) {
1147+
disqualified =
1148+
!isResultBuilderMethodReference(cs.getASTContext(), UDE);
1149+
} else {
1150+
disqualified = true;
1151+
}
1152+
}
1153+
}
1154+
1155+
if (disqualified)
1156+
break;
1157+
1158+
curr = parent;
1159+
}
1160+
1161+
if (disqualified)
1162+
continue;
1163+
1164+
if (auto depth = cs.getExprDepth(member)) {
1165+
if (!best.first || best.second > depth)
1166+
best = std::make_pair(disjunction, depth.value());
1167+
}
1168+
}
1169+
1170+
return best.first;
1171+
}
1172+
11001173
std::optional<std::pair<Constraint *, llvm::TinyPtrVector<Constraint *>>>
11011174
ConstraintSystem::selectDisjunction() {
11021175
SmallVector<Constraint *, 4> disjunctions;
@@ -1111,6 +1184,11 @@ ConstraintSystem::selectDisjunction() {
11111184
llvm::DenseMap<Constraint *, DisjunctionInfo> favorings;
11121185
determineBestChoicesInContext(*this, disjunctions, favorings);
11131186

1187+
if (auto *disjunction =
1188+
selectDisjunctionInResultBuilderContext(*this, disjunctions)) {
1189+
return std::make_pair(disjunction, favorings[disjunction].FavoredChoices);
1190+
}
1191+
11141192
// Pick the disjunction with the smallest number of favored, then active
11151193
// choices.
11161194
auto bestDisjunction = std::min_element(
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
// RUN: %target-typecheck-verify-swift -target %target-cpu-apple-macosx10.15 -swift-version 5
2+
// REQUIRES: OS=macosx
3+
4+
import SwiftUI
5+
6+
func test(a: [(offset: Int, element: Double)],
7+
c: Color,
8+
x: CGFloat,
9+
n: Int
10+
) -> some View {
11+
ForEach(a, id: \.offset) { i, r in
12+
RoundedRectangle(cornerRadius: r, style: .continuous)
13+
.stroke(c, lineWidth: 1)
14+
.padding(.horizontal, x / Double(n) * Double(n - 1 - i) / 2)
15+
.padding(.vertical, x / Double(n) * Double(n - 1 - i) / 2)
16+
}
17+
}

0 commit comments

Comments
 (0)