Skip to content

Commit 0e6e058

Browse files
committed
[TypeChecker] Fix constraint solver to respect LeaveClosureBodyUnchecked flag
1 parent 46ff410 commit 0e6e058

11 files changed

+84
-52
lines changed

include/swift/Sema/ConstraintSystem.h

+12-13
Original file line numberDiff line numberDiff line change
@@ -5014,7 +5014,8 @@ class ConstraintSystem {
50145014
/// \param replaceInvalidRefsWithErrors Indicates whether it's allowed
50155015
/// to replace any discovered invalid member references with `ErrorExpr`.
50165016
static bool preCheckExpression(Expr *&expr, DeclContext *dc,
5017-
bool replaceInvalidRefsWithErrors);
5017+
bool replaceInvalidRefsWithErrors,
5018+
bool leaveClosureBodiesUnchecked);
50185019

50195020
/// Solve the system of constraints generated from provided target.
50205021
///
@@ -5245,6 +5246,16 @@ class ConstraintSystem {
52455246
/// imported from C/ObjectiveC.
52465247
bool isArgumentOfImportedDecl(ConstraintLocatorBuilder locator);
52475248

5249+
/// Check whether given closure should participate in inference e.g.
5250+
/// if it's a single-expression closure - it always does, but
5251+
/// multi-statement closures require special flags.
5252+
bool participatesInInference(ClosureExpr *closure) const;
5253+
5254+
/// Visit each subexpression that will be part of the constraint system
5255+
/// of the given expression, including those in closure bodies that will be
5256+
/// part of the constraint system.
5257+
void forEachExpr(Expr *expr, llvm::function_ref<Expr *(Expr *)> callback);
5258+
52485259
SWIFT_DEBUG_DUMP;
52495260
SWIFT_DEBUG_DUMPER(dump(Expr *));
52505261

@@ -6069,18 +6080,6 @@ BraceStmt *applyResultBuilderTransform(
60696080
constraints::SolutionApplicationTarget)>
60706081
rewriteTarget);
60716082

6072-
/// Determine whether the given closure expression should be type-checked
6073-
/// within the context of its enclosing expression. Otherwise, it will be
6074-
/// separately type-checked once its enclosing expression has determined all
6075-
/// of the parameter and result types without looking at the body.
6076-
bool shouldTypeCheckInEnclosingExpression(ClosureExpr *expr);
6077-
6078-
/// Visit each subexpression that will be part of the constraint system
6079-
/// of the given expression, including those in closure bodies that will be
6080-
/// part of the constraint system.
6081-
void forEachExprInConstraintSystem(
6082-
Expr *expr, llvm::function_ref<Expr *(Expr *)> callback);
6083-
60846083
/// Whether the given parameter requires an argument.
60856084
bool parameterRequiresArgument(
60866085
ArrayRef<AnyFunctionType::Param> params,

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=*/true);
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/CSClosure.cpp

+2-2
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

+4-2
Original file line numberDiff line numberDiff line change
@@ -466,7 +466,8 @@ bool MissingConformanceFailure::diagnoseAsError() {
466466
}
467467

