Skip to content

Commit da61cd6

Browse files
authored
Merge pull request #75533 from hamishknight/prewash
[Sema] A couple of pre-checking cleanups
2 parents fef8782 + a73e44a commit da61cd6

File tree

8 files changed

+84
-293
lines changed

8 files changed

+84
-293
lines changed

include/swift/AST/TypeCheckRequests.h

Lines changed: 3 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -3029,61 +3029,8 @@ class AssociatedConformanceRequest
30293029
void cacheResult(ProtocolConformanceRef value) const;
30303030
};
30313031

3032-
struct PreCheckResultBuilderDescriptor {
3033-
AnyFunctionRef Fn;
3034-
bool SuppressDiagnostics;
3035-
3036-
private:
3037-
// NOTE: Since source tooling (e.g. code completion) might replace the body,
3038-
// we need to take the body into account to calculate 'hash_value' and '=='.
3039-
// Also, we cannot 'getBody()' inside 'hash_value' and '==' because it invokes
3040-
// another request (even if it's cached).
3041-
BraceStmt *Body;
3042-
3043-
public:
3044-
PreCheckResultBuilderDescriptor(AnyFunctionRef Fn, bool suppressDiagnostics)
3045-
: Fn(Fn), SuppressDiagnostics(suppressDiagnostics), Body(Fn.getBody()) {}
3046-
3047-
friend llvm::hash_code
3048-
hash_value(const PreCheckResultBuilderDescriptor &owner) {
3049-
return llvm::hash_combine(owner.Fn, owner.Body);
3050-
}
3051-
3052-
friend bool operator==(const PreCheckResultBuilderDescriptor &lhs,
3053-
const PreCheckResultBuilderDescriptor &rhs) {
3054-
return lhs.Fn == rhs.Fn && lhs.Body == rhs.Body;
3055-
}
3056-
3057-
friend bool operator!=(const PreCheckResultBuilderDescriptor &lhs,
3058-
const PreCheckResultBuilderDescriptor &rhs) {
3059-
return !(lhs == rhs);
3060-
}
3061-
3062-
friend SourceLoc extractNearestSourceLoc(PreCheckResultBuilderDescriptor d) {
3063-
return extractNearestSourceLoc(d.Fn);
3064-
}
3065-
3066-
friend void simple_display(llvm::raw_ostream &out,
3067-
const PreCheckResultBuilderDescriptor &d) {
3068-
simple_display(out, d.Fn);
3069-
}
3070-
};
3071-
3072-
enum class ResultBuilderBodyPreCheck : uint8_t {
3073-
/// There were no problems pre-checking the closure.
3074-
Okay,
3075-
3076-
/// There was an error pre-checking the closure.
3077-
Error,
3078-
3079-
/// The closure has a return statement.
3080-
HasReturnStmt,
3081-
};
3082-
3083-
class PreCheckResultBuilderRequest
3084-
: public SimpleRequest<PreCheckResultBuilderRequest,
3085-
ResultBuilderBodyPreCheck(
3086-
PreCheckResultBuilderDescriptor),
3032+
class BraceHasReturnRequest
3033+
: public SimpleRequest<BraceHasReturnRequest, bool(const BraceStmt *),
30873034
RequestFlags::Cached> {
30883035
public:
30893036
using SimpleRequest::SimpleRequest;
@@ -3092,8 +3039,7 @@ class PreCheckResultBuilderRequest
30923039
friend SimpleRequest;
30933040

30943041
// Evaluation.
3095-
ResultBuilderBodyPreCheck
3096-
evaluate(Evaluator &evaluator, PreCheckResultBuilderDescriptor owner) const;
3042+
bool evaluate(Evaluator &evaluator, const BraceStmt *BS) const;
30973043

30983044
public:
30993045
// Separate caching.

include/swift/AST/TypeCheckerTypeIDZone.def

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -381,8 +381,8 @@ SWIFT_REQUEST(TypeChecker, HasUserDefinedDesignatedInitRequest,
381381
bool(NominalTypeDecl *), Cached, NoLocationInfo)
382382
SWIFT_REQUEST(TypeChecker, HasMemberwiseInitRequest,
383383
bool(StructDecl *), Cached, NoLocationInfo)
384-
SWIFT_REQUEST(TypeChecker, PreCheckResultBuilderRequest,
385-
ResultBuilderBodyPreCheck(PreCheckResultBuilderDescriptor),
384+
SWIFT_REQUEST(TypeChecker, BraceHasReturnRequest,
385+
bool(const BraceStmt *),
386386
Cached, NoLocationInfo)
387387
SWIFT_REQUEST(TypeChecker, ResolveImplicitMemberRequest,
388388
evaluator::SideEffect(NominalTypeDecl *, ImplicitMemberAction),

include/swift/Sema/SyntacticElementTarget.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -591,6 +591,19 @@ class SyntacticElementTarget {
591591
expression.expression = expr;
592592
}
593593

594+
Pattern *getPattern() const {
595+
if (auto *pattern = getAsUninitializedVar())
596+
return pattern;
597+
598+
if (isForInitialization())
599+
return getInitializationPattern();
600+
601+
if (kind == Kind::forEachPreamble)
602+
return forEachStmt.pattern;
603+
604+
return nullptr;
605+
}
606+
594607
void setPattern(Pattern *pattern) {
595608
if (kind == Kind::uninitializedVar) {
596609
assert(uninitializedVar.declaration.is<Pattern *>());

lib/AST/TypeCheckRequests.cpp

Lines changed: 0 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1317,25 +1317,6 @@ void AssociatedConformanceRequest::cacheResult(
13171317
conformance->setAssociatedConformance(index, assocConf);
13181318
}
13191319

1320-
//----------------------------------------------------------------------------//
1321-
// PreCheckResultBuilderRequest computation.
1322-
//----------------------------------------------------------------------------//
1323-
1324-
void swift::simple_display(llvm::raw_ostream &out,
1325-
ResultBuilderBodyPreCheck value) {
1326-
switch (value) {
1327-
case ResultBuilderBodyPreCheck::Okay:
1328-
out << "okay";
1329-
break;
1330-
case ResultBuilderBodyPreCheck::HasReturnStmt:
1331-
out << "has return statement";
1332-
break;
1333-
case ResultBuilderBodyPreCheck::Error:
1334-
out << "error";
1335-
break;
1336-
}
1337-
}
1338-
13391320
//----------------------------------------------------------------------------//
13401321
// HasCircularInheritedProtocolsRequest computation.
13411322
//----------------------------------------------------------------------------//

lib/Sema/BuilderTransform.cpp

Lines changed: 28 additions & 147 deletions
Original file line numberDiff line numberDiff line change
@@ -920,25 +920,10 @@ class ResultBuilderTransform
920920

921921
std::optional<BraceStmt *>
922922
TypeChecker::applyResultBuilderBodyTransform(FuncDecl *func, Type builderType) {
923-
// Pre-check the body: pre-check any expressions in it and look
924-
// for return statements.
925-
//
926-
// If we encountered an error or there was an explicit result type,
927-
// bail out and report that to the caller.
923+
// First look for any return statements, and bail if we have any.
928924
auto &ctx = func->getASTContext();
929-
auto request =
930-
PreCheckResultBuilderRequest{{AnyFunctionRef(func),
931-
/*SuppressDiagnostics=*/false}};
932-
switch (evaluateOrDefault(ctx.evaluator, request,
933-
ResultBuilderBodyPreCheck::Error)) {
934-
case ResultBuilderBodyPreCheck::Okay:
935-
// If the pre-check was okay, apply the result-builder transform.
936-
break;
937-
938-
case ResultBuilderBodyPreCheck::Error:
939-
return nullptr;
940-
941-
case ResultBuilderBodyPreCheck::HasReturnStmt: {
925+
if (evaluateOrDefault(ctx.evaluator, BraceHasReturnRequest{func->getBody()},
926+
false)) {
942927
// One or more explicit 'return' statements were encountered, which
943928
// disables the result builder transform. Warn when we do this.
944929
auto returnStmts = findReturnStatements(func);
@@ -972,7 +957,10 @@ TypeChecker::applyResultBuilderBodyTransform(FuncDecl *func, Type builderType) {
972957

973958
return std::nullopt;
974959
}
975-
}
960+
961+
auto target = SyntacticElementTarget(func);
962+
if (ConstraintSystem::preCheckTarget(target))
963+
return nullptr;
976964

977965
ConstraintSystemOptions options = ConstraintSystemFlags::AllowFixes;
978966
auto resultInterfaceTy = func->getResultInterfaceType();
@@ -1020,8 +1008,7 @@ TypeChecker::applyResultBuilderBodyTransform(FuncDecl *func, Type builderType) {
10201008
cs.Options |= ConstraintSystemFlags::ForCodeCompletion;
10211009
cs.solveForCodeCompletion(solutions);
10221010

1023-
SyntacticElementTarget funcTarget(func);
1024-
CompletionContextFinder analyzer(funcTarget, func->getDeclContext());
1011+
CompletionContextFinder analyzer(target, func->getDeclContext());
10251012
if (analyzer.hasCompletion()) {
10261013
filterSolutionsForCodeCompletion(solutions, analyzer);
10271014
for (const auto &solution : solutions) {
@@ -1068,7 +1055,7 @@ TypeChecker::applyResultBuilderBodyTransform(FuncDecl *func, Type builderType) {
10681055

10691056
case SolutionResult::Kind::UndiagnosedError:
10701057
reportSolutionsToSolutionCallback(salvagedResult);
1071-
cs.diagnoseFailureFor(SyntacticElementTarget(func));
1058+
cs.diagnoseFailureFor(target);
10721059
salvagedResult.markAsDiagnosed();
10731060
return nullptr;
10741061

@@ -1102,8 +1089,7 @@ TypeChecker::applyResultBuilderBodyTransform(FuncDecl *func, Type builderType) {
11021089
cs.applySolution(solutions.front());
11031090

11041091
// Apply the solution to the function body.
1105-
if (auto result =
1106-
cs.applySolution(solutions.front(), SyntacticElementTarget(func))) {
1092+
if (auto result = cs.applySolution(solutions.front(), target)) {
11071093
performSyntacticDiagnosticsForTarget(*result, /*isExprStmt*/ false);
11081094
auto *body = result->getFunctionBody();
11091095

@@ -1144,22 +1130,8 @@ ConstraintSystem::matchResultBuilder(AnyFunctionRef fn, Type builderType,
11441130
// not apply the result builder transform if it contained an explicit return.
11451131
// To maintain source compatibility, we still need to check for HasReturnStmt.
11461132
// https://github.com/apple/swift/issues/64332.
1147-
auto request =
1148-
PreCheckResultBuilderRequest{{fn, /*SuppressDiagnostics=*/false}};
1149-
switch (evaluateOrDefault(getASTContext().evaluator, request,
1150-
ResultBuilderBodyPreCheck::Error)) {
1151-
case ResultBuilderBodyPreCheck::Okay:
1152-
// If the pre-check was okay, apply the result-builder transform.
1153-
break;
1154-
1155-
case ResultBuilderBodyPreCheck::Error: {
1156-
llvm_unreachable(
1157-
"Running PreCheckResultBuilderRequest on a function shouldn't run "
1158-
"preCheckExpression and thus we should never enter this case.");
1159-
break;
1160-
}
1161-
1162-
case ResultBuilderBodyPreCheck::HasReturnStmt:
1133+
if (evaluateOrDefault(getASTContext().evaluator,
1134+
BraceHasReturnRequest{fn.getBody()}, false)) {
11631135
// Diagnostic mode means that solver couldn't reach any viable
11641136
// solution, so let's diagnose presence of a `return` statement
11651137
// in the closure body.
@@ -1260,139 +1232,48 @@ ConstraintSystem::matchResultBuilder(AnyFunctionRef fn, Type builderType,
12601232
}
12611233

12621234
namespace {
1263-
1264-
/// Pre-check all the expressions in the body.
1265-
class PreCheckResultBuilderApplication : public ASTWalker {
1266-
AnyFunctionRef Fn;
1267-
bool SkipPrecheck = false;
1268-
bool SuppressDiagnostics = false;
1235+
class ReturnStmtFinder : public ASTWalker {
12691236
std::vector<ReturnStmt *> ReturnStmts;
1270-
bool HasError = false;
1271-
1272-
bool hasReturnStmt() const { return !ReturnStmts.empty(); }
12731237

12741238
public:
1275-
PreCheckResultBuilderApplication(AnyFunctionRef fn, bool skipPrecheck,
1276-
bool suppressDiagnostics)
1277-
: Fn(fn), SkipPrecheck(skipPrecheck),
1278-
SuppressDiagnostics(suppressDiagnostics) {}
1279-
1280-
const std::vector<ReturnStmt *> getReturnStmts() const { return ReturnStmts; }
1281-
1282-
ResultBuilderBodyPreCheck run() {
1283-
Stmt *oldBody = Fn.getBody();
1284-
1285-
Stmt *newBody = oldBody->walk(*this);
1286-
1287-
// If the walk was aborted, it was because we had a problem of some kind.
1288-
assert((newBody == nullptr) == HasError &&
1289-
"unexpected short-circuit while walking body");
1290-
if (HasError)
1291-
return ResultBuilderBodyPreCheck::Error;
1292-
1293-
assert(oldBody == newBody && "pre-check walk wasn't in-place?");
1294-
1295-
if (hasReturnStmt())
1296-
return ResultBuilderBodyPreCheck::HasReturnStmt;
1297-
1298-
return ResultBuilderBodyPreCheck::Okay;
1239+
static std::vector<ReturnStmt *> find(const BraceStmt *BS) {
1240+
ReturnStmtFinder finder;
1241+
const_cast<BraceStmt *>(BS)->walk(finder);
1242+
return std::move(finder.ReturnStmts);
12991243
}
13001244

13011245
MacroWalking getMacroWalkingBehavior() const override {
13021246
return MacroWalking::Arguments;
13031247
}
13041248

13051249
PreWalkResult<Expr *> walkToExprPre(Expr *E) override {
1306-
if (SkipPrecheck)
1307-
return Action::SkipNode(E);
1308-
1309-
// Pre-check the expression. If this fails, abort the walk immediately.
1310-
// Otherwise, replace the expression with the result of pre-checking.
1311-
// In either case, don't recurse into the expression.
1312-
{
1313-
auto *DC = Fn.getAsDeclContext();
1314-
auto &diagEngine = DC->getASTContext().Diags;
1315-
1316-
// Suppress any diagnostics which could be produced by this expression.
1317-
DiagnosticTransaction transaction(diagEngine);
1318-
1319-
HasError |= ConstraintSystem::preCheckExpression(E, DC);
1320-
1321-
HasError |= transaction.hasErrors();
1322-
1323-
if (!HasError)
1324-
HasError |= containsErrorExpr(E);
1325-
1326-
if (SuppressDiagnostics)
1327-
transaction.abort();
1328-
1329-
if (HasError)
1330-
return Action::Stop();
1331-
1332-
return Action::SkipNode(E);
1333-
}
1250+
return Action::SkipNode(E);
13341251
}
13351252

13361253
PreWalkResult<Stmt *> walkToStmtPre(Stmt *S) override {
13371254
// If we see a return statement, note it..
1338-
if (auto returnStmt = dyn_cast<ReturnStmt>(S)) {
1339-
if (!returnStmt->isImplicit()) {
1340-
ReturnStmts.push_back(returnStmt);
1341-
return Action::SkipNode(S);
1342-
}
1343-
}
1344-
1345-
// Otherwise, recurse into the statement normally.
1346-
return Action::Continue(S);
1347-
}
1348-
1349-
/// Check whether given expression (including single-statement
1350-
/// closures) contains `ErrorExpr` as one of its sub-expressions.
1351-
bool containsErrorExpr(Expr *expr) {
1352-
bool hasError = false;
1353-
1354-
expr->forEachChildExpr([&](Expr *expr) -> Expr * {
1355-
hasError |= isa<ErrorExpr>(expr);
1356-
if (hasError)
1357-
return nullptr;
1255+
auto *returnStmt = dyn_cast<ReturnStmt>(S);
1256+
if (!returnStmt || returnStmt->isImplicit())
1257+
return Action::Continue(S);
13581258

1359-
if (auto *closure = dyn_cast<ClosureExpr>(expr)) {
1360-
if (closure->hasSingleExpressionBody()) {
1361-
hasError |= containsErrorExpr(closure->getSingleExpressionBody());
1362-
return hasError ? nullptr : expr;
1363-
}
1364-
}
1365-
1366-
return expr;
1367-
});
1368-
1369-
return hasError;
1259+
ReturnStmts.push_back(returnStmt);
1260+
return Action::SkipNode(S);
13701261
}
13711262

13721263
/// Ignore patterns.
13731264
PreWalkResult<Pattern *> walkToPatternPre(Pattern *pat) override {
13741265
return Action::SkipNode(pat);
13751266
}
13761267
};
1268+
} // end anonymous namespace
13771269

1378-
}
1379-
1380-
ResultBuilderBodyPreCheck PreCheckResultBuilderRequest::evaluate(
1381-
Evaluator &evaluator, PreCheckResultBuilderDescriptor owner) const {
1382-
// Closures should already be pre-checked when we run this, so there's no need
1383-
// to pre-check them again.
1384-
bool skipPrecheck = owner.Fn.getAbstractClosureExpr();
1385-
return PreCheckResultBuilderApplication(
1386-
owner.Fn, skipPrecheck,
1387-
/*suppressDiagnostics=*/owner.SuppressDiagnostics)
1388-
.run();
1270+
bool BraceHasReturnRequest::evaluate(Evaluator &evaluator,
1271+
const BraceStmt *BS) const {
1272+
return !ReturnStmtFinder::find(BS).empty();
13891273
}
13901274

13911275
std::vector<ReturnStmt *> TypeChecker::findReturnStatements(AnyFunctionRef fn) {
1392-
PreCheckResultBuilderApplication precheck(fn, /*skipPreCheck=*/true,
1393-
/*SuppressDiagnostics=*/true);
1394-
(void)precheck.run();
1395-
return precheck.getReturnStmts();
1276+
return ReturnStmtFinder::find(fn.getBody());
13961277
}
13971278

13981279
ResultBuilderOpSupport TypeChecker::checkBuilderOpSupport(

0 commit comments

Comments
 (0)