diff options
-rw-r--r-- | src/dotty/tools/dotc/Compiler.scala | 3 | ||||
-rw-r--r-- | src/dotty/tools/dotc/ast/Desugar.scala | 2 | ||||
-rw-r--r-- | src/dotty/tools/dotc/core/Decorators.scala | 3 | ||||
-rw-r--r-- | src/dotty/tools/dotc/core/TypeApplications.scala | 6 | ||||
-rw-r--r-- | src/dotty/tools/dotc/core/Types.scala | 45 | ||||
-rw-r--r-- | src/dotty/tools/dotc/printing/PlainPrinter.scala | 3 | ||||
-rw-r--r-- | src/dotty/tools/dotc/reporting/ConsoleReporter.scala | 2 | ||||
-rw-r--r-- | src/dotty/tools/dotc/transform/FirstTransform.scala | 4 | ||||
-rw-r--r-- | src/dotty/tools/dotc/transform/Pickler.scala | 34 | ||||
-rw-r--r-- | src/dotty/tools/dotc/typer/Checking.scala | 2 | ||||
-rw-r--r-- | src/dotty/tools/dotc/typer/FrontEnd.scala | 2 | ||||
-rw-r--r-- | src/dotty/tools/dotc/typer/InstChecks.scala | 90 | ||||
-rw-r--r-- | src/dotty/tools/dotc/typer/RefChecks.scala | 12 | ||||
-rw-r--r-- | test/dotc/tests.scala | 2 | ||||
-rw-r--r-- | tests/neg/instantiateAbstract.scala | 38 | ||||
-rw-r--r-- | tests/neg/selfInheritance.scala | 28 |
16 files changed, 233 insertions, 43 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/ast/Desugar.scala b/src/dotty/tools/dotc/ast/Desugar.scala index 165f4f535..0c13d1ecc 100644 --- a/src/dotty/tools/dotc/ast/Desugar.scala +++ b/src/dotty/tools/dotc/ast/Desugar.scala @@ -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) => diff --git a/src/dotty/tools/dotc/core/Decorators.scala b/src/dotty/tools/dotc/core/Decorators.scala index 1ce834428..4e672ef40 100644 --- a/src/dotty/tools/dotc/core/Decorators.scala +++ b/src/dotty/tools/dotc/core/Decorators.scala @@ -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 */ @@ -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 { diff --git a/src/dotty/tools/dotc/core/TypeApplications.scala b/src/dotty/tools/dotc/core/TypeApplications.scala index df18813b9..de42b3e5f 100644 --- a/src/dotty/tools/dotc/core/TypeApplications.scala +++ b/src/dotty/tools/dotc/core/TypeApplications.scala @@ -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) => @@ -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. */ diff --git a/src/dotty/tools/dotc/core/Types.scala b/src/dotty/tools/dotc/core/Types.scala index e6235695e..fe95219b8 100644 --- a/src/dotty/tools/dotc/core/Types.scala +++ b/src/dotty/tools/dotc/core/Types.scala @@ -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 @@ -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 && @@ -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 diff --git a/src/dotty/tools/dotc/printing/PlainPrinter.scala b/src/dotty/tools/dotc/printing/PlainPrinter.scala index 2762d9b51..0a7edd2aa 100644 --- a/src/dotty/tools/dotc/printing/PlainPrinter.scala +++ b/src/dotty/tools/dotc/printing/PlainPrinter.scala @@ -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 diff --git a/src/dotty/tools/dotc/reporting/ConsoleReporter.scala b/src/dotty/tools/dotc/reporting/ConsoleReporter.scala index a7f7f70bb..f07f43a63 100644 --- a/src/dotty/tools/dotc/reporting/ConsoleReporter.scala +++ b/src/dotty/tools/dotc/reporting/ConsoleReporter.scala @@ -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() 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/transform/Pickler.scala b/src/dotty/tools/dotc/transform/Pickler.scala index 445fed2c4..515dad2aa 100644 --- a/src/dotty/tools/dotc/transform/Pickler.scala +++ b/src/dotty/tools/dotc/transform/Pickler.scala @@ -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] = { diff --git a/src/dotty/tools/dotc/typer/Checking.scala b/src/dotty/tools/dotc/typer/Checking.scala index 9303572d2..b28afa6f2 100644 --- a/src/dotty/tools/dotc/typer/Checking.scala +++ b/src/dotty/tools/dotc/typer/Checking.scala @@ -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 '#'. diff --git a/src/dotty/tools/dotc/typer/FrontEnd.scala b/src/dotty/tools/dotc/typer/FrontEnd.scala index bb313501d..ceb806e52 100644 --- a/src/dotty/tools/dotc/typer/FrontEnd.scala +++ b/src/dotty/tools/dotc/typer/FrontEnd.scala @@ -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 = { diff --git a/src/dotty/tools/dotc/typer/InstChecks.scala b/src/dotty/tools/dotc/typer/InstChecks.scala new file mode 100644 index 000000000..7148a6e68 --- /dev/null +++ b/src/dotty/tools/dotc/typer/InstChecks.scala @@ -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) + } + } +} diff --git a/src/dotty/tools/dotc/typer/RefChecks.scala b/src/dotty/tools/dotc/typer/RefChecks.scala index 9b14fffc0..6a1f3652b 100644 --- a/src/dotty/tools/dotc/typer/RefChecks.scala +++ b/src/dotty/tools/dotc/typer/RefChecks.scala @@ -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. @@ -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 diff --git a/test/dotc/tests.scala b/test/dotc/tests.scala index ebd4673ef..85a3e317a 100644 --- a/test/dotc/tests.scala +++ b/test/dotc/tests.scala @@ -138,6 +138,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(toolsDir, "dotc", failedOther)(allowDeepSubtypes ++ twice) // see dotc_core @Test def dotc_ast = compileDir(dotcDir, "ast", failedOther ++ twice) 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 +} diff --git a/tests/neg/selfInheritance.scala b/tests/neg/selfInheritance.scala new file mode 100644 index 000000000..5f61c5bbb --- /dev/null +++ b/tests/neg/selfInheritance.scala @@ -0,0 +1,28 @@ +trait T { self: B => } + +abstract class A { self: B => + +} + +class B extends A with T { +} + +class C { self: B => + +} + +class D extends A // error + +class E extends T // error + +object Test { + + new B() {} + + new A() {} // error + + object O extends A // error + + object M extends C // error + +} |