Skip to content

Commit 1a210bb

Browse files
authored
Merge pull request swiftlang#42210 from ahoppen/pr/result-builder-type-relations-solver-based
[CodeCompletion] Report type relations when completing inside result builders
2 parents 1d97379 + 0b9644a commit 1a210bb

File tree

6 files changed

+148
-43
lines changed

6 files changed

+148
-43
lines changed

include/swift/Sema/ConstraintSystem.h

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5296,18 +5296,21 @@ class ConstraintSystem {
52965296
= FreeTypeVariableBinding::Disallow,
52975297
bool allowFixes = false);
52985298

5299-
/// Construct and solve a system of constraints based on the given expression
5300-
/// and its contextual information.
5299+
/// Assuming that constraints have already been generated, solve the
5300+
/// constraint system for code completion, writing all solutions to
5301+
/// \p solutions.
53015302
///
53025303
/// This method is designed to be used for code completion which means that
53035304
/// it doesn't mutate given expression, even if there is a single valid
53045305
/// solution, and constraint solver is allowed to produce partially correct
53055306
/// solutions. Such solutions can have any number of holes in them.
53065307
///
5307-
/// \param target The expression involved in code completion.
5308-
///
53095308
/// \param solutions The solutions produced for the given target without
53105309
/// filtering.
5310+
void solveForCodeCompletion(SmallVectorImpl<Solution> &solutions);
5311+
5312+
/// Generate constraints for \p target and solve the resulting constraint
5313+
/// system for code completion (see overload above).
53115314
///
53125315
/// \returns `false` if this call fails (e.g. pre-check or constraint
53135316
/// generation fails), `true` otherwise.

lib/Sema/BuilderTransform.cpp

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1714,10 +1714,10 @@ Optional<BraceStmt *> TypeChecker::applyResultBuilderBodyTransform(
17141714
}
17151715

