Skip to content

Commit 63f355f

Browse files
authored
Merge pull request #39989 from xedin/more-diag-improvements-for-multi-stmt-closures
[TypeChecker] SE-0326: Enable multi-statement closure inference by default
2 parents 4f8584f + b231cde commit 63f355f

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

49 files changed

+480
-247
lines changed

include/swift/Basic/LangOptions.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -682,7 +682,7 @@ namespace swift {
682682

683683
/// Enable experimental support for type inference through multi-statement
684684
/// closures.
685-
bool EnableMultiStatementClosureInference = false;
685+
bool EnableMultiStatementClosureInference = true;
686686

687687
/// See \ref FrontendOptions.PrintFullConvention
688688
bool PrintFullConvention = false;

include/swift/Sema/CSBindings.h

+8
Original file line numberDiff line numberDiff line change
@@ -338,6 +338,14 @@ class BindingSet {
338338

339339
TypeVariableType *getTypeVariable() const { return Info.TypeVar; }
340340

341+
/// Check whether this binding set belongs to a type variable
342+
/// that represents a result type of a closure.
343+
bool forClosureResult() const;
344+
345+
/// Check whether this binding set belongs to a type variable
346+
/// that represents a generic parameter.
347+
bool forGenericParameter() const;
348+
341349
bool canBeNil() const;
342350

343351
/// If this type variable doesn't have any viable bindings, or

include/swift/Sema/CSFix.h

+4
Original file line numberDiff line numberDiff line change
@@ -2282,6 +2282,10 @@ class SpecifyClosureParameterType final : public ConstraintFix {
22822282

22832283
bool diagnose(const Solution &solution, bool asNote = false) const override;
22842284

2285+
bool diagnoseForAmbiguity(CommonFixesArray commonFixes) const override {
2286+
return diagnose(*commonFixes.front().first);
2287+
}
2288+
22852289
static SpecifyClosureParameterType *create(ConstraintSystem &cs,
22862290
ConstraintLocator *locator);
22872291

include/swift/Sema/Constraint.h

+12-3
Original file line numberDiff line numberDiff line change
@@ -436,6 +436,8 @@ class Constraint final : public llvm::ilist_node<Constraint>,
436436
ASTNode Element;
437437
/// Contextual information associated with the element (if any).
438438
ContextualTypeInfo Context;
439+
/// Identifies whether result of this node is unused.
440+
bool IsDiscarded;
439441
} ClosureElement;
440442
};
441443

@@ -489,7 +491,7 @@ class Constraint final : public llvm::ilist_node<Constraint>,
489491
SmallPtrSetImpl<TypeVariableType *> &typeVars);
490492

491493
/// Construct a closure body element constraint.
492-
Constraint(ASTNode node, ContextualTypeInfo context,
494+
Constraint(ASTNode node, ContextualTypeInfo context, bool isDiscarded,
493495
ConstraintLocator *locator,
494496
SmallPtrSetImpl<TypeVariableType *> &typeVars);
495497

@@ -579,12 +581,14 @@ class Constraint final : public llvm::ilist_node<Constraint>,
579581

580582
static Constraint *createClosureBodyElement(ConstraintSystem &cs,
581583
ASTNode node,
582-
ConstraintLocator *locator);
584+
ConstraintLocator *locator,
585+
bool isDiscarded = false);
583586

584587
static Constraint *createClosureBodyElement(ConstraintSystem &cs,
585588
ASTNode node,
586589
ContextualTypeInfo context,
587-
ConstraintLocator *locator);
590+
ConstraintLocator *locator,
591+
bool isDiscarded = false);
588592

589593
/// Determine the kind of constraint.
590594
ConstraintKind getKind() const { return Kind; }
@@ -850,6 +854,11 @@ class Constraint final : public llvm::ilist_node<Constraint>,
850854
return ClosureElement.Context;
851855
}
852856

