Skip to content

Commit 5331e27

Browse files
authored
Merge pull request #41730 from xedin/se-0326-solve-pattern-bindings-via-conjunctions
[SE-0326] Re-enable multi-statement closure inference by default
2 parents c3c7fe0 + 5e4c964 commit 5331e27

27 files changed

+296
-100
lines changed

include/swift/Basic/LangOptions.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -735,7 +735,7 @@ namespace swift {
735735

736736
/// Enable experimental support for type inference through multi-statement
737737
/// closures.
738-
bool EnableMultiStatementClosureInference = false;
738+
bool EnableMultiStatementClosureInference = true;
739739

740740
/// Enable experimental support for generic parameter inference in
741741
/// parameter positions from associated default expressions.

include/swift/Sema/ConstraintLocator.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1058,6 +1058,19 @@ class LocatorPathElt::ClosureBodyElement final
10581058
}
10591059
};
10601060

1061+
class LocatorPathElt::PatternBindingElement final
1062+
: public StoredIntegerElement<1> {
1063+
public:
1064+
PatternBindingElement(unsigned index)
1065+
: StoredIntegerElement(ConstraintLocator::PatternBindingElement, index) {}
1066+
1067+
unsigned getIndex() const { return getValue(); }
1068+
1069+
static bool classof(const LocatorPathElt *elt) {
1070+
return elt->getKind() == ConstraintLocator::PatternBindingElement;
1071+
}
1072+
};
1073+
10611074
namespace details {
10621075
template <typename CustomPathElement>
10631076
class PathElement {

include/swift/Sema/ConstraintLocatorPathElts.def

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -232,6 +232,9 @@ SIMPLE_LOCATOR_PATH_ELT(ImplicitDynamicMemberSubscript)
232232
/// The element of the closure body e.g. statement, declaration, or expression.
233233
CUSTOM_LOCATOR_PATH_ELT(ClosureBodyElement)
234234

235+
/// The element of the pattern binding declaration.
236+
CUSTOM_LOCATOR_PATH_ELT(PatternBindingElement)
237+
235238
#undef LOCATOR_PATH_ELT
236239
#undef CUSTOM_LOCATOR_PATH_ELT
237240
#undef SIMPLE_LOCATOR_PATH_ELT

lib/Sema/CSClosure.cpp

Lines changed: 90 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -483,12 +483,81 @@ class ClosureConstraintGenerator
483483
});
484484
}
485485

