Skip to content

Check that symbol accesses are single threaded #954

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

Closed
wants to merge 10 commits into from
2 changes: 1 addition & 1 deletion src/dotty/tools/dotc/Run.scala
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ class Run(comp: Compiler)(implicit ctx: Context) {
compileSources(sources)
} catch {
case NonFatal(ex) =>
ex.printStackTrace()
ctx.println(i"exception occurred while compiling $units%, %")
throw ex
}
Expand All @@ -50,7 +51,6 @@ class Run(comp: Compiler)(implicit ctx: Context) {
}

protected def compileUnits() = Stats.monitorHeartBeat {
ctx.checkSingleThreaded()
val phases = ctx.squashPhases(ctx.phasePlan,
ctx.settings.Yskip.value, ctx.settings.YstopBefore.value, ctx.settings.YstopAfter.value, ctx.settings.Ycheck.value)
ctx.usePhases(phases)
Expand Down
12 changes: 1 addition & 11 deletions src/dotty/tools/dotc/core/Contexts.scala
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import Flags.ParamAccessor
import util.Positions._
import ast.Trees._
import ast.untpd
import util.{FreshNameCreator, SimpleMap, SourceFile, NoSource}
import util.{FreshNameCreator, SimpleMap, SourceFile, NoSource, CheckedSingleThreaded}
import typer._
import Implicits.ContextualImplicits
import config.Settings._
Expand Down Expand Up @@ -615,16 +615,6 @@ object Contexts {
superIdOfClass.clear()
lastSuperId = -1
}

// Test that access is single threaded

/** The thread on which `checkSingleThreaded was invoked last */
@sharable private var thread: Thread = null

/** Check that we are on the same thread as before */
def checkSingleThreaded() =
if (thread == null) thread = Thread.currentThread()
else assert(thread == Thread.currentThread(), "illegal multithreaded access to ContextBase")
}

