Skip to content

Commit d673ed0

Browse files
authored
Merge pull request swiftlang#30519 from xedin/rdar-52204414
[ConstraintSystem] Don't allow explicit closure result to be implicitly converted to `Void`
2 parents 946f234 + 632e1ff commit d673ed0

13 files changed

+191
-63
lines changed

include/swift/AST/TypeCheckRequests.h

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ class AccessorDecl;
3737
enum class AccessorKind;
3838
class ContextualPattern;
3939
class DefaultArgumentExpr;
40+
class ClosureExpr;
4041
class GenericParamList;
4142
class PrecedenceGroupDecl;
4243
struct PropertyWrapperBackingPropertyInfo;
@@ -2141,6 +2142,24 @@ class ScopedImportLookupRequest
21412142
bool isCached() const { return true; }
21422143
};
21432144

2145+
/// Determine whether closure body has any explicit `return`
2146+
/// statements which could produce a non-void result.
2147+
class ClosureHasExplicitResultRequest
2148+
: public SimpleRequest<ClosureHasExplicitResultRequest, bool(ClosureExpr *),
2149+
CacheKind::Cached> {
2150+
public:
2151+
using SimpleRequest::SimpleRequest;
2152+
2153+
private:
2154+
friend SimpleRequest;
2155+
2156+
llvm::Expected<bool> evaluate(Evaluator &evaluator,
2157+
ClosureExpr *closure) const;
2158+
2159+
public:
2160+
bool isCached() const { return true; }
2161+
};
2162+
21442163
// Allow AnyValue to compare two Type values, even though Type doesn't
21452164
// support ==.
21462165
template<>

include/swift/AST/TypeCheckerTypeIDZone.def

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -232,3 +232,5 @@ SWIFT_REQUEST(TypeChecker, PatternTypeRequest,
232232
Cached, HasNearestLocation)
233233
SWIFT_REQUEST(TypeChecker, ScopedImportLookupRequest,
234234
ArrayRef<ValueDecl *>(ImportDecl *), Cached, NoLocationInfo)
235+
SWIFT_REQUEST(TypeChecker, ClosureHasExplicitResultRequest,
236+
bool(ClosureExpr *), Cached, NoLocationInfo)

lib/Sema/CSDiagnostics.cpp

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -659,6 +659,7 @@ bool GenericArgumentsMismatchFailure::diagnoseAsError() {
659659
break;
660660
}
661661

