diff options
author | Martin Odersky <odersky@gmail.com> | 2015-04-13 15:47:54 +0200 |
---|---|---|
committer | Martin Odersky <odersky@gmail.com> | 2015-04-13 16:06:01 +0200 |
commit | 046376e48bd3c4a294cef239a6cc77a61b62bc6e (patch) | |
tree | d38a3d329da8ece65029b6167e55c62e39e38d2c | |
parent | 04eea24326c3a42ad908fe45e204af41b880f2cd (diff) | |
download | dotty-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.scala | 3 | ||||
-rw-r--r-- | src/dotty/tools/dotc/transform/FirstTransform.scala | 4 | ||||
-rw-r--r-- | src/dotty/tools/dotc/typer/InstChecks.scala | 89 | ||||
-rw-r--r-- | test/dotc/tests.scala | 1 | ||||
-rw-r--r-- | tests/neg/instantiateAbstract.scala | 38 |
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 +} |