857+
bool isDiscardedElement() const {
858+
assert(Kind == ConstraintKind::ClosureBodyElement);
859+
return ClosureElement.IsDiscarded;
860+
}
861+
853862
/// For an applicable function constraint, retrieve the trailing closure
854863
/// matching rule.
855864
Optional<TrailingClosureMatching> getTrailingClosureMatching() const;

include/swift/Sema/ConstraintSystem.h

+14-15
Original file line numberDiff line numberDiff line change
@@ -4845,8 +4845,8 @@ class ConstraintSystem {
48454845
/// Simplify a closure body element constraint by generating required
48464846
/// constraints to represent the given element in constraint system.
48474847
SolutionKind simplifyClosureBodyElementConstraint(
4848-
ASTNode element, ContextualTypeInfo context, TypeMatchOptions flags,
4849-
ConstraintLocatorBuilder locator);
4848+
ASTNode element, ContextualTypeInfo context, bool isDiscarded,
4849+
TypeMatchOptions flags, ConstraintLocatorBuilder locator);
48504850

48514851
public: // FIXME: Public for use by static functions.
48524852
/// Simplify a conversion constraint with a fix applied to it.
@@ -5005,7 +5005,8 @@ class ConstraintSystem {
50055005
/// \param replaceInvalidRefsWithErrors Indicates whether it's allowed
50065006
/// to replace any discovered invalid member references with `ErrorExpr`.
50075007
static bool preCheckExpression(Expr *&expr, DeclContext *dc,
5008-
bool replaceInvalidRefsWithErrors);
5008+
bool replaceInvalidRefsWithErrors,
5009+
bool leaveClosureBodiesUnchecked);
50095010

50105011
/// Solve the system of constraints generated from provided target.
50115012
///
@@ -5236,6 +5237,16 @@ class ConstraintSystem {
52365237
/// imported from C/ObjectiveC.
52375238
bool isArgumentOfImportedDecl(ConstraintLocatorBuilder locator);
52385239

5240+
/// Check whether given closure should participate in inference e.g.
5241+
/// if it's a single-expression closure - it always does, but
5242+
/// multi-statement closures require special flags.
5243+
bool participatesInInference(ClosureExpr *closure) const;
5244+
5245+
/// Visit each subexpression that will be part of the constraint system
5246+
/// of the given expression, including those in closure bodies that will be
5247+
/// part of the constraint system.
5248+
void forEachExpr(Expr *expr, llvm::function_ref<Expr *(Expr *)> callback);
5249+
52395250
SWIFT_DEBUG_DUMP;
52405251
SWIFT_DEBUG_DUMPER(dump(Expr *));
52415252

@@ -6059,18 +6070,6 @@ BraceStmt *applyResultBuilderTransform(
60596070
constraints::SolutionApplicationTarget)>
60606071
rewriteTarget);
60616072

