aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/dotty/runtime/LazyVals.scala6
-rw-r--r--src/dotty/tools/backend/jvm/DottyBackendInterface.scala2
-rw-r--r--src/dotty/tools/backend/jvm/GenBCode.scala10
-rw-r--r--src/dotty/tools/dotc/CompilationUnit.scala4
-rw-r--r--src/dotty/tools/dotc/Compiler.scala18
-rw-r--r--src/dotty/tools/dotc/Run.scala7
-rw-r--r--src/dotty/tools/dotc/ast/TreeInfo.scala28
-rw-r--r--src/dotty/tools/dotc/ast/tpd.scala35
-rw-r--r--src/dotty/tools/dotc/core/Definitions.scala2
-rw-r--r--src/dotty/tools/dotc/core/Denotations.scala38
-rw-r--r--src/dotty/tools/dotc/core/StdNames.scala1
-rw-r--r--src/dotty/tools/dotc/core/SymDenotations.scala10
-rw-r--r--src/dotty/tools/dotc/core/Symbols.scala12
-rw-r--r--src/dotty/tools/dotc/core/pickling/ClassfileParser.scala29
-rw-r--r--src/dotty/tools/dotc/core/pickling/NameBuffer.scala2
-rw-r--r--src/dotty/tools/dotc/core/pickling/PositionPickler.scala2
-rw-r--r--src/dotty/tools/dotc/core/pickling/TastyBuffer.scala5
-rw-r--r--src/dotty/tools/dotc/core/pickling/TreeBuffer.scala3
-rw-r--r--src/dotty/tools/dotc/core/pickling/TreeUnpickler.scala22
-rw-r--r--src/dotty/tools/dotc/printing/PlainPrinter.scala2
-rw-r--r--src/dotty/tools/dotc/transform/Constructors.scala14
-rw-r--r--src/dotty/tools/dotc/transform/Erasure.scala12
-rw-r--r--src/dotty/tools/dotc/transform/ExplicitOuter.scala2
-rw-r--r--src/dotty/tools/dotc/transform/ExtensionMethods.scala2
-rw-r--r--src/dotty/tools/dotc/transform/FirstTransform.scala54
-rw-r--r--src/dotty/tools/dotc/transform/FullParameterization.scala11
-rw-r--r--src/dotty/tools/dotc/transform/Getters.scala20
-rw-r--r--src/dotty/tools/dotc/transform/LambdaLift.scala22
-rw-r--r--src/dotty/tools/dotc/transform/LazyVals.scala196
-rw-r--r--src/dotty/tools/dotc/transform/MacroTransform.scala5
-rw-r--r--src/dotty/tools/dotc/transform/Memoize.scala34
-rw-r--r--src/dotty/tools/dotc/transform/Mixin.scala9
-rw-r--r--src/dotty/tools/dotc/transform/MixinOps.scala2
-rw-r--r--src/dotty/tools/dotc/transform/ParamForwarding.scala71
-rw-r--r--src/dotty/tools/dotc/transform/Pickler.scala59
-rw-r--r--src/dotty/tools/dotc/transform/PostTyper.scala169
-rw-r--r--src/dotty/tools/dotc/transform/SuperAccessors.scala536
-rw-r--r--src/dotty/tools/dotc/transform/SyntheticMethods.scala24
-rw-r--r--src/dotty/tools/dotc/transform/TreeChecker.scala3
-rw-r--r--src/dotty/tools/dotc/transform/TypeUtils.scala5
-rw-r--r--src/dotty/tools/dotc/typer/Applications.scala2
-rw-r--r--src/dotty/tools/dotc/typer/Checking.scala17
-rw-r--r--src/dotty/tools/dotc/typer/InstChecks.scala90
-rw-r--r--src/dotty/tools/dotc/typer/RefChecks.scala14
-rw-r--r--src/dotty/tools/dotc/typer/TypeAssigner.scala8
-rw-r--r--src/scala/annotation/internal/TASTYLongSignature.java12
-rw-r--r--src/scala/annotation/internal/TASTYSignature.java12
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();
+}