Skip to content

Commit 2e92b49

Browse files
committed
[CSBindings] Solver result builder transformed closures as soon as all contextual information becomes available
We determine that based on four criteria: - Builder type doesn't have any unresolved generic parameters; - Contextual type associated with the closure doesn't have any unresolved type variables (which means that all possible contextual information is provided to the body); - All of the references to declarations from outer scope are resolved (i.e. a variable declaration from a parent closure); - The contextual result type is either fully resolved or opaque type. If all the criteria a met the conjunction that represents a closure is going to be picked and solved as the next solver step. Resolves: rdar://104645543 (cherry picked from commit f27369d) (cherry picked from commit a815de7) (cherry picked from commit 38f8be1) (cherry picked from commit 5bb9d75) (cherry picked from commit fda7530) (cherry picked from commit b84d70b)
1 parent 7cc67db commit 2e92b49

File tree

6 files changed

+167
-2
lines changed

6 files changed

+167
-2
lines changed

include/swift/Sema/ConstraintSystem.h

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -486,6 +486,9 @@ class TypeVariableType::Implementation {
486486
/// expression.
487487
bool isCodeCompletionToken() const;
488488

489+
/// Determine whether this type variable represents an opened opaque type.
490+
bool isOpaqueType() const;
491+
489492
/// Retrieve the representative of the equivalence class to which this
490493
/// type variable belongs.
491494
///
@@ -977,6 +980,11 @@ struct AppliedBuilderTransform {
977980
/// converted. Opaque types should be unopened.
978981
Type bodyResultType;
979982

983+
/// If transform is applied to a closure, this type represents
984+
/// contextual type the closure is converted type (e.g. a parameter
985+
/// type or or pattern type).
986+
Type contextualType;
987+
980988
/// The version of the original body with result builder applied
981989
/// as AST transformation.
982990
NullablePtr<BraceStmt> transformedBody;
@@ -5662,7 +5670,7 @@ class ConstraintSystem {
56625670
Optional<TypeMatchResult>
56635671
matchResultBuilder(AnyFunctionRef fn, Type builderType, Type bodyResultType,
56645672
ConstraintKind bodyResultConstraintKind,
5665-
ConstraintLocatorBuilder locator);
5673+
Type contextualType, ConstraintLocatorBuilder locator);
56665674

56675675
/// Matches a wrapped or projected value parameter type to its backing
56685676
/// property wrapper type by applying the property wrapper.

lib/Sema/BuilderTransform.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2311,6 +2311,7 @@ Optional<BraceStmt *> TypeChecker::applyResultBuilderBodyTransform(
23112311

23122312
if (auto result = cs.matchResultBuilder(
23132313
func, builderType, resultContextType, resultConstraintKind,
2314+
/*contextualType=*/Type(),
23142315
cs.getConstraintLocator(func->getBody()))) {
23152316
if (result->isFailure())
23162317
return nullptr;
@@ -2398,6 +2399,7 @@ Optional<ConstraintSystem::TypeMatchResult>
23982399
ConstraintSystem::matchResultBuilder(AnyFunctionRef fn, Type builderType,
23992400
Type bodyResultType,
24002401
ConstraintKind bodyResultConstraintKind,
2402+
Type contextualType,
24012403
ConstraintLocatorBuilder locator) {
24022404
builderType = simplifyType(builderType);
24032405
auto builder = builderType->getAnyNominal();
@@ -2522,6 +2524,7 @@ ConstraintSystem::matchResultBuilder(AnyFunctionRef fn, Type builderType,
25222524

25232525
transformInfo.builderType = builderType;
25242526
transformInfo.bodyResultType = bodyResultType;
2527+
transformInfo.contextualType = contextualType;
25252528
transformInfo.transformedBody = transformedBody->second;
25262529

25272530
// Record the transformation.

lib/Sema/CSBindings.cpp

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1071,6 +1071,61 @@ bool BindingSet::favoredOverConjunction(Constraint *conjunction) const {
10711071
if (forClosureResult() || forGenericParameter())
10721072
return false;
10731073
}
1074+
1075+
auto *locator = conjunction->getLocator();
1076+
if (locator->directlyAt<ClosureExpr>()) {
1077+
auto *closure = castToExpr<ClosureExpr>(locator->getAnchor());
1078+
1079+
if (auto transform = CS.getAppliedResultBuilderTransform(closure)) {
1080+
// Conjunctions that represent closures with result builder transformed
1081+
// bodies could be attempted right after their resolution if they meet
1082+
// all of the following criteria:
1083+
//
1084+
// - Builder type doesn't have any unresolved generic parameters;
1085+
// - Closure doesn't have any parameters;
1086+
// - The contextual result type is either concrete or opaque type.
1087+
auto contextualType = transform->contextualType;
1088+
if (!(contextualType && contextualType->is<FunctionType>()))
1089+
return true;
1090+
1091+
auto *contextualFnType =
1092+
CS.simplifyType(contextualType)->castTo<FunctionType>();
1093+
{
1094+
auto resultType = contextualFnType->getResult();
1095+
if (resultType->hasTypeVariable()) {
1096+
auto *typeVar = resultType->getAs<TypeVariableType>();
1097+
// If contextual result type is represented by an opaque type,
1098+
// it's a strong indication that body is self-contained, otherwise
1099+
// closure might rely on external types flowing into the body for
1100+
// disambiguation of `build{Partial}Block` or `buildFinalResult`
1101+
// calls.
1102+
if (!(typeVar && typeVar->getImpl().isOpaqueType()))
1103+
return true;
1104+
}
1105+
}
1106+
1107+
// If some of the closure parameters are unresolved, the conjunction
1108+
// has to be delayed to give them a chance to be inferred.
1109+
if (llvm::any_of(contextualFnType->getParams(), [](const auto &param) {
1110+
return param.getPlainType()->hasTypeVariable();
1111+
}))
1112+
return true;
1113+
1114+
// Check whether conjunction has any unresolved type variables
1115+
// besides the variable that represents the closure.
1116+
//
1117+
// Conjunction could refer to declarations from outer context
1118+
// (i.e. a variable declared in the outer closure) or generic
1119+
// parameters of the builder type), if any of such references
1120+
// are not yet inferred the conjunction has to be delayed.
1121+
auto *closureType = CS.getType(closure)->castTo<TypeVariableType>();
1122+
return llvm::any_of(
1123+
conjunction->getTypeVariables(), [&](TypeVariableType *typeVar) {
1124+
return !(typeVar == closureType || CS.getFixedType(typeVar));
1125+
});
1126+
}
1127+
}
1128+
10741129
return true;
10751130
}
10761131

lib/Sema/CSSimplify.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10964,7 +10964,7 @@ bool ConstraintSystem::resolveClosure(TypeVariableType *typeVar,
1096410964
if (resultBuilderType) {
1096510965
if (auto result = matchResultBuilder(
1096610966
closure, resultBuilderType, closureType->getResult(),
10967-
ConstraintKind::Conversion, locator)) {
10967+
ConstraintKind::Conversion, contextualType, locator)) {
1096810968
return result->isSuccess();
1096910969
}
1097010970
}

lib/Sema/TypeCheckConstraints.cpp

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,22 @@ bool TypeVariableType::Implementation::isCodeCompletionToken() const {
154154
return locator && locator->directlyAt<CodeCompletionExpr>();
155155
}
156156

157+
bool TypeVariableType::Implementation::isOpaqueType() const {
158+
if (!locator)
159+
return false;
160+
161+
auto GP = locator->getLastElementAs<LocatorPathElt::GenericParameter>();
162+
if (!GP)
163+
return false;
164+
165+
if (auto *GPT = GP->getType()->getAs<GenericTypeParamType>()) {
166+
auto *decl = GPT->getDecl();
167+
return decl && decl->isOpaqueType();
168+
}
169+
170+
return false;
171+
}
172+
157173
void *operator new(size_t bytes, ConstraintSystem& cs,
158174
size_t alignment) {
159175
return cs.getAllocator().Allocate(bytes, alignment);
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
// RUN: %target-typecheck-verify-swift -debug-constraints -disable-availability-checking 2>%t.err
2+
// RUN: %FileCheck %s < %t.err
3+
4+
protocol P<Output> {
5+
associatedtype Output
6+
}
7+
8+
struct S<Output> : P {
9+
init(_: Output) {}
10+
}
11+
12+
@resultBuilder
13+
struct Builder {
14+
static func buildExpression<T>(_ e: T) -> T { e }
15+
16+
static func buildBlock<T1>(_ t1: T1) -> some P<T1> {
17+
return S(t1)
18+
}
19+
20+
static func buildBlock<T1, T2>(_ t1: T1, _ t2: T2) -> some P<(T1, T2)> {
21+
return S((t1, t2))
22+
}
23+
24+
static func buildBlock<T1, T2, T3>(_ t1: T1, _ t2: T2, _ t3: T3) -> some P<(T1, T2, T3)> {
25+
return S((t1, t2, t3))
26+
}
27+
28+
static func buildOptional<T>(_ value: T?) -> T? { return value }
29+
}
30+
31+
do {
32+
func test<T, U>(_: T, @Builder _: () -> some P<U>) {}
33+
34+
// CHECK: ---Initial constraints for the given expression---
35+
// CHECK: (integer_literal_expr type='[[LITERAL_VAR:\$T[0-9]+]]' {{.*}}
36+
// CHECK: (attempting type variable [[CLOSURE:\$T[0-9]+]] := () -> {{.*}}
37+
// CHECK-NOT: (attempting type variable [[LITERAL_VAR]] := {{.*}}
38+
// CHECK: (attempting conjunction element pattern binding element @ 0
39+
// CHECK: (applying conjunction result to outer context
40+
// CHECK: (attempting type variable [[LITERAL_VAR]] := Int
41+
test(42) {
42+
1
43+
""
44+
}
45+
}
46+
47+
do {
48+
func test<T, U>(_: T, @Builder _: (T) -> some P<U>) {}
49+
50+
// CHECK: ---Initial constraints for the given expression---
51+
// CHECK: (integer_literal_expr type='[[LITERAL_VAR:\$T[0-9]+]]' {{.*}}
52+
// CHECK: (attempting type variable [[LITERAL_VAR]] := Int
53+
// CHECK: (attempting conjunction element pattern binding element @ 0
54+
test(42) { v in
55+
v
56+
""
57+
}
58+
}
59+
60+
do {
61+
func test<T: BinaryInteger, U>(@Builder _: (Bool) -> some P<T>, transform: (T?) -> U) {}
62+
63+
// CHECK: ---Initial constraints for the given expression---
64+
// CHECK: (attempting type variable {{.*}} := () -> {{.*}}
65+
// CHECK: (attempting conjunction element pattern binding element @ 0 :
66+
// CHECK-NEXT: (pattern_named 'x')
67+
// CHECK: (attempting conjunction element syntactic element
68+
// CHECK-NEXT: (call_expr {{.*}}
69+
// CHECK: (attempting type variable {{.*}} := (Bool) -> {{.*}}
70+
// CHECK: (attempting conjunction element pattern binding element @ 0
71+
// CHECK: (pattern_named implicit '$__builder{{.*}}')
72+
// CHECK: (applying conjunction result to outer context
73+
// CHECK: (attempting type variable {{.*}} := (Int?) -> {{.*}}
74+
// CHECK: (attempting disjunction choice {{.*}} bound to decl {{.*}}.Int.init(_:)
75+
let _ = {
76+
let x = 42
77+
test { cond in
78+
x
79+
} transform: { v in
80+
Int(v ?? 0)
81+
}
82+
}
83+
}

0 commit comments

Comments
 (0)