aboutsummaryrefslogtreecommitdiff
path: root/src/dotty/tools
diff options
context:
space:
mode:
authorMartin Odersky <odersky@gmail.com>2015-07-01 15:28:11 +0200
committerMartin Odersky <odersky@gmail.com>2015-07-06 17:46:44 +0200
commit64f65182f6e4f80b03d45923e02441dafe0755b4 (patch)
tree68301908a5ce6513b43a9778a1f41b8e48bdbadf /src/dotty/tools
parent70f18eb4aa5aff64aa8571c16026c456bc1db5fc (diff)
downloaddotty-64f65182f6e4f80b03d45923e02441dafe0755b4.tar.gz
dotty-64f65182f6e4f80b03d45923e02441dafe0755b4.tar.bz2
dotty-64f65182f6e4f80b03d45923e02441dafe0755b4.zip
Add reentrancy checking
New miniphase CheckRentrant verifies that compiled program is without vars accessible through global roots if -Ycheck-reentrant option is set. Known shortcoming: Array elements are currently not considered as vars. This is because in many programs arrays are used as an efficient container for immutable fields.
Diffstat (limited to 'src/dotty/tools')
-rw-r--r--src/dotty/tools/dotc/Compiler.scala3
-rw-r--r--src/dotty/tools/dotc/config/ScalaSettings.scala1
-rw-r--r--src/dotty/tools/dotc/transform/CheckReentrant.scala85
-rw-r--r--src/dotty/tools/dotc/transform/CtxLazy.scala12
-rw-r--r--src/dotty/tools/package.scala4
5 files changed, 104 insertions, 1 deletions
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/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/transform/CheckReentrant.scala b/src/dotty/tools/dotc/transform/CheckReentrant.scala
new file mode 100644
index 000000000..aff8a1e7b
--- /dev/null
+++ b/src/dotty/tools/dotc/transform/CheckReentrant.scala
@@ -0,0 +1,85 @@
+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._
+import util.CtxLazy
+
+
+/** The first tree transform
+ * - ensures there are companion objects for all classes except module classes
+ * - eliminates some kinds of trees: Imports, NamedArgs
+ * - stubs out native methods
+ */
+class CheckReentrant extends MiniPhaseTransform { thisTransformer =>
+ import ast.tpd._
+
+ override def phaseName = "checkReentrant"
+
+ def transformInfo(tp: Type, sym: Symbol)(implicit ctx: Context): Type = tp
+
+ 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 = {
+ println(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)) {
+ println(i"GLOBAL ${sym.showLocated}: ${sym.info} <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<")
+ 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..c0bac4e11
--- /dev/null
+++ b/src/dotty/tools/dotc/transform/CtxLazy.scala
@@ -0,0 +1,12 @@
+package dotty.tools.dotc
+package util
+import core.Contexts.Context
+
+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)
+ myValue
+ }
+} \ No newline at end of file
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