17161716
// Solve the constraint system.
1717-
SmallVector<Solution, 4> solutions;
1718-
bool solvingFailed = cs.solve(solutions);
1719-
17201717
if (cs.getASTContext().CompletionCallback) {
1718+
SmallVector<Solution, 4> solutions;
1719+
cs.solveForCodeCompletion(solutions);
1720+
17211721
CompletionContextFinder analyzer(func, func->getDeclContext());
17221722
filterSolutionsForCodeCompletion(solutions, analyzer);
17231723
for (const auto &solution : solutions) {
@@ -1726,6 +1726,9 @@ Optional<BraceStmt *> TypeChecker::applyResultBuilderBodyTransform(
17261726
return nullptr;
17271727
}
17281728

1729+
SmallVector<Solution, 4> solutions;
1730+
bool solvingFailed = cs.solve(solutions);
1731+
17291732
if (solvingFailed || solutions.size() != 1) {
17301733
// Try to fix the system or provide a decent diagnostic.
17311734
auto salvagedResult = cs.salvage();

lib/Sema/CSSolver.cpp

Lines changed: 26 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1516,26 +1516,8 @@ void ConstraintSystem::solveImpl(SmallVectorImpl<Solution> &solutions) {
15161516
}
15171517
}
15181518

1519-
bool ConstraintSystem::solveForCodeCompletion(
1520-
SolutionApplicationTarget &target, SmallVectorImpl<Solution> &solutions) {
1521-
auto *expr = target.getAsExpr();
1522-
// Tell the constraint system what the contextual type is.
1523-
setContextualType(expr, target.getExprContextualTypeLoc(),
1524-
target.getExprContextualTypePurpose());
1525-
1526-
// Set up the expression type checker timer.
1527-
Timer.emplace(expr, *this);
1528-
1529-
shrink(expr);
1530-
1531-
if (isDebugMode()) {
1532-
auto &log = llvm::errs();
1533-
log << "--- Code Completion ---\n";
1534-
}
1535-
1536-
if (generateConstraints(target, FreeTypeVariableBinding::Disallow))
1537-
return false;
1538-
1519+
void ConstraintSystem::solveForCodeCompletion(
1520+
SmallVectorImpl<Solution> &solutions) {
15391521
{
15401522
SolverState state(*this, FreeTypeVariableBinding::Disallow);
15411523

@@ -1556,6 +1538,30 @@ bool ConstraintSystem::solveForCodeCompletion(
15561538
}
15571539
}
15581540

1541+
return;
1542+
}
1543+
1544+
bool ConstraintSystem::solveForCodeCompletion(
1545+
SolutionApplicationTarget &target, SmallVectorImpl<Solution> &solutions) {
1546+
auto *expr = target.getAsExpr();
1547+
// Tell the constraint system what the contextual type is.
1548+
setContextualType(expr, target.getExprContextualTypeLoc(),
1549+
target.getExprContextualTypePurpose());
1550+
1551+
// Set up the expression type checker timer.
1552+
Timer.emplace(expr, *this);
1553+
1554+
shrink(expr);
1555+
1556+
if (isDebugMode()) {
1557+
auto &log = llvm::errs();
1558+
log << "--- Code Completion ---\n";
1559+
}
1560+
1561+
if (generateConstraints(target, FreeTypeVariableBinding::Disallow))
1562+
return false;
1563+
1564+
solveForCodeCompletion(solutions);
15591565
return true;
15601566
}
15611567

lib/Sema/ConstraintSystem.cpp

Lines changed: 68 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3558,18 +3558,79 @@ Type Solution::simplifyTypeForCodeCompletion(Type Ty) const {
35583558

35593559
// Replace all type variables (which must come from placeholders) by their
35603560
// generic parameters. Because we call into simplifyTypeImpl
3561-
Ty = CS.simplifyTypeImpl(Ty, [](TypeVariableType *typeVar) -> Type {
3562-
if (auto *GP = typeVar->getImpl().getGenericParameter()) {
3563-
// Code completion depends on generic parameter type being
3564-
// represented in terms of `ArchetypeType` since it's easy
3565-
// to extract protocol requirements from it.
3566-
if (auto *GPD = GP->getDecl()) {
3567-
return GPD->getInnermostDeclContext()->mapTypeIntoContext(GP);
3561+
Ty = CS.simplifyTypeImpl(Ty, [&CS](TypeVariableType *typeVar) -> Type {
3562+
// Code completion depends on generic parameter type being represented in
3563+
// terms of `ArchetypeType` since it's easy to extract protocol requirements
3564+
// from it.
3565+
auto getTypeVarAsArchetype = [](TypeVariableType *typeVar) -> Type {
3566+
if (auto *GP = typeVar->getImpl().getGenericParameter()) {
3567+
if (auto *GPD = GP->getDecl()) {
3568+
return GPD->getInnermostDeclContext()->mapTypeIntoContext(GP);
3569+
}
3570+
}
3571+
return Type();
3572+
};
3573+
3574+
if (auto archetype = getTypeVarAsArchetype(typeVar)) {
3575+
return archetype;
3576+
}
3577+
3578+
// When applying the logic below to get contextual types inside result
3579+
// builders, the code completion type variable is connected by a one-way
3580+
// constraint to a type variable in the buildBlock call, but that is not the
3581+
// type variable that represents the argument type. We need to find the type
3582+
// variable representing the argument to retrieve protocol requirements from
3583+
// it. Look for a ArgumentConversion constraint that allows us to retrieve
3584+
// the argument type var.
3585+
for (auto argConstraint :
3586+
CS.getConstraintGraph()[typeVar].getConstraints()) {
3587+
if (argConstraint->getKind() == ConstraintKind::ArgumentConversion &&
3588+
argConstraint->getFirstType()->getRValueType()->isEqual(typeVar)) {
3589+
if (auto argTV =
3590+
argConstraint->getSecondType()->getAs<TypeVariableType>()) {
3591+
if (auto archetype = getTypeVarAsArchetype(argTV)) {
3592+
return archetype;
3593+
}
3594+
}
35683595
}
35693596
}
3597+
35703598
return typeVar;
35713599
});
35723600

3601+
// Logic to determine the contextual type inside buildBlock result builders:
3602+
//
3603+
// When completing inside a result builder, the result builder
3604+
// @ViewBuilder var body: some View {
3605+
// Text("Foo")
3606+
// #^COMPLETE^#
3607+
// }
3608+
// gets rewritten to
3609+
// @ViewBuilder var body: some View {
3610+
// let $__builder2: Text
3611+
// let $__builder0 = Text("Foo")
3612+
// let $__builder1 = #^COMPLETE^#
3613+
// $__builder2 = ViewBuilder.buildBlock($__builder0, $__builder1)
3614+
// return $__builder2
3615+
// }
3616+
// Inside the constraint system
3617+
// let $__builder1 = #^COMPLETE^#
3618+
// gets type checked without context, so we can't know the contexutal type for
3619+
// the code completion token. But we know that $__builder1 (and thus the type
3620+
// of #^COMPLETE^#) is used as the second argument to ViewBuilder.buildBlock,
3621+
// so we can extract the contextual type from that call. To do this, figure
3622+
// out the type variable that is used for $__builder1 in the buildBlock call.
3623+
// This type variable is connected to the type variable of $__builder1's
3624+
// definition by a one-way constraint.
3625+
if (auto TV = Ty->getAs<TypeVariableType>()) {
3626+
for (auto constraint : CS.getConstraintGraph()[TV].getConstraints()) {
3627+
if (constraint->getKind() == ConstraintKind::OneWayEqual &&
3628+
constraint->getSecondType()->isEqual(TV)) {
3629+
return simplifyTypeForCodeCompletion(constraint->getFirstType());
3630+
}
3631+
}
3632+
}
3633+
35733634
// Remove any remaining type variables and placeholders
35743635
Ty = simplifyType(Ty);
35753636

lib/Sema/TypeCheckConstraints.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1406,6 +1406,8 @@ void ConstraintSystem::print(raw_ostream &out) const {
14061406
out << " [inout allowed]";
14071407
if (tv->getImpl().canBindToNoEscape())
14081408
out << " [noescape allowed]";
1409+
if (tv->getImpl().canBindToHole())
1410+
out << " [hole allowed]";
14091411
auto rep = getRepresentative(tv);
14101412
if (rep == tv) {
14111413
if (auto fixed = getFixedType(tv)) {

test/IDE/complete_in_result_builder.swift

Lines changed: 39 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -36,25 +36,28 @@ func testGlobalLookup() {
3636
@TupleBuilder<String> var x1 {
3737
#^GLOBAL_LOOKUP^#
3838
// GLOBAL_LOOKUP: Begin completions
39-
// GLOBAL_LOOKUP: Decl[GlobalVar]/CurrModule: MyConstantString[#String#];
39+
// GLOBAL_LOOKUP: Decl[GlobalVar]/CurrModule/TypeRelation[Identical]: MyConstantString[#String#];
4040
// GLOBAL_LOOKUP: End completions
4141
}
4242

4343
@TupleBuilder<String> var x2 {
4444
if true {
45-
#^GLOBAL_LOOKUP_IN_IF_BODY?check=GLOBAL_LOOKUP^#
45+
#^GLOBAL_LOOKUP_IN_IF_BODY?check=GLOBAL_LOOKUP_NO_TYPE_RELATION^#
46+
// GLOBAL_LOOKUP_NO_TYPE_RELATION: Begin completions
47+
// GLOBAL_LOOKUP_NO_TYPE_RELATION: Decl[GlobalVar]/CurrModule: MyConstantString[#String#];
48+
// GLOBAL_LOOKUP_NO_TYPE_RELATION: End completions
4649
}
4750
}
4851

4952
@TupleBuilder<String> var x3 {
5053
if {
51-
#^GLOBAL_LOOKUP_IN_IF_BODY_WITHOUT_CONDITION?check=GLOBAL_LOOKUP^#
54+
#^GLOBAL_LOOKUP_IN_IF_BODY_WITHOUT_CONDITION?check=GLOBAL_LOOKUP_NO_TYPE_RELATION^#
5255
}
5356
}
5457

5558
@TupleBuilder<String> var x4 {
5659
guard else {
57-
#^GLOBAL_LOOKUP_IN_GUARD_BODY_WITHOUT_CONDITION?check=GLOBAL_LOOKUP^#
60+
#^GLOBAL_LOOKUP_IN_GUARD_BODY_WITHOUT_CONDITION?check=GLOBAL_LOOKUP_NO_TYPE_RELATION^#
5861
}
5962
}
6063

@@ -78,13 +81,16 @@ func testStaticMemberLookup() {
7881
@TupleBuilder<String> var x1 {
7982
StringFactory.#^COMPLETE_STATIC_MEMBER^#
8083
// COMPLETE_STATIC_MEMBER: Begin completions
81-
// COMPLETE_STATIC_MEMBER: Decl[StaticMethod]/CurrNominal: makeString({#x: String#})[#String#];
84+
// COMPLETE_STATIC_MEMBER: Decl[StaticMethod]/CurrNominal/TypeRelation[Identical]: makeString({#x: String#})[#String#];
8285
// COMPLETE_STATIC_MEMBER: End completions
8386
}
8487

8588
@TupleBuilder<String> var x2 {
8689
if true {
87-
StringFactory.#^COMPLETE_STATIC_MEMBER_IN_IF_BODY?check=COMPLETE_STATIC_MEMBER^#
90+
StringFactory.#^COMPLETE_STATIC_MEMBER_IN_IF_BODY^#
91+
// COMPLETE_STATIC_MEMBER_IN_IF_BODY: Begin completions
92+
// COMPLETE_STATIC_MEMBER_IN_IF_BODY: Decl[StaticMethod]/CurrNominal: makeString({#x: String#})[#String#];
93+
// COMPLETE_STATIC_MEMBER_IN_IF_BODY: End completions
8894
}
8995
}
9096

@@ -208,13 +214,37 @@ func testCompleteInStringLiteral() {
208214
// STRING_LITERAL_VAR-DAG: Keyword[self]/CurrNominal: self[#Island#]; name=self
209215
// STRING_LITERAL_VAR-DAG: Decl[InstanceVar]/CurrNominal/TypeRelation[Convertible]: turnipPrice[#String#]; name=turnipPrice
210216
// STRING_LITERAL_VAR: End completions
217+
}
211218

212-
213-
func bar(island: Island) {
219+
func bar(island: Island) {
214220
BStack {
215221
Text("\(island.#^STRING_LITERAL_AS_ARGUMENT?check=STRING_LITERAL_VAR^#turnipPrice)")
216222
takeTrailingClosure {}
217-
}
223+
}
224+
}
225+
}
226+
227+
func testTypeRelationInResultBuilder() {
228+
protocol View2 {}
229+
230+
@resultBuilder public struct ViewBuilder2 {
231+
static func buildBlock<Content>(_ content: Content) -> Content where Content : View2 { fatalError() }
232+
static func buildBlock<C0, C1>(_ c0: C0, _ c1: C1) -> C0 where C0 : View2, C1: View2 { fatalError() }
218233
}
219234

235+
struct MyText: View2 {}
236+
237+
struct MyView {
238+
@ViewBuilder2 var body: some View2 {
239+
#^SINGLE_ELEMENT^#
240+
}
241+
// SINGLE_ELEMENT: Begin completions
242+
// SINGLE_ELEMENT-DAG: Decl[Struct]/Local/TypeRelation[Convertible]: MyText[#MyText#];
243+
// SINGLE_ELEMENT: End completions
244+
245+
@ViewBuilder2 var body2: some View2 {
246+
MyText()
247+
#^SECOND_ELEMENT?check=SINGLE_ELEMENT^#
248+
}
249+
}
220250
}

0 commit comments

Comments
 (0)