Skip to content

Commit 57ce1d2

Browse files
authored
Merge pull request #41282 from xedin/too-complex-with-multistmt-cljs
[ConstraintSystem] Adjust expression complexity computation to account for multi-statement closures
2 parents b5f4298 + 1636ee8 commit 57ce1d2

File tree

4 files changed

+95
-21
lines changed

4 files changed

+95
-21
lines changed

include/swift/Sema/ConstraintSystem.h

Lines changed: 27 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -167,18 +167,31 @@ struct RestrictionOrFix {
167167

168168

169169
class ExpressionTimer {
170-
Expr* E;
170+
public:
171+
using AnchorType = llvm::PointerUnion<Expr *, ConstraintLocator *>;
172+
173+
private:
174+
AnchorType Anchor;
171175
ASTContext &Context;
172176
llvm::TimeRecord StartTime;
173177

178+
/// The number of milliseconds from creation until
179+
/// this timer is considered expired.
180+
unsigned ThresholdInMillis;
181+
174182
bool PrintDebugTiming;
175183
bool PrintWarning;
176184

177185
public:
178-
ExpressionTimer(Expr *E, ConstraintSystem &CS);
186+
/// This constructor sets a default threshold defined for all expressions
187+
/// via compiler flag `solver-expression-time-threshold`.
188+
ExpressionTimer(AnchorType Anchor, ConstraintSystem &CS);
189+
ExpressionTimer(AnchorType Anchor, ConstraintSystem &CS, unsigned thresholdInMillis);
179190

180191
~ExpressionTimer();
181192

193+
AnchorType getAnchor() const { return Anchor; }
194+
182195
unsigned getWarnLimit() const {
183196
return Context.TypeCheckerOpts.WarnLongExpressionTypeChecking;
184197
}
@@ -192,13 +205,19 @@ class ExpressionTimer {
192205
return endTime.getProcessTime() - StartTime.getProcessTime();
193206
}
194207

208+
/// Return the remaining process time in milliseconds until the
209+
/// threshold specified during construction is reached.
210+
unsigned getRemainingProcessTimeInMillis() const {
211+
auto elapsed = unsigned(getElapsedProcessTimeInFractionalSeconds());
212+
return elapsed >= ThresholdInMillis ? 0 : ThresholdInMillis - elapsed;
213+
}
214+
195215
// Disable emission of warnings about expressions that take longer
196216
// than the warning threshold.
197217
void disableWarning() { PrintWarning = false; }
198218

199-
bool isExpired(unsigned thresholdInMillis) const {
200-
auto elapsed = getElapsedProcessTimeInFractionalSeconds();
201-
return unsigned(elapsed) > thresholdInMillis;
219+
bool isExpired() const {
220+
return getRemainingProcessTimeInMillis() == 0;
202221
}
203222
};
204223

@@ -5244,9 +5263,7 @@ class ConstraintSystem {
52445263
return isExpressionAlreadyTooComplex= true;
52455264
}
52465265

5247-
const auto timeoutThresholdInMillis =
5248-
getASTContext().TypeCheckerOpts.ExpressionTimeoutThreshold;
5249-
if (Timer && Timer->isExpired(timeoutThresholdInMillis)) {
5266+
if (Timer && Timer->isExpired()) {
52505267
// Disable warnings about expressions that go over the warning
52515268
// threshold since we're arbitrarily ending evaluation and
52525269
// emitting an error.
@@ -5687,6 +5704,8 @@ class ConjunctionElement {
56875704

56885705
bool attempt(ConstraintSystem &cs) const;
56895706

5707+
ConstraintLocator *getLocator() const { return Element->getLocator(); }
5708+
56905709
void print(llvm::raw_ostream &Out, SourceManager *SM) const {
56915710
Out << "conjunction element ";
56925711
Element->print(Out, SM);

lib/Sema/CSStep.cpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -816,6 +816,18 @@ bool ConjunctionStep::attempt(const ConjunctionElement &element) {
816816
// by dropping all scoring information.
817817
CS.CurrentScore = Score();
818818

819+
// Reset the scope counter to avoid "too complex" failures
820+
// when closure has a lot of elements in the body.
821+
CS.CountScopes = 0;
822+
823+
// If timer is enabled, let's reset it so that each element
824+
// (expression) gets a fresh time slice to get solved. This
825+
// is important for closures with large number of statements
826+
// in them.
827+
if (CS.Timer) {
828+
CS.Timer.emplace(element.getLocator(), CS);
829+
}
830+
819831
auto success = element.attempt(CS);
820832

821833
// If element attempt has failed, mark whole conjunction

lib/Sema/CSStep.h

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -857,6 +857,15 @@ class ConjunctionStep : public BindingStep<ConjunctionElementProducer> {
857857
/// The score established before conjunction is attempted.
858858
Score CurrentScore;
859859

860+
/// The number of constraint solver scopes already explored
861+
/// before accepting this conjunction.
862+
llvm::SaveAndRestore<unsigned> OuterScopeCount;
863+
864+
/// The number of milliseconds until outer constraint system
865+
/// is considered "too complex" if timer is enabled.
866+
Optional<std::pair<ExpressionTimer::AnchorType, unsigned>>
867+
OuterTimeRemaining = None;
868+
860869
/// Conjunction constraint associated with this step.
861870
Constraint *Conjunction;
862871
/// Position of the conjunction in the inactive constraints
@@ -889,13 +898,18 @@ class ConjunctionStep : public BindingStep<ConjunctionElementProducer> {
889898
: BindingStep(cs, {cs, conjunction},
890899
conjunction->isIsolated() ? IsolatedSolutions : solutions),
891900
BestScore(getBestScore()), CurrentScore(getCurrentScore()),
892-
Conjunction(conjunction), AfterConjunction(erase(conjunction)),
893-
OuterSolutions(solutions) {
901+
OuterScopeCount(cs.CountScopes, 0), Conjunction(conjunction),
902+
AfterConjunction(erase(conjunction)), OuterSolutions(solutions) {
894903
assert(conjunction->getKind() == ConstraintKind::Conjunction);
895904

896905
// Make a snapshot of the constraint system state before conjunction.
897906
if (conjunction->isIsolated())
898907
Snapshot.emplace(cs, conjunction);
908+
909+
if (cs.Timer) {
910+
auto remainingTime = cs.Timer->getRemainingProcessTimeInMillis();
911+
OuterTimeRemaining.emplace(cs.Timer->getAnchor(), remainingTime);
912+
}
899913
}
900914

901915
~ConjunctionStep() override {
@@ -911,6 +925,12 @@ class ConjunctionStep : public BindingStep<ConjunctionElementProducer> {
911925
// successful outcome should keep a score set by `restoreOuterState`.
912926
if (HadFailure)
913927
restoreOriginalScores();
928+
929+
if (OuterTimeRemaining) {
930+
auto anchor = OuterTimeRemaining->first;
931+
auto remainingTime = OuterTimeRemaining->second;
932+
CS.Timer.emplace(anchor, CS, remainingTime);
933+
}
914934
}
915935

916936
StepResult resume(bool prevFailed) override;

lib/Sema/ConstraintSystem.cpp

Lines changed: 34 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -43,13 +43,18 @@ using namespace inference;
4343

4444
#define DEBUG_TYPE "ConstraintSystem"
4545

46-
ExpressionTimer::ExpressionTimer(Expr *E, ConstraintSystem &CS)
47-
: E(E),
48-
Context(CS.getASTContext()),
46+
ExpressionTimer::ExpressionTimer(AnchorType Anchor, ConstraintSystem &CS)
47+
: ExpressionTimer(
48+
Anchor, CS,
49+
CS.getASTContext().TypeCheckerOpts.ExpressionTimeoutThreshold) {}
50+
51+
ExpressionTimer::ExpressionTimer(AnchorType Anchor, ConstraintSystem &CS,
52+
unsigned thresholdInMillis)
53+
: Anchor(Anchor), Context(CS.getASTContext()),
4954
StartTime(llvm::TimeRecord::getCurrentTime()),
55+
ThresholdInMillis(thresholdInMillis),
5056
PrintDebugTiming(CS.getASTContext().TypeCheckerOpts.DebugTimeExpressions),
51-
PrintWarning(true) {
52-
}
57+
PrintWarning(true) {}
5358

5459
ExpressionTimer::~ExpressionTimer() {
5560
auto elapsed = getElapsedProcessTimeInFractionalSeconds();
@@ -59,21 +64,39 @@ ExpressionTimer::~ExpressionTimer() {
5964
// Round up to the nearest 100th of a millisecond.
6065
llvm::errs() << llvm::format("%0.2f", ceil(elapsed * 100000) / 100)
6166
<< "ms\t";
62-
E->getLoc().print(llvm::errs(), Context.SourceMgr);
67+
if (auto *E = Anchor.dyn_cast<Expr *>()) {
68+
E->getLoc().print(llvm::errs(), Context.SourceMgr);
69+
} else {
70+
auto *locator = Anchor.get<ConstraintLocator *>();
71+
locator->dump(&Context.SourceMgr, llvm::errs());
72+
}
6373
llvm::errs() << "\n";
6474
}
6575

6676
if (!PrintWarning)
6777
return;
6878

79+
ASTNode anchor;
80+
if (auto *locator = Anchor.dyn_cast<ConstraintLocator *>()) {
81+
anchor = simplifyLocatorToAnchor(locator);
82+
// If locator couldn't be simplified down to a single AST
83+
// element, let's warn about its root.
84+
if (!anchor)
85+
anchor = locator->getAnchor();
86+
} else {
87+
anchor = Anchor.get<Expr *>();
88+
}
89+
6990
const auto WarnLimit = getWarnLimit();
70-
if (WarnLimit != 0 && elapsedMS >= WarnLimit && E->getLoc().isValid())
71-
Context.Diags.diagnose(E->getLoc(), diag::debug_long_expression,
72-
elapsedMS, WarnLimit)
73-
.highlight(E->getSourceRange());
91+
if (WarnLimit != 0 && elapsedMS >= WarnLimit &&
92+
anchor.getStartLoc().isValid()) {
93+
Context.Diags
94+
.diagnose(anchor.getStartLoc(), diag::debug_long_expression, elapsedMS,
95+
WarnLimit)
96+
.highlight(anchor.getSourceRange());
97+
}
7498
}
7599

76-
77100
ConstraintSystem::ConstraintSystem(DeclContext *dc,
78101
ConstraintSystemOptions options)
79102
: Context(dc->getASTContext()), DC(dc), Options(options),

0 commit comments

Comments
 (0)