object Context {
Expand Down
26 changes: 25 additions & 1 deletion src/dotty/tools/dotc/core/Denotations.scala
Original file line number Diff line number Diff line change
Expand Up @@ -205,8 +205,32 @@ object Denotations {
denot.symbol
}

def slowSearch(name: Name)(implicit ctx: Context): Symbol =
info.decls.find(_.name == name).getOrElse(NoSymbol)

def requiredMethod(name: PreName)(implicit ctx: Context): TermSymbol =
info.member(name.toTermName).requiredSymbol(_ is Method).asTerm
if (info.exists) {
val meth = info.member(name.toTermName)
if (meth.exists) meth.requiredSymbol(_ is Method).asTerm
else { // Heisenbughunt
println(s"*** missing method: ${name.toString} in $this")
info.decls.checkConsistent()
println(i"decls = ${info.decls}")
if (slowSearch(name.toTermName).exists) {
System.err.println(i"**** slow search found: ${slowSearch(name.toTermName)}")
System.err.println("scope entries found:")
var e = info.decls.lastEntry
while (e != null) {
println(e.name)
e = e.prev
}
System.err.println("no more entries found")
}
throw new TypeError(s"Missing method: $this . $name")
}
}
else throw new TypeError(s"M issing module: $this")

def requiredMethodRef(name: PreName)(implicit ctx: Context): TermRef =
requiredMethod(name).termRef

Expand Down
8 changes: 6 additions & 2 deletions src/dotty/tools/dotc/core/Scopes.scala
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ object Scopes {
* SynchronizedScope as mixin.
*/
class MutableScope protected[Scopes](initElems: ScopeEntry, initSize: Int, val nestingLevel: Int = 0)
extends Scope {
extends Scope with util.CheckedSingleThreaded {

protected[Scopes] def this(base: Scope)(implicit ctx: Context) = {
this(base.lastEntry, base.size, base.nestingLevel + 1)
Expand Down Expand Up @@ -208,8 +208,10 @@ object Scopes {
}

/** create and enter a scope entry */
protected def newScopeEntry(sym: Symbol)(implicit ctx: Context): ScopeEntry =
protected def newScopeEntry(sym: Symbol)(implicit ctx: Context): ScopeEntry = {
checkSingleThreaded()
newScopeEntry(sym.name, sym)
}

private def enterInHash(e: ScopeEntry)(implicit ctx: Context): Unit = {
val idx = e.name.hashCode & (hashTable.length - 1)
Expand Down Expand Up @@ -310,6 +312,7 @@ object Scopes {
/** Lookup a symbol entry matching given name.
*/
override final def lookupEntry(name: Name)(implicit ctx: Context): ScopeEntry = {
checkSingleThreaded()
var e: ScopeEntry = null
if (hashTable ne null) {
e = hashTable(name.hashCode & (hashTable.length - 1))
Expand Down Expand Up @@ -339,6 +342,7 @@ object Scopes {
* Does _not_ include the elements of inherited scopes.
*/
override final def toList: List[Symbol] = {
checkSingleThreaded()
if (elemsCache eq null) {
elemsCache = Nil
var e = lastEntry
Expand Down
9 changes: 6 additions & 3 deletions src/dotty/tools/dotc/core/SymDenotations.scala
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ object SymDenotations {
final val name: Name,
initFlags: FlagSet,
initInfo: Type,
initPrivateWithin: Symbol = NoSymbol) extends SingleDenotation(symbol) {
initPrivateWithin: Symbol = NoSymbol) extends SingleDenotation(symbol) with util.CheckedSingleThreaded {

//assert(symbol.id != 4940, name)

Expand Down Expand Up @@ -148,10 +148,12 @@ object SymDenotations {
* The info is an instance of TypeType iff this is a type denotation
* Uncompleted denotations set myInfo to a LazyType.
*/
final def info(implicit ctx: Context): Type = myInfo match {
final def info(implicit ctx: Context): Type = {
if (exists) checkSingleThreaded()
myInfo match {
case myInfo: LazyType => completeFrom(myInfo); info
case _ => myInfo
}
}}

/** The type info, or, if symbol is not yet completed, the completer */
final def infoOrCompleter = myInfo
Expand Down Expand Up @@ -1534,6 +1536,7 @@ object SymDenotations {
}

override final def findMember(name: Name, pre: Type, excluded: FlagSet)(implicit ctx: Context): Denotation = {
if (exists) checkSingleThreaded()
val raw = if (excluded is Private) nonPrivateMembersNamed(name) else membersNamed(name)
raw.filterExcluded(excluded).asSeenFrom(pre).toDenot(pre)
}
Expand Down
5 changes: 4 additions & 1 deletion src/dotty/tools/dotc/core/Symbols.scala
Original file line number Diff line number Diff line change
Expand Up @@ -364,7 +364,8 @@ object Symbols {
* @param coord The coordinates of the symbol (a position or an index)
* @param id A unique identifier of the symbol (unique per ContextBase)
*/
class Symbol private[Symbols] (val coord: Coord, val id: Int) extends DotClass with printing.Showable {
class Symbol private[Symbols] (val coord: Coord, val id: Int)
extends DotClass with printing.Showable with util.CheckedSingleThreaded {

type ThisName <: Name

Expand All @@ -381,6 +382,8 @@ object Symbols {
final def denot(implicit ctx: Context): SymDenotation = {
var denot = lastDenot
if (!(denot.validFor contains ctx.period)) {
if (denot.exists)
checkSingleThreaded()
denot = denot.current.asInstanceOf[SymDenotation]
lastDenot = denot
}
Expand Down
1 change: 1 addition & 0 deletions src/dotty/tools/dotc/typer/FrontEnd.scala
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ class FrontEnd extends Phase {
try body
catch {
case NonFatal(ex) =>
ex.printStackTrace()
ctx.println(s"exception occurred while $doing ${ctx.compilationUnit}")
throw ex
}
Expand Down
4 changes: 3 additions & 1 deletion src/dotty/tools/dotc/typer/Typer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1151,7 +1151,9 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
try adapt(typedUnadapted(tree, pt), pt, tree)
catch {
case ex: CyclicReference => errorTree(tree, cyclicErrorMsg(ex))
case ex: TypeError => errorTree(tree, ex.getMessage)
case ex: TypeError =>
ex.printStackTrace() // debug
errorTree(tree, ex.getMessage)
}
}

Expand Down
14 changes: 14 additions & 0 deletions src/dotty/tools/dotc/util/CheckedSingleThreaded.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package dotty.tools
package dotc.util

trait CheckedSingleThreaded {

/** The thread on which `checkSingleThreaded was invoked last */
@sharable @volatile private var thread: Thread = null

/** Check that we are on the same thread as before */
def checkSingleThreaded() =
if (thread == null) thread = Thread.currentThread()
else assert(thread == Thread.currentThread(), s"illegal multithreaded access to $this")

}