Skip to content

Commit 540f69a

Browse files
committed
Subject implicit suggestion searches to timeouts
Currently: Max 1 sec to test a single implicit Max 10 secs to make suggestions for a missing implicit import
1 parent 5e3f11c commit 540f69a

File tree

3 files changed

+60
-5
lines changed

3 files changed

+60
-5
lines changed

compiler/src/dotty/tools/dotc/Run.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ class Run(comp: Compiler, ictx: Context) extends ImplicitRunInfo with Constraint
3535
* return immediately. Currently these early abort operatoons are
3636
* `Typer.typed` and `Implicits.typedImplicit`.
3737
*/
38-
var isCancelled = false
38+
@volatile var isCancelled = false
3939

4040
/** Produces the following contexts, from outermost to innermost
4141
*

compiler/src/dotty/tools/dotc/typer/Implicits.scala

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ import transform.SymUtils._
3131
import transform.TypeUtils._
3232
import transform.SyntheticMembers._
3333
import Hashable._
34-
import util.{Property, SourceFile, NoSource}
34+
import util.{Property, SourceFile, NoSource, Scheduler}
3535
import config.Config
3636
import config.Printers.{implicits, implicitsDetailed}
3737
import collection.mutable
@@ -68,6 +68,9 @@ object Implicits {
6868
final val Extension = 4
6969
}
7070

71+
val testOneImplicitTimeOut = 1000
72+
val suggestImplicitTimeOut = 10000
73+
7174
/** A common base class of contextual implicits and of-type implicits which
7275
* represents a set of references to implicit definitions.
7376
*/
@@ -528,10 +531,29 @@ object Implicits {
528531
else Nil
529532

530533
def search(given ctx: Context): List[TermRef] =
531-
roots
532-
.filterNot(root => defn.RootImportTypes.exists(_.symbol == root.symbol))
534+
val scheduler = new Scheduler()
535+
val deadLine = System.currentTimeMillis() + suggestImplicitTimeOut
536+
537+
def test(ref: TermRef)(given Context): Boolean =
538+
System.currentTimeMillis < deadLine
539+
&& {
540+
scheduler.scheduleAfter(testOneImplicitTimeOut) {
541+
println(i"Cancelling test of $ref when making suggestions for error in ${ctx.source}")
542+
ctx.run.isCancelled = true
543+
}
544+
try qualifies(ref)
545+
finally
546+
scheduler.cancel()
547+
ctx.run.isCancelled = false
548+
}
549+
550+
try
551+
roots
552+
.filterNot(root => defn.RootImportTypes.exists(_.symbol == root.symbol))
533553
// don't suggest things that are imported by default
534-
.flatMap(_.implicitMembers.filter(qualifies))
554+
.flatMap(_.implicitMembers.filter(test))
555+
finally scheduler.shutdown()
556+
end search
535557
end suggestions
536558
}
537559

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
package dotty.tools.dotc.util
2+
class Scheduler extends Thread with
3+
4+
private var execTime: Long = Long.MaxValue
5+
private var task: () => Unit = _
6+
private var stopped: Boolean = false
7+
8+
def scheduleAfter(after: Long)(task: => Unit) = synchronized {
9+
this.task = () => task
10+
this.execTime = System.currentTimeMillis + after
11+
}
12+
13+
def cancel(): Unit = synchronized {
14+
execTime = Long.MaxValue
15+
}
16+
17+
def shutdown(): Unit = synchronized {
18+
stopped = true
19+
}
20+
21+
def poll(): Unit = synchronized {
22+
if System.currentTimeMillis() > execTime then
23+
task()
24+
execTime = Long.MaxValue
25+
}
26+
27+
override def run(): Unit =
28+
while !stopped do
29+
poll()
30+
Thread.sleep(1)
31+
32+
start()
33+
end Scheduler

0 commit comments

Comments
 (0)