Skip to content

Commit a9d3df0

Browse files
authored
Merge pull request #74352 from hamishknight/complete-disjunction
[Completion] Skip `tryOptimizeGenericDisjunction` if there's a completion child
2 parents 2f27865 + 7c7e914 commit a9d3df0

File tree

5 files changed

+67
-16
lines changed

5 files changed

+67
-16
lines changed

include/swift/Sema/ConstraintSystem.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2826,6 +2826,10 @@ class ConstraintSystem {
28262826
/// Associate an argument list with a call at a given locator.
28272827
void associateArgumentList(ConstraintLocator *locator, ArgumentList *args);
28282828

2829+
/// If the given node is a function expression with a parent ApplyExpr,
2830+
/// returns the apply, otherwise returns the node itself.
2831+
ASTNode includingParentApply(ASTNode node);
2832+
28292833
std::optional<SelectedOverload>
28302834
findSelectedOverloadFor(ConstraintLocator *locator) const {
28312835
auto result = ResolvedOverloads.find(locator);

lib/Sema/CSRanking.cpp

Lines changed: 3 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1036,18 +1036,9 @@ SolutionCompareResult ConstraintSystem::compareSolutions(
10361036
if (cs.isForCodeCompletion()) {
10371037
// Don't rank based on overload choices of function calls that contain the
10381038
// code completion token.
1039-
ASTNode anchor = simplifyLocatorToAnchor(overload.locator);
1040-
if (auto expr = getAsExpr(anchor)) {
1041-
// If the anchor is a called function, also don't rank overload choices
1042-
// if any of the arguments contain the code completion token.
1043-
if (auto apply = dyn_cast_or_null<ApplyExpr>(cs.getParentExpr(expr))) {
1044-
if (apply->getFn() == expr) {
1045-
anchor = apply;
1046-
}
1047-
}
1048-
}
1049-
if (anchor && cs.containsIDEInspectionTarget(anchor)) {
1050-
continue;
1039+
if (auto anchor = simplifyLocatorToAnchor(overload.locator)) {
1040+
if (cs.containsIDEInspectionTarget(cs.includingParentApply(anchor)))
1041+
continue;
10511042
}
10521043
}
10531044

lib/Sema/CSSolver.cpp

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2064,9 +2064,20 @@ Constraint *ConstraintSystem::getUnboundBindOverloadDisjunction(
20642064

20652065
// Performance hack: if there are two generic overloads, and one is
20662066
// more specialized than the other, prefer the more-specialized one.
2067-
static Constraint *tryOptimizeGenericDisjunction(
2068-
DeclContext *dc,
2069-
ArrayRef<Constraint *> constraints) {
2067+
static Constraint *
2068+
tryOptimizeGenericDisjunction(ConstraintSystem &cs, Constraint *disjunction,
2069+
ArrayRef<Constraint *> constraints) {
2070+
auto *dc = cs.DC;
2071+
2072+
// If we're solving for code completion, and have a child completion token,
2073+
// skip this optimization since the completion token being a placeholder can
2074+
// allow us to prefer an unhelpful disjunction choice.
2075+
if (cs.isForCodeCompletion()) {
2076+
auto anchor = disjunction->getLocator()->getAnchor();
2077+
if (cs.containsIDEInspectionTarget(cs.includingParentApply(anchor)))
2078+
return nullptr;
2079+
}
2080+
20702081
llvm::SmallVector<Constraint *, 4> choices;
20712082
for (auto *choice : constraints) {
20722083
if (choices.size() > 2)
@@ -2311,7 +2322,7 @@ void DisjunctionChoiceProducer::partitionDisjunction(
23112322
SmallVectorImpl<unsigned> &PartitionBeginning) {
23122323
// Apply a special-case rule for favoring one generic function over
23132324
// another.
2314-
if (auto favored = tryOptimizeGenericDisjunction(CS.DC, Choices)) {
2325+
if (auto favored = tryOptimizeGenericDisjunction(CS, Disjunction, Choices)) {
23152326
CS.favorConstraint(favored);
23162327
}
23172328

lib/Sema/ConstraintSystem.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6667,6 +6667,16 @@ static bool shouldHaveDirectCalleeOverload(const CallExpr *callExpr) {
66676667
}
66686668
#endif
66696669

6670+
ASTNode ConstraintSystem::includingParentApply(ASTNode node) {
6671+
if (auto *expr = getAsExpr(node)) {
6672+
if (auto apply = getAsExpr<ApplyExpr>(getParentExpr(expr))) {
6673+
if (apply->getFn() == expr)
6674+
return apply;
6675+
}
6676+
}
6677+
return node;
6678+
}
6679+
66706680
Type Solution::resolveInterfaceType(Type type) const {
66716681
auto resolvedType = type.transform([&](Type type) -> Type {
66726682
if (auto *tvt = type->getAs<TypeVariableType>()) {

test/IDE/complete_rdar127844278.swift

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
// RUN: %batch-code-completion
2+
3+
// rdar://127844278 - Make sure we don't attempt to favor one buildExpression
4+
// call over the other when there's a child code completion.
5+
6+
protocol P {}
7+
protocol Q: P {}
8+
9+
struct S<T>: P {
10+
private init() {}
11+
}
12+
13+
extension S where T == () {
14+
init(a: Void) {}
15+
}
16+
extension S: Q where T == String {
17+
init(b: String) {}
18+
}
19+
20+
@resultBuilder struct Builder {
21+
static func buildExpression<T: P>(_ x: T) -> T { x }
22+
static func buildExpression<T: Q>(_ x: T) -> T { x }
23+
24+
static func buildBlock<T: P>(_ x: T) -> some P { x }
25+
static func buildBlock<T: Q>(_ x: T) -> some P { x }
26+
}
27+
28+
func foo<T>(@Builder _: () -> T) where T: P {}
29+
func foo<T>(@Builder _: () -> T) where T: Q {}
30+
31+
foo {
32+
S(#^COMPLETE^#)
33+
}
34+
// COMPLETE-DAG: Decl[Constructor]/CurrNominal/Flair[ArgLabels]: ['(']{#a: Void#}[')'][#S<()>#]; name=a:
35+
// COMPLETE-DAG: Decl[Constructor]/CurrNominal/Flair[ArgLabels]: ['(']{#b: String#}[')'][#S<String>#]; name=b:

0 commit comments

Comments
 (0)