Skip to content

Commit e3449e9

Browse files
committed
Merge pull request scala#474 from dotty-staging/alt/instantiation-checks
Alt/instantiation checks (2)
2 parents 187480b + 35717b7 commit e3449e9

16 files changed

+233
-43
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/ast/Desugar.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -786,7 +786,7 @@ object desugar {
786786
New(ref(defn.RepeatedAnnot.typeRef), Nil :: Nil),
787787
AppliedTypeTree(ref(seqClass.typeRef), t))
788788
} else {
789-
assert(ctx.mode.isExpr, ctx.mode)
789+
assert(ctx.mode.isExpr || ctx.reporter.hasErrors, ctx.mode)
790790
Select(t, op)
791791
}
792792
case PrefixOp(op, t) =>

src/dotty/tools/dotc/core/Decorators.scala

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import Contexts._, Names._, Phases._, printing.Texts._, printing.Printer, printi
77
import util.Positions.Position, util.SourcePosition
88
import collection.mutable.ListBuffer
99
import dotty.tools.dotc.transform.TreeTransforms._
10+
import typer.Mode
1011
import scala.language.implicitConversions
1112

1213
/** This object provides useful implicit decorators for types defined elsewhere */
@@ -172,7 +173,7 @@ object Decorators {
172173
def treatSingleArg(arg: Any) : Any =
173174
try
174175
arg match {
175-
case arg: Showable => arg.show
176+
case arg: Showable => arg.show(ctx.fresh.addMode(Mode.FutureDefsOK))
176177
case _ => arg
177178
}
178179
catch {

src/dotty/tools/dotc/core/TypeApplications.scala

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -268,9 +268,7 @@ class TypeApplications(val self: Type) extends AnyVal {
268268
case _ => default
269269
}
270270
case tp @ RefinedType(parent, name) if !tp.member(name).symbol.is(ExpandedTypeParam) =>
271-
val pbase = parent.baseTypeWithArgs(base)
272-
if (pbase.member(name).exists) RefinedType(pbase, name, tp.refinedInfo)
273-
else pbase
271+
tp.wrapIfMember(parent.baseTypeWithArgs(base))
274272
case tp: TermRef =>
275273
tp.underlying.baseTypeWithArgs(base)
276274
case AndType(tp1, tp2) =>
@@ -281,7 +279,7 @@ class TypeApplications(val self: Type) extends AnyVal {
281279
default
282280
}
283281
}
284-
282+
285283
/** Translate a type of the form From[T] to To[T], keep other types as they are.
286284
* `from` and `to` must be static classes, both with one type parameter, and the same variance.
287285
*/

src/dotty/tools/dotc/core/Types.scala

Lines changed: 31 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -858,6 +858,13 @@ object Types {
858858
case _ => defn.AnyClass.typeRef
859859
}
860860

861+
/** the self type of the underlying classtype */
862+
def givenSelfType(implicit ctx: Context): Type = this match {
863+
case tp @ RefinedType(parent, name) => tp.wrapIfMember(parent.givenSelfType)
864+
case tp: TypeProxy => tp.underlying.givenSelfType
865+
case _ => NoType
866+
}
867+
861868
/** The parameter types of a PolyType or MethodType, Empty list for others */
862869
final def paramTypess(implicit ctx: Context): List[List[Type]] = this match {
863870
case mt: MethodType => mt.paramTypes :: mt.resultType.paramTypess
@@ -1781,7 +1788,12 @@ object Types {
17811788
if (false) RefinedType(parent, refinedName, refinedInfo)
17821789
else RefinedType(parent, refinedName, rt => refinedInfo.substSkolem(this, SkolemType(rt)))
17831790
}
1784-
1791+
1792+
/** Add this refinement to `parent`, provided If `refinedName` is a member of `parent`. */
1793+
def wrapIfMember(parent: Type)(implicit ctx: Context): Type =
1794+
if (parent.member(refinedName).exists) derivedRefinedType(parent, refinedName, refinedInfo)
1795+
else parent
1796+
17851797
override def equals(that: Any) = that match {
17861798
case that: RefinedType =>
17871799
this.parent == that.parent &&
@@ -2398,22 +2410,27 @@ object Types {
23982410
* - the fully applied reference to the class itself.
23992411
*/
24002412
def selfType(implicit ctx: Context): Type = {
2401-
if (selfTypeCache == null) {
2402-
def fullRef = fullyAppliedRef(cls.typeRef, cls.typeParams)
2403-
def withFullRef(tp: Type): Type =
2404-
if (ctx.erasedTypes) fullRef else AndType(tp, fullRef)
2405-
selfTypeCache = selfInfo match {
2406-
case NoType =>
2407-
fullRef
2408-
case tp: Type =>
2409-
if (cls is Module) tp else withFullRef(tp)
2410-
case self: Symbol =>
2411-
assert(!(cls is Module))
2412-
withFullRef(self.info)
2413+
if (selfTypeCache == null)
2414+
selfTypeCache = {
2415+
def fullRef = fullyAppliedRef(cls.typeRef, cls.typeParams)
2416+
val given = givenSelfType
2417+
val raw =
2418+
if (!given.exists) fullRef
2419+
else if (cls is Module) given
2420+
else if (ctx.erasedTypes) fullRef
2421+
else AndType(given, fullRef)
2422+
raw//.asSeenFrom(prefix, cls.owner)
24132423
}
2414-
}
24152424
selfTypeCache
24162425
}
2426+
2427+
/** The explicitly given self type (self types of modules are assumed to be
2428+
* explcitly given here).
2429+
*/
2430+
override def givenSelfType(implicit ctx: Context): Type = selfInfo match {
2431+
case tp: Type => tp
2432+
case self: Symbol => self.info
2433+
}
24172434

24182435
private var selfTypeCache: Type = null
24192436

src/dotty/tools/dotc/printing/PlainPrinter.scala

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -354,7 +354,8 @@ class PlainPrinter(_ctx: Context) extends Printer {
354354

355355
def toText(sym: Symbol): Text =
356356
(kindString(sym) ~~ {
357-
if (hasMeaninglessName(sym)) simpleNameString(sym.owner) + idString(sym)
357+
if (sym.isAnonymousClass) toText(sym.info.parents, " with ") ~ "{...}"
358+
else if (hasMeaninglessName(sym)) simpleNameString(sym.owner) + idString(sym)
358359
else nameString(sym)
359360
}).close
360361

src/dotty/tools/dotc/reporting/ConsoleReporter.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ class ConsoleReporter(
4141
}
4242

4343
override def doReport(d: Diagnostic)(implicit ctx: Context): Unit =
44-
if (!d.isSuppressed) d match {
44+
if (!d.isSuppressed || !hasErrors) d match {
4545
case d: Error =>
4646
printMessageAndPos(s"error: ${d.msg}", d.pos)
4747
if (ctx.settings.prompt.value) displayPrompt()

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

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

Lines changed: 16 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -27,26 +27,24 @@ class Pickler extends Phase {
2727

2828
override def run(implicit ctx: Context): Unit = {
2929
val unit = ctx.compilationUnit
30-
if (!unit.isJava) {
31-
val tree = unit.tpdTree
32-
pickling.println(i"unpickling in run ${ctx.runId}")
33-
if (ctx.settings.YtestPickler.value) beforePickling(unit) = tree.show
30+
val tree = unit.tpdTree
31+
pickling.println(i"unpickling in run ${ctx.runId}")
32+
if (ctx.settings.YtestPickler.value) beforePickling(unit) = tree.show
3433

35-
val pickler = unit.pickler
36-
val treePkl = new TreePickler(pickler)
37-
treePkl.pickle(tree :: Nil)
38-
unit.addrOfTree = treePkl.buf.addrOfTree
39-
unit.addrOfSym = treePkl.addrOfSym
40-
if (tree.pos.exists)
41-
new PositionPickler(pickler, treePkl.buf.addrOfTree).picklePositions(tree :: Nil, tree.pos)
34+
val pickler = unit.pickler
35+
val treePkl = new TreePickler(pickler)
36+
treePkl.pickle(tree :: Nil)
37+
unit.addrOfTree = treePkl.buf.addrOfTree
38+
unit.addrOfSym = treePkl.addrOfSym
39+
if (tree.pos.exists)
40+
new PositionPickler(pickler, treePkl.buf.addrOfTree).picklePositions(tree :: Nil, tree.pos)
4241

43-
def rawBytes = // not needed right now, but useful to print raw format.
44-
unit.pickler.assembleParts().iterator.grouped(10).toList.zipWithIndex.map {
45-
case (row, i) => s"${i}0: ${row.mkString(" ")}"
46-
}
47-
// println(i"rawBytes = \n$rawBytes%\n%") // DEBUG
48-
if (pickling ne noPrinter) new TastyPrinter(pickler.assembleParts()).printContents()
49-
}
42+
def rawBytes = // not needed right now, but useful to print raw format.
43+
unit.pickler.assembleParts().iterator.grouped(10).toList.zipWithIndex.map {
44+
case (row, i) => s"${i}0: ${row.mkString(" ")}"
45+
}
46+
// println(i"rawBytes = \n$rawBytes%\n%") // DEBUG
47+
if (pickling ne noPrinter) new TastyPrinter(pickler.assembleParts()).printContents()
5048
}
5149

5250
override def runOn(units: List[CompilationUnit])(implicit ctx: Context): List[CompilationUnit] = {

src/dotty/tools/dotc/typer/Checking.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -232,7 +232,7 @@ trait Checking {
232232

233233
/** Check that type `tp` is stable. */
234234
def checkStable(tp: Type, pos: Position)(implicit ctx: Context): Unit =
235-
if (!tp.isStable)
235+
if (!tp.isStable && !tp.isErroneous)
236236
ctx.error(d"$tp is not stable", pos)
237237

238238
/** Check that type `tp` is a legal prefix for '#'.

src/dotty/tools/dotc/typer/FrontEnd.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ class FrontEnd extends Phase {
5151
unitContexts foreach (enterSyms(_))
5252
unitContexts foreach (typeCheck(_))
5353
record("totalTrees", ast.Trees.ntrees)
54-
unitContexts.map(_.compilationUnit)
54+
unitContexts.map(_.compilationUnit).filter(!_.isJava)
5555
}
5656

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

src/dotty/tools/dotc/typer/RefChecks.scala

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,17 @@ object RefChecks {
7171
}
7272
}
7373

74+
/** Check that self type of this class conforms to self types of parents */
75+
private def checkSelfType(clazz: Symbol)(implicit ctx: Context): Unit = clazz.info match {
76+
case cinfo: ClassInfo =>
77+
for (parent <- cinfo.classParents) {
78+
val pself = parent.givenSelfType.asSeenFrom(clazz.thisType, parent.classSymbol)
79+
if (pself.exists && !(cinfo.selfType <:< pself))
80+
ctx.error(d"illegal inheritance: self type ${cinfo.selfType} of $clazz does not conform to self type $pself of parent ${parent.classSymbol}", clazz.pos)
81+
}
82+
case _ =>
83+
}
84+
7485
// Override checking ------------------------------------------------------------
7586

7687
/** 1. Check all members of class `clazz` for overriding conditions.
@@ -770,6 +781,7 @@ class RefChecks extends MiniPhase with SymTransformer { thisTransformer =>
770781
override def transformTemplate(tree: Template)(implicit ctx: Context, info: TransformerInfo) = {
771782
val cls = ctx.owner
772783
checkOverloadedRestrictions(cls)
784+
checkSelfType(cls)
773785
checkAllOverrides(cls)
774786
checkAnyValSubclass(cls)
775787
tree

test/dotc/tests.scala

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,8 @@ class tests extends CompilerTest {
138138
@Test def neg_i0281 = compileFile(negDir, "i0281-null-primitive-conforms", xerrors = 3)
139139
@Test def neg_moduleSubtyping = compileFile(negDir, "moduleSubtyping", xerrors = 4)
140140
@Test def neg_escapingRefs = compileFile(negDir, "escapingRefs", xerrors = 2)
141+
@Test def neg_instantiateAbstract = compileFile(negDir, "instantiateAbstract", xerrors = 8)
142+
@Test def neg_selfInheritance = compileFile(negDir, "selfInheritance", xerrors = 5)
141143

142144
@Test def dotc = compileDir(toolsDir, "dotc", failedOther)(allowDeepSubtypes ++ twice) // see dotc_core
143145
@Test def dotc_ast = compileDir(dotcDir, "ast", failedOther ++ twice)

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)