aboutsummaryrefslogtreecommitdiff
path: root/src/dotty/tools/dotc
diff options
context:
space:
mode:
authorDmitry Petrashko <dark@d-d.me>2015-04-28 11:35:34 +0200
committerDmitry Petrashko <dark@d-d.me>2015-04-28 11:35:34 +0200
commita18b3faeda0a33843bb60cb1475c974aff3a1621 (patch)
tree5f1f515abc423b1168a0ab75d2e52675267e4f46 /src/dotty/tools/dotc
parent6d1138e2ce2d212c051faa20e1f4bf419ede3fdf (diff)
parent2b3591cec6a1d58f3346b6c8933ca0742f13c1cf (diff)
downloaddotty-a18b3faeda0a33843bb60cb1475c974aff3a1621.tar.gz
dotty-a18b3faeda0a33843bb60cb1475c974aff3a1621.tar.bz2
dotty-a18b3faeda0a33843bb60cb1475c974aff3a1621.zip
Merge pull request #495 from dotty-staging/refactor/SuperAccessors
Refactor/super accessors
Diffstat (limited to 'src/dotty/tools/dotc')
-rw-r--r--src/dotty/tools/dotc/Compiler.scala10
-rw-r--r--src/dotty/tools/dotc/ast/tpd.scala2
-rw-r--r--src/dotty/tools/dotc/core/SymDenotations.scala4
-rw-r--r--src/dotty/tools/dotc/core/Symbols.scala12
-rw-r--r--src/dotty/tools/dotc/core/TypeApplications.scala2
-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/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/LambdaLift.scala22
-rw-r--r--src/dotty/tools/dotc/transform/MacroTransform.scala5
-rw-r--r--src/dotty/tools/dotc/transform/ParamForwarding.scala71
-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/TypeUtils.scala5
-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
20 files changed, 543 insertions, 531 deletions
diff --git a/src/dotty/tools/dotc/Compiler.scala b/src/dotty/tools/dotc/Compiler.scala
index cb3d0e21e..1657adbbb 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,
diff --git a/src/dotty/tools/dotc/ast/tpd.scala b/src/dotty/tools/dotc/ast/tpd.scala
index b856e3190..0a1611b61 100644
--- a/src/dotty/tools/dotc/ast/tpd.scala
+++ b/src/dotty/tools/dotc/ast/tpd.scala
@@ -244,7 +244,7 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo {
}
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)
diff --git a/src/dotty/tools/dotc/core/SymDenotations.scala b/src/dotty/tools/dotc/core/SymDenotations.scala
index 3566595f2..83499ca7b 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 {
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/TypeApplications.scala b/src/dotty/tools/dotc/core/TypeApplications.scala
index de42b3e5f..5325189e1 100644
--- a/src/dotty/tools/dotc/core/TypeApplications.scala
+++ b/src/dotty/tools/dotc/core/TypeApplications.scala
@@ -279,7 +279,7 @@ class TypeApplications(val self: Type) extends AnyVal {
default
}
}
-
+
/** Translate a type of the form From[T] to To[T], keep other types as they are.
* `from` and `to` must be static classes, both with one type parameter, and the same variance.
*/
diff --git a/src/dotty/tools/dotc/core/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/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/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/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/ParamForwarding.scala b/src/dotty/tools/dotc/transform/ParamForwarding.scala
new file mode 100644
index 000000000..87ecaba07
--- /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 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.
+ */
+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/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..b111fdb92 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 `invalidOwner` 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/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/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