Skip to content

Commit f3b6ff0

Browse files
authored
Merge pull request #38104 from xedin/drop-optionality-for-contextual-closure-type
[CSSimplify] Look through optionals in contextual type while resolving a closure
2 parents 6fcb47d + 038761f commit f3b6ff0

File tree

2 files changed

+98
-0
lines changed

2 files changed

+98
-0
lines changed

lib/Sema/CSSimplify.cpp

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8699,6 +8699,22 @@ bool ConstraintSystem::resolveClosure(TypeVariableType *typeVar,
86998699
auto *closure = castToExpr<ClosureExpr>(closureLocator->getAnchor());
87008700
auto *inferredClosureType = getClosureType(closure);
87018701

8702+
// Let's look through all optionals associated with contextual
8703+
// type to make it possible to infer parameter/result type of
8704+
// the closure faster e.g.:
8705+
//
8706+
// func test(_: ((Int) -> Void)?) {
8707+
// ...
8708+
// }
8709+
//
8710+
// test { $0 + ... }
8711+
//
8712+
// In this case dropping optionality from contextual type
8713+
// `((Int) -> Void)?` allows `resolveClosure` to infer type
8714+
// of `$0` directly (via `getContextualParamAt`) instead of
8715+
// having to use type variable inference mechanism.
8716+
contextualType = contextualType->lookThroughAllOptionalTypes();
8717+
87028718
auto getContextualParamAt =
87038719
[&contextualType, &inferredClosureType](
87048720
unsigned index) -> Optional<AnyFunctionType::Param> {

unittests/Sema/ConstraintSimplificationTests.cpp

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,10 @@
1111
//===----------------------------------------------------------------------===//
1212

1313
#include "SemaFixture.h"
14+
#include "swift/AST/Decl.h"
15+
#include "swift/AST/Expr.h"
16+
#include "swift/AST/ParameterList.h"
17+
#include "swift/AST/Types.h"
1418
#include "swift/Sema/ConstraintSystem.h"
1519

1620
using namespace swift;
@@ -44,3 +48,81 @@ TEST_F(SemaTest, TestTrailingClosureMatchRecordingForIdenticalFunctions) {
4448
TrailingClosureMatching::Forward, {{0}, {1}}, None};
4549
ASSERT_EQ(choice->second, expected);
4650
}
51+
52+
/// Emulates code like this:
53+
///
54+
/// func test(_: ((Int) -> Void)?) {}
55+
///
56+
/// test { $0 }
57+
///
58+
/// To make sure that closure resolution propagates contextual
59+
/// information into the body of the closure even when contextual
60+
/// type is wrapped in an optional.
61+
TEST_F(SemaTest, TestClosureInferenceFromOptionalContext) {
62+
ConstraintSystem cs(DC, ConstraintSystemOptions());
63+
64+
DeclAttributes closureAttrs;
65+
66+
// Anonymous closure parameter
67+
auto paramName = Context.getIdentifier("0");
68+
69+
auto *paramDecl =
70+
new (Context) ParamDecl(/*specifierLoc=*/SourceLoc(),
71+
/*argumentNameLoc=*/SourceLoc(), paramName,
72+
/*parameterNameLoc=*/SourceLoc(), paramName, DC);
73+
74+
paramDecl->setSpecifier(ParamSpecifier::Default);
75+
76+
auto *closure = new (Context) ClosureExpr(
77+
closureAttrs,
78+
/*bracketRange=*/SourceRange(),
79+
/*capturedSelfDecl=*/nullptr, ParameterList::create(Context, {paramDecl}),
80+
/*asyncLoc=*/SourceLoc(),
81+
/*throwsLoc=*/SourceLoc(),
82+
/*arrowLoc=*/SourceLoc(),
83+
/*inLoc=*/SourceLoc(),
84+
/*explicitResultType=*/nullptr,
85+
/*discriminator=*/0,
86+
/*parent=*/DC);
87+
88+
closure->setImplicit();
89+
90+
closure->setBody(BraceStmt::create(Context, /*startLoc=*/SourceLoc(), {},
91+
/*endLoc=*/SourceLoc()),
92+
/*isSingleExpression=*/false);
93+
94+
auto *closureLoc = cs.getConstraintLocator(closure);
95+
96+
auto *paramTy = cs.createTypeVariable(
97+
cs.getConstraintLocator(closure, LocatorPathElt::TupleElement(0)),
98+
/*options=*/TVO_CanBindToInOut);
99+
100+
auto *resultTy = cs.createTypeVariable(
101+
cs.getConstraintLocator(closure, ConstraintLocator::ClosureResult),
102+
/*options=*/0);
103+
104+
auto extInfo = FunctionType::ExtInfo();
105+
106+
auto defaultTy = FunctionType::get({FunctionType::Param(paramTy, paramName)},
107+
resultTy, extInfo);
108+
109+
cs.setClosureType(closure, defaultTy);
110+
111+
auto *closureTy = cs.createTypeVariable(closureLoc, /*options=*/0);
112+
113+
cs.addUnsolvedConstraint(Constraint::create(
114+
cs, ConstraintKind::DefaultClosureType, closureTy, defaultTy,
115+
cs.getConstraintLocator(closure), /*referencedVars=*/{}));
116+
117+
auto contextualTy =
118+
FunctionType::get({FunctionType::Param(getStdlibType("Int"))},
119+
Context.TheEmptyTupleType, extInfo);
120+
121+
// Try to resolve closure:
122+
// - external type `paramTy` should get bound to `Int`.
123+
// - result type should be bound to `Void`.
124+
cs.resolveClosure(closureTy, OptionalType::get(contextualTy), closureLoc);
125+
126+
ASSERT_TRUE(cs.simplifyType(paramTy)->isEqual(getStdlibType("Int")));
127+
ASSERT_TRUE(cs.simplifyType(resultTy)->isEqual(Context.TheEmptyTupleType));
128+
}

0 commit comments

Comments
 (0)