-
Notifications
You must be signed in to change notification settings - Fork 10.6k
Sema: Fix source location bookkeeping for 'reasonable time' diagnostic #85293
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -236,36 +236,23 @@ struct RestrictionOrFix { | |
|
|
||
|
|
||
| class ExpressionTimer { | ||
| public: | ||
| using AnchorType = llvm::PointerUnion<Expr *, ConstraintLocator *>; | ||
|
|
||
| private: | ||
| AnchorType Anchor; | ||
| ASTContext &Context; | ||
| ConstraintSystem &CS; | ||
| llvm::TimeRecord StartTime; | ||
|
|
||
| /// The number of seconds from creation until | ||
| /// this timer is considered expired. | ||
| unsigned ThresholdInSecs; | ||
|
|
||
| bool PrintDebugTiming; | ||
| bool PrintWarning; | ||
|
|
||
| public: | ||
| const static unsigned NoLimit = (unsigned) -1; | ||
|
|
||
| ExpressionTimer(AnchorType Anchor, ConstraintSystem &CS, | ||
| unsigned thresholdInSecs); | ||
| ExpressionTimer(ConstraintSystem &CS, unsigned thresholdInSecs); | ||
|
|
||
| ~ExpressionTimer(); | ||
|
|
||
| AnchorType getAnchor() const { return Anchor; } | ||
|
|
||
| SourceRange getAffectedRange() const; | ||
|
|
||
| unsigned getWarnLimit() const { | ||
| return Context.TypeCheckerOpts.WarnLongExpressionTypeChecking; | ||
| } | ||
| unsigned getWarnLimit() const; | ||
| llvm::TimeRecord startedAt() const { return StartTime; } | ||
|
|
||
| /// Return the elapsed process time (including fractional seconds) | ||
|
|
@@ -2159,7 +2146,12 @@ struct ClosureIsolatedByPreconcurrency { | |
| /// solution of which assigns concrete types to each of the type variables. | ||
| /// Constraint systems are typically generated given an (untyped) expression. | ||
| class ConstraintSystem { | ||
| public: | ||
| using ExprOrLocator = llvm::PointerUnion<Expr *, ConstraintLocator *>; | ||
|
|
||
| private: | ||
| ASTContext &Context; | ||
| ExprOrLocator CurrentAnchor; | ||
|
|
||
| public: | ||
| DeclContext *DC; | ||
|
|
@@ -5394,6 +5386,9 @@ class ConstraintSystem { | |
| /// \returns The selected conjunction. | ||
| Constraint *selectConjunction(); | ||
|
|
||
| void diagnoseTooComplex(SourceLoc fallbackLoc, | ||
| SolutionResult &result); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If you are willing to work on this I think we should move all this logic into steps instead of the constraint system. In general I think constraint system should have to deal with anything but constraints. The context of "too complex" belongs to the solver itself IMHO, the algorithm that drives the constraint processing. |
||
|
|
||
| /// Solve the system of constraints generated from provided expression. | ||
| /// | ||
| /// \param target The target to generate constraints from. | ||
|
|
@@ -5491,6 +5486,8 @@ class ConstraintSystem { | |
| compareSolutions(ConstraintSystem &cs, ArrayRef<Solution> solutions, | ||
| const SolutionDiff &diff, unsigned idx1, unsigned idx2); | ||
|
|
||
| void startExpressionTimer(); | ||
|
|
||
| public: | ||
| /// Increase the score of the given kind for the current (partial) solution | ||
| /// along the current solver path. | ||
|
|
@@ -5528,7 +5525,6 @@ class ConstraintSystem { | |
| std::optional<unsigned> findBestSolution(SmallVectorImpl<Solution> &solutions, | ||
| bool minimize); | ||
|
|
||
| public: | ||
| /// Apply a given solution to the target, producing a fully | ||
| /// type-checked target or \c None if an error occurred. | ||
| /// | ||
|
|
@@ -5581,7 +5577,17 @@ class ConstraintSystem { | |
| /// resolved before any others. | ||
| void optimizeConstraints(Expr *e); | ||
|
|
||
| void startExpressionTimer(ExpressionTimer::AnchorType anchor); | ||
| /// Set the current sub-expression (of a multi-statement closure, etc) for | ||
| /// the purposes of diagnosing "reasonable time" errors. | ||
| void startExpression(ExprOrLocator anchor); | ||
|
|
||
| /// Returns the above anchor. | ||
| ExprOrLocator getCurrentAnchor() const { | ||
| return CurrentAnchor; | ||
| } | ||
|
|
||
| /// The source range of the above. | ||
| SourceRange getAffectedRange() const; | ||
|
|
||
| /// Determine if we've already explored too many paths in an | ||
| /// attempt to solve this expression. | ||
|
|
@@ -5594,53 +5600,7 @@ class ConstraintSystem { | |
| return range.isValid() ? range : std::optional<SourceRange>(); | ||
| } | ||
|
|
||
| bool isTooComplex(size_t solutionMemory) { | ||
| if (isAlreadyTooComplex.first) | ||
| return true; | ||
|
|
||
| auto CancellationFlag = getASTContext().CancellationFlag; | ||
| if (CancellationFlag && CancellationFlag->load(std::memory_order_relaxed)) | ||
| return true; | ||
|
|
||
| auto markTooComplex = [&](SourceRange range, StringRef reason) { | ||
| if (isDebugMode()) { | ||
| if (solverState) | ||
| llvm::errs().indent(solverState->getCurrentIndent()); | ||
| llvm::errs() << "(too complex: " << reason << ")\n"; | ||
| } | ||
| isAlreadyTooComplex = {true, range}; | ||
| return true; | ||
| }; | ||
|
|
||
| auto used = getASTContext().getSolverMemory() + solutionMemory; | ||
| MaxMemory = std::max(used, MaxMemory); | ||
| auto threshold = getASTContext().TypeCheckerOpts.SolverMemoryThreshold; | ||
| if (MaxMemory > threshold) { | ||
| // No particular location for OoM problems. | ||
| return markTooComplex(SourceRange(), "exceeded memory limit"); | ||
| } | ||
|
|
||
| if (Timer && Timer->isExpired()) { | ||
| // Disable warnings about expressions that go over the warning | ||
| // threshold since we're arbitrarily ending evaluation and | ||
| // emitting an error. | ||
| Timer->disableWarning(); | ||
|
|
||
| return markTooComplex(Timer->getAffectedRange(), "exceeded time limit"); | ||
| } | ||
|
|
||
| auto &opts = getASTContext().TypeCheckerOpts; | ||
|
|
||
| // Bail out once we've looked at a really large number of choices. | ||
| if (opts.SolverScopeThreshold && NumSolverScopes > opts.SolverScopeThreshold) | ||
| return markTooComplex(SourceRange(), "exceeded scope limit"); | ||
|
|
||
| // Bail out once we've taken a really large number of steps. | ||
| if (opts.SolverTrailThreshold && NumTrailSteps > opts.SolverTrailThreshold) | ||
| return markTooComplex(SourceRange(), "exceeded trail limit"); | ||
|
|
||
| return false; | ||
| } | ||
| bool isTooComplex(size_t solutionMemory); | ||
|
|
||
| bool isTooComplex(ArrayRef<Solution> solutions) { | ||
| if (isAlreadyTooComplex.first) | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could we move this to SolverState instead just like we do with indent?