aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMartin Odersky <odersky@gmail.com>2015-04-13 15:47:54 +0200
committerMartin Odersky <odersky@gmail.com>2015-04-13 16:06:01 +0200
commit046376e48bd3c4a294cef239a6cc77a61b62bc6e (patch)
treed38a3d329da8ece65029b6167e55c62e39e38d2c
parent04eea24326c3a42ad908fe45e204af41b880f2cd (diff)
downloaddotty-046376e48bd3c4a294cef239a6cc77a61b62bc6e.tar.gz
dotty-046376e48bd3c4a294cef239a6cc77a61b62bc6e.tar.bz2
dotty-046376e48bd3c4a294cef239a6cc77a61b62bc6e.zip
Added phase to check `New` nodes for instantiability.
- Abstract classes cannot be instantiated (exceptions: parent news and Java annotations) - Instantiateed class must conform to its self type.
-rw-r--r--src/dotty/tools/dotc/Compiler.scala3
-rw-r--r--src/dotty/tools/dotc/transform/FirstTransform.scala4
-rw-r--r--src/dotty/tools/dotc/typer/InstChecks.scala89
-rw-r--r--test/dotc/tests.scala1
-rw-r--r--tests/neg/instantiateAbstract.scala38
5 files changed, 134 insertions, 1 deletions
diff --git a/src/dotty/tools/dotc/Compiler.scala b/src/dotty/tools/dotc/Compiler.scala
index 25a4c578b..c67fb124a 100644
--- a/src/dotty/tools/dotc/Compiler.scala
+++ b/src/dotty/tools/dotc/Compiler.scala
@@ -6,7 +6,7 @@ import Contexts._
import Periods._
import Symbols._
import Scopes._
-import typer.{FrontEnd, Typer, Mode, ImportInfo, RefChecks}
+import typer.{FrontEnd, Typer, Mode, ImportInfo, RefChecks, InstChecks}
import reporting.ConsoleReporter
import dotty.tools.dotc.core.Phases.Phase
import dotty.tools.dotc.transform._
@@ -38,6 +38,7 @@ class Compiler {
def phases: List[List[Phase]] =
List(
List(new FrontEnd),
+ List(new InstChecks),
List(new FirstTransform,
new SyntheticMethods),
List(new SuperAccessors),
diff --git a/src/dotty/tools/dotc/transform/FirstTransform.scala b/src/dotty/tools/dotc/transform/FirstTransform.scala
index cfe650b99..02d0bb2ba 100644
--- a/src/dotty/tools/dotc/transform/FirstTransform.scala
+++ b/src/dotty/tools/dotc/transform/FirstTransform.scala
@@ -35,6 +35,10 @@ class FirstTransform extends MiniPhaseTransform with IdentityDenotTransformer wi
import ast.tpd._
override def phaseName = "firstTransform"
+
+ override def runsAfter = Set(classOf[typer.InstChecks])
+ // This phase makes annotations disappear in types, so InstChecks should
+ // run before so that it can get at all annotations.
def transformInfo(tp: Type, sym: Symbol)(implicit ctx: Context): Type = tp
diff --git a/src/dotty/tools/dotc/typer/InstChecks.scala b/src/dotty/tools/dotc/typer/InstChecks.scala
new file mode 100644
index 000000000..91c109ce8
--- /dev/null
+++ b/src/dotty/tools/dotc/typer/InstChecks.scala
@@ -0,0 +1,89 @@
+package dotty.tools.dotc
+package typer
+
+import core._
+import Contexts.Context
+import Decorators._
+import Phases._
+import Types._, Symbols._, Flags._, StdNames._
+import util.Positions._
+import ast.Trees._
+import typer.ErrorReporting._
+
+/** This checks `New` nodes, verifying that they can be instantiated. */
+class InstChecks extends Phase {
+ import ast.tpd._
+
+ override def phaseName: String = "instchecks"
+
+ override def run(implicit ctx: Context): Unit =
+ instCheck.traverse(ctx.compilationUnit.tpdTree)
+
+ /** Check that `tp` refers to a nonAbstract class
+ * and that the instance conforms to the self type of the created class.
+ */
+ def checkInstantiatable(tp: Type, pos: Position)(implicit ctx: Context): Unit =
+ tp.underlyingClassRef(refinementOK = false) match {
+ case tref: TypeRef =>
+ val cls = tref.symbol
+ if (cls.is(AbstractOrTrait))
+ ctx.error(d"$cls is abstract; cannot be instantiated", pos)
+ if (!cls.is(Module)) {
+ val selfType = tp.givenSelfType.asSeenFrom(tref.prefix, cls.owner)
+ if (selfType.exists && !(tp <:< selfType))
+ ctx.error(d"$tp does not conform to its self type $selfType; cannot be instantiated")
+ }
+ case _ =>
+ }
+
+ def checkValidJavaAnnotation(annot: Tree)(implicit ctx: Context): Unit = {
+ // TODO fill in
+ }
+
+ val instCheck = new TreeTraverser {
+
+ def checkAnnot(annot: Tree)(implicit ctx: Context): Unit =
+ if (annot.symbol.is(JavaDefined)) checkValidJavaAnnotation(annot)
+ else traverse(annot)
+
+ def traverseNoCheck(tree: Tree)(implicit ctx: Context): Unit = tree match {
+ case Apply(fn, args) =>
+ traverseNoCheck(fn)
+ args.foreach(traverse)
+ case TypeApply(fn, args) =>
+ traverseNoCheck(fn)
+ args.foreach(traverse)
+ case Select(qual, nme.CONSTRUCTOR) =>
+ traverseNoCheck(qual)
+ case New(tpt) =>
+ traverse(tpt)
+ case _ =>
+ traverse(tree)
+ }
+
+ def traverse(tree: Tree)(implicit ctx: Context): Unit = tree match {
+ case tree: New =>
+ checkInstantiatable(tree.tpe, tree.pos)
+ traverseChildren(tree)
+ case impl @ Template(constr, parents, self, _) =>
+ traverse(constr)
+ parents.foreach(traverseNoCheck)
+ traverse(self)
+ impl.body.foreach(traverse)
+ case Annotated(annot, tree) =>
+ checkAnnot(annot)
+ traverse(tree)
+ case TypeTree(original) =>
+ tree.tpe match {
+ case AnnotatedType(annot, tpe) => checkAnnot(annot.tree)
+ case _ =>
+ }
+ traverse(original)
+ case tree: MemberDef =>
+ tree.symbol.annotations.foreach(annot => checkAnnot(annot.tree))
+ traverseChildren(tree)
+ case _ =>
+ traverseChildren(tree)
+ }
+ }
+}
diff --git a/test/dotc/tests.scala b/test/dotc/tests.scala
index c3f501a52..b9ed6fb03 100644
--- a/test/dotc/tests.scala
+++ b/test/dotc/tests.scala
@@ -126,6 +126,7 @@ class tests extends CompilerTest {
@Test def neg_i0281 = compileFile(negDir, "i0281-null-primitive-conforms", xerrors = 3)
@Test def neg_moduleSubtyping = compileFile(negDir, "moduleSubtyping", xerrors = 4)
@Test def neg_escapingRefs = compileFile(negDir, "escapingRefs", xerrors = 2)
+ @Test def neg_instantiateAbstract = compileFile(negDir, "instantiateAbstract", xerrors = 8)
@Test def neg_selfInheritance = compileFile(negDir, "selfInheritance", xerrors = 5)
@Test def dotc = compileDir(dotcDir + "tools/dotc", failedOther)(allowDeepSubtypes ++ twice) // see dotc_core
diff --git a/tests/neg/instantiateAbstract.scala b/tests/neg/instantiateAbstract.scala
new file mode 100644
index 000000000..1e119a8b5
--- /dev/null
+++ b/tests/neg/instantiateAbstract.scala
@@ -0,0 +1,38 @@
+abstract class AA
+
+trait TT
+
+class A { self: B =>
+
+}
+
+@scala.annotation.Annotation class C // error
+
+class B extends A() {
+}
+
+object Test {
+
+ @scala.annotation.Annotation type T = String // error
+ @scala.annotation.Annotation val x = 1 // error
+ @scala.annotation.Annotation def f = 1 //error
+
+ (1: @scala.annotation.Annotation) // error
+
+
+ new AA // error
+
+ new TT // error
+
+ new A // error
+
+// the following are OK in Typer but would be caught later in RefChecks
+
+ new A() {}
+
+ new AA() {}
+
+ object O extends A
+
+ object OO extends AA
+}