aboutsummaryrefslogtreecommitdiff
path: root/src/dotty
diff options
context:
space:
mode:
authorodersky <odersky@gmail.com>2015-07-06 19:01:45 +0200
committerodersky <odersky@gmail.com>2015-07-06 19:01:45 +0200
commitb82313268b912c62f9ddc89fefd02be96f2de64c (patch)
treea1f2b2de5751b169558c5a11532ce37bf33f24ad /src/dotty
parent6561d4cadc2e1af354486c34817b86131ffbe1e7 (diff)
parentc7cc6d851b660c97e80cda806bf5366200c8836b (diff)
downloaddotty-b82313268b912c62f9ddc89fefd02be96f2de64c.tar.gz
dotty-b82313268b912c62f9ddc89fefd02be96f2de64c.tar.bz2
dotty-b82313268b912c62f9ddc89fefd02be96f2de64c.zip
Merge pull request #708 from dotty-staging/add/check-reentrant
Check that dotty is reentrant
Diffstat (limited to 'src/dotty')
-rw-r--r--src/dotty/tools/dotc/Bench.scala2
-rw-r--r--src/dotty/tools/dotc/Compiler.scala3
-rw-r--r--src/dotty/tools/dotc/Resident.scala2
-rw-r--r--src/dotty/tools/dotc/ast/Desugar.scala2
-rw-r--r--src/dotty/tools/dotc/ast/Trees.scala19
-rw-r--r--src/dotty/tools/dotc/config/Properties.scala4
-rw-r--r--src/dotty/tools/dotc/config/ScalaSettings.scala1
-rw-r--r--src/dotty/tools/dotc/config/ScalaVersion.scala9
-rw-r--r--src/dotty/tools/dotc/core/Contexts.scala5
-rw-r--r--src/dotty/tools/dotc/core/Denotations.scala6
-rw-r--r--src/dotty/tools/dotc/core/Flags.scala2
-rw-r--r--src/dotty/tools/dotc/core/Names.scala91
-rw-r--r--src/dotty/tools/dotc/core/SymDenotations.scala9
-rw-r--r--src/dotty/tools/dotc/core/Symbols.scala30
-rw-r--r--src/dotty/tools/dotc/core/TypeOps.scala5
-rw-r--r--src/dotty/tools/dotc/core/Types.scala16
-rw-r--r--src/dotty/tools/dotc/parsing/Parsers.scala4
-rw-r--r--src/dotty/tools/dotc/transform/CheckReentrant.scala95
-rw-r--r--src/dotty/tools/dotc/transform/CtxLazy.scala23
-rw-r--r--src/dotty/tools/dotc/transform/TreeChecker.scala2
-rw-r--r--src/dotty/tools/dotc/transform/TreeTransform.scala5
-rw-r--r--src/dotty/tools/dotc/typer/Implicits.scala2
-rw-r--r--src/dotty/tools/dotc/typer/Inferencing.scala2
-rw-r--r--src/dotty/tools/dotc/typer/Mode.scala2
-rw-r--r--src/dotty/tools/dotc/typer/ProtoTypes.scala6
-rw-r--r--src/dotty/tools/dotc/util/SourceFile.scala4
-rw-r--r--src/dotty/tools/dotc/util/SourcePosition.scala5
-rw-r--r--src/dotty/tools/dotc/util/Stats.scala5
-rw-r--r--src/dotty/tools/package.scala4
29 files changed, 251 insertions, 114 deletions
diff --git a/src/dotty/tools/dotc/Bench.scala b/src/dotty/tools/dotc/Bench.scala
index 417054dce..47b5fd6dd 100644
--- a/src/dotty/tools/dotc/Bench.scala
+++ b/src/dotty/tools/dotc/Bench.scala
@@ -10,7 +10,7 @@ import reporting.Reporter
object Bench extends Driver {
- private var numRuns = 1
+ @sharable private var numRuns = 1
def newCompiler(): Compiler = new Compiler
diff --git a/src/dotty/tools/dotc/Compiler.scala b/src/dotty/tools/dotc/Compiler.scala
index a14aa3655..827134e84 100644
--- a/src/dotty/tools/dotc/Compiler.scala
+++ b/src/dotty/tools/dotc/Compiler.scala
@@ -40,7 +40,8 @@ class Compiler {
List(new FrontEnd),
List(new PostTyper),
List(new Pickler),
- List(new FirstTransform),
+ List(new FirstTransform,
+ new CheckReentrant),
List(new RefChecks,
new ElimRepeated,
new NormalizeFlags,
diff --git a/src/dotty/tools/dotc/Resident.scala b/src/dotty/tools/dotc/Resident.scala
index d671ea259..9578e7d2c 100644
--- a/src/dotty/tools/dotc/Resident.scala
+++ b/src/dotty/tools/dotc/Resident.scala
@@ -21,7 +21,7 @@ import scala.annotation.tailrec
*
* dotc> :q // quit
*/
-object Resident extends Driver {
+class Resident extends Driver {
object residentCompiler extends Compiler
diff --git a/src/dotty/tools/dotc/ast/Desugar.scala b/src/dotty/tools/dotc/ast/Desugar.scala
index 660a8dbcb..30467f185 100644
--- a/src/dotty/tools/dotc/ast/Desugar.scala
+++ b/src/dotty/tools/dotc/ast/Desugar.scala
@@ -209,7 +209,7 @@ object desugar {
else tdef
}
- private val synthetic = Modifiers(Synthetic)
+ @sharable private val synthetic = Modifiers(Synthetic)
private def toDefParam(tparam: TypeDef): TypeDef =
tparam.withFlags(Param)
diff --git a/src/dotty/tools/dotc/ast/Trees.scala b/src/dotty/tools/dotc/ast/Trees.scala
index 97facaa5d..f63d32b14 100644
--- a/src/dotty/tools/dotc/ast/Trees.scala
+++ b/src/dotty/tools/dotc/ast/Trees.scala
@@ -1,4 +1,5 @@
-package dotty.tools.dotc
+package dotty.tools
+package dotc
package ast
import core._
@@ -26,7 +27,7 @@ object Trees {
type Untyped = Null
/** The total number of created tree nodes, maintained if Stats.enabled */
- var ntrees = 0
+ @sharable var ntrees = 0
/** Modifiers and annotations for definitions
* @param flags The set flags
@@ -68,7 +69,7 @@ object Trees {
def tokenPos: Seq[(Token, Position)] = ???
}
- private var nextId = 0 // for debugging
+ @sharable private var nextId = 0 // for debugging
type LazyTree = AnyRef /* really: Tree | Lazy[Tree] */
type LazyTreeList = AnyRef /* really: List[Tree] | Lazy[List[Tree]] */
@@ -723,9 +724,9 @@ object Trees {
setMods(Modifiers[T](PrivateLocal))
}
- val theEmptyTree: Thicket[Type] = Thicket(Nil)
- val theEmptyValDef = new EmptyValDef[Type]
- val theEmptyModifiers = new Modifiers()
+ @sharable val theEmptyTree: Thicket[Type] = Thicket(Nil)
+ @sharable val theEmptyValDef = new EmptyValDef[Type]
+ @sharable val theEmptyModifiers = new Modifiers()
def genericEmptyValDef[T >: Untyped]: ValDef[T] = theEmptyValDef.asInstanceOf[ValDef[T]]
def genericEmptyTree[T >: Untyped]: Thicket[T] = theEmptyTree.asInstanceOf[Thicket[T]]
@@ -845,9 +846,9 @@ object Trees {
type Annotated = Trees.Annotated[T]
type Thicket = Trees.Thicket[T]
- val EmptyTree: Thicket = genericEmptyTree
- val EmptyValDef: ValDef = genericEmptyValDef
- val EmptyModifiers: Modifiers = genericEmptyModifiers
+ @sharable val EmptyTree: Thicket = genericEmptyTree
+ @sharable val EmptyValDef: ValDef = genericEmptyValDef
+ @sharable val EmptyModifiers: Modifiers = genericEmptyModifiers
// ----- Auxiliary creation methods ------------------
diff --git a/src/dotty/tools/dotc/config/Properties.scala b/src/dotty/tools/dotc/config/Properties.scala
index d592a7bb1..ec1f24d06 100644
--- a/src/dotty/tools/dotc/config/Properties.scala
+++ b/src/dotty/tools/dotc/config/Properties.scala
@@ -12,7 +12,7 @@ object Properties extends PropertiesTrait {
/** Scala manifest attributes.
*/
- val ScalaCompilerVersion = new AttributeName("Scala-Compiler-Version")
+ @sharable val ScalaCompilerVersion = new AttributeName("Scala-Compiler-Version")
}
trait PropertiesTrait {
@@ -23,7 +23,7 @@ trait PropertiesTrait {
protected val propFilename = "/" + propCategory + ".properties"
/** The loaded properties */
- protected lazy val scalaProps: java.util.Properties = {
+ @sharable protected lazy val scalaProps: java.util.Properties = {
val props = new java.util.Properties
val stream = pickJarBasedOn getResourceAsStream propFilename
if (stream ne null)
diff --git a/src/dotty/tools/dotc/config/ScalaSettings.scala b/src/dotty/tools/dotc/config/ScalaSettings.scala
index f8c155cad..05fefc8b4 100644
--- a/src/dotty/tools/dotc/config/ScalaSettings.scala
+++ b/src/dotty/tools/dotc/config/ScalaSettings.scala
@@ -151,6 +151,7 @@ class ScalaSettings extends Settings.SettingGroup {
val YnoDeepSubtypes = BooleanSetting("-Yno-deep-subtypes", "throw an exception on deep subtyping call stacks.")
val YprintSyms = BooleanSetting("-Yprint-syms", "when printing trees print info in symbols instead of corresponding info in trees.")
val YtestPickler = BooleanSetting("-Ytest-pickler", "self-test for pickling functionality; should be used with -Ystop-after:pickler")
+ val YcheckReentrant = BooleanSetting("-Ycheck-reentrant", "check that compiled program does not contain vars that can be accessed from a global root.")
def stop = YstopAfter
/** Area-specific debug output.
diff --git a/src/dotty/tools/dotc/config/ScalaVersion.scala b/src/dotty/tools/dotc/config/ScalaVersion.scala
index 7d4585441..02ba74af9 100644
--- a/src/dotty/tools/dotc/config/ScalaVersion.scala
+++ b/src/dotty/tools/dotc/config/ScalaVersion.scala
@@ -1,6 +1,7 @@
/* @author James Iry
*/
-package dotty.tools.dotc.config
+package dotty.tools
+package dotc.config
import scala.util.{Try, Success, Failure}
@@ -15,7 +16,7 @@ sealed abstract class ScalaVersion extends Ordered[ScalaVersion] {
/**
* A scala version that sorts higher than all actual versions
*/
-case object NoScalaVersion extends ScalaVersion {
+@sharable case object NoScalaVersion extends ScalaVersion {
def unparse = "none"
def compare(that: ScalaVersion): Int = that match {
@@ -52,7 +53,7 @@ case class SpecificScalaVersion(major: Int, minor: Int, rev: Int, build: ScalaBu
/**
* A Scala version that sorts lower than all actual versions
*/
-case object AnyScalaVersion extends ScalaVersion {
+@sharable case object AnyScalaVersion extends ScalaVersion {
def unparse = "any"
def compare(that: ScalaVersion): Int = that match {
@@ -64,7 +65,7 @@ case object AnyScalaVersion extends ScalaVersion {
/**
* Methods for parsing ScalaVersions
*/
-object ScalaVersion {
+@sharable object ScalaVersion {
private val dot = "\\."
private val dash = "\\-"
private def not(s:String) = s"[^${s}]"
diff --git a/src/dotty/tools/dotc/core/Contexts.scala b/src/dotty/tools/dotc/core/Contexts.scala
index c9deaab10..206ef9d8b 100644
--- a/src/dotty/tools/dotc/core/Contexts.scala
+++ b/src/dotty/tools/dotc/core/Contexts.scala
@@ -28,6 +28,7 @@ import printing._
import config.{Settings, ScalaSettings, Platform, JavaPlatform}
import language.implicitConversions
import DenotTransformers.DenotTransformer
+
object Contexts {
/** A context is passed basically everywhere in dotc.
@@ -473,7 +474,7 @@ object Contexts {
gadt = new GADTMap(SimpleMap.Empty)
}
- object NoContext extends Context {
+ @sharable object NoContext extends Context {
lazy val base = unsupported("base")
override val implicits: ContextualImplicits = new ContextualImplicits(Nil, null)(this)
}
@@ -620,7 +621,7 @@ object Contexts {
/** implicit conversion that injects all ContextBase members into a context */
implicit def toBase(ctx: Context): ContextBase = ctx.base
- val theBase = new ContextBase // !!! DEBUG, so that we can use a minimal context for reporting even in code that normally cannot access a context
+ // @sharable val theBase = new ContextBase // !!! DEBUG, so that we can use a minimal context for reporting even in code that normally cannot access a context
}
/** Info that changes on each compiler run */
diff --git a/src/dotty/tools/dotc/core/Denotations.scala b/src/dotty/tools/dotc/core/Denotations.scala
index 70ca88702..fc97fb32b 100644
--- a/src/dotty/tools/dotc/core/Denotations.scala
+++ b/src/dotty/tools/dotc/core/Denotations.scala
@@ -556,7 +556,7 @@ object Denotations {
*/
private def bringForward()(implicit ctx: Context): SingleDenotation = this match {
case denot: SymDenotation if ctx.stillValid(denot) =>
- if (denot.exists) assert(ctx.runId > validFor.runId, s"denotation $denot invalid in run ${ctx.runId}. ValidFor: $validFor")
+ assert(ctx.runId > validFor.runId, s"denotation $denot invalid in run ${ctx.runId}. ValidFor: $validFor")
var d: SingleDenotation = denot
do {
d.validFor = Period(ctx.period.runId, d.validFor.firstPhaseId, d.validFor.lastPhaseId)
@@ -592,7 +592,9 @@ object Denotations {
assert(false)
}
- if (valid.runId != currentPeriod.runId) initial.bringForward.current
+ if (valid.runId != currentPeriod.runId)
+ if (exists) initial.bringForward.current
+ else this
else {
var cur = this
if (currentPeriod.code > valid.code) {
diff --git a/src/dotty/tools/dotc/core/Flags.scala b/src/dotty/tools/dotc/core/Flags.scala
index f39f2bac6..161c8fde3 100644
--- a/src/dotty/tools/dotc/core/Flags.scala
+++ b/src/dotty/tools/dotc/core/Flags.scala
@@ -136,7 +136,7 @@ object Flags {
private final val FirstNotPickledFlag = 48
private final val MaxFlag = 63
- private var flagName = Array.fill(64, 2)("")
+ private val flagName = Array.fill(64, 2)("")
private def isDefinedAsFlag(idx: Int) = flagName(idx) exists (_.nonEmpty)
diff --git a/src/dotty/tools/dotc/core/Names.scala b/src/dotty/tools/dotc/core/Names.scala
index ea30b1c3b..1ee56fe1c 100644
--- a/src/dotty/tools/dotc/core/Names.scala
+++ b/src/dotty/tools/dotc/core/Names.scala
@@ -1,4 +1,5 @@
-package dotty.tools.dotc
+package dotty.tools
+package dotc
package core
import scala.io.Codec
@@ -151,11 +152,13 @@ object Names {
override def seq = toCollection(this)
}
- class TermName(val start: Int, val length: Int, private[Names] var next: TermName) extends Name {
+ class TermName(val start: Int, val length: Int, @sharable private[Names] var next: TermName) extends Name {
+ // `next` is @sharable because it is only modified in the synchronized block of termName.
type ThisName = TermName
def isTypeName = false
def isTermName = true
+ @sharable // because it is only modified in the synchronized block of toTypeName.
@volatile private[this] var _typeName: TypeName = null
def toTypeName: TypeName = {
@@ -200,44 +203,21 @@ object Names {
private final val fillFactor = 0.7
/** Memory to store all names sequentially. */
+ @sharable // because it's only mutated in synchronized block of termName
private[dotty] var chrs: Array[Char] = new Array[Char](InitialNameSize)
/** The number of characters filled. */
+ @sharable // because it's only mutated in synchronized block of termName
private var nc = 0
/** Hashtable for finding term names quickly. */
+ @sharable // because it's only mutated in synchronized block of termName
private var table = new Array[TermName](InitialHashSize)
/** The number of defined names. */
+ @sharable // because it's only mutated in synchronized block of termName
private var size = 1
- /** Make sure the capacity of the character array is at least `n` */
- private def ensureCapacity(n: Int) =
- if (n > chrs.length) {
- val newchrs = new Array[Char](chrs.length * 2)
- chrs.copyToArray(newchrs)
- chrs = newchrs
- }
-
- /** Make sure the hash table is large enough for the given load factor */
- private def incTableSize() = {
- size += 1
- if (size.toDouble / table.size > fillFactor) {
- val oldTable = table
- table = new Array[TermName](table.size * 2)
- for (i <- 0 until oldTable.size) rehash(oldTable(i))
- }
- }
-
- /** Rehash chain of names */
- private def rehash(name: TermName): Unit =
- if (name != null) {
- rehash(name.next)
- val h = hashValue(chrs, name.start, name.length) & (table.size - 1)
- name.next = table(h)
- table(h) = name
- }
-
/** The hash of a name made of from characters cs[offset..offset+len-1]. */
private def hashValue(cs: Array[Char], offset: Int, len: Int): Int =
if (len > 0)
@@ -257,24 +237,53 @@ object Names {
i == len
}
- /** Enter characters into chrs array. */
- private def enterChars(cs: Array[Char], offset: Int, len: Int): Unit = {
- ensureCapacity(nc + len)
- var i = 0
- while (i < len) {
- chrs(nc + i) = cs(offset + i)
- i += 1
- }
- nc += len
- }
-
/** Create a term name from the characters in cs[offset..offset+len-1].
* Assume they are already encoded.
*/
def termName(cs: Array[Char], offset: Int, len: Int): TermName = {
util.Stats.record("termName")
val h = hashValue(cs, offset, len) & (table.size - 1)
+
synchronized {
+
+ /** Make sure the capacity of the character array is at least `n` */
+ def ensureCapacity(n: Int) =
+ if (n > chrs.length) {
+ val newchrs = new Array[Char](chrs.length * 2)
+ chrs.copyToArray(newchrs)
+ chrs = newchrs
+ }
+
+ /** Enter characters into chrs array. */
+ def enterChars(): Unit = {
+ ensureCapacity(nc + len)
+ var i = 0
+ while (i < len) {
+ chrs(nc + i) = cs(offset + i)
+ i += 1
+ }
+ nc += len
+ }
+
+ /** Rehash chain of names */
+ def rehash(name: TermName): Unit =
+ if (name != null) {
+ rehash(name.next)
+ val h = hashValue(chrs, name.start, name.length) & (table.size - 1)
+ name.next = table(h)
+ table(h) = name
+ }
+
+ /** Make sure the hash table is large enough for the given load factor */
+ def incTableSize() = {
+ size += 1
+ if (size.toDouble / table.size > fillFactor) {
+ val oldTable = table
+ table = new Array[TermName](table.size * 2)
+ for (i <- 0 until oldTable.size) rehash(oldTable(i))
+ }
+ }
+
val next = table(h)
var name = next
while (name ne null) {
@@ -283,7 +292,7 @@ object Names {
name = name.next
}
name = new TermName(nc, len, next)
- enterChars(cs, offset, len)
+ enterChars()
table(h) = name
incTableSize()
name
diff --git a/src/dotty/tools/dotc/core/SymDenotations.scala b/src/dotty/tools/dotc/core/SymDenotations.scala
index 164b0b8f3..53973b587 100644
--- a/src/dotty/tools/dotc/core/SymDenotations.scala
+++ b/src/dotty/tools/dotc/core/SymDenotations.scala
@@ -1,4 +1,5 @@
-package dotty.tools.dotc
+package dotty.tools
+package dotc
package core
import Periods._, Contexts._, Symbols._, Denotations._, Names._, NameOps._, Annotations._
@@ -1713,8 +1714,8 @@ object SymDenotations {
validFor = Period.allInRun(NoRunId) // will be brought forward automatically
}
- val NoDenotation = new NoDenotation
- val NotDefinedHereDenotation = new NoDenotation
+ @sharable val NoDenotation = new NoDenotation
+ @sharable val NotDefinedHereDenotation = new NoDenotation
// ---- Completion --------------------------------------------------------
@@ -1757,7 +1758,7 @@ object SymDenotations {
val NoSymbolFn = (ctx: Context) => NoSymbol
/** A missing completer */
- class NoCompleter extends LazyType {
+ @sharable class NoCompleter extends LazyType {
def complete(denot: SymDenotation)(implicit ctx: Context): Unit = unsupported("complete")
}
diff --git a/src/dotty/tools/dotc/core/Symbols.scala b/src/dotty/tools/dotc/core/Symbols.scala
index 602bdba80..ce308459a 100644
--- a/src/dotty/tools/dotc/core/Symbols.scala
+++ b/src/dotty/tools/dotc/core/Symbols.scala
@@ -38,12 +38,12 @@ trait Symbols { this: Context =>
* Note this uses a cast instead of a direct type refinement because
* it's debug-friendlier not to create an anonymous class here.
*/
- def newNakedSymbol[N <: Name](coord: Coord = NoCoord): Symbol { type ThisName = N } =
- new Symbol(coord).asInstanceOf[Symbol { type ThisName = N }]
+ def newNakedSymbol[N <: Name](coord: Coord = NoCoord)(implicit ctx: Context): Symbol { type ThisName = N } =
+ new Symbol(coord, ctx.nextId).asInstanceOf[Symbol { type ThisName = N }]
/** Create a class symbol without a denotation. */
- def newNakedClassSymbol(coord: Coord = NoCoord, assocFile: AbstractFile = null) =
- new ClassSymbol(coord, assocFile)
+ def newNakedClassSymbol(coord: Coord = NoCoord, assocFile: AbstractFile = null)(implicit ctx: Context) =
+ new ClassSymbol(coord, assocFile, ctx.nextId)
// ---- Symbol creation methods ----------------------------------
@@ -364,22 +364,16 @@ trait Symbols { this: Context =>
object Symbols {
- var _nextId = 0 // !!! DEBUG, use global counter instead
- def nextId = { _nextId += 1; _nextId }
-
-
/** A Symbol represents a Scala definition/declaration or a package.
+ * @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) extends DotClass with printing.Showable {
+ class Symbol private[Symbols] (val coord: Coord, val id: Int) extends DotClass with printing.Showable {
type ThisName <: Name
- private[this] var _id: Int = nextId
//assert(_id != 30214)
- /** The unique id of this symbol */
- def id = _id
-
/** The last denotation of this symbol */
private[this] var lastDenot: SymDenotation = _
@@ -514,8 +508,8 @@ object Symbols {
type TermSymbol = Symbol { type ThisName = TermName }
type TypeSymbol = Symbol { type ThisName = TypeName }
- class ClassSymbol private[Symbols] (coord: Coord, val assocFile: AbstractFile)
- extends Symbol(coord) {
+ class ClassSymbol private[Symbols] (coord: Coord, val assocFile: AbstractFile, id: Int)
+ extends Symbol(coord, id) {
type ThisName = TypeName
@@ -551,12 +545,12 @@ object Symbols {
override protected def prefixString = "ClassSymbol"
}
- class ErrorSymbol(val underlying: Symbol, msg: => String)(implicit ctx: Context) extends Symbol(NoCoord) {
+ class ErrorSymbol(val underlying: Symbol, msg: => String)(implicit ctx: Context) extends Symbol(NoCoord, ctx.nextId) {
type ThisName = underlying.ThisName
denot = underlying.denot
}
- object NoSymbol extends Symbol(NoCoord) {
+ @sharable object NoSymbol extends Symbol(NoCoord, 0) {
denot = NoDenotation
override def associatedFile(implicit ctx: Context): AbstractFile = NoSource.file
@@ -590,5 +584,5 @@ object Symbols {
/** The current class */
def currentClass(implicit ctx: Context): ClassSymbol = ctx.owner.enclosingClass.asClass
- var stubs: List[Symbol] = Nil // diagnostic
+ @sharable var stubs: List[Symbol] = Nil // diagnostic only
}
diff --git a/src/dotty/tools/dotc/core/TypeOps.scala b/src/dotty/tools/dotc/core/TypeOps.scala
index 2b2ef83a2..b61d39749 100644
--- a/src/dotty/tools/dotc/core/TypeOps.scala
+++ b/src/dotty/tools/dotc/core/TypeOps.scala
@@ -1,4 +1,5 @@
-package dotty.tools.dotc
+package dotty.tools
+package dotc
package core
import Contexts._, Types._, Symbols._, Names._, Flags._, Scopes._
@@ -572,5 +573,5 @@ trait TypeOps { this: Context => // TODO: Make standalone object.
object TypeOps {
val emptyDNF = (Nil, Set[Name]()) :: Nil
- var track = false // !!!DEBUG
+ @sharable var track = false // !!!DEBUG
}
diff --git a/src/dotty/tools/dotc/core/Types.scala b/src/dotty/tools/dotc/core/Types.scala
index f4dbfb0e3..61ab1133b 100644
--- a/src/dotty/tools/dotc/core/Types.scala
+++ b/src/dotty/tools/dotc/core/Types.scala
@@ -1,4 +1,5 @@
-package dotty.tools.dotc
+package dotty.tools
+package dotc
package core
import util.common._
@@ -35,7 +36,7 @@ import language.implicitConversions
object Types {
- private var nextId = 0
+ @sharable private var nextId = 0
/** The class of types.
* The principal subclasses and sub-objects are as follows:
@@ -73,6 +74,7 @@ object Types {
// ----- Tests -----------------------------------------------------
+ // debug only: a unique identifier for a type
val uniqId = {
nextId = nextId + 1
// if (nextId == 19555)
@@ -2753,13 +2755,13 @@ object Types {
case class ImportType(expr: Tree) extends UncachedGroundType
/** Sentinel for "missing type" */
- case object NoType extends CachedGroundType {
+ @sharable case object NoType extends CachedGroundType {
override def exists = false
override def computeHash = hashSeed
}
/** Missing prefix */
- case object NoPrefix extends CachedGroundType {
+ @sharable case object NoPrefix extends CachedGroundType {
override def computeHash = hashSeed
}
@@ -2776,7 +2778,7 @@ object Types {
final class CachedWildcardType(optBounds: Type) extends WildcardType(optBounds)
- object WildcardType extends WildcardType(NoType) {
+ @sharable object WildcardType extends WildcardType(NoType) {
def apply(bounds: TypeBounds)(implicit ctx: Context) = unique(new CachedWildcardType(bounds))
}
@@ -2984,7 +2986,7 @@ object Types {
}
}
- object IdentityTypeMap extends TypeMap()(NoContext) {
+ @sharable object IdentityTypeMap extends TypeMap()(NoContext) {
override def stopAtStatic = true
def apply(tp: Type) = tp
}
@@ -3218,7 +3220,7 @@ object Types {
// ----- Debug ---------------------------------------------------------
- var debugTrace = false
+ @sharable var debugTrace = false
val watchList = List[String](
) map (_.toTypeName)
diff --git a/src/dotty/tools/dotc/parsing/Parsers.scala b/src/dotty/tools/dotc/parsing/Parsers.scala
index 2a42a7fa9..d6125f236 100644
--- a/src/dotty/tools/dotc/parsing/Parsers.scala
+++ b/src/dotty/tools/dotc/parsing/Parsers.scala
@@ -36,11 +36,11 @@ object Parsers {
def nonePositive: Boolean = parCounts forall (_ <= 0)
}
- object Location extends Enumeration {
+ @sharable object Location extends Enumeration {
val InParens, InBlock, InPattern, ElseWhere = Value
}
- object ParamOwner extends Enumeration {
+ @sharable object ParamOwner extends Enumeration {
val Class, Type, TypeParam, Def = Value
}
diff --git a/src/dotty/tools/dotc/transform/CheckReentrant.scala b/src/dotty/tools/dotc/transform/CheckReentrant.scala
new file mode 100644
index 000000000..2569b3aae
--- /dev/null
+++ b/src/dotty/tools/dotc/transform/CheckReentrant.scala
@@ -0,0 +1,95 @@
+package dotty.tools.dotc
+package transform
+
+import core._
+import Names._
+import dotty.tools.dotc.transform.TreeTransforms.{AnnotationTransformer, TransformerInfo, MiniPhaseTransform, TreeTransformer}
+import ast.Trees._
+import Flags._
+import Types._
+import Constants.Constant
+import Contexts.Context
+import Symbols._
+import SymDenotations._
+import Decorators._
+import dotty.tools.dotc.core.Annotations.ConcreteAnnotation
+import dotty.tools.dotc.core.Denotations.SingleDenotation
+import scala.collection.mutable
+import DenotTransformers._
+import typer.Checking
+import Names.Name
+import NameOps._
+import StdNames._
+
+
+/** A no-op transform that checks whether the compiled sources are re-entrant.
+ * If -Ycheck:reentrant is set, the phase makes sure that there are no variables
+ * that are accessible from a global object. It excludes from checking paths that
+ * are labeled with one of the annotations
+ *
+ * @sharable Indicating a class or val can be safely shared
+ * @unshared Indicating an object will not be accessed from multiple threads
+ *
+ * Currently the analysis is only intended to check the dotty compiler itself. To make
+ * it generally useful we'd need to add at least the following:
+ *
+ * - Handle polymorphic instantiation: We might instantiate a generic class
+ * with a type that contains vars. If the class contains fields of the generic
+ * type, this may constitute a path to a shared var, which currently goes undetected.
+ * - Handle arrays: Array elements are currently ignored because they are often used
+ * in an immutable way anyway. To do better, it would be helpful to have a type
+ * for immutable array.
+ */
+class CheckReentrant extends MiniPhaseTransform { thisTransformer =>
+ import ast.tpd._
+
+ override def phaseName = "checkReentrant"
+
+ private var shared: Set[Symbol] = Set()
+ private var seen: Set[ClassSymbol] = Set()
+ private var indent: Int = 0
+
+ private val sharableAnnot = new CtxLazy(implicit ctx =>
+ ctx.requiredClass("dotty.tools.sharable"))
+ private val unsharedAnnot = new CtxLazy(implicit ctx =>
+ ctx.requiredClass("dotty.tools.unshared"))
+
+ def isIgnored(sym: Symbol)(implicit ctx: Context) =
+ sym.hasAnnotation(sharableAnnot()) ||
+ sym.hasAnnotation(unsharedAnnot())
+
+ def scanning(sym: Symbol)(op: => Unit)(implicit ctx: Context): Unit = {
+ ctx.log(i"${" " * indent}scanning $sym")
+ indent += 1
+ try op
+ finally indent -= 1
+ }
+
+ def addVars(cls: ClassSymbol)(implicit ctx: Context): Unit = {
+ if (!seen.contains(cls) && !isIgnored(cls)) {
+ seen += cls
+ scanning(cls) {
+ for (sym <- cls.classInfo.decls)
+ if (sym.isTerm && !sym.isSetter && !isIgnored(sym))
+ if (sym.is(Mutable)) {
+ ctx.error(
+ i"""possible data race involving globally reachable ${sym.showLocated}: ${sym.info}
+ | use -Ylog:checkReentrant+ to find out more about why the variable is reachable.""".stripMargin)
+ shared += sym
+ } else if (!sym.is(Method) || sym.is(Accessor | ParamAccessor)) {
+ scanning(sym) {
+ sym.info.widenExpr.classSymbols.foreach(addVars)
+ }
+ }
+ for (parent <- cls.classInfo.classParents)
+ addVars(parent.symbol.asClass)
+ }
+ }
+ }
+
+ override def transformTemplate(tree: Template)(implicit ctx: Context, info: TransformerInfo): Tree = {
+ if (ctx.settings.YcheckReentrant.value && tree.symbol.owner.isStaticOwner)
+ addVars(tree.symbol.owner.asClass)
+ tree
+ }
+} \ No newline at end of file
diff --git a/src/dotty/tools/dotc/transform/CtxLazy.scala b/src/dotty/tools/dotc/transform/CtxLazy.scala
new file mode 100644
index 000000000..7b317abef
--- /dev/null
+++ b/src/dotty/tools/dotc/transform/CtxLazy.scala
@@ -0,0 +1,23 @@
+package dotty.tools.dotc
+package transform
+import core.Contexts.Context
+
+/** Utility class for lazy values whose evaluation depends on a context.
+ * This should be used whenever the evaluation of a lazy expression
+ * depends on some context, but the value can be re-used afterwards
+ * with a different context.
+ *
+ * A typical use case is a lazy val in a phase object which exists once per root context where
+ * the expression intiializing the lazy val depends only on the root context, but not any changes afterwards.
+ */
+class CtxLazy[T](expr: Context => T) {
+ private var myValue: T = _
+ private var forced = false
+ def apply()(implicit ctx: Context): T = {
+ if (!forced) {
+ myValue = expr(ctx)
+ forced = true
+ }
+ myValue
+ }
+} \ No newline at end of file
diff --git a/src/dotty/tools/dotc/transform/TreeChecker.scala b/src/dotty/tools/dotc/transform/TreeChecker.scala
index 347eb8c01..0e69b9d1f 100644
--- a/src/dotty/tools/dotc/transform/TreeChecker.scala
+++ b/src/dotty/tools/dotc/transform/TreeChecker.scala
@@ -308,5 +308,3 @@ class TreeChecker extends Phase with SymTransformer {
}
}
}
-
-object TreeChecker extends TreeChecker
diff --git a/src/dotty/tools/dotc/transform/TreeTransform.scala b/src/dotty/tools/dotc/transform/TreeTransform.scala
index 43d77d766..62cd5dbe9 100644
--- a/src/dotty/tools/dotc/transform/TreeTransform.scala
+++ b/src/dotty/tools/dotc/transform/TreeTransform.scala
@@ -1,4 +1,5 @@
-package dotty.tools.dotc
+package dotty.tools
+package dotc
package transform
import dotty.tools.dotc.ast.tpd
@@ -194,7 +195,7 @@ object TreeTransforms {
}
}
- val NoTransform = new TreeTransform {
+ @sharable val NoTransform = new TreeTransform {
def phase = unsupported("phase")
idx = -1
}
diff --git a/src/dotty/tools/dotc/typer/Implicits.scala b/src/dotty/tools/dotc/typer/Implicits.scala
index f79288e74..5444dddb0 100644
--- a/src/dotty/tools/dotc/typer/Implicits.scala
+++ b/src/dotty/tools/dotc/typer/Implicits.scala
@@ -681,4 +681,4 @@ class TermRefSet(implicit ctx: Context) extends mutable.Traversable[TermRef] {
f(TermRef(pre, sym))
}
-object EmptyTermRefSet extends TermRefSet()(NoContext)
+@sharable object EmptyTermRefSet extends TermRefSet()(NoContext)
diff --git a/src/dotty/tools/dotc/typer/Inferencing.scala b/src/dotty/tools/dotc/typer/Inferencing.scala
index 79e00db6b..8df544dd6 100644
--- a/src/dotty/tools/dotc/typer/Inferencing.scala
+++ b/src/dotty/tools/dotc/typer/Inferencing.scala
@@ -257,7 +257,7 @@ trait Inferencing { this: Checking =>
}
/** An enumeration controlling the degree of forcing in "is-dully-defined" checks. */
-object ForceDegree extends Enumeration {
+@sharable object ForceDegree extends Enumeration {
val none, // don't force type variables
noBottom, // force type variables, fail if forced to Nothing or Null
all = Value // force type variables, don't fail
diff --git a/src/dotty/tools/dotc/typer/Mode.scala b/src/dotty/tools/dotc/typer/Mode.scala
index b903049d2..8dd4c5b68 100644
--- a/src/dotty/tools/dotc/typer/Mode.scala
+++ b/src/dotty/tools/dotc/typer/Mode.scala
@@ -18,7 +18,7 @@ case class Mode(val bits: Int) extends AnyVal {
object Mode {
val None = Mode(0)
- private var modeName = new Array[String](32)
+ private val modeName = new Array[String](32)
def newMode(bit: Int, name: String): Mode = {
modeName(bit) = name
diff --git a/src/dotty/tools/dotc/typer/ProtoTypes.scala b/src/dotty/tools/dotc/typer/ProtoTypes.scala
index 13011cb85..40e919ee5 100644
--- a/src/dotty/tools/dotc/typer/ProtoTypes.scala
+++ b/src/dotty/tools/dotc/typer/ProtoTypes.scala
@@ -151,7 +151,7 @@ object ProtoTypes {
* operation is further selection. In this case, the expression need not be a value.
* @see checkValue
*/
- object AnySelectionProto extends SelectionProto(nme.WILDCARD, WildcardType, NoViewsAllowed)
+ @sharable object AnySelectionProto extends SelectionProto(nme.WILDCARD, WildcardType, NoViewsAllowed)
/** A prototype for selections in pattern constructors */
class UnapplySelectionProto(name: Name) extends SelectionProto(name, WildcardType, NoViewsAllowed)
@@ -308,7 +308,7 @@ object ProtoTypes {
*
* [] _
*/
- object AnyFunctionProto extends UncachedGroundType with MatchAlways
+ @sharable object AnyFunctionProto extends UncachedGroundType with MatchAlways
/** Add all parameters in given polytype `pt` to the constraint's domain.
* If the constraint contains already some of these parameters in its domain,
@@ -429,7 +429,7 @@ object ProtoTypes {
def apply(tp: Type) = wildApprox(tp, this)
}
- private lazy val dummyTree = untpd.Literal(Constant(null))
+ @sharable private lazy val dummyTree = untpd.Literal(Constant(null))
/** Dummy tree to be used as an argument of a FunProto or ViewProto type */
object dummyTreeOfType {
diff --git a/src/dotty/tools/dotc/util/SourceFile.scala b/src/dotty/tools/dotc/util/SourceFile.scala
index 5e8f85e28..da2e54132 100644
--- a/src/dotty/tools/dotc/util/SourceFile.scala
+++ b/src/dotty/tools/dotc/util/SourceFile.scala
@@ -12,7 +12,7 @@ import ScriptSourceFile._
import Positions._
object ScriptSourceFile {
- private val headerPattern = Pattern.compile("""^(::)?!#.*(\r|\n|\r\n)""", Pattern.MULTILINE)
+ @sharable private val headerPattern = Pattern.compile("""^(::)?!#.*(\r|\n|\r\n)""", Pattern.MULTILINE)
private val headerStarts = List("#!", "::#!")
def apply(file: AbstractFile, content: Array[Char]) = {
@@ -131,7 +131,7 @@ case class SourceFile(file: AbstractFile, content: Array[Char]) {
override def toString = file.toString
}
-object NoSource extends SourceFile("<no source>", Nil) {
+@sharable object NoSource extends SourceFile("<no source>", Nil) {
override def exists = false
}
diff --git a/src/dotty/tools/dotc/util/SourcePosition.scala b/src/dotty/tools/dotc/util/SourcePosition.scala
index c88cbc78b..9e02841f2 100644
--- a/src/dotty/tools/dotc/util/SourcePosition.scala
+++ b/src/dotty/tools/dotc/util/SourcePosition.scala
@@ -1,4 +1,5 @@
-package dotty.tools.dotc.util
+package dotty.tools
+package dotc.util
import Positions.{Position, NoPosition}
@@ -23,7 +24,7 @@ case class SourcePosition(source: SourceFile, pos: Position) {
}
/** A sentinel for a non-existing source position */
-object NoSourcePosition extends SourcePosition(NoSource, NoPosition) {
+@sharable object NoSourcePosition extends SourcePosition(NoSource, NoPosition) {
override def toString = "?"
}
diff --git a/src/dotty/tools/dotc/util/Stats.scala b/src/dotty/tools/dotc/util/Stats.scala
index d899e9546..fdd3602c9 100644
--- a/src/dotty/tools/dotc/util/Stats.scala
+++ b/src/dotty/tools/dotc/util/Stats.scala
@@ -1,10 +1,11 @@
-package dotty.tools.dotc
+package dotty.tools
+package dotc
package util
import core.Contexts._
import collection.mutable
-object Stats {
+@sharable object Stats {
final val enabled = true
diff --git a/src/dotty/tools/package.scala b/src/dotty/tools/package.scala
index f23b62862..5dae82b71 100644
--- a/src/dotty/tools/package.scala
+++ b/src/dotty/tools/package.scala
@@ -1,9 +1,13 @@
package dotty
+import scala.annotation.Annotation
package object tools {
type FatalError = scala.reflect.internal.FatalError
val FatalError = scala.reflect.internal.FatalError
+ class sharable extends Annotation
+ class unshared extends Annotation
+
val ListOfNil = Nil :: Nil
/** True if two lists have the same length. Since calling length on linear sequences