Skip to content

Alt/instantiation checks (2) #474

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 8 commits into from
Apr 20, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion src/dotty/tools/dotc/Compiler.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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._
Expand Down Expand Up @@ -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),
Expand Down
2 changes: 1 addition & 1 deletion src/dotty/tools/dotc/ast/Desugar.scala
Original file line number Diff line number Diff line change
Expand Up @@ -786,7 +786,7 @@ object desugar {
New(ref(defn.RepeatedAnnot.typeRef), Nil :: Nil),
AppliedTypeTree(ref(seqClass.typeRef), t))
} else {
assert(ctx.mode.isExpr, ctx.mode)
assert(ctx.mode.isExpr || ctx.reporter.hasErrors, ctx.mode)
Select(t, op)
}
case PrefixOp(op, t) =>
Expand Down
3 changes: 2 additions & 1 deletion src/dotty/tools/dotc/core/Decorators.scala
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import Contexts._, Names._, Phases._, printing.Texts._, printing.Printer, printi
import util.Positions.Position, util.SourcePosition
import collection.mutable.ListBuffer
import dotty.tools.dotc.transform.TreeTransforms._
import typer.Mode
import scala.language.implicitConversions

/** This object provides useful implicit decorators for types defined elsewhere */
Expand Down Expand Up @@ -172,7 +173,7 @@ object Decorators {
def treatSingleArg(arg: Any) : Any =
try
arg match {
case arg: Showable => arg.show
case arg: Showable => arg.show(ctx.fresh.addMode(Mode.FutureDefsOK))
case _ => arg
}
catch {
Expand Down
6 changes: 2 additions & 4 deletions src/dotty/tools/dotc/core/TypeApplications.scala
Original file line number Diff line number Diff line change
Expand Up @@ -268,9 +268,7 @@ class TypeApplications(val self: Type) extends AnyVal {
case _ => default
}
case tp @ RefinedType(parent, name) if !tp.member(name).symbol.is(ExpandedTypeParam) =>
val pbase = parent.baseTypeWithArgs(base)
if (pbase.member(name).exists) RefinedType(pbase, name, tp.refinedInfo)
else pbase
tp.wrapIfMember(parent.baseTypeWithArgs(base))
case tp: TermRef =>
tp.underlying.baseTypeWithArgs(base)
case AndType(tp1, tp2) =>
Expand All @@ -281,7 +279,7 @@ class TypeApplications(val self: Type) extends AnyVal {
default
}
}

/** Translate a type of the form From[T] to To[T], keep other types as they are.
* `from` and `to` must be static classes, both with one type parameter, and the same variance.
*/
Expand Down
45 changes: 31 additions & 14 deletions src/dotty/tools/dotc/core/Types.scala
Original file line number Diff line number Diff line change
Expand Up @@ -858,6 +858,13 @@ object Types {
case _ => defn.AnyClass.typeRef
}

/** the self type of the underlying classtype */
def givenSelfType(implicit ctx: Context): Type = this match {
case tp @ RefinedType(parent, name) => tp.wrapIfMember(parent.givenSelfType)
case tp: TypeProxy => tp.underlying.givenSelfType
case _ => NoType
}

/** The parameter types of a PolyType or MethodType, Empty list for others */
final def paramTypess(implicit ctx: Context): List[List[Type]] = this match {
case mt: MethodType => mt.paramTypes :: mt.resultType.paramTypess
Expand Down Expand Up @@ -1781,7 +1788,12 @@ object Types {
if (false) RefinedType(parent, refinedName, refinedInfo)
else RefinedType(parent, refinedName, rt => refinedInfo.substSkolem(this, SkolemType(rt)))
}


/** Add this refinement to `parent`, provided If `refinedName` is a member of `parent`. */
def wrapIfMember(parent: Type)(implicit ctx: Context): Type =
if (parent.member(refinedName).exists) derivedRefinedType(parent, refinedName, refinedInfo)
else parent

override def equals(that: Any) = that match {
case that: RefinedType =>
this.parent == that.parent &&
Expand Down Expand Up @@ -2398,22 +2410,27 @@ object Types {
* - the fully applied reference to the class itself.
*/
def selfType(implicit ctx: Context): Type = {
if (selfTypeCache == null) {
def fullRef = fullyAppliedRef(cls.typeRef, cls.typeParams)
def withFullRef(tp: Type): Type =
if (ctx.erasedTypes) fullRef else AndType(tp, fullRef)
selfTypeCache = selfInfo match {
case NoType =>
fullRef
case tp: Type =>
if (cls is Module) tp else withFullRef(tp)
case self: Symbol =>
assert(!(cls is Module))
withFullRef(self.info)
if (selfTypeCache == null)
selfTypeCache = {
def fullRef = fullyAppliedRef(cls.typeRef, cls.typeParams)
val given = givenSelfType
val raw =
if (!given.exists) fullRef
else if (cls is Module) given
else if (ctx.erasedTypes) fullRef
else AndType(given, fullRef)
raw//.asSeenFrom(prefix, cls.owner)
}
}
selfTypeCache
}

/** The explicitly given self type (self types of modules are assumed to be
* explcitly given here).
*/
override def givenSelfType(implicit ctx: Context): Type = selfInfo match {
case tp: Type => tp
case self: Symbol => self.info
}

private var selfTypeCache: Type = null

Expand Down
3 changes: 2 additions & 1 deletion src/dotty/tools/dotc/printing/PlainPrinter.scala
Original file line number Diff line number Diff line change
Expand Up @@ -354,7 +354,8 @@ class PlainPrinter(_ctx: Context) extends Printer {

def toText(sym: Symbol): Text =
(kindString(sym) ~~ {
if (hasMeaninglessName(sym)) simpleNameString(sym.owner) + idString(sym)
if (sym.isAnonymousClass) toText(sym.info.parents, " with ") ~ "{...}"
else if (hasMeaninglessName(sym)) simpleNameString(sym.owner) + idString(sym)
else nameString(sym)
}).close

Expand Down
2 changes: 1 addition & 1 deletion src/dotty/tools/dotc/reporting/ConsoleReporter.scala
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ class ConsoleReporter(
}

override def doReport(d: Diagnostic)(implicit ctx: Context): Unit =
if (!d.isSuppressed) d match {
if (!d.isSuppressed || !hasErrors) d match {
case d: Error =>
printMessageAndPos(s"error: ${d.msg}", d.pos)
if (ctx.settings.prompt.value) displayPrompt()
Expand Down
4 changes: 4 additions & 0 deletions src/dotty/tools/dotc/transform/FirstTransform.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
34 changes: 16 additions & 18 deletions src/dotty/tools/dotc/transform/Pickler.scala
Original file line number Diff line number Diff line change
Expand Up @@ -27,26 +27,24 @@ class Pickler extends Phase {

override def run(implicit ctx: Context): Unit = {
val unit = ctx.compilationUnit
if (!unit.isJava) {
val tree = unit.tpdTree
pickling.println(i"unpickling in run ${ctx.runId}")
if (ctx.settings.YtestPickler.value) beforePickling(unit) = tree.show
val tree = unit.tpdTree
pickling.println(i"unpickling in run ${ctx.runId}")
if (ctx.settings.YtestPickler.value) beforePickling(unit) = tree.show

val pickler = unit.pickler
val treePkl = new TreePickler(pickler)
treePkl.pickle(tree :: Nil)
unit.addrOfTree = treePkl.buf.addrOfTree
unit.addrOfSym = treePkl.addrOfSym
if (tree.pos.exists)
new PositionPickler(pickler, treePkl.buf.addrOfTree).picklePositions(tree :: Nil, tree.pos)
val pickler = unit.pickler
val treePkl = new TreePickler(pickler)
treePkl.pickle(tree :: Nil)
unit.addrOfTree = treePkl.buf.addrOfTree
unit.addrOfSym = treePkl.addrOfSym
if (tree.pos.exists)
new PositionPickler(pickler, treePkl.buf.addrOfTree).picklePositions(tree :: Nil, tree.pos)

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

override def runOn(units: List[CompilationUnit])(implicit ctx: Context): List[CompilationUnit] = {
Expand Down
2 changes: 1 addition & 1 deletion src/dotty/tools/dotc/typer/Checking.scala
Original file line number Diff line number Diff line change
Expand Up @@ -232,7 +232,7 @@ trait Checking {

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

/** Check that type `tp` is a legal prefix for '#'.
Expand Down
2 changes: 1 addition & 1 deletion src/dotty/tools/dotc/typer/FrontEnd.scala
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ class FrontEnd extends Phase {
unitContexts foreach (enterSyms(_))
unitContexts foreach (typeCheck(_))
record("totalTrees", ast.Trees.ntrees)
unitContexts.map(_.compilationUnit)
unitContexts.map(_.compilationUnit).filter(!_.isJava)
}

override def run(implicit ctx: Context): Unit = {
Expand Down
90 changes: 90 additions & 0 deletions src/dotty/tools/dotc/typer/InstChecks.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
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._
import DenotTransformers._

/** This checks `New` nodes, verifying that they can be instantiated. */
class InstChecks extends Phase with IdentityDenotTransformer {
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)
}
}
}
12 changes: 12 additions & 0 deletions src/dotty/tools/dotc/typer/RefChecks.scala
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,17 @@ object RefChecks {
}
}

/** Check that self type of this class conforms to self types of parents */
private def checkSelfType(clazz: Symbol)(implicit ctx: Context): Unit = clazz.info match {
case cinfo: ClassInfo =>
for (parent <- cinfo.classParents) {
val pself = parent.givenSelfType.asSeenFrom(clazz.thisType, parent.classSymbol)
if (pself.exists && !(cinfo.selfType <:< pself))
ctx.error(d"illegal inheritance: self type ${cinfo.selfType} of $clazz does not conform to self type $pself of parent ${parent.classSymbol}", clazz.pos)
}
case _ =>
}

// Override checking ------------------------------------------------------------

/** 1. Check all members of class `clazz` for overriding conditions.
Expand Down Expand Up @@ -770,6 +781,7 @@ class RefChecks extends MiniPhase with SymTransformer { thisTransformer =>
override def transformTemplate(tree: Template)(implicit ctx: Context, info: TransformerInfo) = {
val cls = ctx.owner
checkOverloadedRestrictions(cls)
checkSelfType(cls)
checkAllOverrides(cls)
checkAnyValSubclass(cls)
tree
Expand Down
2 changes: 2 additions & 0 deletions test/dotc/tests.scala
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,8 @@ 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
@Test def dotc_ast = compileDir(dotcDir + "tools/dotc/ast", failedOther ++ twice)
Expand Down
38 changes: 38 additions & 0 deletions tests/neg/instantiateAbstract.scala
Original file line number Diff line number Diff line change
@@ -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
}
Loading