486+
void visitPatternBinding(PatternBindingDecl *patternBinding,
487+
SmallVectorImpl<ElementInfo> &patterns) {
488+
auto *baseLoc = cs.getConstraintLocator(
489+
locator, LocatorPathElt::ClosureBodyElement(patternBinding));
490+
491+
for (unsigned index : range(patternBinding->getNumPatternEntries())) {
492+
auto *pattern = TypeChecker::resolvePattern(
493+
patternBinding->getPattern(index), patternBinding->getDeclContext(),
494+
/*isStmtCondition=*/true);
495+
496+
if (!pattern) {
497+
hadError = true;
498+
return;
499+
}
500+
501+
// Reset binding to point to the resolved pattern. This is required
502+
// before calling `forPatternBindingDecl`.
503+
patternBinding->setPattern(index, pattern,
504+
patternBinding->getInitContext(index));
505+
506+
patterns.push_back(makeElement(
507+
patternBinding,
508+
cs.getConstraintLocator(
509+
baseLoc, LocatorPathElt::PatternBindingElement(index))));
510+
}
511+
}
512+
513+
void visitPatternBindingElement(PatternBindingDecl *patternBinding) {
514+
assert(locator->isLastElement<LocatorPathElt::PatternBindingElement>());
515+
516+
auto index =
517+
locator->castLastElementTo<LocatorPathElt::PatternBindingElement>()
518+
.getIndex();
519+
520+
auto contextualPattern =
521+
ContextualPattern::forPatternBindingDecl(patternBinding, index);
522+
Type patternType = TypeChecker::typeCheckPattern(contextualPattern);
523+
524+
// Fail early if pattern couldn't be type-checked.
525+
if (!patternType || patternType->hasError()) {
526+
hadError = true;
527+
return;
528+
}
529+
530+
auto *pattern = patternBinding->getPattern(index);
531+
auto *init = patternBinding->getInit(index);
532+
533+
if (!init && patternBinding->isDefaultInitializable(index) &&
534+
pattern->hasStorage()) {
535+
init = TypeChecker::buildDefaultInitializer(patternType);
536+
}
537+
538+
auto target = init ? SolutionApplicationTarget::forInitialization(
539+
init, patternBinding->getDeclContext(),
540+
patternType, patternBinding, index,
541+
/*bindPatternVarsOneWay=*/false)
542+
: SolutionApplicationTarget::forUninitializedVar(
543+
patternBinding, index, patternType);
544+
545+
if (cs.generateConstraints(target, FreeTypeVariableBinding::Disallow)) {
546+
hadError = true;
547+
return;
548+
}
549+
550+
// Keep track of this binding entry.
551+
cs.setSolutionApplicationTarget({patternBinding, index}, target);
552+
}
553+
486554
void visitDecl(Decl *decl) {
487555
if (isSupportedMultiStatementClosure()) {
488556
if (auto patternBinding = dyn_cast<PatternBindingDecl>(decl)) {
489-
SolutionApplicationTarget target(patternBinding);
490-
if (cs.generateConstraints(target, FreeTypeVariableBinding::Disallow))
491-
hadError = true;
557+
if (locator->isLastElement<LocatorPathElt::PatternBindingElement>())
558+
visitPatternBindingElement(patternBinding);
559+
else
560+
llvm_unreachable("cannot visit pattern binding directly");
492561
return;
493562
}
494563
}
@@ -788,6 +857,13 @@ class ClosureConstraintGenerator
788857
element.is<Expr *>() &&
789858
(!ctx.LangOpts.Playground && !ctx.LangOpts.DebuggerSupport);
790859

860+
if (auto *decl = element.dyn_cast<Decl *>()) {
861+
if (auto *PDB = dyn_cast<PatternBindingDecl>(decl)) {
862+
visitPatternBinding(PDB, elements);
863+
continue;
864+
}
865+
}
866+
791867
elements.push_back(makeElement(
792868
element,
793869
cs.getConstraintLocator(
@@ -1600,6 +1676,17 @@ void ConjunctionElement::findReferencedVariables(
16001676

16011677
TypeVariableRefFinder refFinder(cs, locator->getAnchor(), typeVars);
16021678

1679+
if (auto *patternBinding =
1680+
dyn_cast_or_null<PatternBindingDecl>(element.dyn_cast<Decl *>())) {
1681+
if (auto patternBindingElt =
1682+
locator
1683+
->getLastElementAs<LocatorPathElt::PatternBindingElement>()) {
1684+
if (auto *init = patternBinding->getInit(patternBindingElt->getIndex()))
1685+
init->walk(refFinder);
1686+
return;
1687+
}
1688+
}
1689+
16031690
if (element.is<Decl *>() || element.is<StmtConditionElement *>() ||
16041691
element.is<Expr *>() || element.isStmt(StmtKind::Return))
16051692
element.walk(refFinder);

lib/Sema/CSGen.cpp

Lines changed: 64 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2299,9 +2299,71 @@ namespace {
22992299
locator);
23002300
}
23012301

2302-
// If we have a type to ascribe to the variable, do so now.
2303-
if (oneWayVarType)
2302+
// Ascribe a type to the declaration so it's always available to
2303+
// constraint system.
2304+
if (oneWayVarType) {
23042305
CS.setType(var, oneWayVarType);
2306+
} else if (externalPatternType) {
2307+
// If there is an externally imposed type, that's what the
2308+
// declaration is going to be bound to.
2309+
CS.setType(var, externalPatternType);
2310+
} else {
2311+
// Otherwise, let's use the type of the pattern. The type
2312+
// of the declaration has to be r-value, so let's add an
2313+
// equality constraint if pattern type has any type variables
2314+
// that are allowed to be l-value.
2315+
bool foundLValueVars = false;
2316+
2317+
// Note that it wouldn't be always correct to allocate a single type
2318+
// variable, that disallows l-value types, to use as a declaration
2319+
// type because equality constraint would drop TVO_CanBindToLValue
2320+
// from the right-hand side (which is not the case for `OneWayEqual`)
2321+
// e.g.:
2322+
//
2323+
// sturct S { var x, y: Int }
2324+
//
2325+
// func test(s: S) {
2326+
// let (x, y) = (s.x, s.y)
2327+
// }
2328+
//
2329+
// Single type variable approach results in the following constraint:
2330+
// `$T_x_y = ($T_s_x, $T_s_y)` where both `$T_s_x` and `$T_s_y` have
2331+
// to allow l-value, but `$T_x_y` does not. Early simplication of `=`
2332+
// constraint (due to right-hand side being a "concrete" tuple type)
2333+
// would drop l-value option from `$T_s_x` and `$T_s_y` which leads to
2334+
// a failure during member lookup because `x` and `y` are both
2335+
// `@lvalue Int`. To avoid that, declaration type would mimic pattern
2336+
// type with all l-value options stripped, so the equality constraint
2337+
// becomes `($T_x, $_T_y) = ($T_s_x, $T_s_y)` which doesn't result in
2338+
// stripping of l-value flag from the right-hand side since
2339+
// simplification can only happen when either side is resolved.
2340+
auto declTy = varType.transform([&](Type type) -> Type {
2341+
if (auto *typeVar = type->getAs<TypeVariableType>()) {
2342+
if (typeVar->getImpl().canBindToLValue()) {
2343+
foundLValueVars = true;
2344+
2345+
// Drop l-value from the options but preserve the rest.
2346+
auto options = typeVar->getImpl().getRawOptions();
2347+
options &= ~TVO_CanBindToLValue;
2348+
2349+
return CS.createTypeVariable(typeVar->getImpl().getLocator(),
2350+
options);
2351+
}
2352+
}
2353+
return type;
2354+
});
2355+
2356+
// If pattern types allows l-value types, let's create an
2357+
// equality constraint between r-value only declaration type
2358+
// and l-value pattern type that would take care of looking
2359+
// through l-values when necessary.
2360+
if (foundLValueVars) {
2361+
CS.addConstraint(ConstraintKind::Equal, declTy, varType,
2362+
CS.getConstraintLocator(locator));
2363+
}
2364+
2365+
CS.setType(var, declTy);
2366+
}
23052367

23062368
return setType(varType);
23072369
}

lib/Sema/Constraint.cpp

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -388,8 +388,21 @@ void Constraint::print(llvm::raw_ostream &Out, SourceManager *sm) const {
388388
}
389389

390390
if (Kind == ConstraintKind::ClosureBodyElement) {
391-
Out << "closure body element ";
392-
getClosureElement().dump(Out);
391+
auto *locator = getLocator();
392+
auto element = getClosureElement();
393+
394+
if (auto patternBindingElt =
395+
locator
396+
->getLastElementAs<LocatorPathElt::PatternBindingElement>()) {
397+
auto *patternBinding = cast<PatternBindingDecl>(element.get<Decl *>());
398+
Out << "pattern binding element @ ";
399+
Out << patternBindingElt->getIndex() << " : ";
400+
patternBinding->getPattern(patternBindingElt->getIndex())->dump(Out);
401+
} else {
402+
Out << "closure body element ";
403+
getClosureElement().dump(Out);
404+
}
405+
393406
return;
394407
}
395408

lib/Sema/ConstraintLocator.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,7 @@ unsigned LocatorPathElt::getNewSummaryFlags() const {
9696
case ConstraintLocator::ClosureBodyElement:
9797
case ConstraintLocator::PackType:
9898
case ConstraintLocator::PackElement:
99+
case ConstraintLocator::PatternBindingElement:
99100
return 0;
100101

101102
case ConstraintLocator::FunctionArgument:
@@ -578,6 +579,14 @@ void ConstraintLocator::dump(SourceManager *sm, raw_ostream &out) const {
578579
out << "pack element #" << llvm::utostr(packElt.getIndex());
579580
break;
580581
}
582+
583+
case PatternBindingElement: {
584+
auto patternBindingElt =
585+
elt.castTo<LocatorPathElt::PatternBindingElement>();
586+
out << "pattern binding element #"
587+
<< llvm::utostr(patternBindingElt.getIndex());
588+
break;
589+
}
581590
}
582591
}
583592
out << ']';

test/Constraints/closures.swift

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -252,7 +252,7 @@ struct CC {}
252252
func callCC<U>(_ f: (CC) -> U) -> () {}
253253

254254
func typeCheckMultiStmtClosureCrash() {
255-
callCC { // expected-error {{cannot infer return type for closure with multiple statements; add explicit type to disambiguate}} {{none}}
255+
callCC {
256256
_ = $0
257257
return 1
258258
}
@@ -312,24 +312,23 @@ func testAcceptNothingToInt(ac1: @autoclosure () -> Int) {
312312
struct Thing {
313313
init?() {}
314314
}
315-
// This throws a compiler error
316-
let things = Thing().map { thing in // expected-error {{cannot infer return type for closure with multiple statements; add explicit type to disambiguate}} {{34-34=-> <#Result#> }}
317-
// Commenting out this makes it compile
315+
316+
let things = Thing().map { thing in
318317
_ = thing
319318
return thing
320319
}
321320

322321

323322
// <rdar://problem/21675896> QoI: [Closure return type inference] Swift cannot find members for the result of inlined lambdas with branches
324323
func r21675896(file : String) {
325-
let x: String = { // expected-error {{cannot infer return type for closure with multiple statements; add explicit type to disambiguate}} {{20-20= () -> <#Result#> in }}
324+
let x: String = {
326325
if true {
327326
return "foo"
328327
}
329328
else {
330329
return file
331330
}
332-
}().pathExtension
331+
}().pathExtension // expected-error {{value of type 'String' has no member 'pathExtension'}}
333332
}
334333

335334

@@ -360,7 +359,7 @@ func someGeneric19997471<T>(_ x: T) {
360359

361360

362361
// <rdar://problem/20921068> Swift fails to compile: [0].map() { _ in let r = (1,2).0; return r }
363-
[0].map { // expected-error {{cannot infer return type for closure with multiple statements; add explicit type to disambiguate}} {{5-5=-> <#Result#> }}
362+
let _ = [0].map {
364363
_ in
365364
let r = (1,2).0
366365
return r
@@ -408,7 +407,7 @@ func r20789423() {
408407
print(p.f(p)()) // expected-error {{cannot convert value of type 'C' to expected argument type 'Int'}}
409408
// expected-error@-1:11 {{cannot call value of non-function type '()'}}
410409

411-
let _f = { (v: Int) in // expected-error {{cannot infer return type for closure with multiple statements; add explicit type to disambiguate}} {{23-23=-> <#Result#> }}
410+
let _f = { (v: Int) in
412411
print("a")
413412
return "hi"
414413
}
@@ -1127,7 +1126,7 @@ func rdar76058892() {
11271126
func experiment(arr: [S]?) {
11281127
test { // expected-error {{contextual closure type '() -> String' expects 0 arguments, but 1 was used in closure body}}
11291128
if let arr = arr {
1130-
arr.map($0.test) // expected-note {{anonymous closure parameter '$0' is used here}}
1129+
arr.map($0.test) // expected-note {{anonymous closure parameter '$0' is used here}} // expected-error {{generic parameter 'T' could not be inferred}}
11311130
}
11321131
}
11331132
}

test/Constraints/diagnostics.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,7 @@ func ***~(_: Int, _: String) { }
148148
i ***~ i // expected-error{{cannot convert value of type 'Int' to expected argument type 'String'}}
149149

150150
@available(*, unavailable, message: "call the 'map()' method on the sequence")
151-
public func myMap<C : Collection, T>(
151+
public func myMap<C : Collection, T>( // expected-note {{'myMap' has been explicitly marked unavailable here}}
152152
_ source: C, _ transform: (C.Iterator.Element) -> T
153153
) -> [T] {
154154
fatalError("unavailable function can't be called")
@@ -161,7 +161,7 @@ public func myMap<T, U>(_ x: T?, _ f: (T) -> U) -> U? {
161161

162162
// <rdar://problem/20142523>
163163
func rdar20142523() {
164-
myMap(0..<10, { x in // expected-error{{cannot infer return type for closure with multiple statements; add explicit type to disambiguate}} {{21-21=-> <#Result#> }} {{educational-notes=complex-closure-inference}}
164+
_ = myMap(0..<10, { x in // expected-error {{'myMap' is unavailable: call the 'map()' method on the sequence}}
165165
()
166166
return x
167167
})

test/Constraints/members.swift

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -595,10 +595,10 @@ func rdar50679161() {
595595

596596
func foo() {
597597
_ = { () -> Void in
598+
// Missing `.self` or `init` is not diagnosed here because there are errors in
599+
// `if let` statement and `MiscDiagnostics` only run if the body is completely valid.
598600
var foo = S
599-
// expected-error@-1 {{expected member name or constructor call after type name}}
600-
// expected-note@-2 {{add arguments after the type to construct a value of the type}}
601-
// expected-note@-3 {{use '.self' to reference the type object}}
601+
602602
if let v = Int?(1) {
603603
var _ = Q(
604604
a: v + foo.w,
@@ -610,6 +610,14 @@ func rdar50679161() {
610610
)
611611
}
612612
}
613+
614+
_ = { () -> Void in
615+
var foo = S
616+
// expected-error@-1 {{expected member name or constructor call after type name}}
617+
// expected-note@-2 {{add arguments after the type to construct a value of the type}}
618+
// expected-note@-3 {{use '.self' to reference the type object}}
619+
print(foo)
620+
}
613621
}
614622
}
615623

0 commit comments

Comments
 (0)