468468
bool hasFix = false;
469-
forEachExprInConstraintSystem(caseExpr, [&](Expr *expr) -> Expr * {
469+
auto &cs = getConstraintSystem();
470+
cs.forEachExpr(caseExpr, [&](Expr *expr) -> Expr * {
470471
hasFix |= anchors.count(expr);
471472
return hasFix ? nullptr : expr;
472473
});
@@ -3594,7 +3595,8 @@ bool MissingMemberFailure::diagnoseAsError() {
35943595

35953596
bool hasUnresolvedPattern = false;
35963597
if (auto *E = getAsExpr(anchor)) {
3597-
forEachExprInConstraintSystem(const_cast<Expr *>(E), [&](Expr *expr) {
3598+
auto &cs = getConstraintSystem();
3599+
cs.forEachExpr(const_cast<Expr *>(E), [&](Expr *expr) {
35983600
hasUnresolvedPattern |= isa<UnresolvedPatternExpr>(expr);
35993601
return hasUnresolvedPattern ? nullptr : expr;
36003602
});

lib/Sema/CSGen.cpp

+2-2
Original file line numberDiff line numberDiff line change
@@ -2105,7 +2105,7 @@ namespace {
21052105
// If this is a multi-statement closure, let's mark result
21062106
// as potential hole right away.
21072107
return Type(CS.createTypeVariable(
2108-
resultLocator, shouldTypeCheckInEnclosingExpression(closure)
2108+
resultLocator, CS.participatesInInference(closure)
21092109
? 0
21102110
: TVO_CanBindToHole));
21112111
}();
@@ -2627,7 +2627,7 @@ namespace {
26272627
// genreation only if closure is going to participate
26282628
// in the type-check. This allows us to delay validation of
26292629
// multi-statement closures until body is opened.
2630-
if (shouldTypeCheckInEnclosingExpression(closure) &&
2630+
if (CS.participatesInInference(closure) &&
26312631
collectVarRefs.hasErrorExprs) {
26322632
return Type();
26332633
}

lib/Sema/CSSimplify.cpp

+2-2
Original file line numberDiff line numberDiff line change
@@ -2040,7 +2040,7 @@ static bool fixMissingArguments(ConstraintSystem &cs, ASTNode anchor,
20402040

20412041
// Something like `foo { x in }` or `foo { $0 }`
20422042
if (auto *closure = getAsExpr<ClosureExpr>(anchor)) {
2043-
forEachExprInConstraintSystem(closure, [&](Expr *expr) -> Expr * {
2043+
cs.forEachExpr(closure, [&](Expr *expr) -> Expr * {
20442044
if (auto *UDE = dyn_cast<UnresolvedDotExpr>(expr)) {
20452045
if (!isParam(UDE->getBase()))
20462046
return expr;
@@ -11662,7 +11662,7 @@ bool ConstraintSystem::recordFix(ConstraintFix *fix, unsigned impact) {
1166211662

1166311663
bool found = false;
1166411664
if (auto *expr = getAsExpr(anchor)) {
11665-
forEachExprInConstraintSystem(expr, [&](Expr *subExpr) -> Expr * {
11665+
forEachExpr(expr, [&](Expr *subExpr) -> Expr * {
1166611666
found |= anchors.count(subExpr);
1166711667
return subExpr;
1166811668
});

lib/Sema/ConstraintSystem.cpp

+12
Original file line numberDiff line numberDiff line change
@@ -5874,6 +5874,18 @@ bool ConstraintSystem::isReadOnlyKeyPathComponent(
58745874
return false;
58755875
}
58765876

5877+
bool ConstraintSystem::participatesInInference(ClosureExpr *closure) const {
5878+
if (closure->hasSingleExpressionBody())
5879+
return true;
5880+
5881+
if (Options.contains(ConstraintSystemFlags::LeaveClosureBodyUnchecked))
5882+
return false;
5883+
5884+
auto &ctx = closure->getASTContext();
5885+
return !closure->hasEmptyBody() &&
5886+
ctx.TypeCheckerOpts.EnableMultiStatementClosureInference;
5887+
}
5888+
58775889
TypeVarBindingProducer::TypeVarBindingProducer(BindingSet &bindings)
58785890
: BindingProducer(bindings.getConstraintSystem(),
58795891
bindings.getTypeVariable()->getImpl().getLocator()),

lib/Sema/PreCheckExpr.cpp

+20-7
Original file line numberDiff line numberDiff line change
@@ -893,6 +893,8 @@ namespace {
893893
/// implicit `ErrorExpr` in place of invalid references.
894894
bool UseErrorExprs;
895895

896+
bool LeaveClosureBodiesUnchecked;
897+
896898
/// A stack of expressions being walked, used to determine where to
897899
/// insert RebindSelfInConstructorExpr nodes.
898900
llvm::SmallVector<Expr *, 8> ExprStack;
@@ -1080,9 +1082,11 @@ namespace {
10801082

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

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

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

14341443
// Update the current DeclContext to be the closure we're about to
14351444
// recurse into.
@@ -2082,11 +2091,15 @@ Expr *PreCheckExpression::simplifyTypeConstructionWithLiteralArg(Expr *E) {
20822091
/// Pre-check the expression, validating any types that occur in the
20832092
/// expression and folding sequence expressions.
20842093
bool ConstraintSystem::preCheckExpression(Expr *&expr, DeclContext *dc,
2085-
bool replaceInvalidRefsWithErrors) {
2094+
bool replaceInvalidRefsWithErrors,
2095+
bool leaveClosureBodiesUnchecked) {
20862096
auto &ctx = dc->getASTContext();
20872097
FrontendStatsTracer StatsTracer(ctx.Stats, "precheck-expr", expr);
20882098

2089-
PreCheckExpression preCheck(dc, expr, replaceInvalidRefsWithErrors);
2099+
PreCheckExpression preCheck(dc, expr,
2100+
replaceInvalidRefsWithErrors,
2101+
leaveClosureBodiesUnchecked);
2102+
20902103
// Perform the pre-check.
20912104
if (auto result = expr->walk(preCheck)) {
20922105
expr = result;

lib/Sema/TypeCheckCodeCompletion.cpp

+12-4
Original file line numberDiff line numberDiff line change
@@ -206,7 +206,9 @@ class SanitizeExpr : public ASTWalker {
206206
// If this is a closure, only walk into its children if they
207207
// are type-checked in the context of the enclosing expression.
208208
if (auto closure = dyn_cast<ClosureExpr>(expr)) {
209-
if (!shouldTypeCheckInEnclosingExpression(closure))
209+
// TODO: This has to be deleted once `EnableMultiStatementClosureInference`
210+
// is enabled by default.
211+
if (!closure->hasSingleExpressionBody())
210212
return { false, expr };
211213
for (auto &Param : *closure->getParameters()) {
212214
Param->setSpecifier(swift::ParamSpecifier::Default);
@@ -305,8 +307,11 @@ getTypeOfExpressionWithoutApplying(Expr *&expr, DeclContext *dc,
305307
PrettyStackTraceExpr stackTrace(Context, "type-checking", expr);
306308
referencedDecl = nullptr;
307309

310+
ConstraintSystemOptions options;
311+
options |= ConstraintSystemFlags::SuppressDiagnostics;
312+
308313
// Construct a constraint system from this expression.
309-
ConstraintSystem cs(dc, ConstraintSystemFlags::SuppressDiagnostics);
314+
ConstraintSystem cs(dc, options);
310315

311316
// Attempt to solve the constraint system.
312317
const Type originalType = expr->getType();
@@ -788,7 +793,8 @@ bool TypeChecker::typeCheckForCodeCompletion(
788793
// expression and folding sequence expressions.
789794
auto failedPreCheck = ConstraintSystem::preCheckExpression(
790795
expr, DC,
791-
/*replaceInvalidRefsWithErrors=*/true);
796+
/*replaceInvalidRefsWithErrors=*/true,
797+
/*leaveClosureBodiesUnchecked=*/true);
792798

793799
target.setExpr(expr);
794800

@@ -892,7 +898,9 @@ static Optional<Type> getTypeOfCompletionContextExpr(
892898
Expr *&parsedExpr,
893899
ConcreteDeclRef &referencedDecl) {
894900
if (constraints::ConstraintSystem::preCheckExpression(
895-
parsedExpr, DC, /*replaceInvalidRefsWithErrors=*/true))
901+
parsedExpr, DC,
902+
/*replaceInvalidRefsWithErrors=*/true,
903+
/*leaveClosureBodiesUnchecked=*/true))
896904
return None;
897905

898906
switch (kind) {

lib/Sema/TypeCheckConstraints.cpp

+14-17
Original file line numberDiff line numberDiff line change
@@ -344,7 +344,8 @@ TypeChecker::typeCheckExpression(
344344
// First, pre-check the expression, validating any types that occur in the
345345
// expression and folding sequence expressions.
346346
if (ConstraintSystem::preCheckExpression(
347-
expr, dc, /*replaceInvalidRefsWithErrors=*/true)) {
347+
expr, dc, /*replaceInvalidRefsWithErrors=*/true,
348+
options.contains(TypeCheckExprFlags::LeaveClosureBodyUnchecked))) {
348349
target.setExpr(expr);
349350
return None;
350351
}
@@ -588,14 +589,17 @@ bool TypeChecker::typeCheckForEachBinding(DeclContext *dc, ForEachStmt *stmt) {
588589
// Precheck the sequence.
589590
Expr *sequence = stmt->getSequence();
590591
if (ConstraintSystem::preCheckExpression(
591-
sequence, dc, /*replaceInvalidRefsWithErrors=*/true))
592+
sequence, dc, /*replaceInvalidRefsWithErrors=*/true,
593+
/*leaveClosureBodiesUnchecked=*/false))
592594
return failed();
593595
stmt->setSequence(sequence);
594596

595597
// Precheck the filtering condition.
596598
if (Expr *whereExpr = stmt->getWhere()) {
597599
if (ConstraintSystem::preCheckExpression(
598-
whereExpr, dc, /*replaceInvalidRefsWithErrors=*/true))
600+
whereExpr, dc,
601+
/*replaceInvalidRefsWithErrors=*/true,
602+
/*leaveClosureBodiesUnchecked=*/false))
599603
return failed();
600604

601605
stmt->setWhere(whereExpr);
@@ -2052,26 +2056,19 @@ HasDynamicCallableAttributeRequest::evaluate(Evaluator &evaluator,
20522056
});
20532057
}
20542058

2055-
bool swift::shouldTypeCheckInEnclosingExpression(ClosureExpr *expr) {
2056-
if (expr->hasSingleExpressionBody())
2057-
return true;
2058-
2059-
auto &ctx = expr->getASTContext();
2060-
return !expr->hasEmptyBody() &&
2061-
ctx.TypeCheckerOpts.EnableMultiStatementClosureInference;
2062-
}
2063-
2064-
void swift::forEachExprInConstraintSystem(
2059+
void ConstraintSystem::forEachExpr(
20652060
Expr *expr, llvm::function_ref<Expr *(Expr *)> callback) {
20662061
struct ChildWalker : ASTWalker {
2062+
ConstraintSystem &CS;
20672063
llvm::function_ref<Expr *(Expr *)> callback;
20682064

2069-
ChildWalker(llvm::function_ref<Expr *(Expr *)> callback)
2070-
: callback(callback) {}
2065+
ChildWalker(ConstraintSystem &CS,
2066+
llvm::function_ref<Expr *(Expr *)> callback)
2067+
: CS(CS), callback(callback) {}
20712068

20722069
std::pair<bool, Expr *> walkToExprPre(Expr *E) override {
20732070
if (auto closure = dyn_cast<ClosureExpr>(E)) {
2074-
if (!shouldTypeCheckInEnclosingExpression(closure))
2071+
if (!CS.participatesInInference(closure))
20752072
return { false, callback(E) };
20762073
}
20772074
return { true, callback(E) };
@@ -2084,5 +2081,5 @@ void swift::forEachExprInConstraintSystem(
20842081
bool walkToTypeReprPre(TypeRepr *T) override { return false; }
20852082
};
20862083

2087-
expr->walk(ChildWalker(callback));
2084+
expr->walk(ChildWalker(*this, callback));
20882085
}

0 commit comments

Comments
 (0)