From 2152b4b85fcfb48cb49868200c96fd47023602ea Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Tue, 21 Apr 2015 10:37:14 +0200 Subject: [PATCH 01/16] New utility method in TypeUtils: ensureMethodic Used in at least two places, so it's of general use. --- .../tools/dotc/transform/FullParameterization.scala | 11 +++-------- src/dotty/tools/dotc/transform/SuperAccessors.scala | 8 ++++---- src/dotty/tools/dotc/transform/TypeUtils.scala | 5 +++++ 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/dotty/tools/dotc/transform/FullParameterization.scala b/src/dotty/tools/dotc/transform/FullParameterization.scala index acfeda48ee4d..f46942fb325d 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/SuperAccessors.scala b/src/dotty/tools/dotc/transform/SuperAccessors.scala index 8857b6921582..25db34fc3139 100644 --- a/src/dotty/tools/dotc/transform/SuperAccessors.scala +++ b/src/dotty/tools/dotc/transform/SuperAccessors.scala @@ -12,7 +12,7 @@ import Types._, Contexts._, Constants._, Names._, NameOps._, Flags._, DenotTrans 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: @@ -36,7 +36,7 @@ import Symbols._ * (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) * @@ -93,7 +93,7 @@ class SuperAccessors extends MacroTransform with IdentityDenotTransformer { this 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) @@ -274,7 +274,7 @@ class SuperAccessors extends MacroTransform with IdentityDenotTransformer { this val alias = inheritedAccessor(sym) if (alias.exists) { def forwarder(implicit ctx: Context) = { - sym.copySymDenotation(initFlags = sym.flags | Method, info = ensureMethodic(sym.info)) + sym.copySymDenotation(initFlags = sym.flags | Method, info = sym.info.ensureMethodic) .installAfter(thisTransformer) val superAcc = Super(This(currentClass), tpnme.EMPTY, inConstrCall = false).select(alias) diff --git a/src/dotty/tools/dotc/transform/TypeUtils.scala b/src/dotty/tools/dotc/transform/TypeUtils.scala index e510fcc88aa4..5ef1e5ed373b 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) + } } From e439a1ad248dad77cb71e824e19c348d8498cc85 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Tue, 21 Apr 2015 10:41:32 +0200 Subject: [PATCH 02/16] Breakout and re-enable forward param accessors code. The code got accidentally disabled when refactoring SuperAccessors for pickling. The forwardParamAccessor method was applied only to non-parameters where it is the identity (we really shopuld get test paramAccessors working, then this would not have happened. In the interest of better modularity, the code was placed in its own trait, because it overlaps only marginally with the rest of SuperAccessors functionality. --- .../transform/ForwardParamAccessors.scala | 73 ++++++++++++++++ .../tools/dotc/transform/SuperAccessors.scala | 84 ++----------------- 2 files changed, 82 insertions(+), 75 deletions(-) create mode 100644 src/dotty/tools/dotc/transform/ForwardParamAccessors.scala diff --git a/src/dotty/tools/dotc/transform/ForwardParamAccessors.scala b/src/dotty/tools/dotc/transform/ForwardParamAccessors.scala new file mode 100644 index 000000000000..b65681a4b5db --- /dev/null +++ b/src/dotty/tools/dotc/transform/ForwardParamAccessors.scala @@ -0,0 +1,73 @@ +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. + */ +trait ForwardParamAccessors extends DenotTransformer { thisTransformer: MacroTransform => + import ast.tpd._ + + def currentClass(implicit ctx: Context) = ctx.owner.enclosingClass.asClass + + def forwardParamAccessors(impl: Template)(implicit ctx: Context): Template = { + def fwd(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 = 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)) + } +} \ No newline at end of file diff --git a/src/dotty/tools/dotc/transform/SuperAccessors.scala b/src/dotty/tools/dotc/transform/SuperAccessors.scala index 25db34fc3139..f0163bc722f5 100644 --- a/src/dotty/tools/dotc/transform/SuperAccessors.scala +++ b/src/dotty/tools/dotc/transform/SuperAccessors.scala @@ -49,7 +49,9 @@ import Symbols._, TypeUtils._ * * TODO: Rename phase to "Accessors" because it handles more than just super accessors */ -class SuperAccessors extends MacroTransform with IdentityDenotTransformer { thisTransformer => +class SuperAccessors extends MacroTransform + with IdentityDenotTransformer + with ForwardParamAccessors { thisTransformer => import tpd._ @@ -71,17 +73,6 @@ class SuperAccessors extends MacroTransform with IdentityDenotTransformer { this private val accDefs = mutable.Map[Symbol, ListBuffer[Tree]]() - private def storeAccessorDefinition(clazz: Symbol, tree: Tree) = { - val buf = accDefs.getOrElse(clazz, sys.error("no acc def buf for " + clazz)) - buf += tree - } - - /** 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 ensureAccessor(sel: Select)(implicit ctx: Context) = { val Select(qual, name) = sel val sym = sel.symbol @@ -97,7 +88,7 @@ class SuperAccessors extends MacroTransform with IdentityDenotTransformer { this // 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 } @@ -234,63 +225,6 @@ class SuperAccessors extends MacroTransform with IdentityDenotTransformer { this 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 = 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 - } - def transformTemplate = { val ownStats = new ListBuffer[Tree] accDefs(currentClass) = ownStats @@ -298,16 +232,16 @@ class SuperAccessors extends MacroTransform with IdentityDenotTransformer { this // 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 vd: ValOrDefDef => vd.symbol is ParamAccessor case _ => false } ownStats ++= params - val rest1 = forwardParamAccessors(transformStats(rest, tree.symbol)) + val rest1 = transformStats(rest, tree.symbol) accDefs -= currentClass ownStats ++= rest1 cpy.Template(impl)(body = ownStats.toList) } - transformTemplate + forwardParamAccessors(transformTemplate) case TypeApply(sel @ Select(This(_), name), args) => mayNeedProtectedAccessor(sel, args, goToSuper = false) @@ -460,7 +394,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) @@ -491,7 +425,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) From dab8aa18419e7b6354d979440cae5105e2672061 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Tue, 21 Apr 2015 11:37:49 +0200 Subject: [PATCH 03/16] Move checkCompanionNameClashes from SuperAccessors to RefChecks Done in the interest of slimming down and modularizing SuperAccessors. --- .../tools/dotc/transform/SuperAccessors.scala | 65 +------------------ src/dotty/tools/dotc/typer/RefChecks.scala | 14 ++++ 2 files changed, 15 insertions(+), 64 deletions(-) diff --git a/src/dotty/tools/dotc/transform/SuperAccessors.scala b/src/dotty/tools/dotc/transform/SuperAccessors.scala index f0163bc722f5..213a2369d0e7 100644 --- a/src/dotty/tools/dotc/transform/SuperAccessors.scala +++ b/src/dotty/tools/dotc/transform/SuperAccessors.scala @@ -44,9 +44,6 @@ import Symbols._, TypeUtils._ * * (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 @@ -103,61 +100,7 @@ class SuperAccessors extends MacroTransform } } - /** Check that a class and its companion object to not both define - * a class or module with same name - */ - 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 = { + 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}") @@ -217,12 +160,6 @@ class SuperAccessors extends MacroTransform 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 => def transformTemplate = { diff --git a/src/dotty/tools/dotc/typer/RefChecks.scala b/src/dotty/tools/dotc/typer/RefChecks.scala index 6a1f3652b444..93cd412f280d 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 From d72645211880225d13764cd2d0764eee0efb069e Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Tue, 21 Apr 2015 11:40:48 +0200 Subject: [PATCH 04/16] Drop restriction that SuperAccessors should not touch patterns Not sure why we need to do this, and in any case it's not sure what constitutes a pattern. There are certainly some parts of patterns (e.g. prefixes of unapplies, or their implicit arguments) that should be transformed under SuperAccessors, so the previous condition was too coarse. We include the test case that motivated the restriction. It looks like it works now. --- src/dotty/tools/dotc/transform/SuperAccessors.scala | 5 ----- tests/pos/t4062.scala | 7 +++++++ 2 files changed, 7 insertions(+), 5 deletions(-) create mode 100644 tests/pos/t4062.scala diff --git a/src/dotty/tools/dotc/transform/SuperAccessors.scala b/src/dotty/tools/dotc/transform/SuperAccessors.scala index 213a2369d0e7..cfd78a91d02b 100644 --- a/src/dotty/tools/dotc/transform/SuperAccessors.scala +++ b/src/dotty/tools/dotc/transform/SuperAccessors.scala @@ -155,11 +155,6 @@ class SuperAccessors extends MacroTransform 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 impl: Template => def transformTemplate = { diff --git a/tests/pos/t4062.scala b/tests/pos/t4062.scala new file mode 100644 index 000000000000..63e05b7392e1 --- /dev/null +++ b/tests/pos/t4062.scala @@ -0,0 +1,7 @@ +class A(val f : String) + +class B(a : Option[String], f : String) extends A(f) { + a match { + case Some(`f`) => print(f) + } +} From 65596eecc3c7670e7000f064a31dc61f575217eb Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Tue, 21 Apr 2015 12:28:09 +0200 Subject: [PATCH 05/16] Be smarter about validity of owners Previous scheme never went back once owner was taken to be invalid: all enclosed code was assumed to be with invalid owners. This is not true if the enclosed code contains a class or method. Also previous scheme looked at the owner, whereas the only thing that matters is the enclosing class. Therefore, by-name arguments are no longer considered to be regions with invalid owners. Also: run everything at thisTransform.next, except install forwarders at thisTransform. Previous scheme was weird in that it switched to thisTransform.next in an Apply node, but never switched back, even if said Apply node contains nested classes that need forwarders. --- .../transform/ForwardParamAccessors.scala | 4 +- .../tools/dotc/transform/SuperAccessors.scala | 59 +++++++++---------- 2 files changed, 30 insertions(+), 33 deletions(-) diff --git a/src/dotty/tools/dotc/transform/ForwardParamAccessors.scala b/src/dotty/tools/dotc/transform/ForwardParamAccessors.scala index b65681a4b5db..ff4dc265280b 100644 --- a/src/dotty/tools/dotc/transform/ForwardParamAccessors.scala +++ b/src/dotty/tools/dotc/transform/ForwardParamAccessors.scala @@ -25,7 +25,7 @@ trait ForwardParamAccessors extends DenotTransformer { thisTransformer: MacroTra def currentClass(implicit ctx: Context) = ctx.owner.enclosingClass.asClass def forwardParamAccessors(impl: Template)(implicit ctx: Context): Template = { - def fwd(stats: List[Tree]): List[Tree] = { + 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 { @@ -68,6 +68,6 @@ trait ForwardParamAccessors extends DenotTransformer { thisTransformer: MacroTra stats map forwardParamAccessor } - cpy.Template(impl)(body = fwd(impl.body)) + cpy.Template(impl)(body = fwd(impl.body)(ctx.withPhase(thisTransformer))) } } \ No newline at end of file diff --git a/src/dotty/tools/dotc/transform/SuperAccessors.scala b/src/dotty/tools/dotc/transform/SuperAccessors.scala index cfd78a91d02b..ffad534b4aeb 100644 --- a/src/dotty/tools/dotc/transform/SuperAccessors.scala +++ b/src/dotty/tools/dotc/transform/SuperAccessors.scala @@ -55,18 +55,36 @@ class SuperAccessors extends MacroTransform /** the following two members override abstract members in Transform */ override def phaseName: String = "superaccessors" + override def transformPhase(implicit ctx: Context) = thisTransformer.next + 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 var invalidEnclClass: Symbol = NoSymbol + + private def withInvalidCurrentClass[A](trans: => A)(implicit ctx: Context): A = { + val saved = invalidEnclClass + invalidEnclClass = ctx.owner + try trans + finally invalidEnclClass = saved + } + + private def validCurrentClass(implicit ctx: Context): Boolean = + ctx.owner.enclosingClass != invalidEnclClass private val accDefs = mutable.Map[Symbol, ListBuffer[Tree]]() @@ -92,15 +110,7 @@ class SuperAccessors extends MacroTransform 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) - } - } - - private def transformSuperSelect(sel: Select)(implicit ctx: Context): Tree = { + 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}") @@ -126,7 +136,7 @@ class SuperAccessors extends MacroTransform } if (name.isTermName && mix == tpnme.EMPTY && - ((clazz is Trait) || clazz != ctx.owner.enclosingClass || !validCurrentOwner)) + ((clazz is Trait) || clazz != ctx.owner.enclosingClass || !validCurrentClass)) ensureAccessor(sel)(ctx.withPhase(thisTransformer.next)) else sel } @@ -246,7 +256,7 @@ class SuperAccessors extends MacroTransform case tree: DefDef => cpy.DefDef(tree)( - rhs = if (isMethodWithExtension(sym)) withInvalidOwner(transform(tree.rhs)) else transform(tree.rhs)) + rhs = if (isMethodWithExtension(sym)) withInvalidCurrentClass(transform(tree.rhs)) else transform(tree.rhs)) case TypeApply(sel @ Select(qual, name), args) => mayNeedProtectedAccessor(sel, args, goToSuper = true) @@ -265,12 +275,6 @@ class SuperAccessors extends MacroTransform } 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)) - } - case _ => super.transform(tree) } @@ -284,13 +288,6 @@ class SuperAccessors extends MacroTransform } } - private def withInvalidOwner[A](trans: => A): A = { - val saved = validCurrentOwner - validCurrentOwner = false - try trans - finally validCurrentOwner = saved - } - /** Add a protected accessor, if needed, and return a tree that calls * the accessor and returns the same member. The result is already * typed. @@ -382,7 +379,7 @@ class SuperAccessors extends MacroTransform 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) From ed160675ddfc4410d1dcaa55ecbd2dec7f842013 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Wed, 22 Apr 2015 12:07:53 +0200 Subject: [PATCH 06/16] Make currentClass available from Symbols It's needed beyond MacroTransform, and its definition is independent. Also, make `defn` in Symbols not implicit. (I think its implicitness was an oversight). --- src/dotty/tools/dotc/core/Symbols.scala | 8 ++++++-- src/dotty/tools/dotc/transform/MacroTransform.scala | 5 ----- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/src/dotty/tools/dotc/core/Symbols.scala b/src/dotty/tools/dotc/core/Symbols.scala index 47342cb56d33..b482dcae409c 100644 --- a/src/dotty/tools/dotc/core/Symbols.scala +++ b/src/dotty/tools/dotc/core/Symbols.scala @@ -552,13 +552,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/transform/MacroTransform.scala b/src/dotty/tools/dotc/transform/MacroTransform.scala index 0f57c3ff5f6a..9634decaa166 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) From 158c9632e9138a91a47ce13386bce0ab41db434b Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Wed, 22 Apr 2015 12:10:05 +0200 Subject: [PATCH 07/16] Super accessor refactoring New phase: PostTransform, runs after Typer. SuperAccessors and ParamForwarders (renamed from ForwardParamAccessors) are helper objects of post transform. Next: Add instChecks as well. --- src/dotty/tools/dotc/Compiler.scala | 2 +- ...mAccessors.scala => ParamForwarding.scala} | 4 +- .../tools/dotc/transform/PostTyper.scala | 78 ++++ .../tools/dotc/transform/SuperAccessors.scala | 353 +++++++++--------- test/dotc/tests.scala | 2 - 5 files changed, 254 insertions(+), 185 deletions(-) rename src/dotty/tools/dotc/transform/{ForwardParamAccessors.scala => ParamForwarding.scala} (94%) create mode 100644 src/dotty/tools/dotc/transform/PostTyper.scala diff --git a/src/dotty/tools/dotc/Compiler.scala b/src/dotty/tools/dotc/Compiler.scala index c67fb124a710..536a04c2225b 100644 --- a/src/dotty/tools/dotc/Compiler.scala +++ b/src/dotty/tools/dotc/Compiler.scala @@ -38,10 +38,10 @@ class Compiler { def phases: List[List[Phase]] = List( List(new FrontEnd), + List(new PostTyper), 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 RefChecks, new ElimRepeated, diff --git a/src/dotty/tools/dotc/transform/ForwardParamAccessors.scala b/src/dotty/tools/dotc/transform/ParamForwarding.scala similarity index 94% rename from src/dotty/tools/dotc/transform/ForwardParamAccessors.scala rename to src/dotty/tools/dotc/transform/ParamForwarding.scala index ff4dc265280b..883a33ff2705 100644 --- a/src/dotty/tools/dotc/transform/ForwardParamAccessors.scala +++ b/src/dotty/tools/dotc/transform/ParamForwarding.scala @@ -19,11 +19,9 @@ import Contexts._, Types._, Symbols._, Flags._, TypeUtils._, DenotTransformers._ * Do the same also if there are intermediate inaccessible parameter accessor forwarders. * The aim of this transformation is to avoid redundant parameter accessor fields. */ -trait ForwardParamAccessors extends DenotTransformer { thisTransformer: MacroTransform => +class ParamForwarding(thisTransformer: DenotTransformer) { import ast.tpd._ - def currentClass(implicit ctx: Context) = ctx.owner.enclosingClass.asClass - def forwardParamAccessors(impl: Template)(implicit ctx: Context): Template = { def fwd(stats: List[Tree])(implicit ctx: Context): List[Tree] = { val (superArgs, superParamNames) = impl.parents match { diff --git a/src/dotty/tools/dotc/transform/PostTyper.scala b/src/dotty/tools/dotc/transform/PostTyper.scala new file mode 100644 index 000000000000..4ad2cef01fbf --- /dev/null +++ b/src/dotty/tools/dotc/transform/PostTyper.scala @@ -0,0 +1,78 @@ +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.InstChecks +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) + * + * 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 instChecks = new InstChecks + + class PostTyperTransformer extends Transformer { + + override def transform(tree: Tree)(implicit ctx: Context): Tree = + try tree match { + case impl: Template => + def trans(impl: Template) = + cpy.Template(impl)(body = transformStats(impl.body, impl.symbol)) + paramFwd.forwardParamAccessors(superAcc.wrapTemplate(impl)(trans)) + case tree @ TypeApply(sel: Select, args) => + val sel1 = superAcc.transformSelect(super.transform(sel), args) + if (superAcc.isProtectedAccessor(sel1)) sel1 else cpy.TypeApply(tree)(sel1, args) + case sel: Select => + superAcc.transformSelect(super.transform(sel), Nil) + case tree: DefDef => + superAcc.wrapDefDef(tree)(cpy.DefDef(tree)(rhs = transform(tree.rhs))) + case tree: Assign => + superAcc.transformAssign(super.transform(tree)) +// case tree: Apply if tree.symbol.isConstructor => +// instChecks.checkInstantiable(tree) +// super.transform(tree) + case _ => + 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 ffad534b4aeb..a0069878d08b 100644 --- a/src/dotty/tools/dotc/transform/SuperAccessors.scala +++ b/src/dotty/tools/dotc/transform/SuperAccessors.scala @@ -5,7 +5,6 @@ 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._ @@ -14,23 +13,14 @@ import util.Positions._ import Decorators._ 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 @@ -43,24 +33,11 @@ import Symbols._, TypeUtils._ * (3) Super calls do not go to some synthetic members of Any (see isDisallowed) * * (4) Super calls do not go to synthetic field accessors - * - * TODO: Rename phase to "Accessors" because it handles more than just super accessors */ -class SuperAccessors extends MacroTransform - with IdentityDenotTransformer - with ForwardParamAccessors { thisTransformer => +class SuperAccessors(thisTransformer: DenotTransformer) { import tpd._ - /** the following two members override abstract members in Transform */ - override def phaseName: String = "superaccessors" - - override def transformPhase(implicit ctx: Context) = thisTransformer.next - - protected def newTransformer(implicit ctx: Context): Transformer = - new SuperAccTransformer - - class SuperAccTransformer extends Transformer { /** Some parts of trees will get a new owner in subsequent phases. * These are value class methods, which will become extension methods. @@ -85,14 +62,16 @@ class SuperAccessors extends MacroTransform private def validCurrentClass(implicit ctx: Context): Boolean = ctx.owner.enclosingClass != invalidEnclClass + + /** List buffers for new accessor definitions, indexed by class */ + private val accDefs = mutable.Map[Symbol, mutable.ListBuffer[Tree]]() - private val accDefs = mutable.Map[Symbol, ListBuffer[Tree]]() - - private def ensureAccessor(sel: Select)(implicit ctx: Context) = { + /** 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") @@ -110,13 +89,20 @@ class SuperAccessors extends MacroTransform This(clazz).select(superAcc).withPos(sel.pos) } - private def transformSuperSelect(sel: Select)(implicit ctx: Context): Tree = { + /** Check selection `super.f` for conforming to rules. If necessary, + * replace by a super accessor call. + */ + 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 || @@ -137,12 +123,13 @@ class SuperAccessors extends MacroTransform } if (name.isTermName && mix == tpnme.EMPTY && ((clazz is Trait) || clazz != ctx.owner.enclosingClass || !validCurrentClass)) - ensureAccessor(sel)(ctx.withPhase(thisTransformer.next)) + 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._ @@ -153,158 +140,41 @@ class SuperAccessors extends MacroTransform (sym eq Any_##) } - override def transform(tree: Tree)(implicit ctx: Context): Tree = { - val sym = tree.symbol - - 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 { - case impl: Template => - - 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 is ParamAccessor - case _ => false - } - ownStats ++= params - val rest1 = transformStats(rest, tree.symbol) - accDefs -= currentClass - ownStats ++= rest1 - cpy.Template(impl)(body = ownStats.toList) - } - forwardParamAccessors(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)) withInvalidCurrentClass(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 _ => - super.transform(tree) - } - catch { - case ex : AssertionError => - if (sym != null && sym != NoSymbol) - Console.println("TRANSFORM: " + tree.symbol.sourceFile) - - Console.println("TREE: " + tree) - throw ex - } + /** 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 } /** 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 = { - val Select(qual, _) = tree - val sym = tree.symbol.asTerm + 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) - val accName = sym.name.protectedAccessorName + 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 + } // 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 receiverType = + if (isThisType(sym.info.finalResultType)) clazz.thisType + else clazz.classInfo.selfType val accType = { def accTypeOf(tpe: Type): Type = tpe match { case tpe: PolyType => @@ -314,6 +184,57 @@ class SuperAccessors extends MacroTransform } 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 + } + + 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. + */ + 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) + assert(clazz.exists, sym) + ctx.debuglog("Decided for host class: " + clazz) + + val accName = sym.name.protectedAccessorName + + // 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 + 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) @@ -338,7 +259,7 @@ class SuperAccessors extends MacroTransform /** 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) @@ -422,11 +343,85 @@ class SuperAccessors extends MacroTransform 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/test/dotc/tests.scala b/test/dotc/tests.scala index 85a3e317ac56..b0b6da420876 100644 --- a/test/dotc/tests.scala +++ b/test/dotc/tests.scala @@ -88,8 +88,6 @@ class tests extends CompilerTest { @Test def pos_all = compileFiles(posDir) // twice omitted to make tests run faster - - @Test def new_all = compileFiles(newDir, twice) @Test def neg_blockescapes() = compileFile(negDir, "blockescapesNeg", xerrors = 1) From 7e60221120015be2607947f2de55ca4984db7077 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Wed, 22 Apr 2015 13:15:11 +0200 Subject: [PATCH 08/16] Roll InstChecks into PostTyper Move InstChecks functionality into PostTyper in order to save a separate traversal. --- src/dotty/tools/dotc/Compiler.scala | 1 - .../tools/dotc/core/SymDenotations.scala | 4 + .../tools/dotc/transform/FirstTransform.scala | 4 - .../tools/dotc/transform/PostTyper.scala | 89 +++++++++++++++--- src/dotty/tools/dotc/typer/InstChecks.scala | 90 ------------------- 5 files changed, 80 insertions(+), 108 deletions(-) delete mode 100644 src/dotty/tools/dotc/typer/InstChecks.scala diff --git a/src/dotty/tools/dotc/Compiler.scala b/src/dotty/tools/dotc/Compiler.scala index 536a04c2225b..73040ca9c368 100644 --- a/src/dotty/tools/dotc/Compiler.scala +++ b/src/dotty/tools/dotc/Compiler.scala @@ -39,7 +39,6 @@ class Compiler { List( List(new FrontEnd), List(new PostTyper), - List(new InstChecks), List(new FirstTransform, new SyntheticMethods), List(new Pickler), // Pickler needs to come last in a group since it should not pickle trees generated later diff --git a/src/dotty/tools/dotc/core/SymDenotations.scala b/src/dotty/tools/dotc/core/SymDenotations.scala index 3566595f2360..0b5e10220e7a 100644 --- a/src/dotty/tools/dotc/core/SymDenotations.scala +++ b/src/dotty/tools/dotc/core/SymDenotations.scala @@ -210,6 +210,10 @@ object SymDenotations { /** Does this denotation have an annotation matching the given class symbol? */ 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] = diff --git a/src/dotty/tools/dotc/transform/FirstTransform.scala b/src/dotty/tools/dotc/transform/FirstTransform.scala index 02d0bb2ba285..cfe650b99b1e 100644 --- a/src/dotty/tools/dotc/transform/FirstTransform.scala +++ b/src/dotty/tools/dotc/transform/FirstTransform.scala @@ -35,10 +35,6 @@ class FirstTransform extends MiniPhaseTransform with IdentityDenotTransformer wi import ast.tpd._ override def phaseName = "firstTransform" - - override def runsAfter = Set(classOf[typer.InstChecks]) - // This phase makes annotations disappear in types, so InstChecks should - // run before so that it can get at all annotations. def transformInfo(tp: Type, sym: Symbol)(implicit ctx: Context): Type = tp diff --git a/src/dotty/tools/dotc/transform/PostTyper.scala b/src/dotty/tools/dotc/transform/PostTyper.scala index 4ad2cef01fbf..98c110e11690 100644 --- a/src/dotty/tools/dotc/transform/PostTyper.scala +++ b/src/dotty/tools/dotc/transform/PostTyper.scala @@ -7,7 +7,7 @@ import scala.collection.{ mutable, immutable } import ValueClasses._ import scala.annotation.tailrec import core._ -import typer.InstChecks +import typer.ErrorReporting._ import Types._, Contexts._, Constants._, Names._, NameOps._, Flags._, DenotTransformers._ import SymDenotations._, Symbols._, StdNames._, Annotations._, Trees._, Scopes._, Denotations._ import util.Positions._ @@ -22,6 +22,8 @@ import Symbols._, TypeUtils._ * 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) Check that `New` nodes can be instantiated, and that annotations are valid * * 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 @@ -44,28 +46,89 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisTran val superAcc = new SuperAccessors(thisTransformer) val paramFwd = new ParamForwarding(thisTransformer) -// val instChecks = new InstChecks + + /** Check that `tp` refers to a nonAbstract class + * and that the instance conforms to the self type of the created class. + */ + private 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 _ => + } + + 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 + } 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) override def transform(tree: Tree)(implicit ctx: Context): Tree = try tree match { case impl: Template => - def trans(impl: Template) = - cpy.Template(impl)(body = transformStats(impl.body, impl.symbol)) - paramFwd.forwardParamAccessors(superAcc.wrapTemplate(impl)(trans)) + val saved = parentNews + parentNews ++= impl.parents.flatMap(newPart) + try + paramFwd.forwardParamAccessors( + superAcc.wrapTemplate(impl)( + super.transform(_).asInstanceOf[Template])) + finally parentNews = saved case tree @ TypeApply(sel: Select, args) => - val sel1 = superAcc.transformSelect(super.transform(sel), args) - if (superAcc.isProtectedAccessor(sel1)) sel1 else cpy.TypeApply(tree)(sel1, args) + val args1 = transform(args) + val sel1 = superAcc.transformSelect(super.transform(sel), args1) + if (superAcc.isProtectedAccessor(sel1)) sel1 else cpy.TypeApply(tree)(sel1, args1) case sel: Select => superAcc.transformSelect(super.transform(sel), Nil) - case tree: DefDef => - superAcc.wrapDefDef(tree)(cpy.DefDef(tree)(rhs = transform(tree.rhs))) - case tree: Assign => + case tree @ Assign(sel: Select, _) => superAcc.transformAssign(super.transform(tree)) -// case tree: Apply if tree.symbol.isConstructor => -// instChecks.checkInstantiable(tree) -// super.transform(tree) + 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) => + checkInstantiable(tree.tpe, tree.pos) + super.transform(tree) + case 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 _ => super.transform(tree) } diff --git a/src/dotty/tools/dotc/typer/InstChecks.scala b/src/dotty/tools/dotc/typer/InstChecks.scala deleted file mode 100644 index 7148a6e6845d..000000000000 --- 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) - } - } -} From 3a158c0a047d2842c0604faf75251c0828408bc9 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Wed, 22 Apr 2015 13:34:35 +0200 Subject: [PATCH 09/16] Roll SyntheticMethods into PostTyper --- src/dotty/tools/dotc/Compiler.scala | 7 ++--- .../tools/dotc/transform/PostTyper.scala | 14 ++++++---- .../dotc/transform/SyntheticMethods.scala | 28 +++++++++---------- 3 files changed, 26 insertions(+), 23 deletions(-) diff --git a/src/dotty/tools/dotc/Compiler.scala b/src/dotty/tools/dotc/Compiler.scala index 73040ca9c368..e1146330fd42 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._ @@ -39,9 +39,8 @@ class Compiler { List( List(new FrontEnd), List(new PostTyper), - List(new FirstTransform, - new SyntheticMethods), - List(new Pickler), // Pickler needs to come last in a group since it should not pickle trees generated later + List(new FirstTransform), + List(new Pickler), List(new RefChecks, new ElimRepeated, new NormalizeFlags, diff --git a/src/dotty/tools/dotc/transform/PostTyper.scala b/src/dotty/tools/dotc/transform/PostTyper.scala index 98c110e11690..1b42a2501f39 100644 --- a/src/dotty/tools/dotc/transform/PostTyper.scala +++ b/src/dotty/tools/dotc/transform/PostTyper.scala @@ -23,7 +23,9 @@ import Symbols._, TypeUtils._ * field (corresponding = super class field is initialized with subclass field) * (@see ForwardParamAccessors) * - * (3) Check that `New` nodes can be instantiated, and that annotations are valid + * (3) Add synthetic methods (@see SyntheticMethods) + * + * (4) Check that `New` nodes can be instantiated, and that annotations are valid * * 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 @@ -46,7 +48,8 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisTran val superAcc = new SuperAccessors(thisTransformer) val paramFwd = new ParamForwarding(thisTransformer) - + val synthMth = new SyntheticMethods(thisTransformer) + /** Check that `tp` refers to a nonAbstract class * and that the instance conforms to the self type of the created class. */ @@ -99,9 +102,10 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisTran val saved = parentNews parentNews ++= impl.parents.flatMap(newPart) try - paramFwd.forwardParamAccessors( - superAcc.wrapTemplate(impl)( - super.transform(_).asInstanceOf[Template])) + synthMth.addSyntheticMethods( + paramFwd.forwardParamAccessors( + superAcc.wrapTemplate(impl)( + super.transform(_).asInstanceOf[Template]))) finally parentNews = saved case tree @ TypeApply(sel: Select, args) => val args1 = transform(args) diff --git a/src/dotty/tools/dotc/transform/SyntheticMethods.scala b/src/dotty/tools/dotc/transform/SyntheticMethods.scala index 4726105c684f..222b64575053 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 valueSymbols: List[Symbol] = _ - private var caseSymbols: List[Symbol] = _ - - 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 - } + private var myValueSymbols: List[Symbol] = Nil + private var myCaseSymbols: List[Symbol] = Nil + + 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) + } + + 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 } From 1d4e4a6d4784edfe1d2490de7ceff9d3c82d4d27 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Wed, 22 Apr 2015 14:25:04 +0200 Subject: [PATCH 10/16] Remove dead skipJava code from FirstTransform. Frontend already drops all Java compilation units, so there's nothing to do for FirstTransform. --- src/dotty/tools/dotc/transform/FirstTransform.scala | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/dotty/tools/dotc/transform/FirstTransform.scala b/src/dotty/tools/dotc/transform/FirstTransform.scala index cfe650b99b1e..da0ec68d32f0 100644 --- a/src/dotty/tools/dotc/transform/FirstTransform.scala +++ b/src/dotty/tools/dotc/transform/FirstTransform.scala @@ -29,7 +29,6 @@ import StdNames._ * - inserts `.package` for selections of package object members * - checks the bounds of AppliedTypeTrees * - stubs out native methods - * - removes java-defined ASTs */ class FirstTransform extends MiniPhaseTransform with IdentityDenotTransformer with AnnotationTransformer { thisTransformer => import ast.tpd._ @@ -97,10 +96,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) = { From 79958518b4f95b3dd8e34d543757034d181e4514 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Wed, 22 Apr 2015 14:52:34 +0200 Subject: [PATCH 11/16] Roll some of FirstTransform functionaility into PostTyper Everything that needs to be done before pickling moves to PostTyper. The idea is that we want to make Pickler come before FirstTransform. --- .../tools/dotc/transform/FirstTransform.scala | 44 +-------- .../tools/dotc/transform/PostTyper.scala | 90 ++++++++++++------- src/dotty/tools/dotc/typer/Checking.scala | 17 ++++ 3 files changed, 76 insertions(+), 75 deletions(-) diff --git a/src/dotty/tools/dotc/transform/FirstTransform.scala b/src/dotty/tools/dotc/transform/FirstTransform.scala index da0ec68d32f0..aecc1b86f96b 100644 --- a/src/dotty/tools/dotc/transform/FirstTransform.scala +++ b/src/dotty/tools/dotc/transform/FirstTransform.scala @@ -24,10 +24,7 @@ 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 */ class FirstTransform extends MiniPhaseTransform with IdentityDenotTransformer with AnnotationTransformer { thisTransformer => @@ -111,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/PostTyper.scala b/src/dotty/tools/dotc/transform/PostTyper.scala index 1b42a2501f39..c6031b31e77a 100644 --- a/src/dotty/tools/dotc/transform/PostTyper.scala +++ b/src/dotty/tools/dotc/transform/PostTyper.scala @@ -8,6 +8,7 @@ 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._ @@ -27,6 +28,14 @@ import Symbols._, TypeUtils._ * * (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 @@ -49,24 +58,7 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisTran val superAcc = new SuperAccessors(thisTransformer) val paramFwd = new ParamForwarding(thisTransformer) val synthMth = new SyntheticMethods(thisTransformer) - - /** Check that `tp` refers to a nonAbstract class - * and that the instance conforms to the self type of the created class. - */ - private 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 _ => - } - + private def newPart(tree: Tree): Option[New] = methPart(tree) match { case Select(nu: New, _) => Some(nu) case _ => None @@ -76,6 +68,22 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisTran // 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 @@ -96,25 +104,41 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisTran 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 tree match { - case impl: Template => + 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 ++= impl.parents.flatMap(newPart) + parentNews ++= tree.parents.flatMap(newPart) try synthMth.addSyntheticMethods( paramFwd.forwardParamAccessors( - superAcc.wrapTemplate(impl)( + superAcc.wrapTemplate(tree)( super.transform(_).asInstanceOf[Template]))) finally parentNews = saved - case tree @ TypeApply(sel: Select, args) => - val args1 = transform(args) - val sel1 = superAcc.transformSelect(super.transform(sel), args1) - if (superAcc.isProtectedAccessor(sel1)) sel1 else cpy.TypeApply(tree)(sel1, args1) - case sel: Select => - superAcc.transformSelect(super.transform(sel), Nil) - case tree @ Assign(sel: Select, _) => - superAcc.transformAssign(super.transform(tree)) case tree: DefDef => transformAnnots(tree) superAcc.wrapDefDef(tree)(super.transform(tree).asInstanceOf[DefDef]) @@ -122,9 +146,9 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisTran transformAnnots(tree) super.transform(tree) case tree: New if !inJavaAnnot && !parentNews.contains(tree) => - checkInstantiable(tree.tpe, tree.pos) + Checking.checkInstantiable(tree.tpe, tree.pos) super.transform(tree) - case Annotated(annot, annotated) => + case tree @ Annotated(annot, annotated) => cpy.Annotated(tree)(transformAnnot(annot), transform(annotated)) case tree: TypeTree => tree.withType( @@ -133,7 +157,7 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisTran case tpe => tpe } ) - case _ => + case tree => super.transform(tree) } catch { diff --git a/src/dotty/tools/dotc/typer/Checking.scala b/src/dotty/tools/dotc/typer/Checking.scala index b28afa6f2cfd..148e318852bb 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. */ From a884556d910b333f6d550ef7624c100d476862b7 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Wed, 22 Apr 2015 16:19:58 +0200 Subject: [PATCH 12/16] Anchor import symbols at current owner Needed to harmonize behavior of Typer/Namer and tpd. This is needed for making pickling, then unpickling the identity. --- src/dotty/tools/dotc/ast/tpd.scala | 2 +- src/dotty/tools/dotc/core/Symbols.scala | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/dotty/tools/dotc/ast/tpd.scala b/src/dotty/tools/dotc/ast/tpd.scala index 955439413ebb..f8c8a3d9faec 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/Symbols.scala b/src/dotty/tools/dotc/core/Symbols.scala index b482dcae409c..bce0a5450f0e 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) = From 1d56a547b56b2c82bf70d8701a077c13ffb7f80d Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Wed, 22 Apr 2015 16:21:14 +0200 Subject: [PATCH 13/16] Unpickle Imports Was missing before. Needed a tweak in PlainPrinter for printing import symbol references (their denotation is not current after pickling, so they would have printed differently after and before pickling). --- .../dotc/core/pickling/TreeUnpickler.scala | 22 +++++++++++++++++-- .../tools/dotc/printing/PlainPrinter.scala | 2 +- tests/new/test.scala | 12 ++++++++++ 3 files changed, 33 insertions(+), 3 deletions(-) create mode 100644 tests/new/test.scala diff --git a/src/dotty/tools/dotc/core/pickling/TreeUnpickler.scala b/src/dotty/tools/dotc/core/pickling/TreeUnpickler.scala index a58fc9071742..59bd017fa521 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 => @@ -669,6 +669,24 @@ class TreeUnpickler(reader: TastyReader, tastyName: TastyName.Table) { case _ => 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 0a7edd2aa779..b60ba72af51c 100644 --- a/src/dotty/tools/dotc/printing/PlainPrinter.scala +++ b/src/dotty/tools/dotc/printing/PlainPrinter.scala @@ -109,7 +109,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 => "" diff --git a/tests/new/test.scala b/tests/new/test.scala new file mode 100644 index 000000000000..5d2bc0ff5211 --- /dev/null +++ b/tests/new/test.scala @@ -0,0 +1,12 @@ +trait T { + object O +} + +class C extends T + +object Test { + + val c = new C + c.O + +} From ba9db4e44b77302542a76040c456d70e513769f9 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Wed, 22 Apr 2015 16:22:28 +0200 Subject: [PATCH 14/16] Swap Pickler and FirstTransform This means we now also pickle Imports and NamedArgs. --- src/dotty/tools/dotc/Compiler.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dotty/tools/dotc/Compiler.scala b/src/dotty/tools/dotc/Compiler.scala index e1146330fd42..3d70fcb43684 100644 --- a/src/dotty/tools/dotc/Compiler.scala +++ b/src/dotty/tools/dotc/Compiler.scala @@ -39,8 +39,8 @@ class Compiler { List( List(new FrontEnd), List(new PostTyper), - List(new FirstTransform), List(new Pickler), + List(new FirstTransform), List(new RefChecks, new ElimRepeated, new NormalizeFlags, From 36c229ba8c3d44546b34446ca36c8f335a9c5f47 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Wed, 22 Apr 2015 18:18:38 +0200 Subject: [PATCH 15/16] Add runsAfterGroupOf constraint to ExtensionMethods --- src/dotty/tools/dotc/transform/ExtensionMethods.scala | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/dotty/tools/dotc/transform/ExtensionMethods.scala b/src/dotty/tools/dotc/transform/ExtensionMethods.scala index 26f26fc2ffb0..df769a80f356 100644 --- a/src/dotty/tools/dotc/transform/ExtensionMethods.scala +++ b/src/dotty/tools/dotc/transform/ExtensionMethods.scala @@ -30,6 +30,8 @@ class ExtensionMethods extends MiniPhaseTransform with DenotTransformer with Ful override def phaseName: String = "extmethods" 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 => From 2b3591cec6a1d58f3346b6c8933ca0742f13c1cf Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Thu, 23 Apr 2015 21:06:42 +0200 Subject: [PATCH 16/16] Remove trailing whitespace I have figured out how to make this the default in Eclipse, so hopefully we won't see many repeats of this. --- .../tools/dotc/core/SymDenotations.scala | 4 +- src/dotty/tools/dotc/core/Symbols.scala | 2 +- .../tools/dotc/core/TypeApplications.scala | 2 +- src/dotty/tools/dotc/core/Types.scala | 8 ++-- .../dotc/core/pickling/TreeUnpickler.scala | 2 +- .../dotc/transform/ExtensionMethods.scala | 2 +- .../tools/dotc/transform/LambdaLift.scala | 22 +++++----- .../dotc/transform/ParamForwarding.scala | 4 +- .../tools/dotc/transform/PostTyper.scala | 42 +++++++++---------- .../tools/dotc/transform/SuperAccessors.scala | 28 ++++++------- .../dotc/transform/SyntheticMethods.scala | 6 +-- .../tools/dotc/transform/TypeUtils.scala | 2 +- 12 files changed, 62 insertions(+), 62 deletions(-) diff --git a/src/dotty/tools/dotc/core/SymDenotations.scala b/src/dotty/tools/dotc/core/SymDenotations.scala index 0b5e10220e7a..83499ca7b53c 100644 --- a/src/dotty/tools/dotc/core/SymDenotations.scala +++ b/src/dotty/tools/dotc/core/SymDenotations.scala @@ -210,9 +210,9 @@ object SymDenotations { /** Does this denotation have an annotation matching the given class symbol? */ 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 = + final def transformAnnotations(f: Annotation => Annotation)(implicit ctx: Context): Unit = annotations = annotations.mapConserve(f) /** Optionally, the annotation matching the given class symbol */ diff --git a/src/dotty/tools/dotc/core/Symbols.scala b/src/dotty/tools/dotc/core/Symbols.scala index bce0a5450f0e..36167ae7f7dc 100644 --- a/src/dotty/tools/dotc/core/Symbols.scala +++ b/src/dotty/tools/dotc/core/Symbols.scala @@ -560,7 +560,7 @@ object Symbols { /** The Definitions object */ def defn(implicit ctx: Context): Definitions = ctx.definitions - + /** The current class */ def currentClass(implicit ctx: Context): ClassSymbol = ctx.owner.enclosingClass.asClass diff --git a/src/dotty/tools/dotc/core/TypeApplications.scala b/src/dotty/tools/dotc/core/TypeApplications.scala index de42b3e5fea5..5325189e1161 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/Types.scala b/src/dotty/tools/dotc/core/Types.scala index fe95219b8415..e290e8868664 100644 --- a/src/dotty/tools/dotc/core/Types.scala +++ b/src/dotty/tools/dotc/core/Types.scala @@ -1788,12 +1788,12 @@ object Types { if (false) RefinedType(parent, refinedName, refinedInfo) else RefinedType(parent, refinedName, rt => refinedInfo.substSkolem(this, SkolemType(rt))) } - + /** Add this refinement to `parent`, provided If `refinedName` is a member of `parent`. */ def wrapIfMember(parent: Type)(implicit ctx: Context): Type = if (parent.member(refinedName).exists) derivedRefinedType(parent, refinedName, refinedInfo) else parent - + override def equals(that: Any) = that match { case that: RefinedType => this.parent == that.parent && @@ -2414,7 +2414,7 @@ object Types { selfTypeCache = { def fullRef = fullyAppliedRef(cls.typeRef, cls.typeParams) val given = givenSelfType - val raw = + val raw = if (!given.exists) fullRef else if (cls is Module) given else if (ctx.erasedTypes) fullRef @@ -2423,7 +2423,7 @@ object Types { } selfTypeCache } - + /** The explicitly given self type (self types of modules are assumed to be * explcitly given here). */ diff --git a/src/dotty/tools/dotc/core/pickling/TreeUnpickler.scala b/src/dotty/tools/dotc/core/pickling/TreeUnpickler.scala index 59bd017fa521..ba3023ed1b07 100644 --- a/src/dotty/tools/dotc/core/pickling/TreeUnpickler.scala +++ b/src/dotty/tools/dotc/core/pickling/TreeUnpickler.scala @@ -669,7 +669,7 @@ class TreeUnpickler(reader: TastyReader, tastyName: TastyName.Table) { case _ => readTerm()(ctx.withOwner(exprOwner)) } - + def readImport()(implicit ctx: Context): Tree = { readByte() readEnd() diff --git a/src/dotty/tools/dotc/transform/ExtensionMethods.scala b/src/dotty/tools/dotc/transform/ExtensionMethods.scala index df769a80f356..ae22adc39d93 100644 --- a/src/dotty/tools/dotc/transform/ExtensionMethods.scala +++ b/src/dotty/tools/dotc/transform/ExtensionMethods.scala @@ -30,7 +30,7 @@ class ExtensionMethods extends MiniPhaseTransform with DenotTransformer with Ful override def phaseName: String = "extmethods" 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 { diff --git a/src/dotty/tools/dotc/transform/LambdaLift.scala b/src/dotty/tools/dotc/transform/LambdaLift.scala index 9b35d1d99def..1a23d887c36e 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/ParamForwarding.scala b/src/dotty/tools/dotc/transform/ParamForwarding.scala index 883a33ff2705..87ecaba07956 100644 --- a/src/dotty/tools/dotc/transform/ParamForwarding.scala +++ b/src/dotty/tools/dotc/transform/ParamForwarding.scala @@ -21,7 +21,7 @@ import Contexts._, Types._, Symbols._, Flags._, TypeUtils._, DenotTransformers._ */ 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 { @@ -68,4 +68,4 @@ class ParamForwarding(thisTransformer: DenotTransformer) { cpy.Template(impl)(body = fwd(impl.body)(ctx.withPhase(thisTransformer))) } -} \ No newline at end of file +} diff --git a/src/dotty/tools/dotc/transform/PostTyper.scala b/src/dotty/tools/dotc/transform/PostTyper.scala index c6031b31e77a..55270f233738 100644 --- a/src/dotty/tools/dotc/transform/PostTyper.scala +++ b/src/dotty/tools/dotc/transform/PostTyper.scala @@ -16,26 +16,26 @@ 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 @@ -54,16 +54,16 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisTran 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 } @@ -79,17 +79,17 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisTran tparam.info.asSeenFrom(tycon.tpe.normalizedPrefix, tparam.owner.owner).bounds) Checking.checkBounds(args, bounds, _.substDealias(tparams, _)) norm(tree) - case _ => + 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 @@ -97,10 +97,10 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisTran 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) @@ -114,7 +114,7 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisTran superAcc.transformSelect(super.transform(tree), targs) } } - + override def transform(tree: Tree)(implicit ctx: Context): Tree = try normalizeTypeTree(tree) match { case tree: Ident => @@ -133,7 +133,7 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisTran case tree: Template => val saved = parentNews parentNews ++= tree.parents.flatMap(newPart) - try + try synthMth.addSyntheticMethods( paramFwd.forwardParamAccessors( superAcc.wrapTemplate(tree)( diff --git a/src/dotty/tools/dotc/transform/SuperAccessors.scala b/src/dotty/tools/dotc/transform/SuperAccessors.scala index a0069878d08b..b111fdb9289a 100644 --- a/src/dotty/tools/dotc/transform/SuperAccessors.scala +++ b/src/dotty/tools/dotc/transform/SuperAccessors.scala @@ -43,11 +43,11 @@ class SuperAccessors(thisTransformer: DenotTransformer) { * 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. */ @@ -59,10 +59,10 @@ class SuperAccessors(thisTransformer: DenotTransformer) { try trans finally invalidEnclClass = saved } - - private def validCurrentClass(implicit ctx: Context): Boolean = + + private def validCurrentClass(implicit ctx: Context): Boolean = ctx.owner.enclosingClass != invalidEnclClass - + /** List buffers for new accessor definitions, indexed by class */ private val accDefs = mutable.Map[Symbol, mutable.ListBuffer[Tree]]() @@ -140,7 +140,7 @@ class SuperAccessors(thisTransformer: DenotTransformer) { (sym eq Any_##) } - /** Replace `sel` (or `sel[targs]` if `targs` is nonempty) with a protected accessor + /** 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) = { @@ -204,12 +204,12 @@ class SuperAccessors(thisTransformer: DenotTransformer) { ctx.debuglog(s"Replaced $sel with $res") res } - + 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. @@ -225,8 +225,8 @@ class SuperAccessors(thisTransformer: DenotTransformer) { // 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 + val receiverType = + if (isThisType(sym.info.finalResultType)) clazz.thisType else clazz.classInfo.selfType def accTypeOf(tpe: Type): Type = tpe match { case tpe: PolyType => @@ -401,10 +401,10 @@ class SuperAccessors(thisTransformer: DenotTransformer) { val setter = protectedSetter(lhs) ctx.debuglog("Replaced " + tree + " with " + setter) setter.appliedTo(qual, rhs) - } - else tree + } + 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] @@ -417,7 +417,7 @@ class SuperAccessors(thisTransformer: DenotTransformer) { case vd: ValOrDefDef => vd.symbol.flags is ParamAccessor case _ => false } - cpy.Template(impl)(body = params ++ accessors ++ rest) + cpy.Template(impl)(body = params ++ accessors ++ rest) } } diff --git a/src/dotty/tools/dotc/transform/SyntheticMethods.scala b/src/dotty/tools/dotc/transform/SyntheticMethods.scala index 222b64575053..9d0aebe450d9 100644 --- a/src/dotty/tools/dotc/transform/SyntheticMethods.scala +++ b/src/dotty/tools/dotc/transform/SyntheticMethods.scala @@ -31,18 +31,18 @@ import scala.language.postfixOps * def equals(other: Any): Boolean * def hashCode(): Int */ -class SyntheticMethods(thisTransformer: DenotTransformer) { +class SyntheticMethods(thisTransformer: DenotTransformer) { import ast.tpd._ private var myValueSymbols: List[Symbol] = Nil private var myCaseSymbols: List[Symbol] = Nil - + 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) } - + def valueSymbols(implicit ctx: Context) = { initSymbols; myValueSymbols } def caseSymbols(implicit ctx: Context) = { initSymbols; myCaseSymbols } diff --git a/src/dotty/tools/dotc/transform/TypeUtils.scala b/src/dotty/tools/dotc/transform/TypeUtils.scala index 5ef1e5ed373b..c01b6478cfa6 100644 --- a/src/dotty/tools/dotc/transform/TypeUtils.scala +++ b/src/dotty/tools/dotc/transform/TypeUtils.scala @@ -26,7 +26,7 @@ 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)