Skip to content

Commit 8303319

Browse files
committed
[TypeChecker] Fix constraint solver to respect LeaveClosureBodyUnchecked flag
1 parent 3927f56 commit 8303319

11 files changed

+84
-52
lines changed

include/swift/Sema/ConstraintSystem.h

Lines changed: 12 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -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,

lib/Sema/BuilderTransform.cpp

Lines changed: 3 additions & 1 deletion
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=*/true);
1937+
19361938
HasError |= transaction.hasErrors();
19371939

19381940
if (!HasError)

lib/Sema/CSApply.cpp

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4107,8 +4107,7 @@ namespace {
41074107
if (expr->isLiteralInit()) {
41084108
auto *literalInit = expr->getSubExpr();
41094109
if (auto *call = dyn_cast<CallExpr>(literalInit)) {
4110-
forEachExprInConstraintSystem(call->getFn(),
4111-
[&](Expr *subExpr) -> Expr * {
4110+
cs.forEachExpr(call->getFn(), [&](Expr *subExpr) -> Expr * {
41124111
auto *TE = dyn_cast<TypeExpr>(subExpr);
41134112
if (!TE)
41144113
return subExpr;

lib/Sema/CSClosure.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -854,7 +854,7 @@ class ClosureConstraintGenerator
854854

855855
bool isSupportedMultiStatementClosure() const {
856856
return !closure->hasSingleExpressionBody() &&
857-
shouldTypeCheckInEnclosingExpression(closure);
857+
cs.participatesInInference(closure);
858858
}
859859

860860
#define UNSUPPORTED_STMT(STMT) void visit##STMT##Stmt(STMT##Stmt *) { \
@@ -885,7 +885,7 @@ class ClosureConstraintGenerator
885885
bool ConstraintSystem::generateConstraints(ClosureExpr *closure) {
886886
auto &ctx = closure->getASTContext();
887887

888-
if (shouldTypeCheckInEnclosingExpression(closure)) {
888+
if (participatesInInference(closure)) {
889889
ClosureConstraintGenerator generator(*this, closure,
890890
getConstraintLocator(closure));
891891
generator.visit(closure->getBody());

lib/Sema/CSDiagnostics.cpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -463,7 +463,8 @@ bool MissingConformanceFailure::diagnoseAsError() {
463463
}
464464

465465
bool hasFix = false;
466-
forEachExprInConstraintSystem(caseExpr, [&](Expr *expr) -> Expr * {
466+
auto &cs = getConstraintSystem();
467+
cs.forEachExpr(caseExpr, [&](Expr *expr) -> Expr * {
467468
hasFix |= anchors.count(expr);
468469
return hasFix ? nullptr : expr;
469470
});
@@ -3591,7 +3592,8 @@ bool MissingMemberFailure::diagnoseAsError() {
35913592

35923593
bool hasUnresolvedPattern = false;
35933594
if (auto *E = getAsExpr(anchor)) {
3594-
forEachExprInConstraintSystem(const_cast<Expr *>(E), [&](Expr *expr) {
3595+
auto &cs = getConstraintSystem();
3596+
cs.forEachExpr(const_cast<Expr *>(E), [&](Expr *expr) {
35953597
hasUnresolvedPattern |= isa<UnresolvedPatternExpr>(expr);
35963598
return hasUnresolvedPattern ? nullptr : expr;
35973599
});

lib/Sema/CSGen.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2090,7 +2090,7 @@ namespace {
20902090
// If this is a multi-statement closure, let's mark result
20912091
// as potential hole right away.
20922092
return Type(CS.createTypeVariable(
2093-
resultLocator, shouldTypeCheckInEnclosingExpression(closure)
2093+
resultLocator, CS.participatesInInference(closure)
20942094
? 0
20952095
: TVO_CanBindToHole));
20962096
}();
@@ -2612,7 +2612,7 @@ namespace {
26122612
// genreation only if closure is going to participate
26132613
// in the type-check. This allows us to delay validation of
26142614
// multi-statement closures until body is opened.
2615-
if (shouldTypeCheckInEnclosingExpression(closure) &&
2615+
if (CS.participatesInInference(closure) &&
26162616
collectVarRefs.hasErrorExprs) {
26172617
return Type();
26182618
}

lib/Sema/CSSimplify.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2011,7 +2011,7 @@ static bool fixMissingArguments(ConstraintSystem &cs, ASTNode anchor,
20112011

20122012
// Something like `foo { x in }` or `foo { $0 }`
20132013
if (auto *closure = getAsExpr<ClosureExpr>(anchor)) {
2014-
forEachExprInConstraintSystem(closure, [&](Expr *expr) -> Expr * {
2014+
cs.forEachExpr(closure, [&](Expr *expr) -> Expr * {
20152015
if (auto *UDE = dyn_cast<UnresolvedDotExpr>(expr)) {
20162016
if (!isParam(UDE->getBase()))
20172017
return expr;
@@ -11546,7 +11546,7 @@ bool ConstraintSystem::recordFix(ConstraintFix *fix, unsigned impact) {
1154611546

1154711547
bool found = false;
1154811548
if (auto *expr = getAsExpr(anchor)) {
11549-
forEachExprInConstraintSystem(expr, [&](Expr *subExpr) -> Expr * {
11549+
forEachExpr(expr, [&](Expr *subExpr) -> Expr * {
1155011550
found |= anchors.count(subExpr);
1155111551
return subExpr;
1155211552
});

lib/Sema/ConstraintSystem.cpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5845,6 +5845,18 @@ bool ConstraintSystem::isReadOnlyKeyPathComponent(
58455845
return false;
58465846
}
58475847

5848+
bool ConstraintSystem::participatesInInference(ClosureExpr *closure) const {
5849+
if (closure->hasSingleExpressionBody())
5850+
return true;
5851+
5852+
if (Options.contains(ConstraintSystemFlags::LeaveClosureBodyUnchecked))
5853+
return false;
5854+
5855+
auto &ctx = closure->getASTContext();
5856+
return !closure->hasEmptyBody() &&
5857+
ctx.TypeCheckerOpts.EnableMultiStatementClosureInference;
5858+
}
5859+
58485860
TypeVarBindingProducer::TypeVarBindingProducer(BindingSet &bindings)
58495861
: BindingProducer(bindings.getConstraintSystem(),
58505862
bindings.getTypeVariable()->getImpl().getLocator()),

lib/Sema/PreCheckExpr.cpp

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -894,6 +894,8 @@ namespace {
894894
/// implicit `ErrorExpr` in place of invalid references.
895895
bool UseErrorExprs;
896896

897+
bool LeaveClosureBodiesUnchecked;
898+
897899
/// A stack of expressions being walked, used to determine where to
898900
/// insert RebindSelfInConstructorExpr nodes.
899901
llvm::SmallVector<Expr *, 8> ExprStack;
@@ -1081,9 +1083,11 @@ namespace {
10811083

10821084
public:
10831085
PreCheckExpression(DeclContext *dc, Expr *parent,
1084-
bool replaceInvalidRefsWithErrors)
1085-
: Ctx(dc->getASTContext()), DC(dc), ParentExpr(parent),
1086-
UseErrorExprs(replaceInvalidRefsWithErrors) {}
1086+
bool replaceInvalidRefsWithErrors,
1087+
bool leaveClosureBodiesUnchecked)
1088+
: Ctx(dc->getASTContext()), DC(dc),
1089+
ParentExpr(parent), UseErrorExprs(replaceInvalidRefsWithErrors),
1090+
LeaveClosureBodiesUnchecked(leaveClosureBodiesUnchecked) {}
10871091

10881092
ASTContext &getASTContext() const { return Ctx; }
10891093

@@ -1429,8 +1433,13 @@ namespace {
14291433
/// true when we want the body to be considered part of this larger expression.
14301434
bool PreCheckExpression::walkToClosureExprPre(ClosureExpr *closure) {
14311435
// If we won't be checking the body of the closure, don't walk into it here.
1432-
if (!shouldTypeCheckInEnclosingExpression(closure))
1433-
return false;
1436+
if (!closure->hasSingleExpressionBody()) {
1437+
if (LeaveClosureBodiesUnchecked)
1438+
return false;
1439+
1440+
if (!Ctx.TypeCheckerOpts.EnableMultiStatementClosureInference)
1441+
return false;
1442+
}
14341443

14351444
// Update the current DeclContext to be the closure we're about to
14361445
// recurse into.
@@ -2086,11 +2095,15 @@ Expr *PreCheckExpression::simplifyTypeConstructionWithLiteralArg(Expr *E) {
20862095
/// Pre-check the expression, validating any types that occur in the
20872096
/// expression and folding sequence expressions.
20882097
bool ConstraintSystem::preCheckExpression(Expr *&expr, DeclContext *dc,
2089-
bool replaceInvalidRefsWithErrors) {
2098+
bool replaceInvalidRefsWithErrors,
2099+
bool leaveClosureBodiesUnchecked) {
20902100
auto &ctx = dc->getASTContext();
20912101
FrontendStatsTracer StatsTracer(ctx.Stats, "precheck-expr", expr);
20922102

2093-
PreCheckExpression preCheck(dc, expr, replaceInvalidRefsWithErrors);
2103+
PreCheckExpression preCheck(dc, expr,
2104+
replaceInvalidRefsWithErrors,
2105+
leaveClosureBodiesUnchecked);
2106+
20942107
// Perform the pre-check.
20952108
if (auto result = expr->walk(preCheck)) {
20962109
expr = result;

lib/Sema/TypeCheckCodeCompletion.cpp

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -236,7 +236,9 @@ class SanitizeExpr : public ASTWalker {
236236
// If this is a closure, only walk into its children if they
237237
// are type-checked in the context of the enclosing expression.
238238
if (auto closure = dyn_cast<ClosureExpr>(expr)) {
239-
if (!shouldTypeCheckInEnclosingExpression(closure))
239+
// TODO: This has to be deleted once `EnableMultiStatementClosureInference`
240+
// is enabled by default.
241+
if (!closure->hasSingleExpressionBody())
240242
return { false, expr };
241243
for (auto &Param : *closure->getParameters()) {
242244
Param->setSpecifier(swift::ParamSpecifier::Default);
@@ -335,8 +337,11 @@ getTypeOfExpressionWithoutApplying(Expr *&expr, DeclContext *dc,
335337
PrettyStackTraceExpr stackTrace(Context, "type-checking", expr);
336338
referencedDecl = nullptr;
337339

340+
ConstraintSystemOptions options;
341+
options |= ConstraintSystemFlags::SuppressDiagnostics;
342+
338343
// Construct a constraint system from this expression.
339-
ConstraintSystem cs(dc, ConstraintSystemFlags::SuppressDiagnostics);
344+
ConstraintSystem cs(dc, options);
340345

341346
// Attempt to solve the constraint system.
342347
const Type originalType = expr->getType();
@@ -818,7 +823,8 @@ bool TypeChecker::typeCheckForCodeCompletion(
818823
// expression and folding sequence expressions.
819824
auto failedPreCheck = ConstraintSystem::preCheckExpression(
820825
expr, DC,
821-
/*replaceInvalidRefsWithErrors=*/true);
826+
/*replaceInvalidRefsWithErrors=*/true,
827+
/*leaveClosureBodiesUnchecked=*/true);
822828

823829
target.setExpr(expr);
824830

@@ -922,7 +928,9 @@ static Optional<Type> getTypeOfCompletionContextExpr(
922928
Expr *&parsedExpr,
923929
ConcreteDeclRef &referencedDecl) {
924930
if (constraints::ConstraintSystem::preCheckExpression(
925-
parsedExpr, DC, /*replaceInvalidRefsWithErrors=*/true))
931+
parsedExpr, DC,
932+
/*replaceInvalidRefsWithErrors=*/true,
933+
/*leaveClosureBodiesUnchecked=*/true))
926934
return None;
927935

928936
switch (kind) {

0 commit comments

Comments
 (0)