diff options
Diffstat (limited to 'src')
47 files changed, 922 insertions, 721 deletions
diff --git a/src/dotty/runtime/LazyVals.scala b/src/dotty/runtime/LazyVals.scala index 4130d4d60..2aa45e6fd 100644 --- a/src/dotty/runtime/LazyVals.scala +++ b/src/dotty/runtime/LazyVals.scala @@ -11,8 +11,8 @@ object LazyVals { final val BITS_PER_LAZY_VAL = 2 final val LAZY_VAL_MASK = 3 - @inline def STATE(cur: Long, ord: Long) = (cur >> (ord * BITS_PER_LAZY_VAL)) & LAZY_VAL_MASK - @inline def CAS(t: Object, offset: Long, e: Long, v: Long, ord: Int) = { + @inline def STATE(cur: Long, ord: Int) = (cur >> (ord * BITS_PER_LAZY_VAL)) & LAZY_VAL_MASK + @inline def CAS(t: Object, offset: Long, e: Long, v: Int, ord: Int) = { val mask = ~(LAZY_VAL_MASK << ord * BITS_PER_LAZY_VAL) val n = (e & mask) | (v << (ord * BITS_PER_LAZY_VAL)) compareAndSet(t, offset, e, n) @@ -65,7 +65,7 @@ object LazyVals { monitors(id) } - @inline def getOffset(obj: Object, name: String) = unsafe.objectFieldOffset(obj.getClass.getDeclaredField(name)) + @inline def getOffset(clz: Class[_], name: String) = unsafe.objectFieldOffset(clz.getDeclaredField(name)) object Names { final val state = "STATE" diff --git a/src/dotty/tools/backend/jvm/DottyBackendInterface.scala b/src/dotty/tools/backend/jvm/DottyBackendInterface.scala index 009b7fb2b..2e904cc23 100644 --- a/src/dotty/tools/backend/jvm/DottyBackendInterface.scala +++ b/src/dotty/tools/backend/jvm/DottyBackendInterface.scala @@ -589,7 +589,7 @@ class DottyBackendInterface()(implicit ctx: Context) extends BackendInterface{ def isDeferred: Boolean = sym is Flags.Deferred def isPrivate: Boolean = sym is Flags.Private def getsJavaFinalFlag: Boolean = - isFinal && !toDenot(sym).isClassConstructor && !(sym is Flags.Mutable) && !(sym.enclosingClass is Flags.JavaInterface) + isFinal && !toDenot(sym).isClassConstructor && !(sym is Flags.Mutable) && !(sym.enclosingClass is Flags.Trait) def getsJavaPrivateFlag: Boolean = isPrivate //|| (sym.isPrimaryConstructor && sym.owner.isTopLevelModuleClass) diff --git a/src/dotty/tools/backend/jvm/GenBCode.scala b/src/dotty/tools/backend/jvm/GenBCode.scala index a53e910ae..221843881 100644 --- a/src/dotty/tools/backend/jvm/GenBCode.scala +++ b/src/dotty/tools/backend/jvm/GenBCode.scala @@ -6,7 +6,7 @@ import dotty.tools.dotc.ast.tpd import dotty.tools.dotc.core.Phases.Phase import scala.collection.mutable -import scala.tools.asm.{ClassVisitor, MethodVisitor, FieldVisitor} +import scala.tools.asm.{CustomAttr, ClassVisitor, MethodVisitor, FieldVisitor} import scala.tools.nsc.Settings import scala.tools.nsc.backend.jvm._ import dotty.tools.dotc @@ -27,6 +27,7 @@ import scala.tools.asm import scala.tools.asm.tree._ import dotty.tools.dotc.util.{Positions, DotClass} import tpd._ +import StdNames._ import scala.tools.nsc.backend.jvm.opt.LocalOpt @@ -174,6 +175,13 @@ class GenBCodePipeline(val entryPoints: List[Symbol], val int: DottyBackendInter val outF = if (needsOutFolder) getOutFolder(claszSymbol, pcb.thisName) else null; val plainC = pcb.cnode + if (claszSymbol.isClass) // @DarkDimius is this test needed here? + for (pickler <- ctx.compilationUnit.picklers.get(claszSymbol.asClass)) { + val binary = pickler.assembleParts() + val dataAttr = new CustomAttr(nme.TASTYATTR.toString, binary) + plainC.visitAttribute(dataAttr) + } + // -------------- bean info class, if needed -------------- val beanC = if (claszSymbol hasAnnotation int.BeanInfoAttr) { diff --git a/src/dotty/tools/dotc/CompilationUnit.scala b/src/dotty/tools/dotc/CompilationUnit.scala index 60e16ec3e..4f8c30aab 100644 --- a/src/dotty/tools/dotc/CompilationUnit.scala +++ b/src/dotty/tools/dotc/CompilationUnit.scala @@ -19,11 +19,11 @@ class CompilationUnit(val source: SourceFile) { def isJava = source.file.name.endsWith(".java") /** - * Pickler used to create TASTY sections. + * Picklers used to create TASTY sections, indexed by toplevel class to which they belong. * Sections: Header, ASTs and Positions are populated by `pickler` phase. * Subsequent phases can add new sections. */ - lazy val pickler: TastyPickler = new TastyPickler() + var picklers: Map[ClassSymbol, TastyPickler] = Map() /** * Addresses in TASTY file of trees, stored by pickling. diff --git a/src/dotty/tools/dotc/Compiler.scala b/src/dotty/tools/dotc/Compiler.scala index cb3d0e21e..102d99347 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, InstChecks} +import typer.{FrontEnd, Typer, Mode, ImportInfo, RefChecks} import reporting.ConsoleReporter import dotty.tools.dotc.core.Phases.Phase import dotty.tools.dotc.transform._ @@ -38,11 +38,9 @@ class Compiler { def phases: List[List[Phase]] = List( List(new FrontEnd), - List(new InstChecks), - List(new FirstTransform, - new SyntheticMethods), - List(new SuperAccessors), - List(new Pickler), // Pickler needs to come last in a group since it should not pickle trees generated later + List(new PostTyper), + List(new Pickler), + List(new FirstTransform), List(new RefChecks, new ElimRepeated, new NormalizeFlags, @@ -51,8 +49,7 @@ class Compiler { List(new PatternMatcher, new ExplicitOuter, new Splitter), - List(new LazyVals, - new SeqLiterals, + List(new SeqLiterals, new InterceptedMethods, new Literalize, new Getters, @@ -60,11 +57,12 @@ class Compiler { new ResolveSuper), List(new Erasure), List(new Mixin, + new LazyVals, new Memoize, - new CapturedVars, + new CapturedVars, // capturedVars has a transformUnit: no phases should introduce local mutable vars here new Constructors, new FunctionalInterfaces), - List(new LambdaLift, + List(new LambdaLift, // in this mini-phase block scopes are incorrect. No phases that rely on scopes should be here new Flatten, new RestoreScopes), List(/*new PrivateToStatic,*/ new CollectEntryPoints, new LabelDefs, new ElimWildcardIdents, new TraitConstructors), diff --git a/src/dotty/tools/dotc/Run.scala b/src/dotty/tools/dotc/Run.scala index af9c878f0..3d4cd988e 100644 --- a/src/dotty/tools/dotc/Run.scala +++ b/src/dotty/tools/dotc/Run.scala @@ -10,6 +10,7 @@ import reporting.Reporter import transform.TreeChecker import java.io.{BufferedWriter, OutputStreamWriter} import scala.reflect.io.VirtualFile +import scala.util.control.NonFatal class Run(comp: Compiler)(implicit ctx: Context) { @@ -27,9 +28,13 @@ class Run(comp: Compiler)(implicit ctx: Context) { } } - def compile(fileNames: List[String]): Unit = { + def compile(fileNames: List[String]): Unit = try { val sources = fileNames map getSource compileSources(sources) + } catch { + case NonFatal(ex) => + println(s"exception occurred while compiling $units%, %") + throw ex } /** TODO: There's a fundamental design problem here: We assmble phases using `squash` diff --git a/src/dotty/tools/dotc/ast/TreeInfo.scala b/src/dotty/tools/dotc/ast/TreeInfo.scala index 1ba7acb48..6d1c04978 100644 --- a/src/dotty/tools/dotc/ast/TreeInfo.scala +++ b/src/dotty/tools/dotc/ast/TreeInfo.scala @@ -462,6 +462,34 @@ trait TypedTreeInfo extends TreeInfo[Type] { self: Trees.Instance[Type] => accum(Nil, root) } + + /** The top level classes in this tree, including only those module classes that + * are not a linked class of some other class in the result. + */ + def topLevelClasses(tree: Tree)(implicit ctx: Context): List[ClassSymbol] = tree match { + case PackageDef(_, stats) => stats.flatMap(topLevelClasses) + case tdef: TypeDef if tdef.symbol.isClass => tdef.symbol.asClass :: Nil + case _ => Nil + } + + /** The tree containing only the top-level classes and objects matching either `cls` or its companion object */ + def sliceTopLevel(tree: Tree, cls: ClassSymbol)(implicit ctx: Context): List[Tree] = tree match { + case PackageDef(pid, stats) => + cpy.PackageDef(tree)(pid, stats.flatMap(sliceTopLevel(_, cls))) :: Nil + case tdef: TypeDef => + val sym = tdef.symbol + assert(sym.isClass) + if (cls == sym || cls == sym.linkedClass) tdef :: Nil + else Nil + case vdef: ValDef => + val sym = vdef.symbol + assert(sym is Module) + if (cls == sym.companionClass || cls == sym.moduleClass) vdef :: Nil + else Nil + case tree => + tree :: Nil + } + /** The statement sequence that contains a definition of `sym`, or Nil * if none was found. * For a tree to be found, The symbol must have a position and its definition diff --git a/src/dotty/tools/dotc/ast/tpd.scala b/src/dotty/tools/dotc/ast/tpd.scala index 955439413..de0ef3344 100644 --- a/src/dotty/tools/dotc/ast/tpd.scala +++ b/src/dotty/tools/dotc/ast/tpd.scala @@ -2,6 +2,7 @@ package dotty.tools package dotc package ast +import dotty.tools.dotc.transform.ExplicitOuter import dotty.tools.dotc.typer.ProtoTypes.FunProtoTyped import transform.SymUtils._ import core._ @@ -243,8 +244,19 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { ta.assignType(untpd.TypeDef(cls.name, impl), cls) } + // { <label> def while$(): Unit = if (cond) { body; while$() } ; while$() } + def WhileDo(owner: Symbol, cond: Tree, body: List[Tree])(implicit ctx: Context): Tree = { + val sym = ctx.newSymbol(owner, nme.WHILE_PREFIX, Flags.Label | Flags.Synthetic, + MethodType(Nil, defn.UnitType), coord = cond.pos) + + val call = Apply(ref(sym), Nil) + val rhs = If(cond, Block(body, call), unitLiteral) + Block(List(DefDef(sym, rhs)), call) + } + + def Import(expr: Tree, selectors: List[untpd.Tree])(implicit ctx: Context): Import = - ta.assignType(untpd.Import(expr, selectors), ctx.newImportSymbol(expr)) + ta.assignType(untpd.Import(expr, selectors), ctx.newImportSymbol(ctx.owner, expr)) def PackageDef(pid: RefTree, stats: List[Tree])(implicit ctx: Context): PackageDef = ta.assignType(untpd.PackageDef(pid, stats), pid) @@ -288,7 +300,16 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { if (tp.isType) TypeTree(tp) else if (prefixIsElidable(tp)) Ident(tp) else tp.prefix match { - case pre: SingletonType => singleton(pre).select(tp) + case pre: SingletonType => + val prefix = + singleton(pre) match { + case t: This if ctx.erasedTypes && !(t.symbol == ctx.owner.enclosingClass || t.symbol.isStaticOwner) => + // after erasure outer paths should be respected + new ExplicitOuter.OuterOps(ctx).path(t.tpe.widen.classSymbol) + case t => + t + } + prefix.select(tp) case pre => SelectFromTypeTree(TypeTree(pre), tp) } // no checks necessary @@ -383,6 +404,7 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { else if (tpw isRef defn.ShortClass) Literal(Constant(0.toShort)) else Literal(Constant(null)).select(defn.Any_asInstanceOf).appliedToType(tpe) } + private class FindLocalDummyAccumulator(cls: ClassSymbol)(implicit ctx: Context) extends TreeAccumulator[Symbol] { def apply(sym: Symbol, tree: Tree)(implicit ctx: Context) = if (sym.exists) sym @@ -562,7 +584,7 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { loop(from.owner, from :: froms, to :: tos) else { //println(i"change owner ${from :: froms}%, % ==> $tos of $tree") - new TreeTypeMap(oldOwners = from :: froms, newOwners = tos).apply(tree) + new TreeTypeMap(oldOwners = from :: froms, newOwners = tos)(ctx.withMode(Mode.FutureDefsOK)).apply(tree) } } loop(from, Nil, to :: Nil) @@ -577,8 +599,11 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { def traverse(tree: Tree)(implicit ctx: Context) = tree match { case tree: DefTree => val sym = tree.symbol - if (sym.denot(ctx.withPhase(trans)).owner == from) - sym.copySymDenotation(owner = to).installAfter(trans) + if (sym.denot(ctx.withPhase(trans)).owner == from) { + val d = sym.copySymDenotation(owner = to) + d.installAfter(trans) + d.transformAfter(trans, d => if (d.owner eq from) d.copySymDenotation(owner = to) else d) + } if (sym.isWeakOwner) traverseChildren(tree) case _ => traverseChildren(tree) diff --git a/src/dotty/tools/dotc/core/Definitions.scala b/src/dotty/tools/dotc/core/Definitions.scala index 7accf9148..7de254008 100644 --- a/src/dotty/tools/dotc/core/Definitions.scala +++ b/src/dotty/tools/dotc/core/Definitions.scala @@ -335,6 +335,8 @@ class Definitions { lazy val ContravariantBetweenClass = ctx.requiredClass("dotty.annotation.internal.ContravariantBetween") lazy val ScalaSignatureAnnot = ctx.requiredClass("scala.reflect.ScalaSignature") lazy val ScalaLongSignatureAnnot = ctx.requiredClass("scala.reflect.ScalaLongSignature") + lazy val TASTYSignatureAnnot = ctx.requiredClass("scala.annotation.internal.TASTYSignature") + lazy val TASTYLongSignatureAnnot = ctx.requiredClass("scala.annotation.internal.TASTYLongSignature") lazy val DeprecatedAnnot = ctx.requiredClass("scala.deprecated") lazy val MigrationAnnot = ctx.requiredClass("scala.annotation.migration") lazy val AnnotationDefaultAnnot = ctx.requiredClass("dotty.annotation.internal.AnnotationDefault") diff --git a/src/dotty/tools/dotc/core/Denotations.scala b/src/dotty/tools/dotc/core/Denotations.scala index a30cff714..6502c4a40 100644 --- a/src/dotty/tools/dotc/core/Denotations.scala +++ b/src/dotty/tools/dotc/core/Denotations.scala @@ -620,14 +620,9 @@ object Denotations { // println(s"installing $this after $phase/${phase.id}, valid = ${current.validFor}") // printPeriods(current) this.validFor = Period(ctx.runId, targetId, current.validFor.lastPhaseId) - if (current.validFor.firstPhaseId == targetId) { - // replace current with this denotation - var prev = current - while (prev.nextInRun ne current) prev = prev.nextInRun - prev.nextInRun = this - this.nextInRun = current.nextInRun - current.validFor = Nowhere - } else { + if (current.validFor.firstPhaseId == targetId) + replaceDenotation(current) + else { // insert this denotation after current current.validFor = Period(ctx.runId, current.validFor.firstPhaseId, targetId - 1) this.nextInRun = current.nextInRun @@ -637,6 +632,33 @@ object Denotations { } } + /** Apply a transformation `f` to all denotations in this group that start at or after + * given phase. Denotations are replaced while keeping the same validity periods. + */ + protected def transformAfter(phase: DenotTransformer, f: SymDenotation => SymDenotation)(implicit ctx: Context): Unit = { + var current = symbol.current + while (current.validFor.firstPhaseId < phase.id && (current.nextInRun.validFor.code > current.validFor.code)) + current = current.nextInRun + var hasNext = true + while ((current.validFor.firstPhaseId >= phase.id) && hasNext) { + val current1: SingleDenotation = f(current.asSymDenotation) + if (current1 ne current) { + current1.validFor = current.validFor + current1.replaceDenotation(current) + } + hasNext = current1.nextInRun.validFor.code > current1.validFor.code + current = current1.nextInRun + } + } + + private def replaceDenotation(current: SingleDenotation): Unit = { + var prev = current + while (prev.nextInRun ne current) prev = prev.nextInRun + prev.nextInRun = this + this.nextInRun = current.nextInRun + current.validFor = Nowhere + } + def staleSymbolError(implicit ctx: Context) = { def ownerMsg = this match { case denot: SymDenotation => s"in ${denot.owner}" diff --git a/src/dotty/tools/dotc/core/StdNames.scala b/src/dotty/tools/dotc/core/StdNames.scala index 0a6c43f4e..829ff8b8f 100644 --- a/src/dotty/tools/dotc/core/StdNames.scala +++ b/src/dotty/tools/dotc/core/StdNames.scala @@ -215,6 +215,7 @@ object StdNames { final val RuntimeParamAnnotationATTR: N = "RuntimeVisibleParameterAnnotations" // RetentionPolicy.RUNTIME (annotations on parameters) final val ScalaATTR: N = "Scala" final val ScalaSignatureATTR: N = "ScalaSig" + final val TASTYATTR: N = "TASTY" final val SignatureATTR: N = "Signature" final val SourceFileATTR: N = "SourceFile" final val SyntheticATTR: N = "Synthetic" diff --git a/src/dotty/tools/dotc/core/SymDenotations.scala b/src/dotty/tools/dotc/core/SymDenotations.scala index 3566595f2..bcd46810e 100644 --- a/src/dotty/tools/dotc/core/SymDenotations.scala +++ b/src/dotty/tools/dotc/core/SymDenotations.scala @@ -211,6 +211,10 @@ object SymDenotations { final def hasAnnotation(cls: Symbol)(implicit ctx: Context) = dropOtherAnnotations(annotations, cls).nonEmpty + /** Apply transform `f` to all annotations of this denotation */ + final def transformAnnotations(f: Annotation => Annotation)(implicit ctx: Context): Unit = + annotations = annotations.mapConserve(f) + /** Optionally, the annotation matching the given class symbol */ final def getAnnotation(cls: Symbol)(implicit ctx: Context): Option[Annotation] = dropOtherAnnotations(annotations, cls) match { @@ -1039,6 +1043,12 @@ object SymDenotations { /** Install this denotation as the result of the given denotation transformer. */ override def installAfter(phase: DenotTransformer)(implicit ctx: Context): Unit = super.installAfter(phase) + + /** Apply a transformation `f` to all denotations in this group that start at or after + * given phase. Denotations are replaced while keeping the same validity periods. + */ + override def transformAfter(phase: DenotTransformer, f: SymDenotation => SymDenotation)(implicit ctx: Context): Unit = + super.transformAfter(phase, f) } /** The contents of a class definition during a period diff --git a/src/dotty/tools/dotc/core/Symbols.scala b/src/dotty/tools/dotc/core/Symbols.scala index 52040bcfd..9f18e723c 100644 --- a/src/dotty/tools/dotc/core/Symbols.scala +++ b/src/dotty/tools/dotc/core/Symbols.scala @@ -228,8 +228,8 @@ trait Symbols { this: Context => newSymbol(cls, nme.localDummyName(cls), EmptyFlags, NoType) /** Create an import symbol pointing back to given qualifier `expr`. */ - def newImportSymbol(expr: Tree, coord: Coord = NoCoord) = - newSymbol(NoSymbol, nme.IMPORT, EmptyFlags, ImportType(expr), coord = coord) + def newImportSymbol(owner: Symbol, expr: Tree, coord: Coord = NoCoord) = + newSymbol(owner, nme.IMPORT, EmptyFlags, ImportType(expr), coord = coord) /** Create a class constructor symbol for given class `cls`. */ def newConstructor(cls: ClassSymbol, flags: FlagSet, paramNames: List[TermName], paramTypes: List[Type], privateWithin: Symbol = NoSymbol, coord: Coord = NoCoord) = @@ -558,13 +558,17 @@ object Symbols { ctx.newSymbol(owner, name, flags, info, privateWithin, coord) } - implicit def defn(implicit ctx: Context): Definitions = ctx.definitions - /** Makes all denotation operations available on symbols */ implicit def toDenot(sym: Symbol)(implicit ctx: Context): SymDenotation = sym.denot /** Makes all class denotations available on class symbols */ implicit def toClassDenot(cls: ClassSymbol)(implicit ctx: Context): ClassDenotation = cls.classDenot + /** The Definitions object */ + def defn(implicit ctx: Context): Definitions = ctx.definitions + + /** The current class */ + def currentClass(implicit ctx: Context): ClassSymbol = ctx.owner.enclosingClass.asClass + var stubs: List[Symbol] = Nil // diagnostic } diff --git a/src/dotty/tools/dotc/core/pickling/ClassfileParser.scala b/src/dotty/tools/dotc/core/pickling/ClassfileParser.scala index 3d47678b7..21c9aa84d 100644 --- a/src/dotty/tools/dotc/core/pickling/ClassfileParser.scala +++ b/src/dotty/tools/dotc/core/pickling/ClassfileParser.scala @@ -664,11 +664,17 @@ class ClassfileParser( i < attrs } - def unpickle(bytes: Array[Byte]): Boolean = { + def unpickleScala(bytes: Array[Byte]): Boolean = { new UnPickler(bytes, classRoot, moduleRoot)(ctx).run() true } + def unpickleTASTY(bytes: Array[Byte]): Boolean = { + new DottyUnpickler(bytes) + .enter(roots = Set(classRoot, moduleRoot, moduleRoot.sourceModule)) + true + } + def parseScalaSigBytes: Array[Byte] = { val tag = in.nextByte.toChar assert(tag == STRING_TAG, tag) @@ -688,6 +694,11 @@ class ClassfileParser( pool.getBytes(entries.toList) } + if (scan(tpnme.TASTYATTR)) { + val attrLen = in.nextInt + return unpickleTASTY(in.nextBytes(attrLen)) + } + if (scan(tpnme.RuntimeAnnotationATTR)) { val attrLen = in.nextInt val nAnnots = in.nextChar @@ -698,12 +709,16 @@ class ClassfileParser( var j = 0 while (j < nArgs) { val argName = pool.getName(in.nextChar) - if (attrClass == defn.ScalaSignatureAnnot && argName == nme.bytes) - return unpickle(parseScalaSigBytes) - else if (attrClass == defn.ScalaLongSignatureAnnot && argName == nme.bytes) - return unpickle(parseScalaLongSigBytes) - else - parseAnnotArg(skip = true) + if (argName == nme.bytes) + if (attrClass == defn.ScalaSignatureAnnot) + return unpickleScala(parseScalaSigBytes) + else if (attrClass == defn.ScalaLongSignatureAnnot) + return unpickleScala(parseScalaLongSigBytes) + else if (attrClass == defn.TASTYSignatureAnnot) + return unpickleTASTY(parseScalaSigBytes) + else if (attrClass == defn.TASTYLongSignatureAnnot) + return unpickleTASTY(parseScalaLongSigBytes) + parseAnnotArg(skip = true) j += 1 } i += 1 diff --git a/src/dotty/tools/dotc/core/pickling/NameBuffer.scala b/src/dotty/tools/dotc/core/pickling/NameBuffer.scala index 2a6239c5a..7ea94089f 100644 --- a/src/dotty/tools/dotc/core/pickling/NameBuffer.scala +++ b/src/dotty/tools/dotc/core/pickling/NameBuffer.scala @@ -11,7 +11,7 @@ import scala.io.Codec import TastyName._ import PickleFormat._ -class NameBuffer extends TastyBuffer(100000) { +class NameBuffer extends TastyBuffer(10000) { private val nameRefs = new mutable.LinkedHashMap[TastyName, NameRef] diff --git a/src/dotty/tools/dotc/core/pickling/PositionPickler.scala b/src/dotty/tools/dotc/core/pickling/PositionPickler.scala index e8a0b3d01..1e36105cb 100644 --- a/src/dotty/tools/dotc/core/pickling/PositionPickler.scala +++ b/src/dotty/tools/dotc/core/pickling/PositionPickler.scala @@ -38,7 +38,7 @@ object PositionPickler { import PositionPickler._ class PositionPickler(pickler: TastyPickler, addrOfTree: Tree => Option[Addr]) { - val buf = new TastyBuffer(100000) + val buf = new TastyBuffer(5000) pickler.newSection("Positions", buf) import buf._ diff --git a/src/dotty/tools/dotc/core/pickling/TastyBuffer.scala b/src/dotty/tools/dotc/core/pickling/TastyBuffer.scala index a67722227..f57c15a3d 100644 --- a/src/dotty/tools/dotc/core/pickling/TastyBuffer.scala +++ b/src/dotty/tools/dotc/core/pickling/TastyBuffer.scala @@ -44,7 +44,8 @@ class TastyBuffer(initialSize: Int) { /** Write a byte of data. */ def writeByte(b: Int): Unit = { - if (length == bytes.length) bytes = dble(bytes) + if (length >= bytes.length) + bytes = dble(bytes) bytes(length) = b.toByte length += 1 } @@ -116,6 +117,8 @@ class TastyBuffer(initialSize: Int) { def putNat(at: Addr, x: Int, width: Int): Unit = { var y = x var w = width + if(at.index + w >= bytes.length) + bytes = dble(bytes) var digit = y & 0x7f | 0x80 while (w > 0) { w -= 1 diff --git a/src/dotty/tools/dotc/core/pickling/TreeBuffer.scala b/src/dotty/tools/dotc/core/pickling/TreeBuffer.scala index c224fc30b..393ffd278 100644 --- a/src/dotty/tools/dotc/core/pickling/TreeBuffer.scala +++ b/src/dotty/tools/dotc/core/pickling/TreeBuffer.scala @@ -8,10 +8,9 @@ import TastyBuffer.{Addr, AddrWidth} import config.Printers.pickling import ast.tpd.Tree -class TreeBuffer extends TastyBuffer(1000000) { +class TreeBuffer extends TastyBuffer(50000) { private final val ItemsOverOffsets = 2 - private val initialOffsetSize = bytes.length / (AddrWidth * ItemsOverOffsets) private var offsets = new Array[Int](initialOffsetSize) private var isRelative = new Array[Boolean](initialOffsetSize) diff --git a/src/dotty/tools/dotc/core/pickling/TreeUnpickler.scala b/src/dotty/tools/dotc/core/pickling/TreeUnpickler.scala index a58fc9071..ba3023ed1 100644 --- a/src/dotty/tools/dotc/core/pickling/TreeUnpickler.scala +++ b/src/dotty/tools/dotc/core/pickling/TreeUnpickler.scala @@ -657,10 +657,10 @@ class TreeUnpickler(reader: TastyReader, tastyName: TastyName.Table) { } def readIndexedStat(exprOwner: Symbol)(implicit ctx: Context): Tree = nextByte match { - case TYPEDEF | VALDEF | DEFDEF | IMPORT => + case TYPEDEF | VALDEF | DEFDEF => readIndexedDef() case IMPORT => - ??? + readImport() case PACKAGE => val start = currentAddr processPackage { (pid, end) => implicit ctx => @@ -670,6 +670,24 @@ class TreeUnpickler(reader: TastyReader, tastyName: TastyName.Table) { readTerm()(ctx.withOwner(exprOwner)) } + def readImport()(implicit ctx: Context): Tree = { + readByte() + readEnd() + val expr = readTerm() + def readSelectors(): List[untpd.Tree] = nextByte match { + case RENAMED => + readByte() + readEnd() + untpd.Pair(untpd.Ident(readName()), untpd.Ident(readName())) :: readSelectors() + case IMPORTED => + readByte() + untpd.Ident(readName()) :: readSelectors() + case _ => + Nil + } + Import(expr, readSelectors()) + } + def readIndexedStats(exprOwner: Symbol, end: Addr)(implicit ctx: Context): List[Tree] = until(end)(readIndexedStat(exprOwner)) diff --git a/src/dotty/tools/dotc/printing/PlainPrinter.scala b/src/dotty/tools/dotc/printing/PlainPrinter.scala index f3ffd6f6f..099ca93cf 100644 --- a/src/dotty/tools/dotc/printing/PlainPrinter.scala +++ b/src/dotty/tools/dotc/printing/PlainPrinter.scala @@ -114,7 +114,7 @@ class PlainPrinter(_ctx: Context) extends Printer { homogenize(tp) match { case tp: TypeType => toTextRHS(tp) - case tp: TermRef if !tp.denotationIsCurrent || tp.symbol.is(Module) => + case tp: TermRef if !tp.denotationIsCurrent || tp.symbol.is(Module) || tp.symbol.name.isImportName => toTextRef(tp) ~ ".type" case tp: TermRef if tp.denot.isOverloaded => "<overloaded " ~ toTextRef(tp) ~ ">" diff --git a/src/dotty/tools/dotc/transform/Constructors.scala b/src/dotty/tools/dotc/transform/Constructors.scala index cd64497e9..ddd64d500 100644 --- a/src/dotty/tools/dotc/transform/Constructors.scala +++ b/src/dotty/tools/dotc/transform/Constructors.scala @@ -80,11 +80,10 @@ class Constructors extends MiniPhaseTransform with SymTransformer { thisTransfor // (2) If the parameter accessor reference was to an alias getter, // drop the () when replacing by the parameter. object intoConstr extends TreeMap { - private var excluded: FlagSet = _ override def transform(tree: Tree)(implicit ctx: Context): Tree = tree match { case Ident(_) | Select(This(_), _) => var sym = tree.symbol - if (sym is (ParamAccessor, butNot = excluded)) sym = sym.subst(accessors, paramSyms) + if (sym is (ParamAccessor, butNot = Mutable)) sym = sym.subst(accessors, paramSyms) if (sym.owner.isConstructor) ref(sym).withPos(tree.pos) else tree case Apply(fn, Nil) => val fn1 = transform(fn) @@ -95,9 +94,8 @@ class Constructors extends MiniPhaseTransform with SymTransformer { thisTransfor if (noDirectRefsFrom(tree)) tree else super.transform(tree) } - def apply(tree: Tree, inSuperCall: Boolean = false)(implicit ctx: Context): Tree = { - this.excluded = if (inSuperCall) EmptyFlags else Mutable - transform(tree) + def apply(tree: Tree, prevOwner: Symbol)(implicit ctx: Context): Tree = { + transform(tree).changeOwnerAfter(prevOwner, constr.symbol, thisTransform) } } @@ -153,19 +151,19 @@ class Constructors extends MiniPhaseTransform with SymTransformer { thisTransfor val sym = stat.symbol if (isRetained(sym)) { if (!stat.rhs.isEmpty && !isWildcardArg(stat.rhs)) - constrStats += Assign(ref(sym), intoConstr(stat.rhs)).withPos(stat.pos) + constrStats += Assign(ref(sym), intoConstr(stat.rhs, sym)).withPos(stat.pos) clsStats += cpy.ValDef(stat)(rhs = EmptyTree) } else if (!stat.rhs.isEmpty) { sym.copySymDenotation( initFlags = sym.flags &~ Private, owner = constr.symbol).installAfter(thisTransform) - constrStats += intoConstr(stat) + constrStats += intoConstr(stat, sym) } case _: DefTree => clsStats += stat case _ => - constrStats += intoConstr(stat) + constrStats += intoConstr(stat, tree.symbol) } splitStats(stats1) case Nil => diff --git a/src/dotty/tools/dotc/transform/Erasure.scala b/src/dotty/tools/dotc/transform/Erasure.scala index 640d5f13e..51a06f9ff 100644 --- a/src/dotty/tools/dotc/transform/Erasure.scala +++ b/src/dotty/tools/dotc/transform/Erasure.scala @@ -502,7 +502,17 @@ object Erasure extends TypeTestsCasts{ traverse(newStats, oldStats) } + + private final val NoBridgeFlags = Flags.Accessor | Flags.Deferred | Flags.Lazy + /** Create a bridge DefDef which overrides a parent method. + * + * @param newDef The DefDef which needs bridging because its signature + * does not match the parent method signature + * @param parentSym A symbol corresponding to the parent method to override + * @return A new DefDef whose signature matches the parent method + * and whose body only contains a call to newDef + */ def makeBridgeDef(newDef: tpd.DefDef, parentSym: Symbol)(implicit ctx: Context): tpd.DefDef = { val newDefSym = newDef.symbol val currentClass = newDefSym.owner.asClass @@ -512,7 +522,7 @@ object Erasure extends TypeTestsCasts{ ??? } val bridge = ctx.newSymbol(currentClass, - parentSym.name, parentSym.flags | Flags.Bridge, parentSym.info, coord = newDefSym.owner.coord).asTerm + parentSym.name, parentSym.flags &~ NoBridgeFlags | Flags.Bridge, parentSym.info, coord = newDefSym.owner.coord).asTerm bridge.enteredAfter(ctx.phase.prev.asInstanceOf[DenotTransformer]) // this should be safe, as we're executing in context of next phase ctx.debuglog(s"generating bridge from ${newDefSym} to $bridge") diff --git a/src/dotty/tools/dotc/transform/ExplicitOuter.scala b/src/dotty/tools/dotc/transform/ExplicitOuter.scala index 9fc164d3b..20e367e1f 100644 --- a/src/dotty/tools/dotc/transform/ExplicitOuter.scala +++ b/src/dotty/tools/dotc/transform/ExplicitOuter.scala @@ -28,8 +28,8 @@ import collection.mutable * * - add outer parameters to constructors * - pass outer arguments in constructor calls - * - replace outer this by outer paths. * + * replacement of outer this by outer paths is done in Erasure. * needs to run after pattern matcher as it can add outer checks and force creation of $outer */ class ExplicitOuter extends MiniPhaseTransform with InfoTransformer { thisTransformer => diff --git a/src/dotty/tools/dotc/transform/ExtensionMethods.scala b/src/dotty/tools/dotc/transform/ExtensionMethods.scala index 26f26fc2f..ae22adc39 100644 --- a/src/dotty/tools/dotc/transform/ExtensionMethods.scala +++ b/src/dotty/tools/dotc/transform/ExtensionMethods.scala @@ -31,6 +31,8 @@ class ExtensionMethods extends MiniPhaseTransform with DenotTransformer with Ful override def runsAfter: Set[Class[_ <: Phase]] = Set(classOf[ElimRepeated]) + override def runsAfterGroupsOf = Set(classOf[FirstTransform]) // need companion objects to exist + override def transform(ref: SingleDenotation)(implicit ctx: Context): SingleDenotation = ref match { case ref: ClassDenotation if ref is ModuleClass => ref.linkedClass match { diff --git a/src/dotty/tools/dotc/transform/FirstTransform.scala b/src/dotty/tools/dotc/transform/FirstTransform.scala index 02d0bb2ba..aecc1b86f 100644 --- a/src/dotty/tools/dotc/transform/FirstTransform.scala +++ b/src/dotty/tools/dotc/transform/FirstTransform.scala @@ -24,21 +24,13 @@ import StdNames._ /** The first tree transform * - ensures there are companion objects for all classes except module classes - * - eliminates some kinds of trees: Imports, NamedArgs, all TypTrees other than TypeTree - * - converts Select/Ident/SelectFromTypeTree nodes that refer to types to TypeTrees. - * - inserts `.package` for selections of package object members - * - checks the bounds of AppliedTypeTrees + * - eliminates some kinds of trees: Imports, NamedArgs * - stubs out native methods - * - removes java-defined ASTs */ class FirstTransform extends MiniPhaseTransform with IdentityDenotTransformer with AnnotationTransformer { thisTransformer => 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 @@ -101,10 +93,7 @@ class FirstTransform extends MiniPhaseTransform with IdentityDenotTransformer wi case stat => stat } - def skipJava(stats: List[Tree]): List[Tree] = // packages get a JavaDefined flag. Dont skip them - stats.filter(t => !(t.symbol is(Flags.JavaDefined, Flags.Package))) - - addMissingCompanions(reorder(skipJava(stats))) + addMissingCompanions(reorder(stats)) } override def transformDefDef(ddef: DefDef)(implicit ctx: Context, info: TransformerInfo) = { @@ -119,47 +108,10 @@ class FirstTransform extends MiniPhaseTransform with IdentityDenotTransformer wi override def transformStats(trees: List[Tree])(implicit ctx: Context, info: TransformerInfo): List[Tree] = ast.Trees.flatten(reorderAndComplete(trees)(ctx.withPhase(thisTransformer.next))) - private def normalizeType(tree: Tree)(implicit ctx: Context) = - if (tree.isType) TypeTree(tree.tpe).withPos(tree.pos) else tree - - override def transformIdent(tree: Ident)(implicit ctx: Context, info: TransformerInfo) = tree.tpe match { - case tpe: ThisType => - /* - A this reference hide in a self ident, and be subsequently missed - when deciding on whether outer accessors are needed and computing outer paths. - We do this normalization directly after Typer, because during typer the - ident should rest available for hyperlinking.*/ - This(tpe.cls).withPos(tree.pos) - case _ => normalizeType(tree) - } - - - - override def transformSelect(tree: Select)(implicit ctx: Context, info: TransformerInfo) = - normalizeType { - val qual = tree.qualifier - qual.symbol.moduleClass.denot match { - case pkg: PackageClassDenotation if !tree.symbol.maybeOwner.is(Package) => - cpy.Select(tree)(qual select pkg.packageObj.symbol, tree.name) - case _ => - tree - } - } - - override def transformSelectFromTypeTree(tree: SelectFromTypeTree)(implicit ctx: Context, info: TransformerInfo) = - normalizeType(tree) - override def transformOther(tree: Tree)(implicit ctx: Context, info: TransformerInfo) = tree match { case tree: Import => EmptyTree case tree: NamedArg => transform(tree.arg) - case AppliedTypeTree(tycon, args) => - val tparams = tycon.tpe.typeSymbol.typeParams - val bounds = tparams.map(tparam => - tparam.info.asSeenFrom(tycon.tpe.normalizedPrefix, tparam.owner.owner).bounds) - Checking.checkBounds(args, bounds, _.substDealias(tparams, _)) - normalizeType(tree) - case tree => - normalizeType(tree) + case tree => tree } // invariants: all modules have companion objects diff --git a/src/dotty/tools/dotc/transform/FullParameterization.scala b/src/dotty/tools/dotc/transform/FullParameterization.scala index acfeda48e..f46942fb3 100644 --- a/src/dotty/tools/dotc/transform/FullParameterization.scala +++ b/src/dotty/tools/dotc/transform/FullParameterization.scala @@ -6,6 +6,7 @@ import Types._ import Contexts._ import Symbols._ import Decorators._ +import TypeUtils._ import StdNames.nme import NameOps._ import ast._ @@ -128,14 +129,8 @@ trait FullParameterization { */ def memberSignature(info: Type)(implicit ctx: Context): Signature = info match { case info: PolyType => memberSignature(info.resultType) - case info @ MethodType(nme.SELF :: Nil, _) => - val normalizedResultType = info.resultType match { - case rtp: MethodType => rtp - case rtp => ExprType(rtp) - } - normalizedResultType.signature - case _ => - Signature.NotAMethod + case info @ MethodType(nme.SELF :: Nil, _) => info.resultType.ensureMethodic.signature + case _ => Signature.NotAMethod } /** The type parameters (skolems) of the method definition `originalDef`, diff --git a/src/dotty/tools/dotc/transform/Getters.scala b/src/dotty/tools/dotc/transform/Getters.scala index 918a92a04..e1c35feba 100644 --- a/src/dotty/tools/dotc/transform/Getters.scala +++ b/src/dotty/tools/dotc/transform/Getters.scala @@ -16,15 +16,21 @@ import Decorators._ /** Performs the following rewritings for fields of a class: * * <mods> val x: T = e - * --> <mods> <stable> def x: T = e + * --> <mods> <stable> <accessor> def x: T = e * <mods> var x: T = e - * --> <mods> def x: T = e + * --> <mods> <accessor> def x: T = e * * <mods> val x: T - * --> <mods> <stable> def x: T + * --> <mods> <stable> <accessor> def x: T + * + * <mods> lazy val x: T = e + * --> <mods> <accessor> lazy def x: T =e * * <mods> var x: T - * --> <mods> def x: T + * --> <mods> <accessor> def x: T + * + * <mods> non-static <module> val x$ = e + * --> <mods> <module> <accessor> def x$ = e * * Omitted from the rewritings are * @@ -47,10 +53,10 @@ class Getters extends MiniPhaseTransform with SymTransformer { thisTransform => override def transformSym(d: SymDenotation)(implicit ctx: Context): SymDenotation = { def noGetterNeeded = d.is(NoGetterNeeded) || - d.initial.asInstanceOf[SymDenotation].is(PrivateLocal) && !d.owner.is(Trait) || + d.initial.asInstanceOf[SymDenotation].is(PrivateLocal) && !d.owner.is(Trait) && !d.is(Flags.Lazy) || d.is(Module) && d.isStatic || d.isSelfSym - if (d.isTerm && d.owner.isClass && d.info.isValueType && !noGetterNeeded) { + if (d.isTerm && (d.is(Lazy) || d.owner.isClass) && d.info.isValueType && !noGetterNeeded) { val maybeStable = if (d.isStable) Stable else EmptyFlags d.copySymDenotation( initFlags = d.flags | maybeStable | AccessorCreationFlags, @@ -58,7 +64,7 @@ class Getters extends MiniPhaseTransform with SymTransformer { thisTransform => } else d } - private val NoGetterNeeded = Method | Param | JavaDefined | JavaStatic | Lazy + private val NoGetterNeeded = Method | Param | JavaDefined | JavaStatic override def transformValDef(tree: ValDef)(implicit ctx: Context, info: TransformerInfo): Tree = if (tree.symbol is Method) DefDef(tree.symbol.asTerm, tree.rhs) else tree diff --git a/src/dotty/tools/dotc/transform/LambdaLift.scala b/src/dotty/tools/dotc/transform/LambdaLift.scala index 9b35d1d99..1a23d887c 100644 --- a/src/dotty/tools/dotc/transform/LambdaLift.scala +++ b/src/dotty/tools/dotc/transform/LambdaLift.scala @@ -107,18 +107,18 @@ class LambdaLift extends MiniPhase with IdentityDenotTransformer { thisTransform * in `enclosure` or there is an intermediate class properly containing `enclosure` * in which `sym` is also free. Also, update `liftedOwner` of `enclosure` so * that `enclosure` can access `sym`, or its proxy in an intermediate class. - * This means: - * + * This means: + * * 1. If there is an intermediate class in which `sym` is free, `enclosure` - * must be contained in that class (in order to access the `sym proxy stored + * must be contained in that class (in order to access the `sym proxy stored * in the class). - * + * * 2. If there is no intermediate class, `enclosure` must be contained * in the class enclosing `sym`. - * + * * Return the closest enclosing intermediate class between `enclosure` and * the owner of sym, or NoSymbol if none exists. - * + * * pre: sym.owner.isTerm, (enclosure.isMethod || enclosure.isClass) * * The idea of `markFree` is illustrated with an example: @@ -150,10 +150,10 @@ class LambdaLift extends MiniPhase with IdentityDenotTransformer { thisTransform else { ctx.log(i"mark free: ${sym.showLocated} with owner ${sym.maybeOwner} marked free in $enclosure") ctx.debuglog(i"$enclosure != ${sym.enclosure}") - val intermediate = + val intermediate = if (enclosure.is(PackageClass)) enclosure - else markFree(sym, enclosure.skipConstructor.enclosure) - // `enclosure` might be a constructor, in which case we want the enclosure + else markFree(sym, enclosure.skipConstructor.enclosure) + // `enclosure` might be a constructor, in which case we want the enclosure // of the enclosing class, so skipConstructor is needed here. if (intermediate.exists) { narrowLiftedOwner(enclosure, intermediate) @@ -394,12 +394,12 @@ class LambdaLift extends MiniPhase with IdentityDenotTransformer { thisTransform val sym = tree.symbol tree.tpe match { case tpe @ TermRef(prefix, _) => - if (prefix eq NoPrefix) + if (prefix eq NoPrefix) if (sym.enclosure != currentEnclosure && !sym.isStatic) (if (sym is Method) memberRef(sym) else proxyRef(sym)).withPos(tree.pos) else if (sym.owner.isClass) // sym was lifted out ref(sym).withPos(tree.pos) - else + else tree else if (!prefixIsElidable(tpe)) ref(tpe) else tree diff --git a/src/dotty/tools/dotc/transform/LazyVals.scala b/src/dotty/tools/dotc/transform/LazyVals.scala index a28102d7b..62dc2f085 100644 --- a/src/dotty/tools/dotc/transform/LazyVals.scala +++ b/src/dotty/tools/dotc/transform/LazyVals.scala @@ -1,6 +1,8 @@ package dotty.tools.dotc package transform +import dotty.tools.dotc.typer.Mode + import scala.collection.mutable import core._ import Contexts._ @@ -14,54 +16,46 @@ import dotty.tools.dotc.ast.{untpd, tpd} import dotty.tools.dotc.core.Constants.Constant import dotty.tools.dotc.core.Types.{ExprType, NoType, MethodType} import dotty.tools.dotc.core.Names.Name -import dotty.runtime.{LazyVals => RLazyVals} // dotty deviation import SymUtils._ import scala.collection.mutable.ListBuffer import dotty.tools.dotc.core.Denotations.SingleDenotation import dotty.tools.dotc.core.SymDenotations.SymDenotation import dotty.tools.dotc.core.DenotTransformers.{SymTransformer, IdentityDenotTransformer, DenotTransformer} +import Erasure.Boxing.adaptToType -class LazyVals extends MiniPhaseTransform with SymTransformer { +class LazyVals extends MiniPhaseTransform with IdentityDenotTransformer { + import LazyVals._ import tpd._ - - def transformSym(d: SymDenotation)(implicit ctx: Context): SymDenotation = { - if (d is(Flags.Lazy, butNot = Flags.ModuleVal | Flags.Method)) { - // Method flag is set on lazy vals coming from Unpickler. They are already methods and shouldn't be transformed twice - d.copySymDenotation( - initFlags = d.flags | Flags.Method, - info = ExprType(d.info)) - } - else d - } - def transformer = new LazyVals val containerFlags = Flags.Synthetic | Flags.Mutable | Flags.Lazy - val initFlags = Flags.Synthetic | Flags.Method + val initFlags = Flags.Synthetic | Flags.Method + + val containerFlagsMask = Flags.Method | Flags.Lazy | Flags.Accessor | Flags.Module /** this map contains mutable state of transformation: OffsetDefs to be appended to companion object definitions, * and number of bits currently used */ class OffsetInfo(var defs: List[Tree], var ord:Int) - val appendOffsetDefs = mutable.Map.empty[Name, OffsetInfo] + val appendOffsetDefs = mutable.Map.empty[Symbol, OffsetInfo] override def phaseName: String = "LazyVals" - /** List of names of phases that should have finished processing of tree - * before this phase starts processing same tree */ - // override def ensureAfter: Set[String] = Set("mixin") + /** List of names of phases that should have finished processing of tree + * before this phase starts processing same tree */ + override def runsAfter = Set(classOf[Mixin]) - override def transformValDef(tree: ValDef)(implicit ctx: Context, info: TransformerInfo): Tree = { - if (!(tree.mods is Flags.Lazy) || (tree.mods is Flags.ModuleVal)) tree + override def transformDefDef(tree: DefDef)(implicit ctx: Context, info: TransformerInfo): Tree = { + if (!(tree.symbol is Flags.Lazy) || tree.symbol.owner.is(Flags.Trait)) tree else { - val isField = tree.symbol.owner.isClass + val isField = tree.symbol.owner.isClass - if (isField) { - if (tree.symbol.isVolatile) transformFieldValDefVolatile(tree) - else transformFieldValDefNonVolatile(tree) - } - else transformLocalValDef(tree) + if (isField) { + if (tree.symbol.isVolatile || tree.symbol.is(Flags.Module)) transformMemberDefVolatile(tree) + else transformMemberDefNonVolatile(tree) + } + else transformLocalDef(tree) } } @@ -70,11 +64,12 @@ class LazyVals extends MiniPhaseTransform with SymTransformer { override def transformTypeDef(tree: TypeDef)(implicit ctx: Context, info: TransformerInfo): Tree = { if (!tree.symbol.isClass) tree else { - appendOffsetDefs.get(tree.symbol.name) match { + appendOffsetDefs.get(tree.symbol) match { case None => tree case Some(data) => val template = tree.rhs.asInstanceOf[Template] - ClassDef(tree.symbol.asClass, template.constr, data.defs.mapConserve(transformFollowingDeep) ::: template.body) + val newTemplate = cpy.Template(template)(body = data.defs ::: template.body) + cpy.TypeDef(tree)(rhs = newTemplate) //(ctx.withMode(Mode.FutureDefsOK)) } } } @@ -82,12 +77,11 @@ class LazyVals extends MiniPhaseTransform with SymTransformer { * with a LazyHolder from * dotty.runtime(eg dotty.runtime.LazyInt) */ - def transformLocalValDef(x: ValDef)(implicit ctx: Context) = x match { - case ValDef(name, tpt, _) => + def transformLocalDef(x: DefDef)(implicit ctx: Context) = { val valueInitter = x.rhs - val holderName = ctx.freshName(name.toString + StdNames.nme.LAZY_LOCAL).toTermName - val initName = ctx.freshName(name.toString + StdNames.nme.LAZY_LOCAL_INIT).toTermName - val tpe = x.tpe.widen + val holderName = ctx.freshName(x.name ++ StdNames.nme.LAZY_LOCAL).toTermName + val initName = ctx.freshName(x.name ++ StdNames.nme.LAZY_LOCAL_INIT).toTermName + val tpe = x.tpe.widen.resultType.widen val holderType = if (tpe isRef defn.IntClass) "LazyInt" @@ -105,17 +99,19 @@ class LazyVals extends MiniPhaseTransform with SymTransformer { val holderSymbol = ctx.newSymbol(x.symbol.owner, holderName, containerFlags, holderImpl.typeRef, coord = x.pos) val initSymbol = ctx.newSymbol(x.symbol.owner, initName, initFlags, MethodType(Nil, tpe), coord = x.pos) - val result = ref(holderSymbol).select("value".toTermName) - val flag = ref(holderSymbol).select("initialized".toTermName) + val result = ref(holderSymbol).select(lazyNme.value) + val flag = ref(holderSymbol).select(lazyNme.initialized) val initer = valueInitter.changeOwner(x.symbol, initSymbol) val initBody = - ref(holderSymbol).select(defn.Object_synchronized).appliedToType(tpe).appliedTo( - mkNonThreadSafeDef(result, flag, initer).ensureConforms(tpe)) + adaptToType( + ref(holderSymbol).select(defn.Object_synchronized).appliedTo( + adaptToType(mkNonThreadSafeDef(result, flag, initer), defn.ObjectType)), + tpe) val initTree = DefDef(initSymbol, initBody) val holderTree = ValDef(holderSymbol, New(holderImpl.typeRef, List())) val methodBody = { tpd.If(flag, EmptyTree, ref(initSymbol)) - result.ensureConforms(tpe) + result.ensureApplied.ensureConforms(tpe) } val methodTree = DefDef(x.symbol.asTerm, methodBody) ctx.debuglog(s"found a lazy val ${x.show},\n rewrote with ${holderTree.show}") @@ -124,7 +120,16 @@ class LazyVals extends MiniPhaseTransform with SymTransformer { override def transformStats(trees: List[tpd.Tree])(implicit ctx: Context, info: TransformerInfo): List[tpd.Tree] = { - val (holders, stats) = trees.partition { _.symbol.flags == containerFlags} + // backend requires field usage to be after field definition + // need to bring containers to start of method + val (holders, stats) = + atGroupEnd { implicit ctx: Context => + trees.partition { + _.symbol.flags.&~(Flags.Touched) == containerFlags + // Filtering out Flags.Touched is not required currently, as there are no LazyTypes involved here + // but just to be more safe + } + } holders:::stats } @@ -140,10 +145,10 @@ class LazyVals extends MiniPhaseTransform with SymTransformer { */ def mkNonThreadSafeDef(target: Tree, flag: Tree, rhs: Tree)(implicit ctx: Context) = { - val setFlag = Assign(flag, Literal(Constants.Constant(true))) - val setTarget = Assign(target, rhs) - val init = Block(List(setFlag, setTarget), target) - If(flag, target, init) + val setFlag = flag.becomes(Literal(Constants.Constant(true))) + val setTarget = target.becomes(rhs) + val init = Block(List(setFlag, setTarget), target.ensureApplied) + If(flag.ensureApplied, target.ensureApplied, init) } /** Create non-threadsafe lazy accessor for not-nullable types equivalent to such code @@ -157,34 +162,36 @@ class LazyVals extends MiniPhaseTransform with SymTransformer { def mkDefNonThreadSafeNonNullable(target: Symbol, rhs: Tree)(implicit ctx: Context) = { val cond = ref(target).select(nme.eq).appliedTo(Literal(Constant(null))) val exp = ref(target) - val setTarget = Assign(exp, rhs) + val setTarget = exp.becomes(rhs) val init = Block(List(setTarget), exp) If(cond, init, exp) } - def transformFieldValDefNonVolatile(x: ValDef)(implicit ctx: Context) = x match { - case ValDef(name, tpt, _) if (x.mods is Flags.Lazy) => + def transformMemberDefNonVolatile(x: DefDef)(implicit ctx: Context) = { val claz = x.symbol.owner.asClass - val tpe = x.tpe.widen + val tpe = x.tpe.widen.resultType.widen assert(!(x.mods is Flags.Mutable)) - val containerName = ctx.freshName(name.toString + StdNames.nme.LAZY_LOCAL).toTermName - val containerSymbol = ctx.newSymbol(claz, containerName, (x.mods &~ Flags.Lazy | containerFlags).flags, tpe, coord = x.symbol.coord).enteredAfter(this) + val containerName = ctx.freshName(x.name ++ StdNames.nme.LAZY_LOCAL).toTermName + val containerSymbol = ctx.newSymbol(claz, containerName, + x.symbol.flags &~ containerFlagsMask | containerFlags | Flags.Private, + tpe, coord = x.symbol.coord + ).entered val containerTree = ValDef(containerSymbol, initValue(tpe)) - if (x.tpe.isNotNull && tpe <:< defn.AnyRefType) { // can use 'null' value instead of flag + if (x.tpe.isNotNull && tpe <:< defn.ObjectType) { // can use 'null' value instead of flag val slowPath = DefDef(x.symbol.asTerm, mkDefNonThreadSafeNonNullable(containerSymbol, x.rhs)) Thicket(List(containerTree, slowPath)) } else { - val flagName = ctx.freshName(name.toString + StdNames.nme.BITMAP_PREFIX).toTermName - val flagSymbol = ctx.newSymbol(x.symbol.owner, flagName, containerFlags, defn.BooleanType) + val flagName = ctx.freshName(x.name ++ StdNames.nme.BITMAP_PREFIX).toTermName + val flagSymbol = ctx.newSymbol(x.symbol.owner, flagName, containerFlags | Flags.Private, defn.BooleanType).entered val flag = ValDef(flagSymbol, Literal(Constants.Constant(false))) val slowPath = DefDef(x.symbol.asTerm, mkNonThreadSafeDef(ref(containerSymbol), ref(flagSymbol), x.rhs)) Thicket(List(containerTree, flag, slowPath)) } } - /** Create non-threadsafe lazy accessor equivalent to such code + /** Create a threadsafe lazy accessor equivalent to such code * * def methodSymbol(): Int = { * val result: Int = 0 @@ -215,39 +222,38 @@ class LazyVals extends MiniPhaseTransform with SymTransformer { * } * result * } - * FIXME: Don't use strings with toTermName, use predefined names instead. */ def mkThreadSafeDef(methodSymbol: TermSymbol, claz: ClassSymbol, ord: Int, target: Symbol, rhs: Tree, tp: Types.Type, offset: Tree, getFlag: Tree, stateMask: Tree, casFlag: Tree, setFlagState: Tree, waitOnLock: Tree)(implicit ctx: Context) = { val initState = Literal(Constants.Constant(0)) val computeState = Literal(Constants.Constant(1)) val notifyState = Literal(Constants.Constant(2)) val computedState = Literal(Constants.Constant(3)) - val flagSymbol = ctx.newSymbol(methodSymbol, "flag".toTermName, containerFlags, defn.LongType) + val flagSymbol = ctx.newSymbol(methodSymbol, lazyNme.flag, containerFlags, defn.LongType) val flagDef = ValDef(flagSymbol, Literal(Constant(0L))) val thiz = This(claz)(ctx.fresh.setOwner(claz)) - val resultSymbol = ctx.newSymbol(methodSymbol, "result".toTermName, containerFlags, tp) + val resultSymbol = ctx.newSymbol(methodSymbol, lazyNme.result, containerFlags, tp) val resultDef = ValDef(resultSymbol, initValue(tp)) - val retrySymbol = ctx.newSymbol(methodSymbol, "retry".toTermName, containerFlags, defn.BooleanType) + val retrySymbol = ctx.newSymbol(methodSymbol, lazyNme.retry, containerFlags, defn.BooleanType) val retryDef = ValDef(retrySymbol, Literal(Constants.Constant(true))) val whileCond = ref(retrySymbol) val compute = { - val handlerSymbol = ctx.newSymbol(methodSymbol, "$anonfun".toTermName, Flags.Synthetic, - MethodType(List("x$1".toTermName), List(defn.ThrowableType), defn.IntType)) + val handlerSymbol = ctx.newSymbol(methodSymbol, nme.ANON_FUN, Flags.Synthetic, + MethodType(List(nme.x_1), List(defn.ThrowableType), defn.IntType)) val caseSymbol = ctx.newSymbol(methodSymbol, nme.DEFAULT_EXCEPTION_NAME, Flags.Synthetic, defn.ThrowableType) val complete = setFlagState.appliedTo(thiz, offset, initState, Literal(Constant(ord))) val handler = CaseDef(Bind(caseSymbol, ref(caseSymbol)), EmptyTree, Block(List(complete), Throw(ref(caseSymbol)) )) - val compute = Assign(ref(resultSymbol), rhs) + val compute = ref(resultSymbol).becomes(rhs) val tr = Try(compute, List(handler), EmptyTree) - val assign = Assign(ref(target), ref(resultSymbol)) - val noRetry = Assign(ref(retrySymbol), Literal(Constants.Constant(false))) + val assign = ref(target).becomes(ref(resultSymbol)) + val noRetry = ref(retrySymbol).becomes(Literal(Constants.Constant(false))) val body = If(casFlag.appliedTo(thiz, offset, ref(flagSymbol), computeState, Literal(Constant(ord))), Block(tr :: assign :: complete :: noRetry :: Nil, Literal(Constant(()))), Literal(Constant(()))) @@ -266,38 +272,39 @@ class LazyVals extends MiniPhaseTransform with SymTransformer { } val computed = { - val noRetry = Assign(ref(retrySymbol), Literal(Constants.Constant(false))) - val result = Assign(ref(resultSymbol), ref(target)) + val noRetry = ref(retrySymbol).becomes(Literal(Constants.Constant(false))) + val result = ref(resultSymbol).becomes(ref(target)) val body = Block(noRetry :: result :: Nil, Literal(Constant(()))) CaseDef(computedState, EmptyTree, body) } + val default = CaseDef(untpd.Ident(nme.WILDCARD).withType(defn.LongType), EmptyTree, Literal(Constant(()))) + val cases = Match(stateMask.appliedTo(ref(flagSymbol), Literal(Constant(ord))), - List(compute, waitFirst, waitSecond, computed)) //todo: annotate with @switch + List(compute, waitFirst, waitSecond, computed, default)) //todo: annotate with @switch - val whileBody = Block(List(Assign(ref(flagSymbol), getFlag.appliedTo(thiz, offset))), cases) - val cycle = untpd.WhileDo(whileCond, whileBody).withTypeUnchecked(defn.UnitType) + val whileBody = List(ref(flagSymbol).becomes(getFlag.appliedTo(thiz, offset)), cases) + val cycle = WhileDo(methodSymbol, whileCond, whileBody) DefDef(methodSymbol, Block(resultDef :: retryDef :: flagDef :: cycle :: Nil, ref(resultSymbol))) } - def transformFieldValDefVolatile(x: ValDef)(implicit ctx: Context) = x match { - case ValDef(name, tpt, _) if (x.mods is Flags.Lazy) => + def transformMemberDefVolatile(x: DefDef)(implicit ctx: Context) = { assert(!(x.mods is Flags.Mutable)) - val tpe = x.tpe.widen + val tpe = x.tpe.widen.resultType.widen val claz = x.symbol.owner.asClass - val thiz = This(claz)(ctx.fresh.setOwner(claz)) + val thizClass = Literal(Constant(claz.info)) val companion = claz.companionModule val helperModule = ctx.requiredModule("dotty.runtime.LazyVals") - val getOffset = Select(ref(helperModule), RLazyVals.Names.getOffset.toTermName) + val getOffset = Select(ref(helperModule), lazyNme.RLazyVals.getOffset) var offsetSymbol: TermSymbol = null var flag: Tree = EmptyTree var ord = 0 // compute or create appropriate offsetSymol, bitmap and bits used by current ValDef - appendOffsetDefs.get(companion.name.moduleClassName) match { + appendOffsetDefs.get(companion.moduleClass) match { case Some(info) => - val flagsPerLong = 64 / RLazyVals.BITS_PER_LAZY_VAL + val flagsPerLong = 64 / dotty.runtime.LazyVals.BITS_PER_LAZY_VAL info.ord += 1 ord = info.ord % flagsPerLong val id = info.ord / flagsPerLong @@ -310,7 +317,7 @@ class LazyVals extends MiniPhaseTransform with SymTransformer { val flagName = (StdNames.nme.BITMAP_PREFIX + id.toString).toTermName val flagSymbol = ctx.newSymbol(claz, flagName, containerFlags, defn.LongType).enteredAfter(this) flag = ValDef(flagSymbol, Literal(Constants.Constant(0L))) - val offsetTree = ValDef(offsetSymbol, getOffset.appliedTo(thiz, Literal(Constant(flagName.toString)))) + val offsetTree = ValDef(offsetSymbol, getOffset.appliedTo(thizClass, Literal(Constant(flagName.toString)))) info.defs = offsetTree :: info.defs } @@ -319,20 +326,20 @@ class LazyVals extends MiniPhaseTransform with SymTransformer { val flagName = (StdNames.nme.BITMAP_PREFIX + "0").toTermName val flagSymbol = ctx.newSymbol(claz, flagName, containerFlags, defn.LongType).enteredAfter(this) flag = ValDef(flagSymbol, Literal(Constants.Constant(0L))) - val offsetTree = ValDef(offsetSymbol, getOffset.appliedTo(thiz, Literal(Constant(flagName.toString)))) - appendOffsetDefs += (companion.name.moduleClassName -> new OffsetInfo(List(offsetTree), ord)) + val offsetTree = ValDef(offsetSymbol, getOffset.appliedTo(thizClass, Literal(Constant(flagName.toString)))) + appendOffsetDefs += (companion.moduleClass -> new OffsetInfo(List(offsetTree), ord)) } - val containerName = ctx.freshName(name.toString + StdNames.nme.LAZY_LOCAL).toTermName - val containerSymbol = ctx.newSymbol(claz, containerName, (x.mods &~ Flags.Lazy | containerFlags).flags, tpe, coord = x.symbol.coord).enteredAfter(this) + val containerName = ctx.freshName(x.name ++ StdNames.nme.LAZY_LOCAL).toTermName + val containerSymbol = ctx.newSymbol(claz, containerName, (x.mods &~ containerFlagsMask | containerFlags).flags, tpe, coord = x.symbol.coord).entered val containerTree = ValDef(containerSymbol, initValue(tpe)) - val offset = Select(ref(companion), offsetSymbol.name) - val getFlag = Select(ref(helperModule), RLazyVals.Names.get.toTermName) - val setFlag = Select(ref(helperModule), RLazyVals.Names.setFlag.toTermName) - val wait = Select(ref(helperModule), RLazyVals.Names.wait4Notification.toTermName) - val state = Select(ref(helperModule), RLazyVals.Names.state.toTermName) - val cas = Select(ref(helperModule), RLazyVals.Names.cas.toTermName) + val offset = ref(companion).ensureApplied.select(offsetSymbol) + val getFlag = Select(ref(helperModule), lazyNme.RLazyVals.get) + val setFlag = Select(ref(helperModule), lazyNme.RLazyVals.setFlag) + val wait = Select(ref(helperModule), lazyNme.RLazyVals.wait4Notification) + val state = Select(ref(helperModule), lazyNme.RLazyVals.state) + val cas = Select(ref(helperModule), lazyNme.RLazyVals.cas) val accessor = mkThreadSafeDef(x.symbol.asTerm, claz, ord, containerSymbol, x.rhs, tpe, offset, getFlag, state, cas, setFlag, wait) if (flag eq EmptyTree) @@ -341,5 +348,24 @@ class LazyVals extends MiniPhaseTransform with SymTransformer { } } +object LazyVals { + object lazyNme { + object RLazyVals { + import dotty.runtime.LazyVals._ + val get = Names.get.toTermName + val setFlag = Names.setFlag.toTermName + val wait4Notification = Names.wait4Notification.toTermName + val state = Names.state.toTermName + val cas = Names.cas.toTermName + val getOffset = Names.getOffset.toTermName + } + val flag = "flag".toTermName + val result = "result".toTermName + val value = "value".toTermName + val initialized = "initialized".toTermName + val retry = "retry".toTermName + } +} + diff --git a/src/dotty/tools/dotc/transform/MacroTransform.scala b/src/dotty/tools/dotc/transform/MacroTransform.scala index 0f57c3ff5..9634decaa 100644 --- a/src/dotty/tools/dotc/transform/MacroTransform.scala +++ b/src/dotty/tools/dotc/transform/MacroTransform.scala @@ -38,11 +38,6 @@ abstract class MacroTransform extends Phase { ctx.fresh.setTree(tree).setOwner(owner) } - /** The current enclosing class - * @pre We must be inside a class - */ - def currentClass(implicit ctx: Context): ClassSymbol = ctx.owner.enclosingClass.asClass - def transformStats(trees: List[Tree], exprOwner: Symbol)(implicit ctx: Context): List[Tree] = { def transformStat(stat: Tree): Tree = stat match { case _: Import | _: DefTree => transform(stat) diff --git a/src/dotty/tools/dotc/transform/Memoize.scala b/src/dotty/tools/dotc/transform/Memoize.scala index 75a195032..d96a52868 100644 --- a/src/dotty/tools/dotc/transform/Memoize.scala +++ b/src/dotty/tools/dotc/transform/Memoize.scala @@ -48,38 +48,26 @@ import Decorators._ case _ => } - override def prepareForDefDef(tree: DefDef)(implicit ctx: Context) = { - val sym = tree.symbol - if (sym.isGetter && !sym.is(NoFieldNeeded)) { - // allocate field early so that initializer has the right owner for subsequeny phases in - // the group. - val maybeMutable = if (sym is Stable) EmptyFlags else Mutable - val field = ctx.newSymbol( - owner = ctx.owner, - name = sym.name.asTermName.fieldName, - flags = Private | maybeMutable, - info = sym.info.resultType, - coord = tree.pos).enteredAfter(thisTransform) - tree.rhs.changeOwnerAfter(sym, field, thisTransform) - } - this - } - override def transformDefDef(tree: DefDef)(implicit ctx: Context, info: TransformerInfo): Tree = { val sym = tree.symbol - def field = { - val field = sym.field.asTerm - assert(field.exists, i"no field for ${sym.showLocated} in ${sym.owner.info.decls.toList.map{_.showDcl}}%; %") - field - } + + def newField = ctx.newSymbol( + owner = ctx.owner, + name = sym.name.asTermName.fieldName, + flags = Private | (if (sym is Stable) EmptyFlags else Mutable), + info = sym.info.resultType, + coord = tree.pos).enteredAfter(thisTransform) + + lazy val field = sym.field.orElse(newField).asTerm if (sym.is(Accessor, butNot = NoFieldNeeded)) if (sym.isGetter) { + tree.rhs.changeOwnerAfter(sym, field, thisTransform) val fieldDef = transformFollowing(ValDef(field, tree.rhs)) val getterDef = cpy.DefDef(tree)(rhs = transformFollowingDeep(ref(field))) Thicket(fieldDef, getterDef) } else if (sym.isSetter) { - if (!sym.is(ParamAccessor)) { val Literal(Constant(())) = tree.rhs } + if (!sym.is(ParamAccessor)) { val Literal(Constant(())) = tree.rhs } // this is intended as an assertion val initializer = Assign(ref(field), ref(tree.vparamss.head.head.symbol)) cpy.DefDef(tree)(rhs = transformFollowingDeep(initializer)) } diff --git a/src/dotty/tools/dotc/transform/Mixin.scala b/src/dotty/tools/dotc/transform/Mixin.scala index 7e307c736..e20468899 100644 --- a/src/dotty/tools/dotc/transform/Mixin.scala +++ b/src/dotty/tools/dotc/transform/Mixin.scala @@ -97,7 +97,9 @@ class Mixin extends MiniPhaseTransform with SymTransformer { thisTransform => def traitDefs(stats: List[Tree]): List[Tree] = { val initBuf = new mutable.ListBuffer[Tree] stats flatMap { - case stat: DefDef if stat.symbol.isGetter && !stat.rhs.isEmpty => + case stat: DefDef if stat.symbol.isGetter && !stat.rhs.isEmpty && !stat.symbol.is(Flags.Lazy) => + // make initializer that has all effects of previous getter, + // replace getter rhs with empty tree. val vsym = stat.symbol val isym = initializer(vsym) val rhs = Block( @@ -150,7 +152,8 @@ class Mixin extends MiniPhaseTransform with SymTransformer { thisTransform => def traitInits(mixin: ClassSymbol): List[Tree] = for (getter <- mixin.info.decls.filter(getr => getr.isGetter && !wasDeferred(getr)).toList) yield { - DefDef(implementation(getter.asTerm), superRef(initializer(getter)).appliedToNone) + // transformFollowing call is needed to make memoize & lazy vals run + transformFollowing(DefDef(implementation(getter.asTerm), superRef(initializer(getter)).appliedToNone)) } def setters(mixin: ClassSymbol): List[Tree] = @@ -163,7 +166,7 @@ class Mixin extends MiniPhaseTransform with SymTransformer { thisTransform => if (cls is Trait) traitDefs(impl.body) else { val mixInits = mixins.flatMap { mixin => - traitInits(mixin) ::: superCallOpt(mixin) ::: setters(mixin) + flatten(traitInits(mixin)) ::: superCallOpt(mixin) ::: setters(mixin) } superCallOpt(superCls) ::: mixInits ::: impl.body }) diff --git a/src/dotty/tools/dotc/transform/MixinOps.scala b/src/dotty/tools/dotc/transform/MixinOps.scala index e6074323a..1dce85eaa 100644 --- a/src/dotty/tools/dotc/transform/MixinOps.scala +++ b/src/dotty/tools/dotc/transform/MixinOps.scala @@ -17,7 +17,7 @@ class MixinOps(cls: ClassSymbol, thisTransform: DenotTransformer)(implicit ctx: member.copy( owner = cls, name = member.name.stripScala2LocalSuffix, - flags = member.flags &~ Deferred &~ Module, + flags = member.flags &~ Deferred, info = cls.thisType.memberInfo(member)).enteredAfter(thisTransform).asTerm def superRef(target: Symbol, pos: Position = cls.pos): Tree = { diff --git a/src/dotty/tools/dotc/transform/ParamForwarding.scala b/src/dotty/tools/dotc/transform/ParamForwarding.scala new file mode 100644 index 000000000..d017e75a2 --- /dev/null +++ b/src/dotty/tools/dotc/transform/ParamForwarding.scala @@ -0,0 +1,71 @@ +package dotty.tools.dotc +package transform + +import core._ +import ast.Trees._ +import Contexts._, Types._, Symbols._, Flags._, TypeUtils._, DenotTransformers._, StdNames._ + +/** For all parameter accessors + * + * val x: T = ... + * + * if + * (1) x is forwarded in the supercall to a parameter that's also named `x` + * (2) the superclass parameter accessor for `x` is accessible from the current class + * change the accessor to + * + * def x: T = super.x.asInstanceOf[T] + * + * Do the same also if there are intermediate inaccessible parameter accessor forwarders. + * The aim of this transformation is to avoid redundant parameter accessor fields. + */ +class ParamForwarding(thisTransformer: DenotTransformer) { + import ast.tpd._ + + def forwardParamAccessors(impl: Template)(implicit ctx: Context): Template = { + def fwd(stats: List[Tree])(implicit ctx: Context): List[Tree] = { + val (superArgs, superParamNames) = impl.parents match { + case superCall @ Apply(fn, args) :: _ => + fn.tpe.widen match { + case MethodType(paramNames, _) => (args, paramNames) + case _ => (Nil, Nil) + } + case _ => (Nil, Nil) + } + def inheritedAccessor(sym: Symbol): Symbol = { + val candidate = sym.owner.asClass.superClass + .info.decl(sym.name).suchThat(_ is (ParamAccessor, butNot = Mutable)).symbol + if (candidate.isAccessibleFrom(currentClass.thisType, superAccess = true)) candidate + else if (candidate is Method) inheritedAccessor(candidate) + else NoSymbol + } + def forwardParamAccessor(stat: Tree): Tree = { + stat match { + case stat: ValDef => + val sym = stat.symbol.asTerm + if (sym is (PrivateLocalParamAccessor, butNot = Mutable)) { + val idx = superArgs.indexWhere(_.symbol == sym) + if (idx >= 0 && superParamNames(idx) == stat.name) { // supercall to like-named parameter + val alias = inheritedAccessor(sym) + if (alias.exists) { + def forwarder(implicit ctx: Context) = { + sym.copySymDenotation(initFlags = sym.flags | Method, info = sym.info.ensureMethodic) + .installAfter(thisTransformer) + val superAcc = + Super(This(currentClass), tpnme.EMPTY, inConstrCall = false).select(alias) + DefDef(sym, superAcc.ensureConforms(sym.info.widen)) + } + return forwarder(ctx.withPhase(thisTransformer.next)) + } + } + } + case _ => + } + stat + } + stats map forwardParamAccessor + } + + cpy.Template(impl)(body = fwd(impl.body)(ctx.withPhase(thisTransformer))) + } +} diff --git a/src/dotty/tools/dotc/transform/Pickler.scala b/src/dotty/tools/dotc/transform/Pickler.scala index 515dad2aa..c09ba6889 100644 --- a/src/dotty/tools/dotc/transform/Pickler.scala +++ b/src/dotty/tools/dotc/transform/Pickler.scala @@ -9,6 +9,8 @@ import config.Printers.{noPrinter, pickling} import java.io.PrintStream import Periods._ import Phases._ +import Symbols._ +import Flags.Module import collection.mutable /** This phase pickles trees */ @@ -23,28 +25,41 @@ class Pickler extends Phase { s.close } - private val beforePickling = new mutable.HashMap[CompilationUnit, String] + private val beforePickling = new mutable.HashMap[ClassSymbol, String] + + /** Drop any elements of this list that are linked module classes of other elements in the list */ + private def dropCompanionModuleClasses(clss: List[ClassSymbol])(implicit ctx: Context): List[ClassSymbol] = { + val companionModuleClasses = + clss.filterNot(_ is Module).map(_.linkedClass).filterNot(_.isAbsent) + clss.filterNot(companionModuleClasses.contains) + } override def run(implicit ctx: Context): Unit = { val unit = ctx.compilationUnit - 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) + for { cls <- dropCompanionModuleClasses(topLevelClasses(unit.tpdTree)) + tree <- sliceTopLevel(unit.tpdTree, cls) } { + if (ctx.settings.YtestPickler.value) beforePickling(cls) = tree.show + val pickler = new TastyPickler() + unit.picklers += (cls -> 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(" ")}" + def rawBytes = // not needed right now, but useful to print raw format. + 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) { + println(i"**** pickled info of $cls") + new TastyPrinter(pickler.assembleParts()).printContents() } - // 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] = { @@ -58,23 +73,23 @@ class Pickler extends Phase { pickling.println(i"testing unpickler at run ${ctx.runId}") ctx.definitions.init val unpicklers = - for (unit <- units) yield { - val unpickler = new DottyUnpickler(unit.pickler.assembleParts()) + for (unit <- units; (cls, pickler) <- unit.picklers) yield { + val unpickler = new DottyUnpickler(pickler.assembleParts()) unpickler.enter(roots = Set()) - unpickler + cls -> unpickler } pickling.println("************* entered toplevel ***********") - for ((unpickler, unit) <- unpicklers zip units) { + for ((cls, unpickler) <- unpicklers) { val unpickled = unpickler.body(readPositions = false) - testSame(i"$unpickled%\n%", beforePickling(unit), unit) + testSame(i"$unpickled%\n%", beforePickling(cls), cls) } } - private def testSame(unpickled: String, previous: String, unit: CompilationUnit)(implicit ctx: Context) = + private def testSame(unpickled: String, previous: String, cls: ClassSymbol)(implicit ctx: Context) = if (previous != unpickled) { output("before-pickling.txt", previous) output("after-pickling.txt", unpickled) - ctx.error(s"""pickling difference for $unit, for details: + ctx.error(s"""pickling difference for ${cls.fullName}, for details: | | diff before-pickling.txt after-pickling.txt""".stripMargin) } diff --git a/src/dotty/tools/dotc/transform/PostTyper.scala b/src/dotty/tools/dotc/transform/PostTyper.scala new file mode 100644 index 000000000..55270f233 --- /dev/null +++ b/src/dotty/tools/dotc/transform/PostTyper.scala @@ -0,0 +1,169 @@ +package dotty.tools.dotc +package transform + +import dotty.tools.dotc.transform.TreeTransforms.{TransformerInfo, TreeTransform, TreeTransformer} +import dotty.tools.dotc.ast.{Trees, tpd} +import scala.collection.{ mutable, immutable } +import ValueClasses._ +import scala.annotation.tailrec +import core._ +import typer.ErrorReporting._ +import typer.Checking +import Types._, Contexts._, Constants._, Names._, NameOps._, Flags._, DenotTransformers._ +import SymDenotations._, Symbols._, StdNames._, Annotations._, Trees._, Scopes._, Denotations._ +import util.Positions._ +import Decorators._ +import Symbols._, TypeUtils._ + +/** A macro transform that runs immediately after typer and that performs the following functions: + * + * (1) Add super accessors and protected accessors (@see SuperAccessors) + * + * (2) Convert parameter fields that have the same name as a corresponding + * public parameter field in a superclass to a forwarder to the superclass + * field (corresponding = super class field is initialized with subclass field) + * (@see ForwardParamAccessors) + * + * (3) Add synthetic methods (@see SyntheticMethods) + * + * (4) Check that `New` nodes can be instantiated, and that annotations are valid + * + * (5) Convert all trees representing types to TypeTrees. + * + * (6) Check the bounds of AppliedTypeTrees + * + * (7) Insert `.package` for selections of package object members + * + * (8) Replaces self references by name with `this` + * + * The reason for making this a macro transform is that some functions (in particular + * super and protected accessors and instantiation checks) are naturally top-down and + * don't lend themselves to the bottom-up approach of a mini phase. The other two functions + * (forwarding param accessors and synthetic methods) only apply to templates and fit + * mini-phase or subfunction of a macro phase equally well. But taken by themselves + * they do not warrant their own group of miniphases before pickling. + */ +class PostTyper extends MacroTransform with IdentityDenotTransformer { thisTransformer => + + import tpd._ + + /** the following two members override abstract members in Transform */ + override def phaseName: String = "posttyper" + + override def transformPhase(implicit ctx: Context) = thisTransformer.next + + protected def newTransformer(implicit ctx: Context): Transformer = + new PostTyperTransformer + + val superAcc = new SuperAccessors(thisTransformer) + val paramFwd = new ParamForwarding(thisTransformer) + val synthMth = new SyntheticMethods(thisTransformer) + + private def newPart(tree: Tree): Option[New] = methPart(tree) match { + case Select(nu: New, _) => Some(nu) + case _ => None + } + + private def checkValidJavaAnnotation(annot: Tree)(implicit ctx: Context): Unit = { + // TODO fill in + } + + private def normalizeTypeTree(tree: Tree)(implicit ctx: Context) = { + def norm(tree: Tree) = if (tree.isType) TypeTree(tree.tpe).withPos(tree.pos) else tree + tree match { + case tree: TypeTree => + tree + case AppliedTypeTree(tycon, args) => + val tparams = tycon.tpe.typeSymbol.typeParams + val bounds = tparams.map(tparam => + tparam.info.asSeenFrom(tycon.tpe.normalizedPrefix, tparam.owner.owner).bounds) + Checking.checkBounds(args, bounds, _.substDealias(tparams, _)) + norm(tree) + case _ => + norm(tree) + } + } + + class PostTyperTransformer extends Transformer { + + private var inJavaAnnot: Boolean = false + + private var parentNews: Set[New] = Set() + + private def transformAnnot(annot: Tree)(implicit ctx: Context): Tree = { + val saved = inJavaAnnot + inJavaAnnot = annot.symbol is JavaDefined + if (inJavaAnnot) checkValidJavaAnnotation(annot) + try transform(annot) + finally inJavaAnnot = saved + } + + private def transformAnnot(annot: Annotation)(implicit ctx: Context): Annotation = + annot.derivedAnnotation(transformAnnot(annot.tree)) + + private def transformAnnots(tree: MemberDef)(implicit ctx: Context): Unit = + tree.symbol.transformAnnotations(transformAnnot) + + private def transformSelect(tree: Select, targs: List[Tree])(implicit ctx: Context) = { + val qual = tree.qualifier + qual.symbol.moduleClass.denot match { + case pkg: PackageClassDenotation if !tree.symbol.maybeOwner.is(Package) => + assert(targs.isEmpty) + cpy.Select(tree)(qual select pkg.packageObj.symbol, tree.name) + case _ => + superAcc.transformSelect(super.transform(tree), targs) + } + } + + override def transform(tree: Tree)(implicit ctx: Context): Tree = + try normalizeTypeTree(tree) match { + case tree: Ident => + tree.tpe match { + case tpe: ThisType => This(tpe.cls).withPos(tree.pos) + case _ => tree + } + case tree: Select => + transformSelect(tree, Nil) + case tree @ TypeApply(sel: Select, args) => + val args1 = transform(args) + val sel1 = transformSelect(sel, args1) + if (superAcc.isProtectedAccessor(sel1)) sel1 else cpy.TypeApply(tree)(sel1, args1) + case tree @ Assign(sel: Select, _) => + superAcc.transformAssign(super.transform(tree)) + case tree: Template => + val saved = parentNews + parentNews ++= tree.parents.flatMap(newPart) + try + synthMth.addSyntheticMethods( + paramFwd.forwardParamAccessors( + superAcc.wrapTemplate(tree)( + super.transform(_).asInstanceOf[Template]))) + finally parentNews = saved + case tree: DefDef => + transformAnnots(tree) + superAcc.wrapDefDef(tree)(super.transform(tree).asInstanceOf[DefDef]) + case tree: MemberDef => + transformAnnots(tree) + super.transform(tree) + case tree: New if !inJavaAnnot && !parentNews.contains(tree) => + Checking.checkInstantiable(tree.tpe, tree.pos) + super.transform(tree) + case tree @ Annotated(annot, annotated) => + cpy.Annotated(tree)(transformAnnot(annot), transform(annotated)) + case tree: TypeTree => + tree.withType( + tree.tpe match { + case AnnotatedType(annot, tpe) => AnnotatedType(transformAnnot(annot), tpe) + case tpe => tpe + } + ) + case tree => + super.transform(tree) + } + catch { + case ex : AssertionError => + println(i"error while transforming $tree") + throw ex + } + } +} diff --git a/src/dotty/tools/dotc/transform/SuperAccessors.scala b/src/dotty/tools/dotc/transform/SuperAccessors.scala index 8857b6921..a37b8df1f 100644 --- a/src/dotty/tools/dotc/transform/SuperAccessors.scala +++ b/src/dotty/tools/dotc/transform/SuperAccessors.scala @@ -5,174 +5,104 @@ import dotty.tools.dotc.transform.TreeTransforms.{TransformerInfo, TreeTransform import dotty.tools.dotc.ast.{Trees, tpd} import scala.collection.{ mutable, immutable } import ValueClasses._ -import mutable.ListBuffer import scala.annotation.tailrec import core._ import Types._, Contexts._, Constants._, Names._, NameOps._, Flags._, DenotTransformers._ import SymDenotations._, Symbols._, StdNames._, Annotations._, Trees._, Scopes._, Denotations._ import util.Positions._ import Decorators._ -import Symbols._ +import Symbols._, TypeUtils._ -/** This phase performs the following functions, each of which could be split out in a - * mini-phase: +/** This class performs the following functions: * * (1) Adds super accessors for all super calls that either * appear in a trait or have as a target a member of some outer class. * - * (2) Converts parameter fields that have the same name as a corresponding - * public parameter field in a superclass to a forwarder to the superclass - * field (corresponding = super class field is initialized with subclass field) - * - * (3) Adds protected accessors if the access to the protected member happens + * (2) Adds protected accessors if the access to the protected member happens * in a class which is not a subclass of the member's owner. * - * (4) Finally, the phase used to mangle the names of class-members which are - * private up to an enclosing non-package class, in order to avoid overriding conflicts. - * This is currently disabled, and class-qualified private is deprecated. - * * It also checks that: * * (1) Symbols accessed from super are not abstract, or are overridden by * an abstract override. * - * (2) If a symbol accessed accessed from super is defined in a real class (not a trait), + * (2) If a symbol accessed from super is defined in a real class (not a trait), * there are no abstract members which override this member in Java's rules * (see SI-4989; such an access would lead to illegal bytecode) * * (3) Super calls do not go to some synthetic members of Any (see isDisallowed) * * (4) Super calls do not go to synthetic field accessors - * - * (5) A class and its companion object do not both define a class or module with the - * same name. - * - * TODO: Rename phase to "Accessors" because it handles more than just super accessors */ -class SuperAccessors extends MacroTransform with IdentityDenotTransformer { thisTransformer => +class SuperAccessors(thisTransformer: DenotTransformer) { import tpd._ - /** the following two members override abstract members in Transform */ - override def phaseName: String = "superaccessors" - - protected def newTransformer(implicit ctx: Context): Transformer = - new SuperAccTransformer - - class SuperAccTransformer extends Transformer { - /** validCurrentOwner arrives undocumented, but I reverse engineer it to be - * a flag for needsProtectedAccessor which is false while transforming either - * a by-name argument block or a closure. This excludes them from being - * considered able to access protected members via subclassing (why?) which in turn - * increases the frequency with which needsProtectedAccessor will be true. + /** Some parts of trees will get a new owner in subsequent phases. + * These are value class methods, which will become extension methods. + * (By-name arguments used to be included also, but these + * don't get a new class anymore, they are just wrapped in a new method). + * + * These regions will have to be treated specially for the purpose + * of adding accessors. For instance, super calls from these regions + * always have to go through an accessor. + * + * The `invalidEnclClass` field, if different from NoSymbol, + * contains the symbol that is not a valid owner. */ - private var validCurrentOwner = true - - private val accDefs = mutable.Map[Symbol, ListBuffer[Tree]]() + private var invalidEnclClass: Symbol = NoSymbol - private def storeAccessorDefinition(clazz: Symbol, tree: Tree) = { - val buf = accDefs.getOrElse(clazz, sys.error("no acc def buf for " + clazz)) - buf += tree + private def withInvalidCurrentClass[A](trans: => A)(implicit ctx: Context): A = { + val saved = invalidEnclClass + invalidEnclClass = ctx.owner + try trans + finally invalidEnclClass = saved } - /** Turn types which are not methodic into ExprTypes. */ - private def ensureMethodic(tpe: Type)(implicit ctx: Context) = tpe match { - case tpe: MethodicType => tpe - case _ => ExprType(tpe) - } + private def validCurrentClass(implicit ctx: Context): Boolean = + ctx.owner.enclosingClass != invalidEnclClass - private def ensureAccessor(sel: Select)(implicit ctx: Context) = { + /** List buffers for new accessor definitions, indexed by class */ + private val accDefs = mutable.Map[Symbol, mutable.ListBuffer[Tree]]() + + /** A super accessor call corresponding to `sel` */ + private def superAccessorCall(sel: Select)(implicit ctx: Context) = { val Select(qual, name) = sel - val sym = sel.symbol - val clazz = qual.symbol.asClass - val supername = name.superName + val sym = sel.symbol + val clazz = qual.symbol.asClass + val supername = name.superName val superAcc = clazz.info.decl(supername).suchThat(_.signature == sym.signature).symbol orElse { ctx.debuglog(s"add super acc ${sym.showLocated} to $clazz") val maybeDeferred = if (clazz is Trait) Deferred else EmptyFlags val acc = ctx.newSymbol( clazz, supername, SuperAccessor | Private | Artifact | Method | maybeDeferred, - ensureMethodic(sel.tpe.widenSingleton), coord = sym.coord).enteredAfter(thisTransformer) + sel.tpe.widenSingleton.ensureMethodic, coord = sym.coord).enteredAfter(thisTransformer) // Diagnostic for SI-7091 if (!accDefs.contains(clazz)) ctx.error(s"Internal error: unable to store accessor definition in ${clazz}. clazz.hasPackageFlag=${clazz is Package}. Accessor required for ${sel} (${sel.show})", sel.pos) - else storeAccessorDefinition(clazz, DefDef(acc, EmptyTree)) + else accDefs(clazz) += DefDef(acc, EmptyTree) acc } This(clazz).select(superAcc).withPos(sel.pos) } - private def transformArgs(formals: List[Type], args: List[Tree])(implicit ctx: Context) = - args.zipWithConserve(formals) {(arg, formal) => - formal match { - case _: ExprType => withInvalidOwner(transform(arg)) - case _ => transform(arg) - } - } - - /** Check that a class and its companion object to not both define - * a class or module with same name + /** Check selection `super.f` for conforming to rules. If necessary, + * replace by a super accessor call. */ - private def checkCompanionNameClashes(cls: ClassSymbol)(implicit ctx: Context): Unit = - if (!(cls.owner is ModuleClass)) { - val other = cls.owner.linkedClass.info.decl(cls.name) - if (other.symbol.isClass) - ctx.error(s"name clash: ${cls.owner} defines $cls" + "\n" + - s"and its companion ${cls.owner.companionModule} also defines $other", - cls.pos) - } - - /** Expand all declarations in this class which are private within a class. - * Note: It's not sure whether this is the right way. Persumably, we expand - * qualified privates to prvent them from overriding or be overridden by - * symbols that are defined in classes where the qualified private is not - * visible. But it seems a bit dubiuous to do this between type checking - * and refchecks. - */ - def expandQualifiedPrivates(cls: ClassSymbol)(implicit ctx: Context) = { - val decls = cls.info.decls - val decls1: MutableScope = newScope - def needsExpansion(sym: Symbol) = - sym.privateWithin.isClass && - !(sym is Protected) && - !(sym.privateWithin is ModuleClass) && - !(sym is ExpandedName) && - !sym.isConstructor - val nextCtx = ctx.withPhase(thisTransformer.next) - for (s <- decls) { - // !!! hacky to do this by mutation; would be better to do with an infotransformer - // !!! also, why is this done before pickling? - if (needsExpansion(s)) { - ctx.deprecationWarning(s"private qualified with a class has been deprecated, use package enclosing ${s.privateWithin} instead", s.pos) - /* disabled for now - decls.openForMutations.unlink(s) - s.copySymDenotation(name = s.name.expandedName(s.privateWithin)) - .installAfter(thisTransformer) - decls1.enter(s)(nextCtx) - ctx.log(i"Expanded ${s.name}, ${s.name(nextCtx)}, sym") - */ - } - } - /* Disabled for now: - if (decls1.nonEmpty) { - for (s <- decls) - if (!needsExpansion(s)) decls1.enter(s)(nextCtx) - val ClassInfo(pre, _, ps, _, selfInfo) = cls.classInfo - cls.copySymDenotation(info = ClassInfo(pre, cls, ps, decls1, selfInfo)) - .installAfter(thisTransformer) - } - */ - } - private def transformSuperSelect(sel: Select)(implicit ctx: Context): Tree = { val Select(sup @ Super(_, mix), name) = sel val sym = sel.symbol assert(sup.symbol.exists, s"missing symbol in $sel: ${sup.tpe}") val clazz = sup.symbol.asClass - if (sym is Deferred) { + if ((sym.isTerm) && !(sym is Method) || (sym is Accessor)) + ctx.error(s"super may be not be used on ${sym.underlyingSymbol}", sel.pos) + else if (isDisallowed(sym)) + ctx.error(s"super not allowed here: use this.${sel.name.decode} instead", sel.pos) + else if (sym is Deferred) { val member = sym.overridingSymbol(clazz) if (mix != tpnme.EMPTY || !member.exists || @@ -192,13 +122,14 @@ class SuperAccessors extends MacroTransform with IdentityDenotTransformer { this } if (name.isTermName && mix == tpnme.EMPTY && - ((clazz is Trait) || clazz != ctx.owner.enclosingClass || !validCurrentOwner)) - ensureAccessor(sel)(ctx.withPhase(thisTransformer.next)) + ((clazz is Trait) || clazz != ctx.owner.enclosingClass || !validCurrentClass)) + superAccessorCall(sel)(ctx.withPhase(thisTransformer.next)) else sel } - // Disallow some super.XX calls targeting Any methods which would - // otherwise lead to either a compiler crash or runtime failure. + /** Disallow some super.XX calls targeting Any methods which would + * otherwise lead to either a compiler crash or runtime failure. + */ private def isDisallowed(sym: Symbol)(implicit ctx: Context) = { val d = defn import d._ @@ -209,228 +140,81 @@ class SuperAccessors extends MacroTransform with IdentityDenotTransformer { this (sym eq Any_##) } - override def transform(tree: Tree)(implicit ctx: Context): Tree = { - val sym = tree.symbol + /** Replace `sel` (or `sel[targs]` if `targs` is nonempty) with a protected accessor + * call, if necessary. + */ + private def ensureProtectedAccessOK(sel: Select, targs: List[Tree])(implicit ctx: Context) = { + val sym = sel.symbol + if (sym.exists && needsProtectedAccessor(sym, sel.pos)) { + ctx.debuglog("Adding protected accessor for " + sel) + protectedAccessorCall(sel, targs) + } else sel + } - def mayNeedProtectedAccessor(sel: Select, targs: List[Tree], goToSuper: Boolean) = - if (sym.exists && needsProtectedAccessor(sym, tree.pos)) { - ctx.debuglog("Adding protected accessor for " + tree) - transform(makeAccessor(sel, targs)) - } - else if (goToSuper) super.transform(tree)(ctx.withPhase(thisTransformer.next)) - else tree - - try tree match { - // Don't transform patterns or strange trees will reach the matcher (ticket #4062) - // TODO Query `ctx.mode is Pattern` instead. - case CaseDef(pat, guard, body) => - cpy.CaseDef(tree)(pat, transform(guard), transform(body)) - - case TypeDef(_, impl: Template) => - val cls = sym.asClass - checkCompanionNameClashes(cls) - expandQualifiedPrivates(cls) - super.transform(tree) - - case impl: Template => - - /** For all parameter accessors - * - * val x: T = ... - * - * if - * (1) x is forwarded in the supercall to a parameter that's also named `x` - * (2) the superclass parameter accessor for `x` is accessible from the current class to - * change the accessor to - * - * def x: T = super.x.asInstanceOf[T] - * - * Do the same also if there are intermediate inaccessible parameter accessor forwarders. - * The aim of this transformation is to avoid redundant parameter accessor fields. - */ - def forwardParamAccessors(stats: List[Tree]): List[Tree] = { - val (superArgs, superParamNames) = impl.parents match { - case superCall @ Apply(fn, args) :: _ => - fn.tpe.widen match { - case MethodType(paramNames, _) => (args, paramNames) - case _ => (Nil, Nil) - } - case _ => (Nil, Nil) - } - def inheritedAccessor(sym: Symbol): Symbol = { - val candidate = sym.owner.asClass.superClass - .info.decl(sym.name).suchThat(_ is (ParamAccessor, butNot = Mutable)).symbol - if (candidate.isAccessibleFrom(currentClass.thisType, superAccess = true)) candidate - else if (candidate is Method) inheritedAccessor(candidate) - else NoSymbol - } - def forwardParamAccessor(stat: Tree): Tree = { - stat match { - case stat: ValDef => - val sym = stat.symbol.asTerm - if (sym is (PrivateLocalParamAccessor, butNot = Mutable)) { - val idx = superArgs.indexWhere(_.symbol == sym) - if (idx >= 0 && superParamNames(idx) == stat.name) { // supercall to like-named parameter - val alias = inheritedAccessor(sym) - if (alias.exists) { - def forwarder(implicit ctx: Context) = { - sym.copySymDenotation(initFlags = sym.flags | Method, info = ensureMethodic(sym.info)) - .installAfter(thisTransformer) - val superAcc = - Super(This(currentClass), tpnme.EMPTY, inConstrCall = false).select(alias) - DefDef(sym, superAcc.ensureConforms(sym.info.widen)) - } - return forwarder(ctx.withPhase(thisTransformer.next)) - } - } - } - case _ => - } - stat - } - stats map forwardParamAccessor - } - - def transformTemplate = { - val ownStats = new ListBuffer[Tree] - accDefs(currentClass) = ownStats - // write super accessors after parameters and type aliases (so - // that order is stable under pickling/unpickling) - val (params, rest) = impl.body span { - case td: TypeDef => !td.isClassDef - case vd: ValOrDefDef => vd.symbol.flags is ParamAccessor - case _ => false - } - ownStats ++= params - val rest1 = forwardParamAccessors(transformStats(rest, tree.symbol)) - accDefs -= currentClass - ownStats ++= rest1 - cpy.Template(impl)(body = ownStats.toList) - } - transformTemplate - - case TypeApply(sel @ Select(This(_), name), args) => - mayNeedProtectedAccessor(sel, args, goToSuper = false) - - case sel @ Select(qual, name) => - def transformSelect = { - - qual match { - case This(_) => - // warn if they are selecting a private[this] member which - // also exists in a superclass, because they may be surprised - // to find out that a constructor parameter will shadow a - // field. See SI-4762. - /* to be added - if (settings.lint) { - if (sym.isPrivateLocal && sym.paramss.isEmpty) { - qual.symbol.ancestors foreach { parent => - parent.info.decls filterNot (x => x.isPrivate || x.isLocalToThis) foreach { m2 => - if (sym.name == m2.name && m2.isGetter && m2.accessed.isMutable) { - unit.warning(sel.pos, - sym.accessString + " " + sym.fullLocationString + " shadows mutable " + m2.name - + " inherited from " + m2.owner + ". Changes to " + m2.name + " will not be visible within " - + sym.owner + " - you may want to give them distinct names.") - } - } - } - } - } - */ - - /* - * A trait which extends a class and accesses a protected member - * of that class cannot implement the necessary accessor method - * because its implementation is in an implementation class (e.g. - * Foo$class) which inherits nothing, and jvm access restrictions - * require the call site to be in an actual subclass. So non-trait - * classes inspect their ancestors for any such situations and - * generate the accessors. See SI-2296. - */ - // FIXME - this should be unified with needsProtectedAccessor, but some - // subtlety which presently eludes me is foiling my attempts. - val shouldEnsureAccessor = ( - (currentClass is Trait) - && (sym is Protected) - && sym.enclosingClass != currentClass - && !(sym.owner is PackageClass) // SI-7091 no accessor needed package owned (ie, top level) symbols - && !(sym.owner is Trait) - && sym.owner.enclosingPackageClass != currentClass.enclosingPackageClass - && qual.symbol.info.member(sym.name).exists - && !needsProtectedAccessor(sym, tree.pos)) - if (shouldEnsureAccessor) { - ctx.log("Ensuring accessor for call to protected " + sym.showLocated + " from " + currentClass) - ensureAccessor(sel) - } else - mayNeedProtectedAccessor(sel, Nil, goToSuper = false) - - case Super(_, mix) => - if ((sym.isTerm) && !(sym is Method) || (sym is Accessor)) { - ctx.error(s"super may be not be used on ${sym.underlyingSymbol}", tree.pos) - } else if (isDisallowed(sym)) { - ctx.error(s"super not allowed here: use this.${name.decode} instead", tree.pos) - } - transformSuperSelect(sel) - - case _ => - mayNeedProtectedAccessor(sel, Nil, goToSuper = true) - } - } - transformSelect - - case tree: DefDef => - cpy.DefDef(tree)( - rhs = if (isMethodWithExtension(sym)) withInvalidOwner(transform(tree.rhs)) else transform(tree.rhs)) - - case TypeApply(sel @ Select(qual, name), args) => - mayNeedProtectedAccessor(sel, args, goToSuper = true) - - case Assign(lhs @ Select(qual, name), rhs) => - def transformAssign = { - if ((lhs.symbol is Mutable) && - (lhs.symbol is JavaDefined) && - needsProtectedAccessor(lhs.symbol, tree.pos)) { - ctx.debuglog("Adding protected setter for " + tree) - val setter = makeSetter(lhs) - ctx.debuglog("Replaced " + tree + " with " + setter) - transform(Apply(setter, qual :: rhs :: Nil)) - } else - super.transform(tree) - } - transformAssign - - case Apply(fn, args) => - val MethodType(_, formals) = fn.tpe.widen - ctx.atPhase(thisTransformer.next) { implicit ctx => - cpy.Apply(tree)(transform(fn), transformArgs(formals, args)) - } + /** Add a protected accessor, if needed, and return a tree that calls + * the accessor and returns the same member. The result is already + * typed. + */ + private def protectedAccessorCall(sel: Select, targs: List[Tree])(implicit ctx: Context): Tree = { + val Select(qual, _) = sel + val sym = sel.symbol.asTerm + val clazz = hostForAccessorOf(sym, currentClass) + assert(clazz.exists, sym) + ctx.debuglog("Decided for host class: " + clazz) - case _ => - super.transform(tree) + val accName = sym.name.protectedAccessorName + + def isThisType(tpe: Type): Boolean = tpe match { + case tpe: ThisType => !tpe.cls.is(PackageClass) + case tpe: TypeProxy => isThisType(tpe.underlying) + case _ => false } - catch { - case ex : AssertionError => - if (sym != null && sym != NoSymbol) - Console.println("TRANSFORM: " + tree.symbol.sourceFile) - Console.println("TREE: " + tree) - throw ex + // if the result type depends on the this type of an enclosing class, the accessor + // has to take an object of exactly this type, otherwise it's more general + val receiverType = + if (isThisType(sym.info.finalResultType)) clazz.thisType + else clazz.classInfo.selfType + val accType = { + def accTypeOf(tpe: Type): Type = tpe match { + case tpe: PolyType => + tpe.derivedPolyType(tpe.paramNames, tpe.paramBounds, accTypeOf(tpe.resultType)) + case _ => + MethodType(receiverType :: Nil)(mt => tpe.substThis(sym.owner.asClass, MethodParam(mt, 0))) + } + accTypeOf(sym.info) + } + val protectedAccessor = clazz.info.decl(accName).suchThat(_.signature == accType.signature).symbol orElse { + val newAcc = ctx.newSymbol( + clazz, accName, Artifact, accType, coord = sel.pos).enteredAfter(thisTransformer) + val code = polyDefDef(newAcc, trefs => vrefss => { + val (receiver :: _) :: tail = vrefss + val base = receiver.select(sym).appliedToTypes(trefs) + (base /: vrefss)(Apply(_, _)) + }) + ctx.debuglog("created protected accessor: " + code) + accDefs(clazz) += code + newAcc } + val res = This(clazz) + .select(protectedAccessor) + .appliedToTypeTrees(targs) + .appliedTo(qual) + .withPos(sel.pos) + ctx.debuglog(s"Replaced $sel with $res") + res } - private def withInvalidOwner[A](trans: => A): A = { - val saved = validCurrentOwner - validCurrentOwner = false - try trans - finally validCurrentOwner = saved + def isProtectedAccessor(tree: Tree)(implicit ctx: Context): Boolean = tree match { + case Apply(TypeApply(Select(_, name), _), qual :: Nil) => name.isProtectedAccessorName + case _ => false } /** Add a protected accessor, if needed, and return a tree that calls * the accessor and returns the same member. The result is already * typed. - * TODO why is targs needed? It looks like we can do without. */ - private def makeAccessor(tree: Select, targs: List[Tree])(implicit ctx: Context): Tree = { + private def protectedAccessor(tree: Select, targs: List[Tree])(implicit ctx: Context): Tree = { val Select(qual, _) = tree val sym = tree.symbol.asTerm val clazz = hostForAccessorOf(sym, currentClass) @@ -441,16 +225,16 @@ class SuperAccessors extends MacroTransform with IdentityDenotTransformer { this // if the result type depends on the this type of an enclosing class, the accessor // has to take an object of exactly this type, otherwise it's more general - val receiverType = if (isThisType(sym.info.finalResultType)) clazz.thisType else clazz.classInfo.selfType - val accType = { - def accTypeOf(tpe: Type): Type = tpe match { - case tpe: PolyType => - tpe.derivedPolyType(tpe.paramNames, tpe.paramBounds, accTypeOf(tpe.resultType)) - case _ => - MethodType(receiverType :: Nil)(mt => tpe.substThis(sym.owner.asClass, MethodParam(mt, 0))) - } - accTypeOf(sym.info) + val receiverType = + if (isThisType(sym.info.finalResultType)) clazz.thisType + else clazz.classInfo.selfType + def accTypeOf(tpe: Type): Type = tpe match { + case tpe: PolyType => + tpe.derivedPolyType(tpe.paramNames, tpe.paramBounds, accTypeOf(tpe.resultType)) + case _ => + MethodType(receiverType :: Nil)(mt => tpe.substThis(sym.owner.asClass, MethodParam(mt, 0))) } + val accType = accTypeOf(sym.info) val protectedAccessor = clazz.info.decl(accName).suchThat(_.signature == accType.signature).symbol orElse { val newAcc = ctx.newSymbol( clazz, accName, Artifact, accType, coord = tree.pos).enteredAfter(thisTransformer) @@ -460,7 +244,7 @@ class SuperAccessors extends MacroTransform with IdentityDenotTransformer { this (base /: vrefss)(Apply(_, _)) }) ctx.debuglog("created protected accessor: " + code) - storeAccessorDefinition(clazz, code) + accDefs(clazz) += code newAcc } val res = This(clazz) @@ -475,7 +259,7 @@ class SuperAccessors extends MacroTransform with IdentityDenotTransformer { this /** Add an accessor for field, if needed, and return a selection tree for it . * The result is not typed. */ - private def makeSetter(tree: Select)(implicit ctx: Context): Tree = { + private def protectedSetter(tree: Select)(implicit ctx: Context): Tree = { val field = tree.symbol.asTerm val clazz = hostForAccessorOf(field, currentClass) assert(clazz.exists, field) @@ -491,7 +275,7 @@ class SuperAccessors extends MacroTransform with IdentityDenotTransformer { this Assign(receiver.select(field), value).withPos(tree.pos) }) ctx.debuglog("created protected setter: " + code) - storeAccessorDefinition(clazz, code) + accDefs(clazz) += code newAcc } This(clazz).select(protectedAccessor).withPos(tree.pos) @@ -516,7 +300,7 @@ class SuperAccessors extends MacroTransform with IdentityDenotTransformer { this val host = hostForAccessorOf(sym, clazz) val selfType = host.classInfo.selfType def accessibleThroughSubclassing = - validCurrentOwner && (selfType <:< sym.owner.typeRef) && !clazz.is(Trait) + validCurrentClass && (selfType <:< sym.owner.typeRef) && !clazz.is(Trait) val isCandidate = ( sym.is(Protected) @@ -559,11 +343,85 @@ class SuperAccessors extends MacroTransform with IdentityDenotTransformer { this else referencingClass - /** Is 'tpe' the type of a member of an enclosing class? */ + /** Is 'tpe' a ThisType, or a type proxy with a ThisType as transitively underlying type? */ private def isThisType(tpe: Type)(implicit ctx: Context): Boolean = tpe match { case tpe: ThisType => !tpe.cls.is(PackageClass) case tpe: TypeProxy => isThisType(tpe.underlying) case _ => false } - } + + /** Transform select node, adding super and protected accessors as needed */ + def transformSelect(tree: Tree, targs: List[Tree])(implicit ctx: Context) = { + val sel @ Select(qual, name) = tree + val sym = sel.symbol + qual match { + case _: This => + /* + * A trait which extends a class and accesses a protected member + * of that class cannot implement the necessary accessor method + * because its implementation is in an implementation class (e.g. + * Foo$class) which inherits nothing, and jvm access restrictions + * require the call site to be in an actual subclass. So non-trait + * classes inspect their ancestors for any such situations and + * generate the accessors. See SI-2296. + */ + // FIXME (from scalac's SuperAccessors) + // - this should be unified with needsProtectedAccessor, but some + // subtlety which presently eludes me is foiling my attempts. + val shouldEnsureAccessor = ( + (currentClass is Trait) + && (sym is Protected) + && sym.enclosingClass != currentClass + && !(sym.owner is PackageClass) // SI-7091 no accessor needed package owned (ie, top level) symbols + && !(sym.owner is Trait) + && sym.owner.enclosingPackageClass != currentClass.enclosingPackageClass + && qual.symbol.info.member(sym.name).exists + && !needsProtectedAccessor(sym, sel.pos)) + if (shouldEnsureAccessor) { + ctx.log("Ensuring accessor for call to protected " + sym.showLocated + " from " + currentClass) + superAccessorCall(sel) + } else + ensureProtectedAccessOK(sel, targs) + + case Super(_, mix) => + transformSuperSelect(sel) + + case _ => + ensureProtectedAccessOK(sel, targs) + } + } + + /** Transform assignment, adding a protected setter if needed */ + def transformAssign(tree: Tree)(implicit ctx: Context) = { + val Assign(lhs @ Select(qual, name), rhs) = tree + if ((lhs.symbol is Mutable) && + (lhs.symbol is JavaDefined) && + needsProtectedAccessor(lhs.symbol, tree.pos)) { + ctx.debuglog("Adding protected setter for " + tree) + val setter = protectedSetter(lhs) + ctx.debuglog("Replaced " + tree + " with " + setter) + setter.appliedTo(qual, rhs) + } + else tree + } + + /** Wrap template to template transform `op` with needed initialization and finalization */ + def wrapTemplate(tree: Template)(op: Template => Template)(implicit ctx: Context) = { + accDefs(currentClass) = new mutable.ListBuffer[Tree] + val impl = op(tree) + val accessors = accDefs.remove(currentClass).get + if (accessors.isEmpty) impl + else { + val (params, rest) = impl.body span { + case td: TypeDef => !td.isClassDef + case vd: ValOrDefDef => vd.symbol.flags is ParamAccessor + case _ => false + } + cpy.Template(impl)(body = params ++ accessors ++ rest) + } + } + + /** Wrap `DefDef` producing operation `op`, potentially setting `invalidClass` info */ + def wrapDefDef(ddef: DefDef)(op: => DefDef)(implicit ctx: Context) = + if (isMethodWithExtension(ddef.symbol)) withInvalidCurrentClass(op) else op } diff --git a/src/dotty/tools/dotc/transform/SyntheticMethods.scala b/src/dotty/tools/dotc/transform/SyntheticMethods.scala index 4726105c6..9d0aebe45 100644 --- a/src/dotty/tools/dotc/transform/SyntheticMethods.scala +++ b/src/dotty/tools/dotc/transform/SyntheticMethods.scala @@ -31,19 +31,20 @@ import scala.language.postfixOps * def equals(other: Any): Boolean * def hashCode(): Int */ -class SyntheticMethods extends MiniPhaseTransform with IdentityDenotTransformer { thisTransformer => +class SyntheticMethods(thisTransformer: DenotTransformer) { import ast.tpd._ - override def phaseName = "synthetics" + private var myValueSymbols: List[Symbol] = Nil + private var myCaseSymbols: List[Symbol] = Nil - private var valueSymbols: List[Symbol] = _ - private var caseSymbols: List[Symbol] = _ + private def initSymbols(implicit ctx: Context) = + if (myValueSymbols.isEmpty) { + myValueSymbols = List(defn.Any_hashCode, defn.Any_equals) + myCaseSymbols = myValueSymbols ++ List(defn.Any_toString, defn.Product_canEqual, defn.Product_productArity) + } - override def prepareForUnit(tree: Tree)(implicit ctx: Context) = { - valueSymbols = List(defn.Any_hashCode, defn.Any_equals) - caseSymbols = valueSymbols ++ List(defn.Any_toString, defn.Product_canEqual, defn.Product_productArity) - this - } + def valueSymbols(implicit ctx: Context) = { initSymbols; myValueSymbols } + def caseSymbols(implicit ctx: Context) = { initSymbols; myCaseSymbols } /** The synthetic methods of the case or value class `clazz`. */ @@ -185,10 +186,9 @@ class SyntheticMethods extends MiniPhaseTransform with IdentityDenotTransformer symbolsToSynthesize flatMap syntheticDefIfMissing } - override def transformTemplate(impl: Template)(implicit ctx: Context, info: TransformerInfo) = + def addSyntheticMethods(impl: Template)(implicit ctx: Context) = if (ctx.owner.is(Case) || isDerivedValueClass(ctx.owner)) - cpy.Template(impl)( - body = impl.body ++ syntheticMethods(ctx.owner.asClass)) + cpy.Template(impl)(body = impl.body ++ syntheticMethods(ctx.owner.asClass)) else impl } diff --git a/src/dotty/tools/dotc/transform/TreeChecker.scala b/src/dotty/tools/dotc/transform/TreeChecker.scala index 1661f7576..7c8ba8432 100644 --- a/src/dotty/tools/dotc/transform/TreeChecker.scala +++ b/src/dotty/tools/dotc/transform/TreeChecker.scala @@ -50,8 +50,7 @@ class TreeChecker extends Phase with SymTransformer { def testDuplicate(sym: Symbol, registry: mutable.Map[String, Symbol], typ: String)(implicit ctx: Context) = { val name = sym.fullName.toString - if (registry.contains(name)) - if (this.flatClasses || !(sym.isAnonymousFunction || sym.isAnonymousClass || sym.isAnonymousModuleVal)) + if (this.flatClasses && registry.contains(name)) printError(s"$typ defined twice $sym ${sym.id} ${registry(name).id}") registry(name) = sym } diff --git a/src/dotty/tools/dotc/transform/TypeUtils.scala b/src/dotty/tools/dotc/transform/TypeUtils.scala index e510fcc88..c01b6478c 100644 --- a/src/dotty/tools/dotc/transform/TypeUtils.scala +++ b/src/dotty/tools/dotc/transform/TypeUtils.scala @@ -26,4 +26,9 @@ class TypeUtils(val self: Type) extends AnyVal { def isPrimitiveValueType(implicit ctx: Context): Boolean = self.classSymbol.isPrimitiveValueClass + + def ensureMethodic(implicit ctx: Context): Type = self match { + case self: MethodicType => self + case _ => ExprType(self) + } } diff --git a/src/dotty/tools/dotc/typer/Applications.scala b/src/dotty/tools/dotc/typer/Applications.scala index dd30c45c0..7a826fee8 100644 --- a/src/dotty/tools/dotc/typer/Applications.scala +++ b/src/dotty/tools/dotc/typer/Applications.scala @@ -192,7 +192,7 @@ trait Applications extends Compatibility { self: Typer => def success = ok protected def methodType = methType.asInstanceOf[MethodType] - private def methString: String = s"method ${methRef.name}: ${methType.show}" + private def methString: String = i"${methRef.symbol}: ${methType.show}" /** Re-order arguments to correctly align named arguments */ def reorder[T >: Untyped](args: List[Trees.Tree[T]]): List[Trees.Tree[T]] = { diff --git a/src/dotty/tools/dotc/typer/Checking.scala b/src/dotty/tools/dotc/typer/Checking.scala index b28afa6f2..148e31885 100644 --- a/src/dotty/tools/dotc/typer/Checking.scala +++ b/src/dotty/tools/dotc/typer/Checking.scala @@ -39,6 +39,23 @@ object Checking { d"Type argument ${arg.tpe} does not conform to $which bound $bound ${err.whyNoMatchStr(arg.tpe, bound)}", arg.pos) + /** Check that `tp` refers to a nonAbstract class + * and that the instance conforms to the self type of the created class. + */ + def checkInstantiable(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 _ => + } + /** A type map which checks that the only cycles in a type are F-bounds * and that protects all F-bounded references by LazyRefs. */ diff --git a/src/dotty/tools/dotc/typer/InstChecks.scala b/src/dotty/tools/dotc/typer/InstChecks.scala deleted file mode 100644 index 7148a6e68..000000000 --- a/src/dotty/tools/dotc/typer/InstChecks.scala +++ /dev/null @@ -1,90 +0,0 @@ -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 6a1f3652b..93cd412f2 100644 --- a/src/dotty/tools/dotc/typer/RefChecks.scala +++ b/src/dotty/tools/dotc/typer/RefChecks.scala @@ -82,6 +82,18 @@ object RefChecks { case _ => } + /** Check that a class and its companion object to not both define + * a class or module with same name + */ + private def checkCompanionNameClashes(cls: Symbol)(implicit ctx: Context): Unit = + if (!(cls.owner is ModuleClass)) { + val other = cls.owner.linkedClass.info.decl(cls.name) + if (other.symbol.isClass) + ctx.error(s"name clash: ${cls.owner} defines $cls" + "\n" + + s"and its companion ${cls.owner.companionModule} also defines $other", + cls.pos) + } + // Override checking ------------------------------------------------------------ /** 1. Check all members of class `clazz` for overriding conditions. @@ -690,6 +702,7 @@ import RefChecks._ * - any value classes conform to rules laid down by `checkAnyValSubClass`. * - this(...) constructor calls do not forward reference other definitions in their block (not even lazy vals). * - no forward reference in a local block jumps over a non-lazy val definition. + * - a class and its companion object do not both define a class or module with the same name. * * 2. It warns about references to symbols labeled deprecated or migration. @@ -782,6 +795,7 @@ class RefChecks extends MiniPhase with SymTransformer { thisTransformer => val cls = ctx.owner checkOverloadedRestrictions(cls) checkSelfType(cls) + checkCompanionNameClashes(cls) checkAllOverrides(cls) checkAnyValSubclass(cls) tree diff --git a/src/dotty/tools/dotc/typer/TypeAssigner.scala b/src/dotty/tools/dotc/typer/TypeAssigner.scala index 2ec510a3d..d3baad848 100644 --- a/src/dotty/tools/dotc/typer/TypeAssigner.scala +++ b/src/dotty/tools/dotc/typer/TypeAssigner.scala @@ -210,7 +210,13 @@ trait TypeAssigner { case p.arrayApply => MethodType(defn.IntType :: Nil, arrayElemType) case p.arrayUpdate => MethodType(defn.IntType :: arrayElemType :: Nil, defn.UnitType) case p.arrayLength => MethodType(Nil, defn.IntType) - case nme.clone_ if qualType.isInstanceOf[JavaArrayType] => MethodType(Nil, qualType) + + // Note that we do not need to handle calls to Array[T]#clone() specially: + // The JLS section 10.7 says "The return type of the clone method of an array type + // T[] is T[]", but the actual return type at the bytecode level is Object which + // is casted to T[] by javac. Since the return type of Array[T]#clone() is Array[T], + // this is exactly what Erasure will do. + case _ => accessibleSelectionType(tree, qual) } tree.withType(tp) diff --git a/src/scala/annotation/internal/TASTYLongSignature.java b/src/scala/annotation/internal/TASTYLongSignature.java new file mode 100644 index 000000000..2278da258 --- /dev/null +++ b/src/scala/annotation/internal/TASTYLongSignature.java @@ -0,0 +1,12 @@ +package scala.annotation.internal; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.TYPE) +public @interface TASTYLongSignature { + public String[] bytes(); +} diff --git a/src/scala/annotation/internal/TASTYSignature.java b/src/scala/annotation/internal/TASTYSignature.java new file mode 100644 index 000000000..a6372f008 --- /dev/null +++ b/src/scala/annotation/internal/TASTYSignature.java @@ -0,0 +1,12 @@ +package scala.annotation.internal; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.TYPE) +public @interface TASTYSignature { + public String bytes(); +} |