@@ -18,12 +18,12 @@ package scala
1818package reflect
1919package internal
2020
21- import scala .collection .immutable
22- import scala .collection .mutable .ListBuffer
23- import util .{ ReusableInstance , Statistics , shortClassOfInstance }
24- import Flags ._
2521import scala .annotation .tailrec
22+ import scala .collection .mutable .{ArrayBuffer , ListBuffer }
2623import scala .reflect .io .{AbstractFile , NoAbstractFile }
24+
25+ import util .{ReusableInstance , Statistics , shortClassOfInstance }
26+ import Flags ._
2727import Variance ._
2828
2929trait Symbols extends api.Symbols { self : SymbolTable =>
@@ -36,14 +36,16 @@ trait Symbols extends api.Symbols { self: SymbolTable =>
3636 protected def nextId () = { ids += 1 ; ids }
3737
3838 /** Used to keep track of the recursion depth on locked symbols */
39- private [this ] var _recursionTable = immutable. Map .empty[Symbol , Int ]
39+ private [this ] var _recursionTable = Map .empty[Symbol , Int ]
4040 def recursionTable = _recursionTable
41- def recursionTable_= (value : immutable. Map [Symbol , Int ]) = _recursionTable = value
41+ def recursionTable_= (value : Map [Symbol , Int ]) = _recursionTable = value
4242
4343 private [this ] var _lockedCount = 0
4444 def lockedCount = this ._lockedCount
4545 def lockedCount_= (i : Int ) = _lockedCount = i
4646
47+ private [this ] val _lockingTrace = ArrayBuffer .empty[Symbol ]
48+ private [this ] val lockTracing : Boolean = self.isSymbolLockTracingEnabled
4749
4850 @ deprecated(" Global existential IDs no longer used" , " 2.12.1" )
4951 private [this ] var existentialIds = 0
@@ -553,19 +555,23 @@ trait Symbols extends api.Symbols { self: SymbolTable =>
553555 // True if the symbol is unlocked.
554556 // True if the symbol is locked but still below the allowed recursion depth.
555557 // False otherwise
556- private [scala] def lockOK : Boolean = {
557- ((_rawflags & LOCKED ) == 0L ) ||
558- ((settings.Yrecursion .value != 0 ) &&
559- (recursionTable get this match {
560- case Some (n) => (n <= settings.Yrecursion .value)
561- case None => true }))
562- }
558+ private [scala] def lockOK : Boolean = (
559+ (_rawflags & LOCKED ) == 0L || {
560+ val limit = settings.Yrecursion .value
561+ limit != 0 && (
562+ recursionTable.get(this ) match {
563+ case Some (n) => n <= limit
564+ case None => true
565+ })
566+ }
567+ )
563568
564569 // Lock a symbol, using the handler if the recursion depth becomes too great.
565570 private [scala] def lock (handler : => Unit ): Boolean = {
571+ if (lockTracing) _lockingTrace.addOne(this )
566572 if ((_rawflags & LOCKED ) != 0L ) {
567573 if (settings.Yrecursion .value != 0 ) {
568- recursionTable get this match {
574+ recursionTable. get( this ) match {
569575 case Some (n) =>
570576 if (n > settings.Yrecursion .value) {
571577 handler
@@ -578,21 +584,25 @@ trait Symbols extends api.Symbols { self: SymbolTable =>
578584 recursionTable += (this -> 1 )
579585 true
580586 }
581- } else { handler; false }
587+ } else {
588+ handler
589+ false
590+ }
582591 } else {
583592 _rawflags |= LOCKED
584593 true
585594 }
586595 }
587596
588597 // Unlock a symbol
589- private [scala] def unlock () = {
598+ private [scala] def unlock (): Unit =
590599 if ((_rawflags & LOCKED ) != 0L ) {
591600 _rawflags &= ~ LOCKED
601+ if (lockTracing && ! _lockingTrace.isEmpty)
602+ _lockingTrace.remove(index = _lockingTrace.size - 1 , count = 1 ) // dropRightInPlace(1)
592603 if (settings.Yrecursion .value != 0 )
593604 recursionTable -= this
594605 }
595- }
596606
597607// ----- tests ----------------------------------------------------------------------
598608
@@ -1553,9 +1563,16 @@ trait Symbols extends api.Symbols { self: SymbolTable =>
15531563 if ((_rawflags & LOCKED ) != 0L ) { // rolled out once for performance
15541564 lock {
15551565 setInfo(ErrorType )
1556- throw CyclicReference (this , tp)
1566+ val trace =
1567+ if (lockTracing) {
1568+ val t = _lockingTrace.toArray
1569+ _lockingTrace.clear()
1570+ t
1571+ } else CyclicReference .emptyTrace
1572+ throw CyclicReference (this , tp, trace)
15571573 }
15581574 } else {
1575+ if (lockTracing) _lockingTrace.addOne(this )
15591576 _rawflags |= LOCKED
15601577 }
15611578 val current = phase
@@ -1568,18 +1585,15 @@ trait Symbols extends api.Symbols { self: SymbolTable =>
15681585 unlock()
15691586 phase = current
15701587 }
1571- } else {
1572- // In runtime reflection, there is only on phase, so don't mutate Global.phase which would lead to warnings
1573- // of data races from when using TSAN to assess thread safety.
1574- try {
1575- tp.complete(this )
1576- } finally {
1577- unlock()
1578- }
15791588 }
1589+ else
1590+ // In runtime reflection, there is only one phase, so don't mutate Global.phase
1591+ // which would lead to warnings of data races from when using TSAN to assess thread safety.
1592+ try tp.complete(this )
1593+ finally unlock()
15801594 } catch {
15811595 case ex : CyclicReference =>
1582- devWarning(" ... hit cycle trying to complete " + this . fullLocationString)
1596+ devWarning(s " ... hit cycle trying to complete $ fullLocationString" )
15831597 throw ex
15841598 }
15851599
@@ -3839,10 +3853,13 @@ trait Symbols extends api.Symbols { self: SymbolTable =>
38393853 else closestEnclMethod(from.owner)
38403854
38413855 /** An exception for cyclic references of symbol definitions */
3842- case class CyclicReference (sym : Symbol , info : Type )
3843- extends TypeError (" illegal cyclic reference involving " + sym) {
3856+ case class CyclicReference (sym : Symbol , info : Type , trace : Array [ Symbol ] = CyclicReference .emptyTrace )
3857+ extends TypeError (s " illegal cyclic reference involving $ sym" ) {
38443858 if (settings.isDebug) printStackTrace()
38453859 }
3860+ object CyclicReference {
3861+ val emptyTrace : Array [Symbol ] = Array .empty[Symbol ]
3862+ }
38463863
38473864 /** A class for type histories */
38483865 private final case class TypeHistory protected (private var _validFrom : Period , private var _info : Type , private var _prev : TypeHistory ) {
0 commit comments