662+
case ConstraintLocator::ClosureBody:
662663
case ConstraintLocator::ClosureResult: {
663664
diagnostic = diag::cannot_convert_closure_result;
664665
break;
@@ -1902,6 +1903,7 @@ bool ContextualFailure::diagnoseAsError() {
19021903

19031904
Diag<Type, Type> diagnostic;
19041905
switch (path.back().getKind()) {
1906+
case ConstraintLocator::ClosureBody:
19051907
case ConstraintLocator::ClosureResult: {
19061908
auto *closure = cast<ClosureExpr>(getRawAnchor());
19071909
if (closure->hasExplicitResultType() &&
@@ -3786,7 +3788,8 @@ bool MissingArgumentsFailure::diagnoseAsError() {
37863788
if (!(locator->isLastElement<LocatorPathElt::ApplyArgToParam>() ||
37873789
locator->isLastElement<LocatorPathElt::ContextualType>() ||
37883790
locator->isLastElement<LocatorPathElt::ApplyArgument>() ||
3789-
locator->isLastElement<LocatorPathElt::ClosureResult>()))
3791+
locator->isLastElement<LocatorPathElt::ClosureResult>() ||
3792+
locator->isLastElement<LocatorPathElt::ClosureBody>()))
37903793
return false;
37913794

37923795
// If this is a misplaced `missng argument` situation, it would be
@@ -4045,7 +4048,8 @@ bool MissingArgumentsFailure::diagnoseClosure(ClosureExpr *closure) {
40454048
if (auto objectType = paramType->getOptionalObjectType())
40464049
paramType = objectType;
40474050
funcType = paramType->getAs<FunctionType>();
4048-
} else if (locator->isLastElement<LocatorPathElt::ClosureResult>()) {
4051+
} else if (locator->isLastElement<LocatorPathElt::ClosureResult>() ||
4052+
locator->isLastElement<LocatorPathElt::ClosureBody>()) {
40494053
// Based on the locator we know this this is something like this:
40504054
// `let _: () -> ((Int) -> Void) = { return {} }`.
40514055
funcType = getType(getRawAnchor())

lib/Sema/CSGen.cpp

Lines changed: 0 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -2197,12 +2197,6 @@ namespace {
21972197
resultTy = CS.createTypeVariable(
21982198
resultLoc,
21992199
closure->hasSingleExpressionBody() ? 0 : TVO_CanBindToHole);
2200-
2201-
if (closureHasNoResult(closure)) {
2202-
// Allow it to default to () if there are no return statements.
2203-
CS.addConstraint(ConstraintKind::Defaultable, resultTy,
2204-
ctx.TheEmptyTupleType, resultLoc);
2205-
}
22062200
}
22072201
}
22082202

@@ -2538,55 +2532,6 @@ namespace {
25382532
return CS.getType(expr->getClosureBody());
25392533
}
25402534

2541-
/// Walk a closure body to determine if it's possible for
2542-
/// it to return with a non-void result.
2543-
static bool closureHasNoResult(ClosureExpr *expr) {
2544-
// A walker that looks for 'return' statements that aren't
2545-
// nested within closures or nested declarations.
2546-
class FindReturns : public ASTWalker {
2547-
bool FoundResultReturn = false;
2548-
bool FoundNoResultReturn = false;
2549-
2550-
std::pair<bool, Expr *> walkToExprPre(Expr *expr) override {
2551-
return { false, expr };
2552-
}
2553-
bool walkToDeclPre(Decl *decl) override {
2554-
return false;
2555-
}
2556-
std::pair<bool, Stmt *> walkToStmtPre(Stmt *stmt) override {
2557-
// Record return statements.
2558-
if (auto ret = dyn_cast<ReturnStmt>(stmt)) {
2559-
// If it has a result, remember that we saw one, but keep
2560-
// traversing in case there's a no-result return somewhere.
2561-
if (ret->hasResult()) {
2562-
FoundResultReturn = true;
2563-
2564-
// Otherwise, stop traversing.
2565-
} else {
2566-
FoundNoResultReturn = true;
2567-
return { false, nullptr };
2568-
}
2569-
}
2570-
return { true, stmt };
2571-
}
2572-
public:
2573-
bool hasNoResult() const {
2574-
return FoundNoResultReturn || !FoundResultReturn;
2575-
}
2576-
};
2577-
2578-
// Don't apply this to single-expression-body closures.
2579-
if (expr->hasSingleExpressionBody())
2580-
return false;
2581-
2582-
auto body = expr->getBody();
2583-
if (!body) return false;
2584-
2585-
FindReturns finder;
2586-
body->walk(finder);
2587-
return finder.hasNoResult();
2588-
}
2589-
25902535
/// Walk a closure AST to determine if it can throw.
25912536
bool closureCanThrow(ClosureExpr *expr) {
25922537
// A walker that looks for 'try' or 'throw' expressions

lib/Sema/CSSimplify.cpp

Lines changed: 26 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3663,6 +3663,7 @@ bool ConstraintSystem::repairFailures(
36633663
break;
36643664
}
36653665

3666+
case ConstraintLocator::ClosureBody:
36663667
case ConstraintLocator::ClosureResult: {
36673668
if (repairViaOptionalUnwrap(*this, lhs, rhs, matchKind, conversionsOrFixes,
36683669
locator))
@@ -4792,9 +4793,21 @@ ConstraintSystem::matchTypes(Type type1, Type type2, ConstraintKind kind,
47924793
// literals and expressions representing an implicit return type of the single
47934794
// expression functions.
47944795
if (auto elt = locator.last()) {
4795-
if (elt->isClosureResult() || elt->isResultOfSingleExprFunction()) {
4796-
if (kind >= ConstraintKind::Subtype &&
4797-
(type1->isUninhabited() || type2->isVoid())) {
4796+
if (kind >= ConstraintKind::Subtype &&
4797+
(type1->isUninhabited() || type2->isVoid())) {
4798+
// A conversion from closure body type to its signature result type.
4799+
if (auto resultElt = elt->getAs<LocatorPathElt::ClosureBody>()) {
4800+
// If a single statement closure has explicit `return` let's
4801+
// forbid conversion to `Void` and report an error instead to
4802+
// honor user's intent.
4803+
if (type1->isUninhabited() || !resultElt->hasExplicitReturn()) {
4804+
increaseScore(SK_FunctionConversion);
4805+
return getTypeMatchSuccess();
4806+
}
4807+
}
4808+
4809+
// Single expression function with implicit `return`.
4810+
if (elt->isResultOfSingleExprFunction()) {
47984811
increaseScore(SK_FunctionConversion);
47994812
return getTypeMatchSuccess();
48004813
}
@@ -6993,18 +7006,26 @@ bool ConstraintSystem::resolveClosure(TypeVariableType *typeVar,
69937006
}
69947007
}
69957008

7009+
bool hasReturn = hasExplicitResult(closure);
7010+
69967011
// If this is a multi-statement closure its body doesn't participate
69977012
// in type-checking.
69987013
if (closure->hasSingleExpressionBody()) {
69997014
auto *closureBody = generateConstraints(closure);
70007015
if (!closureBody)
70017016
return false;
70027017

7003-
// Since result of the closure type has to be r-value type, we have
7004-
// to use equality here.
70057018
addConstraint(
70067019
ConstraintKind::Conversion, getType(closureBody),
70077020
closureType->getResult(),
7021+
getConstraintLocator(closure, LocatorPathElt::ClosureBody(hasReturn)));
7022+
} else if (!hasReturn) {
7023+
// If multi-statement closure doesn't have an explicit result
7024+
// (no `return` statements) let's default it to `Void`.
7025+
auto &ctx = getASTContext();
7026+
addConstraint(
7027+
ConstraintKind::Defaultable, inferredClosureType->getResult(),
7028+
ctx.TheEmptyTupleType,
70087029
getConstraintLocator(closure, ConstraintLocator::ClosureResult));
70097030
}
70107031

lib/Sema/ConstraintLocator.cpp

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,8 @@ void ConstraintLocator::Profile(llvm::FoldingSetNodeID &id, Expr *anchor,
6767
case TypeParameterRequirement:
6868
case ContextualType:
6969
case SynthesizedArgument:
70-
case TernaryBranch: {
70+
case TernaryBranch:
71+
case ClosureBody: {
7172
auto numValues = numNumericValuesInPathElement(elt.getKind());
7273
for (unsigned i = 0; i < numValues; ++i)
7374
id.AddInteger(elt.getValue(i));
@@ -87,6 +88,7 @@ unsigned LocatorPathElt::getNewSummaryFlags() const {
8788
case ConstraintLocator::ApplyFunction:
8889
case ConstraintLocator::SequenceElementType:
8990
case ConstraintLocator::ClosureResult:
91+
case ConstraintLocator::ClosureBody:
9092
case ConstraintLocator::ConstructorMember:
9193
case ConstraintLocator::InstanceType:
9294
case ConstraintLocator::AutoclosureResult:
@@ -327,6 +329,10 @@ void ConstraintLocator::dump(SourceManager *sm, raw_ostream &out) const {
327329
out << "closure result";
328330
break;
329331

332+
case ClosureBody:
333+
out << "type of a closure body";
334+
break;
335+
330336
case ConstructorMember:
331337
out << "constructor member";
332338
break;

lib/Sema/ConstraintLocator.h

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ class ConstraintLocator : public llvm::FoldingSetNode {
7171
case PatternMatch:
7272
return 0;
7373

74+
case ClosureBody:
7475
case ContextualType:
7576
case OpenedGeneric:
7677
case GenericArgument:
@@ -717,6 +718,20 @@ class LocatorPathElt::TypeParameterRequirement final
717718
}
718719
};
719720

721+
class LocatorPathElt::ClosureBody final : public LocatorPathElt {
722+
public:
723+
ClosureBody(bool hasExplicitReturn = false)
724+
: LocatorPathElt(ConstraintLocator::ClosureBody,
725+
hasExplicitReturn) {}
726+
727+
/// Indicates whether body of the closure has any `return` statements.
728+
bool hasExplicitReturn() const { return bool(getValue(0)); }
729+
730+
static bool classof(const LocatorPathElt *elt) {
731+
return elt->getKind() == ConstraintLocator::ClosureBody;
732+
}
733+
};
734+
720735
class LocatorPathElt::ContextualType final : public LocatorPathElt {
721736
public:
722737
ContextualType(bool isForSingleExprFunction = false)

lib/Sema/ConstraintLocatorPathElts.def

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,9 @@ SIMPLE_LOCATOR_PATH_ELT(AutoclosureResult)
5454
/// The result of a closure.
5555
SIMPLE_LOCATOR_PATH_ELT(ClosureResult)
5656

57+
/// The type of a closure body.
58+
CUSTOM_LOCATOR_PATH_ELT(ClosureBody)
59+
5760
/// The lookup for a constructor member.
5861
SIMPLE_LOCATOR_PATH_ELT(ConstructorMember)
5962

lib/Sema/ConstraintSystem.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
#include "swift/AST/Initializer.h"
2525
#include "swift/AST/GenericEnvironment.h"
2626
#include "swift/AST/ParameterList.h"
27+
#include "swift/AST/TypeCheckRequests.h"
2728
#include "swift/Basic/Statistic.h"
2829
#include "llvm/ADT/SetVector.h"
2930
#include "llvm/ADT/SmallString.h"
@@ -3433,6 +3434,7 @@ void constraints::simplifyLocator(Expr *&anchor,
34333434
}
34343435
break;
34353436

3437+
case ConstraintLocator::ClosureBody:
34363438
case ConstraintLocator::ClosureResult:
34373439
if (auto CE = dyn_cast<ClosureExpr>(anchor)) {
34383440
if (CE->hasSingleExpressionBody()) {
@@ -3855,6 +3857,12 @@ bool constraints::isKnownKeyPathDecl(ASTContext &ctx, ValueDecl *decl) {
38553857
decl == ctx.getPartialKeyPathDecl() || decl == ctx.getAnyKeyPathDecl();
38563858
}
38573859

3860+
bool constraints::hasExplicitResult(ClosureExpr *closure) {
3861+
auto &ctx = closure->getASTContext();
3862+
return evaluateOrDefault(ctx.evaluator,
3863+
ClosureHasExplicitResultRequest{closure}, false);
3864+
}
3865+
38583866
static bool isOperator(Expr *expr, StringRef expectedName) {
38593867
auto name = getOperatorName(expr);
38603868
return name ? name->is(expectedName) : false;

lib/Sema/ConstraintSystem.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5127,6 +5127,11 @@ bool isKnownKeyPathType(Type type);
51275127
/// Determine whether given declaration is one for a key path
51285128
/// `{Writable, ReferenceWritable}KeyPath`.
51295129
bool isKnownKeyPathDecl(ASTContext &ctx, ValueDecl *decl);
5130+
5131+
/// Determine whether givne closure has any explicit `return`
5132+
/// statements that could produce non-void result.
5133+
bool hasExplicitResult(ClosureExpr *closure);
5134+
51305135
} // end namespace constraints
51315136

51325137
template<typename ...Args>

0 commit comments

Comments
 (0)