Skip to content

Commit c1db023

Browse files
authored
Merge pull request #85901 from slavapestov/too-complex-source-loc-6.3
Sema: Fix source location bookkeeping for 'reasonable time' diagnostic [6.3]
2 parents 2a03662 + 045852b commit c1db023

File tree

10 files changed

+189
-134
lines changed

10 files changed

+189
-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;
@@ -5390,6 +5379,9 @@ class ConstraintSystem {
53905379
/// \returns The selected conjunction.
53915380
Constraint *selectConjunction();
53925381

5382+
void diagnoseTooComplex(SourceLoc fallbackLoc,
5383+
SolutionResult &result);
5384+
53935385
/// Solve the system of constraints generated from provided expression.
53945386
///
53955387
/// \param target The target to generate constraints from.
@@ -5487,6 +5479,8 @@ class ConstraintSystem {
54875479
compareSolutions(ConstraintSystem &cs, ArrayRef<Solution> solutions,
54885480
const SolutionDiff &diff, unsigned idx1, unsigned idx2);
54895481

5482+
void startExpressionTimer();
5483+
54905484
public:
54915485
/// Increase the score of the given kind for the current (partial) solution
54925486
/// along the current solver path.
@@ -5524,7 +5518,6 @@ class ConstraintSystem {
55245518
std::optional<unsigned> findBestSolution(SmallVectorImpl<Solution> &solutions,
55255519
bool minimize);
55265520

5527-
public:
55285521
/// Apply a given solution to the target, producing a fully
55295522
/// type-checked target or \c None if an error occurred.
55305523
///
@@ -5577,7 +5570,14 @@ class ConstraintSystem {
55775570
/// resolved before any others.
55785571
void optimizeConstraints(Expr *e);
55795572

5580-
void startExpressionTimer(ExpressionTimer::AnchorType anchor);
5573+
/// Set the current sub-expression (of a multi-statement closure, etc) for
5574+
/// the purposes of diagnosing "reasonable time" errors.
5575+
void startExpression(ASTNode node);
5576+
5577+
/// The source range of the target being type checked.
5578+
SourceRange getCurrentSourceRange() const {
5579+
return CurrentRange;
5580+
}
55815581

55825582
/// Determine if we've already explored too many paths in an
55835583
/// attempt to solve this expression.
@@ -5590,53 +5590,7 @@ class ConstraintSystem {
55905590
return range.isValid() ? range : std::optional<SourceRange>();
55915591
}
55925592

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

56415595
bool isTooComplex(ArrayRef<Solution> solutions) {
56425596
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)