Skip to content

[5.3][CodeCompletion] Force apply a viable solution even if ambiguous #32650

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 14 additions & 8 deletions lib/Sema/CSGen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1180,18 +1180,24 @@ namespace {
TVO_CanBindToLValue |
TVO_CanBindToNoEscape);

// Defaults to the type of the base expression if we have a base
// expression.
// FIXME: This is just to keep the old behavior where `foo(base.<HERE>)`
// the argument is type checked to the type of the 'base'. Ideally, code
// completion expression should be defauled to 'UnresolvedType'
// regardless of the existence of the base expression. But the constraint
// system is simply not ready for that.
if (auto base = E->getBase()) {
// Defaults to the type of the base expression if we have a base
// expression.
// FIXME: This is just to keep the old behavior where `foo(base.<HERE>)`
// the argument is type checked to the type of the 'base'. Ideally, code
// completion expression should be defauled to 'UnresolvedType'
// regardless of the existence of the base expression. But the
// constraint system is simply not ready for that.
CS.addConstraint(ConstraintKind::Defaultable, ty, CS.getType(base),
locator);

// Apply a viable solution even if it's ambiguous.
// FIXME: Remove this. This is a hack for code completion which only
// see solution applied AST. In future, code completion collects all
// viable solutions so we need to apply any solution at all.
CS.Options |= ConstraintSystemFlags::ForceApplyViableSolution;
}

return ty;
}

Expand Down
6 changes: 6 additions & 0 deletions lib/Sema/CSRanking.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1335,6 +1335,12 @@ ConstraintSystem::findBestSolution(SmallVectorImpl<Solution> &viable,
return bestIdx;
}

// FIXME: Terrible hack for code completion. But applying a solution is better
// than just failing.
if (Options.contains(ConstraintSystemFlags::ForceApplyViableSolution)) {
return bestIdx;
}

// If there is not a single "better" than others
// solution, which probably means that solutions
// were incomparable, let's just keep the original
Expand Down
5 changes: 5 additions & 0 deletions lib/Sema/ConstraintSystem.h
Original file line number Diff line number Diff line change
Expand Up @@ -1074,6 +1074,11 @@ enum class ConstraintSystemFlags {
/// If set, constraint system always reuses type of pre-typechecked
/// expression, and doesn't dig into its subexpressions.
ReusePrecheckedType = 0x08,

/// FIME: Temporary hack.
/// Force apply a viable solution even if they are ambiguous, so that the
/// client get a solution.
ForceApplyViableSolution = 0x10,
};

/// Options that affect the constraint system as a whole.
Expand Down
38 changes: 38 additions & 0 deletions test/IDE/complete_builder_multiviable.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// RUN: %swift-ide-test -code-completion -source-filename %s -code-completion-token COMPLETE1 | %FileCheck %s --check-prefix=CHECK
// RUN: %swift-ide-test -code-completion -source-filename %s -code-completion-token COMPLETE1 | %FileCheck %s --check-prefix=CHECK

@_functionBuilder
struct Builder {
static func buildBlock<T1>(_ v1: T1) -> T1 { v1 }
static func buildBlock<T1, T2>(_ v1: T1, _ v2: T2) -> (T1, T2) { v1 }
}

struct MyValue {
var title: String
var id: Int
var value: Float
}

func build<T>(@Builder fn: (MyValue) -> T) {}

struct Container {
init(x: Float) {}
init(x: Int) {}
}

func test(foo: Foo) {
build { val in
Container(x: val.#^COMPLETE1^#)
}
build { val in
1 + 2
Container(x: val.#^COMPLETE2^#)
}
}

// CHECK: Begin completions, 4 items
// CHECK: Keyword[self]/CurrNominal: self[#MyValue#]; name=self
// CHECK: Decl[InstanceVar]/CurrNominal: title[#String#]; name=title
// CHECK: Decl[InstanceVar]/CurrNominal/TypeRelation[Identical]: id[#Int#]; name=id
// CHECK: Decl[InstanceVar]/CurrNominal/TypeRelation[Identical]: value[#Float#]; name=value
// CHECK: End completions