Skip to content

Commit 8da3b62

Browse files
authored
Merge pull request #85293 from slavapestov/too-complex-source-loc
Sema: Fix source location bookkeeping for 'reasonable time' diagnostic
2 parents 9fb56b8 + d8cf185 commit 8da3b62

File tree

10 files changed

+188
-134
lines changed

10 files changed

+188
-134
lines changed

include/swift/Sema/ConstraintSystem.h

Lines changed: 19 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -236,36 +236,23 @@ struct RestrictionOrFix {
236236

237237

238238
class ExpressionTimer {
239-
public:
240-
using AnchorType = llvm::PointerUnion<Expr *, ConstraintLocator *>;
241-
242-
private:
243-
AnchorType Anchor;
244-
ASTContext &Context;
239+
ConstraintSystem &CS;
245240
llvm::TimeRecord StartTime;
246241

247242
/// The number of seconds from creation until
248243
/// this timer is considered expired.
249244
unsigned ThresholdInSecs;
250245

251-
bool PrintDebugTiming;
252246
bool PrintWarning;
253247

254248
public:
255249
const static unsigned NoLimit = (unsigned) -1;
256250

257-
ExpressionTimer(AnchorType Anchor, ConstraintSystem &CS,
258-
unsigned thresholdInSecs);
251+
ExpressionTimer(ConstraintSystem &CS, unsigned thresholdInSecs);
259252

260253
~ExpressionTimer();
261254

262-
AnchorType getAnchor() const { return Anchor; }
263-
264-
SourceRange getAffectedRange() const;
265-
266-
unsigned getWarnLimit() const {
267-
return Context.TypeCheckerOpts.WarnLongExpressionTypeChecking;
268-
}
255+
unsigned getWarnLimit() const;
269256
llvm::TimeRecord startedAt() const { return StartTime; }
270257

271258
/// Return the elapsed process time (including fractional seconds)
@@ -2159,7 +2146,9 @@ struct ClosureIsolatedByPreconcurrency {
21592146
/// solution of which assigns concrete types to each of the type variables.
21602147
/// Constraint systems are typically generated given an (untyped) expression.
21612148
class ConstraintSystem {
2149+
private:
21622150
ASTContext &Context;
2151+
SourceRange CurrentRange;
21632152

21642153
public:
21652154
DeclContext *DC;
@@ -5384,6 +5373,9 @@ class ConstraintSystem {
53845373
/// \returns The selected conjunction.
53855374
Constraint *selectConjunction();
53865375

5376+
void diagnoseTooComplex(SourceLoc fallbackLoc,
5377+
SolutionResult &result);
5378+
53875379
/// Solve the system of constraints generated from provided expression.
53885380
///
53895381
/// \param target The target to generate constraints from.
@@ -5481,6 +5473,8 @@ class ConstraintSystem {
54815473
compareSolutions(ConstraintSystem &cs, ArrayRef<Solution> solutions,
54825474
const SolutionDiff &diff, unsigned idx1, unsigned idx2);
54835475

5476+
void startExpressionTimer();
5477+
54845478
public:
54855479
/// Increase the score of the given kind for the current (partial) solution
54865480
/// along the current solver path.
@@ -5518,7 +5512,6 @@ class ConstraintSystem {
55185512
std::optional<unsigned> findBestSolution(SmallVectorImpl<Solution> &solutions,
55195513
bool minimize);
55205514

5521-
public:
55225515
/// Apply a given solution to the target, producing a fully
55235516
/// type-checked target or \c None if an error occurred.
55245517
///
@@ -5571,7 +5564,14 @@ class ConstraintSystem {
55715564
/// resolved before any others.
55725565
void optimizeConstraints(Expr *e);
55735566

5574-
void startExpressionTimer(ExpressionTimer::AnchorType anchor);
5567+
/// Set the current sub-expression (of a multi-statement closure, etc) for
5568+
/// the purposes of diagnosing "reasonable time" errors.
5569+
void startExpression(ASTNode node);
5570+
5571+
/// The source range of the target being type checked.
5572+
SourceRange getCurrentSourceRange() const {
5573+
return CurrentRange;
5574+
}
55755575

55765576
/// Determine if we've already explored too many paths in an
55775577
/// attempt to solve this expression.
@@ -5584,53 +5584,7 @@ class ConstraintSystem {
55845584
return range.isValid() ? range : std::optional<SourceRange>();
55855585
}
55865586

5587-
bool isTooComplex(size_t solutionMemory) {
5588-
if (isAlreadyTooComplex.first)
5589-
return true;
5590-
5591-
auto CancellationFlag = getASTContext().CancellationFlag;
5592-
if (CancellationFlag && CancellationFlag->load(std::memory_order_relaxed))
5593-
return true;
5594-
5595-
auto markTooComplex = [&](SourceRange range, StringRef reason) {
5596-
if (isDebugMode()) {
5597-
if (solverState)
5598-
llvm::errs().indent(solverState->getCurrentIndent());
5599-
llvm::errs() << "(too complex: " << reason << ")\n";
5600-
}
5601-
isAlreadyTooComplex = {true, range};
5602-
return true;
5603-
};
5604-
5605-
auto used = getASTContext().getSolverMemory() + solutionMemory;
5606-
MaxMemory = std::max(used, MaxMemory);
5607-
auto threshold = getASTContext().TypeCheckerOpts.SolverMemoryThreshold;
5608-
if (MaxMemory > threshold) {
5609-
// No particular location for OoM problems.
5610-
return markTooComplex(SourceRange(), "exceeded memory limit");
5611-
}
5612-
5613-
if (Timer && Timer->isExpired()) {
5614-
// Disable warnings about expressions that go over the warning
5615-
// threshold since we're arbitrarily ending evaluation and
5616-
// emitting an error.
5617-
Timer->disableWarning();
5618-
5619-
return markTooComplex(Timer->getAffectedRange(), "exceeded time limit");
5620-
}
5621-
5622-
auto &opts = getASTContext().TypeCheckerOpts;
5623-
5624-
// Bail out once we've looked at a really large number of choices.
5625-
if (opts.SolverScopeThreshold && NumSolverScopes > opts.SolverScopeThreshold)
5626-
return markTooComplex(SourceRange(), "exceeded scope limit");
5627-
5628-
// Bail out once we've taken a really large number of steps.
5629-
if (opts.SolverTrailThreshold && NumTrailSteps > opts.SolverTrailThreshold)
5630-
return markTooComplex(SourceRange(), "exceeded trail limit");
5631-
5632-
return false;
5633-
}
5587+
bool isTooComplex(size_t solutionMemory);
56345588

56355589
bool isTooComplex(ArrayRef<Solution> solutions) {
56365590
if (isAlreadyTooComplex.first)

include/swift/Sema/SolutionResult.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -76,8 +76,10 @@ class SolutionResult {
7676
SolutionResult(const SolutionResult &other) = delete;
7777

7878
SolutionResult(SolutionResult &&other)
79-
: kind(other.kind), numSolutions(other.numSolutions),
80-
solutions(other.solutions) {
79+
: kind(other.kind),
80+
numSolutions(other.numSolutions),
81+
solutions(other.solutions),
82+
TooComplexAt(other.TooComplexAt) {
8183
emittedDiagnostic = false;
8284
other.kind = Error;
8385
other.numSolutions = 0;

lib/Sema/BuilderTransform.cpp

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1053,9 +1053,7 @@ TypeChecker::applyResultBuilderBodyTransform(FuncDecl *func, Type builderType) {
10531053

10541054
case SolutionResult::Kind::TooComplex:
10551055
reportSolutionsToSolutionCallback(salvagedResult);
1056-
func->diagnose(diag::expression_too_complex)
1057-
.highlight(func->getBodySourceRange());
1058-
salvagedResult.markAsDiagnosed();
1056+
cs.diagnoseTooComplex(func->getLoc(), salvagedResult);
10591057
return nullptr;
10601058
}
10611059

lib/Sema/CSSolver.cpp

Lines changed: 17 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -829,9 +829,7 @@ bool ConstraintSystem::Candidate::solve(
829829

830830
// Allocate new constraint system for sub-expression.
831831
ConstraintSystem cs(DC, std::nullopt);
832-
833-
// Set up expression type checker timer for the candidate.
834-
cs.startExpressionTimer(E);
832+
cs.startExpression(E);
835833

836834
// Generate constraints for the new system.
837835
if (auto generatedExpr = cs.generateConstraints(E, DC)) {
@@ -1455,18 +1453,7 @@ ConstraintSystem::solve(SyntacticElementTarget &target,
14551453
return std::nullopt;
14561454

14571455
case SolutionResult::TooComplex: {
1458-
auto affectedRange = solution.getTooComplexAt();
1459-
1460-
// If affected range is unknown, let's use whole
1461-
// target.
1462-
if (!affectedRange)
1463-
affectedRange = target.getSourceRange();
1464-
1465-
getASTContext()
1466-
.Diags.diagnose(affectedRange->Start, diag::expression_too_complex)
1467-
.highlight(*affectedRange);
1468-
1469-
solution.markAsDiagnosed();
1456+
diagnoseTooComplex(target.getLoc(), solution);
14701457
return std::nullopt;
14711458
}
14721459

@@ -1501,6 +1488,19 @@ ConstraintSystem::solve(SyntacticElementTarget &target,
15011488
llvm_unreachable("Loop always returns");
15021489
}
15031490

1491+
void ConstraintSystem::diagnoseTooComplex(SourceLoc fallbackLoc,
1492+
SolutionResult &result) {
1493+
auto affectedRange = result.getTooComplexAt();
1494+
1495+
SourceLoc loc = (affectedRange ? affectedRange->Start : fallbackLoc);
1496+
auto diag = getASTContext().Diags.diagnose(loc, diag::expression_too_complex);
1497+
1498+
if (affectedRange)
1499+
diag.highlight(*affectedRange);
1500+
1501+
result.markAsDiagnosed();
1502+
}
1503+
15041504
SolutionResult
15051505
ConstraintSystem::solveImpl(SyntacticElementTarget &target,
15061506
FreeTypeVariableBinding allowFreeTypeVariables) {
@@ -1518,9 +1518,8 @@ ConstraintSystem::solveImpl(SyntacticElementTarget &target,
15181518

15191519
assert(!solverState && "cannot be used directly");
15201520

1521-
// Set up the expression type checker timer.
15221521
if (Expr *expr = target.getAsExpr())
1523-
startExpressionTimer(expr);
1522+
startExpression(expr);
15241523

15251524
if (generateConstraints(target, allowFreeTypeVariables))
15261525
return SolutionResult::forError();
@@ -1701,8 +1700,7 @@ bool ConstraintSystem::solveForCodeCompletion(
17011700
// Tell the constraint system what the contextual type is.
17021701
setContextualInfo(expr, target.getExprContextualTypeInfo());
17031702

1704-
// Set up the expression type checker timer.
1705-
startExpressionTimer(expr);
1703+
startExpression(expr);
17061704

17071705
shrink(expr);
17081706
}

lib/Sema/CSStep.cpp

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -823,9 +823,13 @@ bool ConjunctionStep::attempt(const ConjunctionElement &element) {
823823
// (expression) gets a fresh time slice to get solved. This
824824
// is important for closures with large number of statements
825825
// in them.
826-
if (CS.Timer) {
826+
if (CS.Timer)
827827
CS.Timer.reset();
828-
CS.startExpressionTimer(element.getLocator());
828+
829+
{
830+
auto *locator = element.getLocator();
831+
auto anchor = simplifyLocatorToAnchor(locator);
832+
CS.startExpression(anchor ? anchor : locator->getAnchor());
829833
}
830834

831835
auto success = element.attempt(CS);

lib/Sema/CSStep.h

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -867,8 +867,7 @@ class ConjunctionStep : public BindingStep<ConjunctionElementProducer> {
867867

868868
/// The number of milliseconds until outer constraint system
869869
/// is considered "too complex" if timer is enabled.
870-
std::optional<std::pair<ExpressionTimer::AnchorType, unsigned>>
871-
OuterTimeRemaining = std::nullopt;
870+
std::optional<unsigned> OuterTimeRemaining = std::nullopt;
872871

873872
/// Conjunction constraint associated with this step.
874873
Constraint *Conjunction;
@@ -910,7 +909,7 @@ class ConjunctionStep : public BindingStep<ConjunctionElementProducer> {
910909

911910
if (cs.Timer) {
912911
auto remainingTime = cs.Timer->getRemainingProcessTimeInSeconds();
913-
OuterTimeRemaining.emplace(cs.Timer->getAnchor(), remainingTime);
912+
OuterTimeRemaining.emplace(remainingTime);
914913
}
915914
}
916915

@@ -925,11 +924,8 @@ class ConjunctionStep : public BindingStep<ConjunctionElementProducer> {
925924
if (HadFailure)
926925
restoreBestScore();
927926

928-
if (OuterTimeRemaining) {
929-
auto anchor = OuterTimeRemaining->first;
930-
auto remainingTime = OuterTimeRemaining->second;
931-
CS.Timer.emplace(anchor, CS, remainingTime);
932-
}
927+
if (OuterTimeRemaining)
928+
CS.Timer.emplace(CS, *OuterTimeRemaining);
933929
}
934930

935931
StepResult resume(bool prevFailed) override;

0 commit comments

Comments
 (0)