6062-
/// Determine whether the given closure expression should be type-checked
6063-
/// within the context of its enclosing expression. Otherwise, it will be
6064-
/// separately type-checked once its enclosing expression has determined all
6065-
/// of the parameter and result types without looking at the body.
6066-
bool shouldTypeCheckInEnclosingExpression(ClosureExpr *expr);
6067-
6068-
/// Visit each subexpression that will be part of the constraint system
6069-
/// of the given expression, including those in closure bodies that will be
6070-
/// part of the constraint system.
6071-
void forEachExprInConstraintSystem(
6072-
Expr *expr, llvm::function_ref<Expr *(Expr *)> callback);
6073-
60746073
/// Whether the given parameter requires an argument.
60756074
bool parameterRequiresArgument(
60766075
ArrayRef<AnyFunctionType::Param> params,

include/swift/Sema/IDETypeChecking.h

+2-1
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,8 @@ namespace swift {
4747
struct PrintOptions;
4848

4949
/// Typecheck binding initializer at \p bindingIndex.
50-
void typeCheckPatternBinding(PatternBindingDecl *PBD, unsigned bindingIndex);
50+
void typeCheckPatternBinding(PatternBindingDecl *PBD, unsigned bindingIndex,
51+
bool leaveClosureBodiesUnchecked);
5152

5253
/// Check if T1 is convertible to T2.
5354
///

lib/IDE/ExprContextAnalysis.cpp

+2-1
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,8 @@ void swift::ide::typeCheckContextAt(DeclContext *DC, SourceLoc Loc) {
9898
[](VarDecl *VD) { (void)VD->getInterfaceType(); });
9999
if (PBD->getInit(i)) {
100100
if (!PBD->isInitializerChecked(i))
101-
typeCheckPatternBinding(PBD, i);
101+
typeCheckPatternBinding(PBD, i,
102+
/*LeaveClosureBodyUnchecked=*/true);
102103
}
103104
}
104105
} else if (auto *defaultArg = dyn_cast<DefaultArgumentInitializer>(DC)) {

lib/Sema/BuilderTransform.cpp

+3-1
Original file line numberDiff line numberDiff line change
@@ -1932,7 +1932,9 @@ class PreCheckResultBuilderApplication : public ASTWalker {
19321932
DiagnosticTransaction transaction(diagEngine);
19331933

19341934
HasError |= ConstraintSystem::preCheckExpression(
1935-
E, DC, /*replaceInvalidRefsWithErrors=*/true);
1935+
E, DC, /*replaceInvalidRefsWithErrors=*/true,
1936+
/*leaveClosureBodiesUnchecked=*/false);
1937+
19361938
HasError |= transaction.hasErrors();
19371939

19381940
if (!HasError)

lib/Sema/CSApply.cpp

+1-2
Original file line numberDiff line numberDiff line change
@@ -4056,8 +4056,7 @@ namespace {
40564056
if (expr->isLiteralInit()) {
40574057
auto *literalInit = expr->getSubExpr();
40584058
if (auto *call = dyn_cast<CallExpr>(literalInit)) {
4059-
forEachExprInConstraintSystem(call->getFn(),
4060-
[&](Expr *subExpr) -> Expr * {
4059+
cs.forEachExpr(call->getFn(), [&](Expr *subExpr) -> Expr * {
40614060
auto *TE = dyn_cast<TypeExpr>(subExpr);
40624061
if (!TE)
40634062
return subExpr;

lib/Sema/CSBindings.cpp

+8
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,14 @@ using namespace swift;
2525
using namespace constraints;
2626
using namespace inference;
2727

28+
bool BindingSet::forClosureResult() const {
29+
return Info.TypeVar->getImpl().isClosureResultType();
30+
}
31+
32+
bool BindingSet::forGenericParameter() const {
33+
return bool(Info.TypeVar->getImpl().getGenericParameter());
34+
}
35+
2836
bool BindingSet::canBeNil() const {
2937
auto &ctx = CS.getASTContext();
3038
return Literals.count(

lib/Sema/CSClosure.cpp

+55-27
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
//
1717
//===----------------------------------------------------------------------===//
1818

19+
#include "MiscDiagnostics.h"
1920
#include "TypeChecker.h"
2021
#include "swift/Sema/ConstraintSystem.h"
2122

@@ -162,8 +163,8 @@ static bool isViableElement(ASTNode element) {
162163
return true;
163164
}
164165

165-
using ElementInfo =
166-
std::tuple<ASTNode, ContextualTypeInfo, ConstraintLocator *>;
166+
using ElementInfo = std::tuple<ASTNode, ContextualTypeInfo,
167+
/*isDiscarded=*/bool, ConstraintLocator *>;
167168

168169
static void createConjunction(ConstraintSystem &cs,
169170
ArrayRef<ElementInfo> elements,
@@ -190,7 +191,8 @@ static void createConjunction(ConstraintSystem &cs,
190191
for (const auto &entry : elements) {
191192
ASTNode element = std::get<0>(entry);
192193
ContextualTypeInfo context = std::get<1>(entry);
193-
ConstraintLocator *elementLoc = std::get<2>(entry);
194+
bool isDiscarded = std::get<2>(entry);
195+
ConstraintLocator *elementLoc = std::get<3>(entry);
194196

195197
if (!isViableElement(element))
196198
continue;
@@ -201,8 +203,8 @@ static void createConjunction(ConstraintSystem &cs,
201203
if (isIsolated)
202204
element.walk(paramCollector);
203205

204-
constraints.push_back(
205-
Constraint::createClosureBodyElement(cs, element, context, elementLoc));
206+
constraints.push_back(Constraint::createClosureBodyElement(
207+
cs, element, context, elementLoc, isDiscarded));
206208
}
207209

208210
// It's possible that there are no viable elements in the body,
@@ -220,8 +222,9 @@ static void createConjunction(ConstraintSystem &cs,
220222
}
221223

222224
ElementInfo makeElement(ASTNode node, ConstraintLocator *locator,
223-
ContextualTypeInfo context = ContextualTypeInfo()) {
224-
return std::make_tuple(node, context, locator);
225+
ContextualTypeInfo context = ContextualTypeInfo(),
226+
bool isDiscarded = false) {
227+
return std::make_tuple(node, context, isDiscarded, locator);
225228
}
226229

227230
static ProtocolDecl *getSequenceProtocol(ASTContext &ctx, SourceLoc loc,
@@ -751,6 +754,8 @@ class ClosureConstraintGenerator
751754

752755
void visitBraceStmt(BraceStmt *braceStmt) {
753756
if (isSupportedMultiStatementClosure()) {
757+
auto &ctx = cs.getASTContext();
758+
754759
if (isChildOf(StmtKind::Case)) {
755760
auto *caseStmt = cast<CaseStmt>(
756761
locator->castLastElementTo<LocatorPathElt::ClosureBodyElement>()
@@ -765,10 +770,15 @@ class ClosureConstraintGenerator
765770

766771
SmallVector<ElementInfo, 4> elements;
767772
for (auto element : braceStmt->getElements()) {
773+
bool isDiscarded =
774+
element.is<Expr *>() &&
775+
(!ctx.LangOpts.Playground && !ctx.LangOpts.DebuggerSupport);
776+
768777
elements.push_back(makeElement(
769778
element,
770779
cs.getConstraintLocator(
771-
locator, LocatorPathElt::ClosureBodyElement(element))));
780+
locator, LocatorPathElt::ClosureBodyElement(element)),
781+
/*contextualInfo=*/{}, isDiscarded));
772782
}
773783

774784
createConjunction(cs, elements, locator);
@@ -845,7 +855,7 @@ class ClosureConstraintGenerator
845855

846856
bool isSupportedMultiStatementClosure() const {
847857
return !closure->hasSingleExpressionBody() &&
848-
shouldTypeCheckInEnclosingExpression(closure);
858+
cs.participatesInInference(closure);
849859
}
850860

851861
#define UNSUPPORTED_STMT(STMT) void visit##STMT##Stmt(STMT##Stmt *) { \
@@ -876,7 +886,7 @@ class ClosureConstraintGenerator
876886
bool ConstraintSystem::generateConstraints(ClosureExpr *closure) {
877887
auto &ctx = closure->getASTContext();
878888

879-
if (shouldTypeCheckInEnclosingExpression(closure)) {
889+
if (participatesInInference(closure)) {
880890
ClosureConstraintGenerator generator(*this, closure,
881891
getConstraintLocator(closure));
882892
generator.visit(closure->getBody());
@@ -925,28 +935,22 @@ bool isConditionOfStmt(ConstraintLocatorBuilder locator) {
925935

926936
ConstraintSystem::SolutionKind
927937
ConstraintSystem::simplifyClosureBodyElementConstraint(
928-
ASTNode element, ContextualTypeInfo context, TypeMatchOptions flags,
929-
ConstraintLocatorBuilder locator) {
938+
ASTNode element, ContextualTypeInfo context, bool isDiscarded,
939+
TypeMatchOptions flags, ConstraintLocatorBuilder locator) {
930940
auto *closure = castToExpr<ClosureExpr>(locator.getAnchor());
931941

932942
ClosureConstraintGenerator generator(*this, closure,
933943
getConstraintLocator(locator));
934944

935945
if (auto *expr = element.dyn_cast<Expr *>()) {
936-
if (context.purpose != CTP_Unused) {
937-
SolutionApplicationTarget target(expr, closure, context.purpose,
938-
context.getType(),
939-
/*isDiscarded=*/false);
940-
941-
if (generateConstraints(target, FreeTypeVariableBinding::Disallow))
942-
return SolutionKind::Error;
943-
944-
setSolutionApplicationTarget(expr, target);
945-
return SolutionKind::Solved;
946-
}
946+
SolutionApplicationTarget target(expr, closure, context.purpose,
947+
context.getType(), isDiscarded);
947948

948-
if (!generateConstraints(expr, closure, /*isInputExpression=*/false))
949+
if (generateConstraints(target, FreeTypeVariableBinding::Disallow))
949950
return SolutionKind::Error;
951+
952+
setSolutionApplicationTarget(expr, target);
953+
return SolutionKind::Solved;
950954
} else if (auto *stmt = element.dyn_cast<Stmt *>()) {
951955
generator.visit(stmt);
952956
} else if (auto *cond = element.dyn_cast<StmtCondition *>()) {
@@ -1160,12 +1164,17 @@ class ClosureConstraintApplication
11601164

11611165
auto forEachTarget =
11621166
rewriteTarget(*cs.getSolutionApplicationTarget(forEachStmt));
1167+
11631168
if (!forEachTarget)
11641169
hadError = true;
11651170

11661171
auto body = visit(forEachStmt->getBody()).get<Stmt *>();
11671172
forEachStmt->setBody(cast<BraceStmt>(body));
11681173

1174+
// Check to see if the sequence expr is throwing (in async context),
1175+
// if so require the stmt to have a `try`.
1176+
hadError |= diagnoseUnhandledThrowsInAsyncContext(closure, forEachStmt);
1177+
11691178
return forEachStmt;
11701179
}
11711180

@@ -1241,13 +1250,32 @@ class ClosureConstraintApplication
12411250
}
12421251

12431252
ASTNode visitBraceStmt(BraceStmt *braceStmt) {
1253+
auto &cs = solution.getConstraintSystem();
1254+
1255+
// Diagnose defer statement being last one in block.
1256+
if (!braceStmt->empty()) {
1257+
if (auto stmt = braceStmt->getLastElement().dyn_cast<Stmt *>()) {
1258+
if (auto deferStmt = dyn_cast<DeferStmt>(stmt)) {
1259+
auto &diags = closure->getASTContext().Diags;
1260+
diags
1261+
.diagnose(deferStmt->getStartLoc(), diag::defer_stmt_at_block_end)
1262+
.fixItReplace(deferStmt->getStartLoc(), "do");
1263+
}
1264+
}
1265+
}
1266+
12441267
for (auto &node : braceStmt->getElements()) {
12451268
if (auto expr = node.dyn_cast<Expr *>()) {
12461269
// Rewrite the expression.
1247-
if (auto rewrittenExpr = rewriteExpr(expr))
1248-
node = rewrittenExpr;
1249-
else
1270+
auto target = *cs.getSolutionApplicationTarget(expr);
1271+
if (auto rewrittenTarget = rewriteTarget(target)) {
1272+
node = rewrittenTarget->getAsExpr();
1273+
1274+
if (target.isDiscardedExpr())
1275+
TypeChecker::checkIgnoredExpr(castToExpr(node));
1276+
} else {
12501277
hadError = true;
1278+
}
12511279
} else if (auto stmt = node.dyn_cast<Stmt *>()) {
12521280
node = visit(stmt);
12531281
} else {

0 commit comments

Comments
 (0)