Skip to content

Commit fa7a6e6

Browse files
author
Nathan Hawes
authored
Merge pull request swiftlang#36149 from nathawes/try-to-fix-missing-enum-completions-again
[CodeCompletion][CSRanking] Still do some filtering of solutions when solving for code completion to improve performance
2 parents bb072a3 + 8a660bb commit fa7a6e6

File tree

3 files changed

+61
-8
lines changed

3 files changed

+61
-8
lines changed

include/swift/Sema/ConstraintSystem.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2800,7 +2800,7 @@ class ConstraintSystem {
28002800
void
28012801
filterSolutions(SmallVectorImpl<Solution> &solutions,
28022802
bool minimize = false) {
2803-
if (solutions.size() < 2 || isForCodeCompletion())
2803+
if (solutions.size() < 2)
28042804
return;
28052805

28062806
if (auto best = findBestSolution(solutions, minimize)) {
@@ -4827,9 +4827,11 @@ class ConstraintSystem {
48274827
/// \param diff The differences among the solutions.
48284828
/// \param idx1 The index of the first solution.
48294829
/// \param idx2 The index of the second solution.
4830+
/// \param isForCodeCompletion Whether solving for code completion.
48304831
static SolutionCompareResult
48314832
compareSolutions(ConstraintSystem &cs, ArrayRef<Solution> solutions,
4832-
const SolutionDiff &diff, unsigned idx1, unsigned idx2);
4833+
const SolutionDiff &diff, unsigned idx1, unsigned idx2,
4834+
bool isForCodeCompletion);
48334835

48344836
public:
48354837
/// Increase the score of the given kind for the current (partial) solution

lib/Sema/CSRanking.cpp

Lines changed: 42 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -757,14 +757,48 @@ static void addKeyPathDynamicMemberOverloads(
757757
}
758758
}
759759

760+
SolutionCompareResult compareSolutionsForCodeCompletion(
761+
ConstraintSystem &cs, ArrayRef<Solution> solutions, unsigned idx1,
762+
unsigned idx2) {
763+
764+
// When solving for code completion we can't consider one solution worse than
765+
// another according to the same rules as regular compilation. For example,
766+
// with the code below:
767+
//
768+
// func foo(_ x: Int) -> Int {}
769+
// func foo<T>(_ x: T) -> String {}
770+
// foo(3).<complete here> // Still want solutions with for both foo
771+
// // overloads - String and Int members are both
772+
// // valid here.
773+
//
774+
// the comparison for regular compilation considers the solution with the more
775+
// specialized `foo` overload `foo(_: Int)` to be better than the solution
776+
// with the generic overload `foo(_: T)` even though both are otherwise
777+
// viable. For code completion purposes offering members of 'String' based
778+
// on the solution with the generic overload is equally as import as offering
779+
// members of 'Int' as choosing one of those completions will then result in
780+
// regular compilation resolving the call to the generic overload instead.
781+
782+
if (solutions[idx1].getFixedScore() == solutions[idx2].getFixedScore())
783+
return SolutionCompareResult::Incomparable;
784+
return solutions[idx1].getFixedScore() < solutions[idx2].getFixedScore()
785+
? SolutionCompareResult::Better
786+
: SolutionCompareResult::Worse;
787+
}
788+
789+
760790
SolutionCompareResult ConstraintSystem::compareSolutions(
761791
ConstraintSystem &cs, ArrayRef<Solution> solutions,
762-
const SolutionDiff &diff, unsigned idx1, unsigned idx2) {
792+
const SolutionDiff &diff, unsigned idx1, unsigned idx2,
793+
bool isForCodeCompletion) {
763794
if (cs.isDebugMode()) {
764795
llvm::errs().indent(cs.solverState->depth * 2)
765796
<< "comparing solutions " << idx1 << " and " << idx2 <<"\n";
766797
}
767798

799+
if (isForCodeCompletion)
800+
return compareSolutionsForCodeCompletion(cs, solutions, idx1, idx2);
801+
768802
// Whether the solutions are identical.
769803
bool identical = true;
770804

@@ -800,7 +834,7 @@ SolutionCompareResult ConstraintSystem::compareSolutions(
800834

801835
SmallVector<SolutionDiff::OverloadDiff, 4> overloadDiff(diff.overloads);
802836
// Single type of keypath dynamic member lookup could refer to different
803-
// member overlaods, we have to do a pair-wise comparison in such cases
837+
// member overloads, we have to do a pair-wise comparison in such cases
804838
// otherwise ranking would miss some viable information e.g.
805839
// `_ = arr[0..<3]` could refer to subscript through writable or read-only
806840
// key path and each of them could also pick overload which returns `Slice<T>`
@@ -1300,7 +1334,8 @@ ConstraintSystem::findBestSolution(SmallVectorImpl<Solution> &viable,
13001334
SmallVector<bool, 16> losers(viable.size(), false);
13011335
unsigned bestIdx = 0;
13021336
for (unsigned i = 1, n = viable.size(); i != n; ++i) {
1303-
switch (compareSolutions(*this, viable, diff, i, bestIdx)) {
1337+
switch (compareSolutions(*this, viable, diff, i, bestIdx,
1338+
isForCodeCompletion())) {
13041339
case SolutionCompareResult::Identical:
13051340
// FIXME: Might want to warn about this in debug builds, so we can
13061341
// find a way to eliminate the redundancy in the search space.
@@ -1324,7 +1359,8 @@ ConstraintSystem::findBestSolution(SmallVectorImpl<Solution> &viable,
13241359
if (i == bestIdx)
13251360
continue;
13261361

1327-
switch (compareSolutions(*this, viable, diff, bestIdx, i)) {
1362+
switch (compareSolutions(*this, viable, diff, bestIdx, i,
1363+
isForCodeCompletion())) {
13281364
case SolutionCompareResult::Identical:
13291365
// FIXME: Might want to warn about this in debug builds, so we can
13301366
// find a way to eliminate the redundancy in the search space.
@@ -1376,7 +1412,8 @@ ConstraintSystem::findBestSolution(SmallVectorImpl<Solution> &viable,
13761412
if (losers[j])
13771413
continue;
13781414

1379-
switch (compareSolutions(*this, viable, diff, i, j)) {
1415+
switch (compareSolutions(*this, viable, diff, i, j,
1416+
isForCodeCompletion())) {
13801417
case SolutionCompareResult::Identical:
13811418
// FIXME: Dub one of these the loser arbitrarily?
13821419
break;

test/IDE/complete_ambiguous.swift

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@
4848
// RUN: %swift-ide-test -code-completion -source-filename %s -code-completion-token=REGULAR_MULTICLOSURE_APPLIED | %FileCheck %s --check-prefix=POINT_MEMBER
4949
// RUN: %swift-ide-test -code-completion -source-filename %s -code-completion-token=BEST_SOLUTION_FILTER | %FileCheck %s --check-prefix=BEST_SOLUTION_FILTER
5050
// RUN: %swift-ide-test -code-completion -source-filename %s -code-completion-token=BEST_SOLUTION_FILTER2 | %FileCheck %s --check-prefix=BEST_SOLUTION_FILTER
51+
// RUN: %swift-ide-test -code-completion -source-filename %s -code-completion-token=BEST_SOLUTION_FILTER_GEN | %FileCheck %s --check-prefix=BEST_SOLUTION_FILTER_GEN
5152
// RUN: %swift-ide-test -code-completion -source-filename %s -code-completion-token=MISSINGARG_INLINE | %FileCheck %s --check-prefix=MISSINGARG_INLINE
5253
// RUN: %swift-ide-test -code-completion -source-filename %s -code-completion-token=MISSINGARG_TRAILING | %FileCheck %s --check-prefix=MISSINGARG_TRAILING
5354

@@ -495,7 +496,7 @@ enum Enum123 {
495496
struct Struct123: Equatable {
496497
var structMem = Enum123.enumElem
497498
}
498-
func testNoBestSolutionFilter() {
499+
func testBestSolutionFilter() {
499500
let a = Struct123();
500501
let b = [Struct123]().first(where: { $0 == a && 1 + 90 * 5 / 8 == 45 * -10 })?.structMem != .#^BEST_SOLUTION_FILTER^#
501502
let c = min(10.3, 10 / 10.4) < 6 / 7 ? true : Optional(a)?.structMem != .#^BEST_SOLUTION_FILTER2^#
@@ -508,3 +509,16 @@ func testNoBestSolutionFilter() {
508509
// BEST_SOLUTION_FILTER-DAG: Decl[EnumElement]/CurrNominal/IsSystem/TypeRelation[Identical]: none[#Optional<Enum123>#]{{; name=.+$}}
509510
// BEST_SOLUTION_FILTER-DAG: Decl[EnumElement]/CurrNominal/IsSystem/TypeRelation[Identical]: some({#Enum123#})[#Optional<Enum123>#]{{; name=.+$}}
510511
// BEST_SOLUTION_FILTER: End completions
512+
513+
func testBestSolutionGeneric() {
514+
struct Test1 {}
515+
func genAndInt(_ x: Int) -> Int { return 1 }
516+
func genAndInt<T>(_ x: T) -> Test1 { return Test1() }
517+
518+
genAndInt(2).#^BEST_SOLUTION_FILTER_GEN^#
519+
}
520+
521+
// BEST_SOLUTION_FILTER_GEN: Begin completions
522+
// BEST_SOLUTION_FILTER_GEN-DAG: Keyword[self]/CurrNominal: self[#Int#]; name=self
523+
// BEST_SOLUTION_FILTER_GEN-DAG: Keyword[self]/CurrNominal: self[#Test1#]; name=self
524+
// BEST_SOLUTION_FILTER_GEN: End completions

0 commit comments

Comments
 (0)