Skip to content

Commit 046376e

Browse files
committed
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.
1 parent 04eea24 commit 046376e

File tree

5 files changed

+134
-1
lines changed

5 files changed

+134
-1
lines changed

src/dotty/tools/dotc/Compiler.scala

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import Contexts._
66
import Periods._
77
import Symbols._
88
import Scopes._
9-
import typer.{FrontEnd, Typer, Mode, ImportInfo, RefChecks}
9+
import typer.{FrontEnd, Typer, Mode, ImportInfo, RefChecks, InstChecks}
1010
import reporting.ConsoleReporter
1111
import dotty.tools.dotc.core.Phases.Phase
1212
import dotty.tools.dotc.transform._
@@ -38,6 +38,7 @@ class Compiler {
3838
def phases: List[List[Phase]] =
3939
List(
4040
List(new FrontEnd),
41+
List(new InstChecks),
4142
List(new FirstTransform,
4243
new SyntheticMethods),
4344
List(new SuperAccessors),

src/dotty/tools/dotc/transform/FirstTransform.scala

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,10 @@ class FirstTransform extends MiniPhaseTransform with IdentityDenotTransformer wi
3535
import ast.tpd._
3636

3737
override def phaseName = "firstTransform"
38+
39+
override def runsAfter = Set(classOf[typer.InstChecks])
40+
// This phase makes annotations disappear in types, so InstChecks should
41+
// run before so that it can get at all annotations.
3842

3943
def transformInfo(tp: Type, sym: Symbol)(implicit ctx: Context): Type = tp
4044

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
package dotty.tools.dotc
2+
package typer
3+
4+
import core._
5+
import Contexts.Context
6+
import Decorators._
7+
import Phases._
8+
import Types._, Symbols._, Flags._, StdNames._
9+
import util.Positions._
10+
import ast.Trees._
11+
import typer.ErrorReporting._
12+
13+
/** This checks `New` nodes, verifying that they can be instantiated. */
14+
class InstChecks extends Phase {
15+
import ast.tpd._
16+
17+
override def phaseName: String = "instchecks"
18+
19+
override def run(implicit ctx: Context): Unit =
20+
instCheck.traverse(ctx.compilationUnit.tpdTree)
21+
22+
/** Check that `tp` refers to a nonAbstract class
23+
* and that the instance conforms to the self type of the created class.
24+
*/
25+
def checkInstantiatable(tp: Type, pos: Position)(implicit ctx: Context): Unit =
26+
tp.underlyingClassRef(refinementOK = false) match {
27+
case tref: TypeRef =>
28+
val cls = tref.symbol
29+
if (cls.is(AbstractOrTrait))
30+
ctx.error(d"$cls is abstract; cannot be instantiated", pos)
31+
if (!cls.is(Module)) {
32+
val selfType = tp.givenSelfType.asSeenFrom(tref.prefix, cls.owner)
33+
if (selfType.exists && !(tp <:< selfType))
34+
ctx.error(d"$tp does not conform to its self type $selfType; cannot be instantiated")
35+
}
36+
case _ =>
37+
}
38+
39+
def checkValidJavaAnnotation(annot: Tree)(implicit ctx: Context): Unit = {
40+
// TODO fill in
41+
}
42+
43+
val instCheck = new TreeTraverser {
44+
45+
def checkAnnot(annot: Tree)(implicit ctx: Context): Unit =
46+
if (annot.symbol.is(JavaDefined)) checkValidJavaAnnotation(annot)
47+
else traverse(annot)
48+
49+
def traverseNoCheck(tree: Tree)(implicit ctx: Context): Unit = tree match {
50+
case Apply(fn, args) =>
51+
traverseNoCheck(fn)
52+
args.foreach(traverse)
53+
case TypeApply(fn, args) =>
54+
traverseNoCheck(fn)
55+
args.foreach(traverse)
56+
case Select(qual, nme.CONSTRUCTOR) =>
57+
traverseNoCheck(qual)
58+
case New(tpt) =>
59+
traverse(tpt)
60+
case _ =>
61+
traverse(tree)
62+
}
63+
64+
def traverse(tree: Tree)(implicit ctx: Context): Unit = tree match {
65+
case tree: New =>
66+
checkInstantiatable(tree.tpe, tree.pos)
67+
traverseChildren(tree)
68+
case impl @ Template(constr, parents, self, _) =>
69+
traverse(constr)
70+
parents.foreach(traverseNoCheck)
71+
traverse(self)
72+
impl.body.foreach(traverse)
73+
case Annotated(annot, tree) =>
74+
checkAnnot(annot)
75+
traverse(tree)
76+
case TypeTree(original) =>
77+
tree.tpe match {
78+
case AnnotatedType(annot, tpe) => checkAnnot(annot.tree)
79+
case _ =>
80+
}
81+
traverse(original)
82+
case tree: MemberDef =>
83+
tree.symbol.annotations.foreach(annot => checkAnnot(annot.tree))
84+
traverseChildren(tree)
85+
case _ =>
86+
traverseChildren(tree)
87+
}
88+
}
89+
}

test/dotc/tests.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,7 @@ class tests extends CompilerTest {
126126
@Test def neg_i0281 = compileFile(negDir, "i0281-null-primitive-conforms", xerrors = 3)
127127
@Test def neg_moduleSubtyping = compileFile(negDir, "moduleSubtyping", xerrors = 4)
128128
@Test def neg_escapingRefs = compileFile(negDir, "escapingRefs", xerrors = 2)
129+
@Test def neg_instantiateAbstract = compileFile(negDir, "instantiateAbstract", xerrors = 8)
129130
@Test def neg_selfInheritance = compileFile(negDir, "selfInheritance", xerrors = 5)
130131

131132
@Test def dotc = compileDir(dotcDir + "tools/dotc", failedOther)(allowDeepSubtypes ++ twice) // see dotc_core

tests/neg/instantiateAbstract.scala

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
abstract class AA
2+
3+
trait TT
4+
5+
class A { self: B =>
6+
7+
}
8+
9+
@scala.annotation.Annotation class C // error
10+
11+
class B extends A() {
12+
}
13+
14+
object Test {
15+
16+
@scala.annotation.Annotation type T = String // error
17+
@scala.annotation.Annotation val x = 1 // error
18+
@scala.annotation.Annotation def f = 1 //error
19+
20+
(1: @scala.annotation.Annotation) // error
21+
22+
23+
new AA // error
24+
25+
new TT // error
26+
27+
new A // error
28+
29+
// the following are OK in Typer but would be caught later in RefChecks
30+
31+
new A() {}
32+
33+
new AA() {}
34+
35+
object O extends A
36+
37+
object OO extends AA
38+
}

0 commit comments

Comments
 (0)