From 9da1f46c11a5342fb21f74e12134fcbe8f1d57de Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Fri, 21 Apr 2017 00:15:30 +0200 Subject: [PATCH 01/10] Break out by-name-closure creation from ElimByName --- .../tools/dotc/transform/ByNameClosures.scala | 78 +++++++++++++++++++ .../tools/dotc/transform/ElimByName.scala | 71 ++--------------- 2 files changed, 85 insertions(+), 64 deletions(-) create mode 100644 compiler/src/dotty/tools/dotc/transform/ByNameClosures.scala diff --git a/compiler/src/dotty/tools/dotc/transform/ByNameClosures.scala b/compiler/src/dotty/tools/dotc/transform/ByNameClosures.scala new file mode 100644 index 000000000000..47d051b45759 --- /dev/null +++ b/compiler/src/dotty/tools/dotc/transform/ByNameClosures.scala @@ -0,0 +1,78 @@ +package dotty.tools.dotc +package transform + +import TreeTransforms._ +import core._ +import Symbols._ +import SymDenotations._ +import Contexts._ +import Types._ +import Flags._ +import Decorators._ +import core.StdNames.nme +import ast.Trees._ + +/** This phase translates arguments to call-by-name parameters, using the rules + * + * x ==> x if x is a => parameter + * e.apply() ==> DummyApply(e) if e is pure + * e ==> DummyApply(() => e) for all other arguments + * + * where + * + * DummyApply: [T](() => T): T + * + * is a synthetic method defined in Definitions. Erasure will later strip these DummyApply wrappers. + */ +class ByNameClosures extends MiniPhaseTransform { thisTransformer => + import ast.tpd._ + + override def phaseName: String = "bynameClosures" + + /** The info of the tree's symbol at phase Nullarify (i.e. before transformation) */ + private def originalDenotation(tree: Tree)(implicit ctx: Context) = + tree.symbol.denot(ctx.withPhase(thisTransformer)) + + /** If denotation had an ExprType before, it now gets a function type */ + protected def exprBecomesFunction(symd: SymDenotation)(implicit ctx: Context) = + (symd is Param) || (symd is (ParamAccessor, butNot = Method)) + + protected def isByNameRef(tree: Tree)(implicit ctx: Context) = { + val origDenot = originalDenotation(tree) + origDenot.info.isInstanceOf[ExprType] && exprBecomesFunction(origDenot) + } + + override def transformApply(tree: Apply)(implicit ctx: Context, info: TransformerInfo): Tree = + ctx.traceIndented(s"transforming ${tree.show} at phase ${ctx.phase}", show = true) { + + def transformArg(arg: Tree, formal: Type): Tree = formal.dealias match { + case formalExpr: ExprType => + var argType = arg.tpe.widenIfUnstable + if (defn.isBottomType(argType)) argType = formal.widenExpr + def wrap(arg: Tree) = ref(defn.dummyApply).appliedToType(argType).appliedTo(arg) + arg match { + case Apply(Select(qual, nme.apply), Nil) + if qual.tpe.derivesFrom(defn.FunctionClass(0)) && isPureExpr(qual) => + wrap(qual) + case _ => + if (isByNameRef(arg) || arg.symbol == defn.dummyApply) arg + else { + val inSuper = if (ctx.mode.is(Mode.InSuperCall)) InSuperCall else EmptyFlags + val meth = ctx.newSymbol( + ctx.owner, nme.ANON_FUN, Synthetic | Method | inSuper, MethodType(Nil, Nil, argType)) + wrap(Closure(meth, _ => + atGroupEnd { implicit ctx: Context => + arg.changeOwner(ctx.owner, meth) + } + )) + } + } + case _ => + arg + } + + val mt @ MethodType(_) = tree.fun.tpe.widen + val args1 = tree.args.zipWithConserve(mt.paramInfos)(transformArg) + cpy.Apply(tree)(tree.fun, args1) + } +} diff --git a/compiler/src/dotty/tools/dotc/transform/ElimByName.scala b/compiler/src/dotty/tools/dotc/transform/ElimByName.scala index 06c48902952d..268054735ce7 100644 --- a/compiler/src/dotty/tools/dotc/transform/ElimByName.scala +++ b/compiler/src/dotty/tools/dotc/transform/ElimByName.scala @@ -20,27 +20,12 @@ import ast.Trees._ * * For the types of parameter symbols: * - * => T ==> () => T + * => T ==> () => T * - * Note that `=> T` types are not eliminated in MethodTypes. This is done later at erasure. - * Terms are rewritten as follows: + * For cbn parameter values * - * x ==> x.apply() if x is a parameter that had type => T - * - * Arguments to call-by-name parameters are translated as follows. First, the argument is - * rewritten by the rules - * - * e.apply() ==> e if e.apply() is an argument to a call-by-name parameter - * expr ==> () => expr if other expr is an argument to a call-by-name parameter - * - * This makes the argument compatible with a parameter type of () => T, which will be the - * formal parameter type at erasure. But to be -Ycheckable until then, any argument - * ARG rewritten by the rules above is again wrapped in an application DummyApply(ARG) - * where - * - * DummyApply: [T](() => T): T - * - * is a synthetic method defined in Definitions. Erasure will later strip these DummyApply wrappers. + * x ==> x() + * CbnArg(x) ==> DummyApply(x) * * Note: This scheme to have inconsistent types between method types (whose formal types are still * ExprTypes and parameter valdefs (which are now FunctionTypes) is not pretty. There are two @@ -53,7 +38,7 @@ import ast.Trees._ * Option 2: Merge ElimByName with erasure, or have it run immediately before. This has not been * tried yet. */ -class ElimByName extends MiniPhaseTransform with InfoTransformer { thisTransformer => +class ElimByName extends ByNameClosures with InfoTransformer { thisTransformer => import ast.tpd._ override def phaseName: String = "elimByName" @@ -62,52 +47,10 @@ class ElimByName extends MiniPhaseTransform with InfoTransformer { thisTransform // assumes idents and selects have symbols; interferes with splitter distribution // that's why it's "after group". - /** The info of the tree's symbol at phase Nullarify (i.e. before transformation) */ - private def originalDenotation(tree: Tree)(implicit ctx: Context) = - tree.symbol.denot(ctx.withPhase(thisTransformer)) - - override def transformApply(tree: Apply)(implicit ctx: Context, info: TransformerInfo): Tree = - ctx.traceIndented(s"transforming ${tree.show} at phase ${ctx.phase}", show = true) { - - def transformArg(arg: Tree, formal: Type): Tree = formal.dealias match { - case formalExpr: ExprType => - var argType = arg.tpe.widenIfUnstable - if (defn.isBottomType(argType)) argType = formal.widenExpr - val argFun = arg match { - case Apply(Select(qual, nme.apply), Nil) - if qual.tpe.derivesFrom(defn.FunctionClass(0)) && isPureExpr(qual) => - qual - case _ => - val inSuper = if (ctx.mode.is(Mode.InSuperCall)) InSuperCall else EmptyFlags - val meth = ctx.newSymbol( - ctx.owner, nme.ANON_FUN, Synthetic | Method | inSuper, MethodType(Nil, Nil, argType)) - Closure(meth, _ => - atGroupEnd { implicit ctx: Context => - arg.changeOwner(ctx.owner, meth) - } - ) - } - ref(defn.dummyApply).appliedToType(argType).appliedTo(argFun) - case _ => - arg - } - - val mt @ MethodType(_) = tree.fun.tpe.widen - val args1 = tree.args.zipWithConserve(mt.paramInfos)(transformArg) - cpy.Apply(tree)(tree.fun, args1) - } - - /** If denotation had an ExprType before, it now gets a function type */ - private def exprBecomesFunction(symd: SymDenotation)(implicit ctx: Context) = - (symd is Param) || (symd is (ParamAccessor, butNot = Method)) - /** Map `tree` to `tree.apply()` is `ftree` was of ExprType and becomes now a function */ - private def applyIfFunction(tree: Tree, ftree: Tree)(implicit ctx: Context) = { - val origDenot = originalDenotation(ftree) - if (exprBecomesFunction(origDenot) && (origDenot.info.isInstanceOf[ExprType])) - tree.select(defn.Function0_apply).appliedToNone + private def applyIfFunction(tree: Tree, ftree: Tree)(implicit ctx: Context) = + if (isByNameRef(ftree)) tree.select(defn.Function0_apply).appliedToNone else tree - } override def transformIdent(tree: Ident)(implicit ctx: Context, info: TransformerInfo): Tree = applyIfFunction(tree, tree) From 18b9a9e083def70107149d852b72b11fb50dd134 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Fri, 21 Apr 2017 01:37:37 +0200 Subject: [PATCH 02/10] Make changeOwnerAfter skip weak owners changeOwner already does this. --- compiler/src/dotty/tools/dotc/ast/tpd.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/src/dotty/tools/dotc/ast/tpd.scala b/compiler/src/dotty/tools/dotc/ast/tpd.scala index cd0115a99ac2..690f18509ebd 100644 --- a/compiler/src/dotty/tools/dotc/ast/tpd.scala +++ b/compiler/src/dotty/tools/dotc/ast/tpd.scala @@ -629,7 +629,7 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { case tree: DefTree => val sym = tree.symbol val prevDenot = sym.denot(ctx.withPhase(trans)) - if (prevDenot.owner == from) { + if (prevDenot.effectiveOwner == from.skipWeakOwner) { val d = sym.copySymDenotation(owner = to) d.installAfter(trans) d.transformAfter(trans, d => if (d.owner eq from) d.copySymDenotation(owner = to) else d) From 4727813c07d0701324bda9c09a682ffdbcd2a39c Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Fri, 21 Apr 2017 01:40:25 +0200 Subject: [PATCH 03/10] Move ByNameClosure earlier in the pipleline - Make it use changeOwnerAfter - Factor out TransformByNameApply as a common base trait between it and ElimByName --- compiler/src/dotty/tools/dotc/Compiler.scala | 3 +- .../tools/dotc/transform/ByNameClosures.scala | 54 +++------------- .../tools/dotc/transform/ElimByName.scala | 4 +- .../dotc/transform/TransformByNameApply.scala | 61 +++++++++++++++++++ tests/neg/tailcall/t1672b.scala | 2 +- 5 files changed, 73 insertions(+), 51 deletions(-) create mode 100644 compiler/src/dotty/tools/dotc/transform/TransformByNameApply.scala diff --git a/compiler/src/dotty/tools/dotc/Compiler.scala b/compiler/src/dotty/tools/dotc/Compiler.scala index 0d3fb5821501..b3882d059ec7 100644 --- a/compiler/src/dotty/tools/dotc/Compiler.scala +++ b/compiler/src/dotty/tools/dotc/Compiler.scala @@ -55,6 +55,7 @@ class Compiler { new ExtensionMethods, // Expand methods of value classes with extension methods new ExpandSAMs, // Expand single abstract method closures to anonymous classes new TailRec, // Rewrite tail recursion to loops + new ByNameClosures), List( // Expand by-name arguments new LiftTry, // Put try expressions that might execute on non-empty stacks into their own methods new ClassOf), // Expand `Predef.classOf` calls. List(new TryCatchPatterns, // Compile cases in try/catch @@ -69,7 +70,7 @@ class Compiler { new SeqLiterals, // Express vararg arguments as arrays new InterceptedMethods, // Special handling of `==`, `|=`, `getClass` methods new Getters, // Replace non-private vals and vars with getter defs (fields are added later) - new ElimByName, // Expand by-name parameters and arguments + new ElimByName, // Expand by-name parameters new AugmentScala2Traits, // Expand traits defined in Scala 2.11 to simulate old-style rewritings new ResolveSuper, // Implement super accessors and add forwarders to trait methods new PrimitiveForwarders, // Add forwarders to trait methods that have a mismatch between generic and primitives diff --git a/compiler/src/dotty/tools/dotc/transform/ByNameClosures.scala b/compiler/src/dotty/tools/dotc/transform/ByNameClosures.scala index 47d051b45759..7b599e3c1b61 100644 --- a/compiler/src/dotty/tools/dotc/transform/ByNameClosures.scala +++ b/compiler/src/dotty/tools/dotc/transform/ByNameClosures.scala @@ -9,8 +9,8 @@ import Contexts._ import Types._ import Flags._ import Decorators._ +import DenotTransformers.IdentityDenotTransformer import core.StdNames.nme -import ast.Trees._ /** This phase translates arguments to call-by-name parameters, using the rules * @@ -24,55 +24,15 @@ import ast.Trees._ * * is a synthetic method defined in Definitions. Erasure will later strip these DummyApply wrappers. */ -class ByNameClosures extends MiniPhaseTransform { thisTransformer => +class ByNameClosures extends TransformByNameApply with IdentityDenotTransformer { thisTransformer => import ast.tpd._ override def phaseName: String = "bynameClosures" - /** The info of the tree's symbol at phase Nullarify (i.e. before transformation) */ - private def originalDenotation(tree: Tree)(implicit ctx: Context) = - tree.symbol.denot(ctx.withPhase(thisTransformer)) - - /** If denotation had an ExprType before, it now gets a function type */ - protected def exprBecomesFunction(symd: SymDenotation)(implicit ctx: Context) = - (symd is Param) || (symd is (ParamAccessor, butNot = Method)) - - protected def isByNameRef(tree: Tree)(implicit ctx: Context) = { - val origDenot = originalDenotation(tree) - origDenot.info.isInstanceOf[ExprType] && exprBecomesFunction(origDenot) - } - - override def transformApply(tree: Apply)(implicit ctx: Context, info: TransformerInfo): Tree = - ctx.traceIndented(s"transforming ${tree.show} at phase ${ctx.phase}", show = true) { - - def transformArg(arg: Tree, formal: Type): Tree = formal.dealias match { - case formalExpr: ExprType => - var argType = arg.tpe.widenIfUnstable - if (defn.isBottomType(argType)) argType = formal.widenExpr - def wrap(arg: Tree) = ref(defn.dummyApply).appliedToType(argType).appliedTo(arg) - arg match { - case Apply(Select(qual, nme.apply), Nil) - if qual.tpe.derivesFrom(defn.FunctionClass(0)) && isPureExpr(qual) => - wrap(qual) - case _ => - if (isByNameRef(arg) || arg.symbol == defn.dummyApply) arg - else { - val inSuper = if (ctx.mode.is(Mode.InSuperCall)) InSuperCall else EmptyFlags - val meth = ctx.newSymbol( - ctx.owner, nme.ANON_FUN, Synthetic | Method | inSuper, MethodType(Nil, Nil, argType)) - wrap(Closure(meth, _ => - atGroupEnd { implicit ctx: Context => - arg.changeOwner(ctx.owner, meth) - } - )) - } - } - case _ => - arg - } - - val mt @ MethodType(_) = tree.fun.tpe.widen - val args1 = tree.args.zipWithConserve(mt.paramInfos)(transformArg) - cpy.Apply(tree)(tree.fun, args1) + override def mkClosure(arg: Tree, argType: Type)(implicit ctx: Context): Tree = { + val inSuper = if (ctx.mode.is(Mode.InSuperCall)) InSuperCall else EmptyFlags + val meth = ctx.newSymbol( + ctx.owner, nme.ANON_FUN, Synthetic | Method | inSuper, MethodType(Nil, Nil, argType)) + Closure(meth, _ => arg.changeOwnerAfter(ctx.owner, meth, thisTransformer)) } } diff --git a/compiler/src/dotty/tools/dotc/transform/ElimByName.scala b/compiler/src/dotty/tools/dotc/transform/ElimByName.scala index 268054735ce7..927fee69c0c2 100644 --- a/compiler/src/dotty/tools/dotc/transform/ElimByName.scala +++ b/compiler/src/dotty/tools/dotc/transform/ElimByName.scala @@ -3,7 +3,7 @@ package transform import TreeTransforms._ import core._ -import DenotTransformers._ +import DenotTransformers.InfoTransformer import Symbols._ import SymDenotations._ import Contexts._ @@ -38,7 +38,7 @@ import ast.Trees._ * Option 2: Merge ElimByName with erasure, or have it run immediately before. This has not been * tried yet. */ -class ElimByName extends ByNameClosures with InfoTransformer { thisTransformer => +class ElimByName extends TransformByNameApply with InfoTransformer { thisTransformer => import ast.tpd._ override def phaseName: String = "elimByName" diff --git a/compiler/src/dotty/tools/dotc/transform/TransformByNameApply.scala b/compiler/src/dotty/tools/dotc/transform/TransformByNameApply.scala new file mode 100644 index 000000000000..17dbf0b3b314 --- /dev/null +++ b/compiler/src/dotty/tools/dotc/transform/TransformByNameApply.scala @@ -0,0 +1,61 @@ +package dotty.tools.dotc +package transform + +import TreeTransforms._ +import core._ +import Symbols._ +import SymDenotations._ +import Contexts._ +import Types._ +import Flags._ +import Decorators._ +import DenotTransformers._ +import core.StdNames.nme +import ast.Trees._ + +/** Abstract base class of ByNameClosures and ELimByName, factoring out the + * common functionality to transform arguments of by-name parameters. + */ +abstract class TransformByNameApply extends MiniPhaseTransform { thisTransformer: DenotTransformer => + import ast.tpd._ + + /** The info of the tree's symbol at phase Nullarify (i.e. before transformation) */ + private def originalDenotation(tree: Tree)(implicit ctx: Context) = + tree.symbol.denot(ctx.withPhase(thisTransformer)) + + /** If denotation had an ExprType before, it now gets a function type */ + protected def exprBecomesFunction(symd: SymDenotation)(implicit ctx: Context) = + (symd is Param) || (symd is (ParamAccessor, butNot = Method)) + + protected def isByNameRef(tree: Tree)(implicit ctx: Context) = { + val origDenot = originalDenotation(tree) + origDenot.info.isInstanceOf[ExprType] && exprBecomesFunction(origDenot) + } + + def mkClosure(arg: Tree, argType: Type)(implicit ctx: Context): Tree = unsupported("mkClosure") + + override def transformApply(tree: Apply)(implicit ctx: Context, info: TransformerInfo): Tree = + ctx.traceIndented(s"transforming ${tree.show} at phase ${ctx.phase}", show = true) { + + def transformArg(arg: Tree, formal: Type): Tree = formal.dealias match { + case formalExpr: ExprType => + var argType = arg.tpe.widenIfUnstable + if (defn.isBottomType(argType)) argType = formal.widenExpr + def wrap(arg: Tree) = ref(defn.dummyApply).appliedToType(argType).appliedTo(arg) + arg match { + case Apply(Select(qual, nme.apply), Nil) + if qual.tpe.derivesFrom(defn.FunctionClass(0)) && isPureExpr(qual) => + wrap(qual) + case _ => + if (isByNameRef(arg) || arg.symbol == defn.dummyApply) arg + else wrap(mkClosure(arg, argType)) + } + case _ => + arg + } + + val mt @ MethodType(_) = tree.fun.tpe.widen + val args1 = tree.args.zipWithConserve(mt.paramInfos)(transformArg) + cpy.Apply(tree)(tree.fun, args1) + } +} diff --git a/tests/neg/tailcall/t1672b.scala b/tests/neg/tailcall/t1672b.scala index 1ae3d8af86d9..c439d60835ca 100644 --- a/tests/neg/tailcall/t1672b.scala +++ b/tests/neg/tailcall/t1672b.scala @@ -46,7 +46,7 @@ object Test1772B { else 1 + (try { throw new RuntimeException } catch { - case _: Throwable => bar(i - 1) // old-error + case _: Throwable => bar(i - 1) // error }) } } From 4b0dc8badeedc7399bfcffa8cdb7562d823c51ac Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sun, 16 Apr 2017 11:04:53 +0200 Subject: [PATCH 04/10] Handle closures in SelectStatic Used to fail in backend when reference of closure was to a static method. The test case failing is 2127.scala --- compiler/src/dotty/tools/dotc/transform/SelectStatic.scala | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/compiler/src/dotty/tools/dotc/transform/SelectStatic.scala b/compiler/src/dotty/tools/dotc/transform/SelectStatic.scala index 5d60bb984102..b90c6ed37620 100644 --- a/compiler/src/dotty/tools/dotc/transform/SelectStatic.scala +++ b/compiler/src/dotty/tools/dotc/transform/SelectStatic.scala @@ -43,6 +43,8 @@ class SelectStatic extends MiniPhaseTransform with IdentityDenotTransformer { th Block(stats, Apply(qual, nm)) case TypeApply(Block(stats, qual), nm) => Block(stats, TypeApply(qual, nm)) + case Closure(env, Block(stats, qual), tpt) => + Block(stats, Closure(env, qual, tpt)) case _ => t } @@ -53,4 +55,8 @@ class SelectStatic extends MiniPhaseTransform with IdentityDenotTransformer { th override def transformTypeApply(tree: tpd.TypeApply)(implicit ctx: Context, info: TransformerInfo): tpd.Tree = { normalize(tree) } + + override def transformClosure(tree: tpd.Closure)(implicit ctx: Context, info: TransformerInfo): tpd.Tree = { + normalize(tree) + } } From 495f2f74c0f1a28062f7878bc99248005c0a049e Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sun, 16 Apr 2017 11:31:21 +0200 Subject: [PATCH 05/10] New phase: HoistSuperArgs Hoist complex supercall arguments out of the class they appear in. This avoids a lot of complexity later on in other transformation phases. --- compiler/src/dotty/tools/dotc/Compiler.scala | 1 + .../src/dotty/tools/dotc/core/NameKinds.scala | 1 + .../tools/dotc/transform/HoistSuperArgs.scala | 171 ++++++++++++++++++ 3 files changed, 173 insertions(+) create mode 100644 compiler/src/dotty/tools/dotc/transform/HoistSuperArgs.scala diff --git a/compiler/src/dotty/tools/dotc/Compiler.scala b/compiler/src/dotty/tools/dotc/Compiler.scala index b3882d059ec7..ccf1fae20bc3 100644 --- a/compiler/src/dotty/tools/dotc/Compiler.scala +++ b/compiler/src/dotty/tools/dotc/Compiler.scala @@ -57,6 +57,7 @@ class Compiler { new TailRec, // Rewrite tail recursion to loops new ByNameClosures), List( // Expand by-name arguments new LiftTry, // Put try expressions that might execute on non-empty stacks into their own methods + new HoistSuperArgs, // Hoist complex arguments of supercalls to enclosing scope new ClassOf), // Expand `Predef.classOf` calls. List(new TryCatchPatterns, // Compile cases in try/catch new PatternMatcher, // Compile pattern matches diff --git a/compiler/src/dotty/tools/dotc/core/NameKinds.scala b/compiler/src/dotty/tools/dotc/core/NameKinds.scala index 21e534a6b05f..95ff963653fa 100644 --- a/compiler/src/dotty/tools/dotc/core/NameKinds.scala +++ b/compiler/src/dotty/tools/dotc/core/NameKinds.scala @@ -221,6 +221,7 @@ object NameKinds { val ExceptionBinderName = new UniqueNameKind("ex") val SkolemName = new UniqueNameKind("?") val LiftedTreeName = new UniqueNameKind("liftedTree") + val SuperArgName = new UniqueNameKind("$superArg$") val UniqueExtMethName = new UniqueNameKind("$extension") { override def unmangle(name: SimpleTermName): TermName = { diff --git a/compiler/src/dotty/tools/dotc/transform/HoistSuperArgs.scala b/compiler/src/dotty/tools/dotc/transform/HoistSuperArgs.scala new file mode 100644 index 000000000000..1b8d22a6303b --- /dev/null +++ b/compiler/src/dotty/tools/dotc/transform/HoistSuperArgs.scala @@ -0,0 +1,171 @@ +package dotty.tools.dotc +package transform + +import TreeTransforms._ +import core.DenotTransformers._ +import core.Symbols._ +import core.Contexts._ +import ast.TreeTypeMap +import core.Types._ +import core.Flags._ +import core.Decorators._ +import collection.mutable +import ast.Trees._ +import core.Names.TermName +import core.NameKinds.SuperArgName +import SymUtils._ + +/** This phase hoists complex arguments to supercalls out of the enclosing class. + * Example: + * + * class B(y: Int) extends A({ def f(x: Int) = x * x; f(y)}) + * + * is translated to + * + * class B(y: Int) extends A(B#B$superArg$1(this.y)) { + * private def B$superArg$1(y: Int): Int = { + * def f(x: Int): Int = x.*(x); f(y) + * } + * } + * + * An argument is complex if it contains a method or template definition, a this or a new, + * or it contains an identifier which needs a `this` prefix to be accessed. This is the case + * if the identifer neither a global reference nor a reference to a parameter of the enclosing class. + * @see needsHoist for an implementation. + * + * A hoisted argument definition gets the parameters of the class it is hoisted from + * as method parameters. The definition is installed in the scope enclosing the class, + * or, if that is a package, it is made a static method of the class itself. + */ +class HoistSuperArgs extends MiniPhaseTransform with IdentityDenotTransformer { thisTransform => + import ast.tpd._ + + def phaseName = "hoistSuperArgs" + + /** Hoist complex arguments in super call `parent` out of the class. + * @return A pair consisting of the transformed super call and a list of super argument + * defininitions. + */ + def hoistSuperArgs(parent: Tree, cls: Symbol)(implicit ctx: Context): (Tree, List[DefDef]) = { + lazy val constr = cls.primaryConstructor + lazy val allParams = // The parameters that can be accessed in the supercall + cls.info.decls.filter(d => d.is(TypeParam) || d.is(TermParamAccessor)) + val superArgDefs = new mutable.ListBuffer[DefDef] + + /** The parameter references defined by the primary constructor info */ + def allParamRefs(tp: Type): List[ParamRef] = tp match { + case tp: LambdaType => tp.paramRefs ++ allParamRefs(tp.resultType) + case _ => Nil + } + + /** Splice `restpe` in final result type position of `tp` */ + def replaceResult(tp: Type, restpe: Type): Type = tp match { + case tp: LambdaType => + tp.derivedLambdaType(resType = replaceResult(tp.resultType, restpe)) + case _ => restpe + } + + /** A method representing a hoisted supercall argument */ + def newSuperArgMethod(argType: Type) = { + val (staticFlag, methOwner) = + if (cls.owner.is(Package)) (JavaStatic, cls) else (EmptyFlags, cls.owner) + val argTypeWrtConstr = argType.subst(allParams, allParamRefs(constr.info)) + // argType with references to paramRefs of the primary constructor instead of + // local parameter accessors + val meth = ctx.newSymbol( + owner = methOwner, + name = SuperArgName.fresh(cls.name.toTermName), + flags = Synthetic | Private | Method | staticFlag, + info = replaceResult(constr.info, argTypeWrtConstr), + coord = constr.coord) + if (methOwner.isClass) meth.enteredAfter(thisTransform) else meth + } + + /** Super call argument is complex, needs to be hoisted */ + def needsHoist(tree: Tree) = tree match { + case _: DefDef => true + case _: Template => true + case _: This => !tree.symbol.isStaticOwner + case _: New => !tree.tpe.typeSymbol.isStatic + case _: RefTree => + var owner = tree.symbol.effectiveOwner + if (owner.isLocalDummy) owner = owner.owner + tree.isTerm && !owner.isStaticOwner && owner != cls + case _ => false + } + + /** If argument is complex, hoist it out into its own method and refer to the + * method instead. + * @return The argument after possible hoisting + * Might append a method definition to `superArgs` as a side effect. + */ + def hoistSuperArg(arg: Tree) = + if (arg.existsSubTree(needsHoist)) { + val superMeth = newSuperArgMethod(arg.tpe) + val superArgDef = polyDefDef(superMeth, trefs => vrefss => { + val paramSyms = trefs.map(_.typeSymbol) ::: vrefss.flatten.map(_.symbol) + val tmap = new TreeTypeMap( + typeMap = new TypeMap { + def apply(tp: Type) = tp match { + case tp: NamedType if tp.symbol.owner == cls && tp.symbol.is(ParamOrAccessor) => + val mappedSym = allParams.zip(paramSyms).toMap.apply(tp.symbol) + if (tp.symbol.isType) mappedSym.typeRef else mappedSym.termRef + case _ => + mapOver(tp) + } + }, + treeMap = { + case tree: RefTree if paramSyms.contains(tree.symbol) => + cpy.Ident(tree)(tree.name).withType(tree.tpe) + case tree => + tree + } + ) + tmap(arg).changeOwnerAfter(constr, superMeth, thisTransform) + }) + superArgDefs += superArgDef + def termParamRefs(tp: Type): List[List[Tree]] = tp match { + case tp: PolyType => + termParamRefs(tp.resultType) + case tp: MethodType => + def paramRef(name: TermName) = + ref(cls.info.decl(name).suchThat(_.is(ParamAccessor)).symbol) + tp.paramNames.map(paramRef) :: termParamRefs(tp.resultType) + case _ => + Nil + } + val res = ref(superMeth) + .appliedToTypes(cls.typeParams.map(_.typeRef)) + .appliedToArgss(termParamRefs(constr.info)) + ctx.log(i"hoist $arg, cls = $cls = $res") + res + } + else arg + + def recur(tree: Tree): Tree = tree match { + case Apply(fn, args) => cpy.Apply(tree)(recur(fn), args.mapconserve(hoistSuperArg)) + case _ => tree + } + (recur(parent), superArgDefs.toList) + } + + override def transformTypeDef(tdef: TypeDef)(implicit ctx: Context, info: TransformerInfo): Tree = + tdef.rhs match { + case impl @ Template(_, superCall :: others, _, _) => + val cls = tdef.symbol + val (hoisted, superArgDefs) = hoistSuperArgs(superCall, cls) + if (superArgDefs.isEmpty) tdef + else { + val (staticSuperArgDefs, enclSuperArgDefs) = + superArgDefs.partition(_.symbol.is(JavaStatic)) + flatTree( + cpy.TypeDef(tdef)( + rhs = cpy.Template(impl)( + parents = hoisted :: others, + body = impl.body ++ staticSuperArgDefs)) :: + enclSuperArgDefs) + } + case _ => + tdef + } +} From 30fb662f7e68bdd7c16d35c59d967d7660900c69 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sun, 16 Apr 2017 12:01:24 +0200 Subject: [PATCH 06/10] Drop InSuperCall flag and special handling involved in it. Require the phases that used ti to be after phase HoistInSuperCall instead. --- compiler/src/dotty/tools/dotc/core/Flags.scala | 8 ++------ compiler/src/dotty/tools/dotc/core/SymDenotations.scala | 4 ++-- .../src/dotty/tools/dotc/core/tasty/TastyFormat.scala | 6 +----- .../src/dotty/tools/dotc/core/tasty/TreePickler.scala | 1 - .../src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala | 1 - .../src/dotty/tools/dotc/transform/Constructors.scala | 5 ++--- compiler/src/dotty/tools/dotc/transform/ElimByName.scala | 1 + .../src/dotty/tools/dotc/transform/ExplicitOuter.scala | 2 +- compiler/src/dotty/tools/dotc/transform/LambdaLift.scala | 8 ++------ compiler/src/dotty/tools/dotc/typer/Namer.scala | 8 ++------ 10 files changed, 13 insertions(+), 31 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/Flags.scala b/compiler/src/dotty/tools/dotc/core/Flags.scala index 84072cd50b85..c1929d882b21 100644 --- a/compiler/src/dotty/tools/dotc/core/Flags.scala +++ b/compiler/src/dotty/tools/dotc/core/Flags.scala @@ -372,9 +372,6 @@ object Flags { /** Symbol always defines a fresh named type */ final val Fresh = commonFlag(45, "") - /** Symbol is defined in a super call */ - final val InSuperCall = commonFlag(46, "") - /** Denotation is in train of being loaded and completed, used to catch cyclic dependencies */ final val Touched = commonFlag(48, "") @@ -451,7 +448,7 @@ object Flags { /** Flags guaranteed to be set upon symbol creation */ final val FromStartFlags = Module | Package | Deferred | MethodOrHKCommon | Param | ParamAccessor | - Scala2ExistentialCommon | Mutable.toCommonFlags | InSuperCall | Touched | JavaStatic | + Scala2ExistentialCommon | Mutable.toCommonFlags | Touched | JavaStatic | CovariantOrOuter | ContravariantOrLabel | CaseAccessorOrBaseTypeArg | Fresh | Frozen | Erroneous | ImplicitCommon | Permanent | Synthetic | SuperAccessorOrScala2x | Inline @@ -511,8 +508,7 @@ object Flags { Accessor | AbsOverride | Stable | Captured | Synchronized /** Flags that can apply to a module class */ - final val RetainedModuleClassFlags: FlagSet = RetainedModuleValAndClassFlags | - InSuperCall | ImplClass + final val RetainedModuleClassFlags: FlagSet = RetainedModuleValAndClassFlags | ImplClass /** Packages and package classes always have these flags set */ final val PackageCreationFlags = diff --git a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala index e5cc94883dd1..f0d71a281504 100644 --- a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala +++ b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala @@ -851,7 +851,7 @@ object SymDenotations { /** The class containing this denotation. * If this denotation is already a class, return itself - * Definitions flagged with InSuperCall are treated specially. + * Definitions flagged with JavaStatic are treated specially. * Their enclosing class is not the lexically enclosing class, * but in turn the enclosing class of the latter. This reflects * the context created by `Context#superCallContext`, `Context#thisCallArgContext` @@ -862,7 +862,7 @@ object SymDenotations { */ final def enclosingClass(implicit ctx: Context): Symbol = { def enclClass(sym: Symbol, skip: Boolean): Symbol = { - def newSkip = sym.is(InSuperCall) || sym.is(JavaStaticTerm) + def newSkip = sym.is(JavaStaticTerm) if (!sym.exists) NoSymbol else if (sym.isClass) diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TastyFormat.scala b/compiler/src/dotty/tools/dotc/core/tasty/TastyFormat.scala index f03e279c65a8..b564c09e996d 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TastyFormat.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TastyFormat.scala @@ -186,7 +186,6 @@ Standard-Section: "ASTs" TopLevelStat* CONTRAVARIANT // type param marked “-” SCALA2X // Imported from Scala2.x DEFAULTparameterized // Method with default params - INSUPERCALL // defined in the argument of a constructor supercall STABLE // Method that is assumed to be stable Annotation Annotation = ANNOTATION Length tycon_Type fullAnnotation_Term @@ -278,8 +277,7 @@ object TastyFormat { final val CONTRAVARIANT = 28 final val SCALA2X = 29 final val DEFAULTparameterized = 30 - final val INSUPERCALL = 31 - final val STABLE = 32 + final val STABLE = 31 final val SHARED = 64 final val TERMREFdirect = 65 @@ -403,7 +401,6 @@ object TastyFormat { | CONTRAVARIANT | SCALA2X | DEFAULTparameterized - | INSUPERCALL | STABLE | ANNOTATION | PRIVATEqualified @@ -469,7 +466,6 @@ object TastyFormat { case CONTRAVARIANT => "CONTRAVARIANT" case SCALA2X => "SCALA2X" case DEFAULTparameterized => "DEFAULTparameterized" - case INSUPERCALL => "INSUPERCALL" case STABLE => "STABLE" case SHARED => "SHARED" diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala b/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala index 5d33738c2b59..8535d0cc85cd 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala @@ -577,7 +577,6 @@ class TreePickler(pickler: TastyPickler) { if (flags is Synthetic) writeByte(SYNTHETIC) if (flags is Artifact) writeByte(ARTIFACT) if (flags is Scala2x) writeByte(SCALA2X) - if (flags is InSuperCall) writeByte(INSUPERCALL) if (sym.isTerm) { if (flags is Implicit) writeByte(IMPLICIT) if ((flags is Lazy) && !(sym is Module)) writeByte(LAZY) diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala index 2908c541e5e9..460af7c0ceea 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala @@ -510,7 +510,6 @@ class TreeUnpickler(reader: TastyReader, nameAtRef: NameRef => TermName, posUnpi case CONTRAVARIANT => addFlag(Contravariant) case SCALA2X => addFlag(Scala2x) case DEFAULTparameterized => addFlag(DefaultParameterized) - case INSUPERCALL => addFlag(InSuperCall) case STABLE => addFlag(Stable) case PRIVATEqualified => readByte() diff --git a/compiler/src/dotty/tools/dotc/transform/Constructors.scala b/compiler/src/dotty/tools/dotc/transform/Constructors.scala index db850e944a25..59582b4d69d4 100644 --- a/compiler/src/dotty/tools/dotc/transform/Constructors.scala +++ b/compiler/src/dotty/tools/dotc/transform/Constructors.scala @@ -30,8 +30,7 @@ class Constructors extends MiniPhaseTransform with IdentityDenotTransformer { th import tpd._ override def phaseName: String = "constructors" - override def runsAfter: Set[Class[_ <: Phase]] = Set(classOf[Memoize]) - + override def runsAfter: Set[Class[_ <: Phase]] = Set(classOf[Memoize], classOf[HoistSuperArgs]) // Collect all private parameter accessors and value definitions that need // to be retained. There are several reasons why a parameter accessor or @@ -103,7 +102,7 @@ class Constructors extends MiniPhaseTransform with IdentityDenotTransformer { th * outer link, so no parameter accessors need to be rewired to parameters */ private def noDirectRefsFrom(tree: Tree)(implicit ctx: Context) = - tree.isDef && tree.symbol.isClass && !tree.symbol.is(InSuperCall) + tree.isDef && tree.symbol.isClass /** Class members that can be eliminated if referenced only from their own * constructor. diff --git a/compiler/src/dotty/tools/dotc/transform/ElimByName.scala b/compiler/src/dotty/tools/dotc/transform/ElimByName.scala index 927fee69c0c2..07790f7e7ec1 100644 --- a/compiler/src/dotty/tools/dotc/transform/ElimByName.scala +++ b/compiler/src/dotty/tools/dotc/transform/ElimByName.scala @@ -43,6 +43,7 @@ class ElimByName extends TransformByNameApply with InfoTransformer { thisTransfo override def phaseName: String = "elimByName" + override def runsAfter = Set(classOf[HoistSuperArgs]) override def runsAfterGroupsOf = Set(classOf[Splitter]) // assumes idents and selects have symbols; interferes with splitter distribution // that's why it's "after group". diff --git a/compiler/src/dotty/tools/dotc/transform/ExplicitOuter.scala b/compiler/src/dotty/tools/dotc/transform/ExplicitOuter.scala index 7ad7fb348447..60086031edff 100644 --- a/compiler/src/dotty/tools/dotc/transform/ExplicitOuter.scala +++ b/compiler/src/dotty/tools/dotc/transform/ExplicitOuter.scala @@ -46,7 +46,7 @@ class ExplicitOuter extends MiniPhaseTransform with InfoTransformer { thisTransf /** List of names of phases that should have finished their processing of all compilation units * before this phase starts */ - override def runsAfter: Set[Class[_ <: Phase]] = Set(classOf[PatternMatcher]) + override def runsAfter: Set[Class[_ <: Phase]] = Set(classOf[PatternMatcher], classOf[HoistSuperArgs]) /** Add outer accessors if a class always needs an outer pointer */ override def transformInfo(tp: Type, sym: Symbol)(implicit ctx: Context) = tp match { diff --git a/compiler/src/dotty/tools/dotc/transform/LambdaLift.scala b/compiler/src/dotty/tools/dotc/transform/LambdaLift.scala index 5aabe1e0c661..ba764a91069c 100644 --- a/compiler/src/dotty/tools/dotc/transform/LambdaLift.scala +++ b/compiler/src/dotty/tools/dotc/transform/LambdaLift.scala @@ -66,7 +66,7 @@ class LambdaLift extends MiniPhase with IdentityDenotTransformer { thisTransform override def relaxedTyping = true - override def runsAfter: Set[Class[_ <: Phase]] = Set(classOf[Constructors]) + override def runsAfter: Set[Class[_ <: Phase]] = Set(classOf[Constructors], classOf[HoistSuperArgs]) // Constructors has to happen before LambdaLift because the lambda lift logic // becomes simpler if it can assume that parameter accessors have already been // converted to parameters in super calls. Without this it is very hard to get @@ -146,14 +146,10 @@ class LambdaLift extends MiniPhase with IdentityDenotTransformer { thisTransform if (sym.maybeOwner.isTerm && owner.isProperlyContainedIn(liftedOwner(sym)) && owner != sym) { - if (sym.is(InSuperCall) && owner.isProperlyContainedIn(sym.enclosingClass)) - narrowLiftedOwner(sym, sym.enclosingClass) - else { ctx.log(i"narrow lifted $sym to $owner") changedLiftedOwner = true liftedOwner(sym) = owner } - } /** Mark symbol `sym` as being free in `enclosure`, unless `sym` is defined * in `enclosure` or there is an intermediate class properly containing `enclosure` @@ -387,7 +383,7 @@ class LambdaLift extends MiniPhase with IdentityDenotTransformer { thisTransform local.copySymDenotation( owner = newOwner, name = newName(local), - initFlags = local.flags &~ (InSuperCall | Module) | Private | maybeStatic, + initFlags = local.flags &~ Module | Private | maybeStatic, // drop Module because class is no longer a singleton in the lifted context. info = liftedInfo(local)).installAfter(thisTransform) } diff --git a/compiler/src/dotty/tools/dotc/typer/Namer.scala b/compiler/src/dotty/tools/dotc/typer/Namer.scala index da9f9f6ac218..23de2a089b93 100644 --- a/compiler/src/dotty/tools/dotc/typer/Namer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Namer.scala @@ -278,14 +278,12 @@ class Namer { typer: Typer => else name } - val inSuperCall = if (ctx.mode is Mode.InSuperCall) InSuperCall else EmptyFlags - tree match { case tree: TypeDef if tree.isClassDef => val name = checkNoConflict(tree.name.encode).toTypeName val flags = checkFlags(tree.mods.flags &~ Implicit) val cls = recordSym(ctx.newClassSymbol( - ctx.owner, name, flags | inSuperCall, + ctx.owner, name, flags, cls => adjustIfModule(new ClassCompleter(cls, tree)(ctx), tree), privateWithinClass(tree.mods), tree.namePos, ctx.source.file), tree) cls.completer.asInstanceOf[ClassCompleter].init() @@ -296,8 +294,6 @@ class Namer { typer: Typer => val isDeferred = lacksDefinition(tree) val deferred = if (isDeferred) Deferred else EmptyFlags val method = if (tree.isInstanceOf[DefDef]) Method else EmptyFlags - val inSuperCall1 = if (tree.mods is ParamOrAccessor) EmptyFlags else inSuperCall - // suppress inSuperCall for constructor parameters val higherKinded = tree match { case TypeDef(_, LambdaTypeTree(_, _)) if isDeferred => HigherKinded case _ => EmptyFlags @@ -320,7 +316,7 @@ class Namer { typer: Typer => } recordSym(ctx.newSymbol( - ctx.owner, name, flags | deferred | method | higherKinded | inSuperCall1, + ctx.owner, name, flags | deferred | method | higherKinded, adjustIfModule(completer, tree), privateWithinClass(tree.mods), tree.namePos), tree) case tree: Import => From fb843ba1d64d2cbc92a379c710b9a2798210a83a Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Fri, 21 Apr 2017 16:14:38 +0200 Subject: [PATCH 07/10] Integration with HoistSuperArgs Do the necessary changes to combine HoistSuperArgs and ByNameClosures. Also, polishings. --- compiler/src/dotty/tools/dotc/Compiler.scala | 4 +-- .../dotty/tools/dotc/core/Definitions.scala | 8 +++--- .../src/dotty/tools/dotc/core/StdNames.scala | 2 +- .../tools/dotc/transform/ByNameClosures.scala | 13 +++++---- .../tools/dotc/transform/ElimByName.scala | 7 ++--- .../dotty/tools/dotc/transform/Erasure.scala | 2 +- .../tools/dotc/transform/HoistSuperArgs.scala | 27 ++++++++++++------- .../dotc/transform/TransformByNameApply.scala | 8 +++--- tests/neg/tailcall/t1672b.scala | 2 +- 9 files changed, 40 insertions(+), 33 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/Compiler.scala b/compiler/src/dotty/tools/dotc/Compiler.scala index ccf1fae20bc3..8ee016117de3 100644 --- a/compiler/src/dotty/tools/dotc/Compiler.scala +++ b/compiler/src/dotty/tools/dotc/Compiler.scala @@ -55,7 +55,7 @@ class Compiler { new ExtensionMethods, // Expand methods of value classes with extension methods new ExpandSAMs, // Expand single abstract method closures to anonymous classes new TailRec, // Rewrite tail recursion to loops - new ByNameClosures), List( // Expand by-name arguments + new ByNameClosures, // Expand arguments to by-name parameters to closures new LiftTry, // Put try expressions that might execute on non-empty stacks into their own methods new HoistSuperArgs, // Hoist complex arguments of supercalls to enclosing scope new ClassOf), // Expand `Predef.classOf` calls. @@ -71,7 +71,7 @@ class Compiler { new SeqLiterals, // Express vararg arguments as arrays new InterceptedMethods, // Special handling of `==`, `|=`, `getClass` methods new Getters, // Replace non-private vals and vars with getter defs (fields are added later) - new ElimByName, // Expand by-name parameters + new ElimByName, // Expand by-name parameter references new AugmentScala2Traits, // Expand traits defined in Scala 2.11 to simulate old-style rewritings new ResolveSuper, // Implement super accessors and add forwarders to trait methods new PrimitiveForwarders, // Add forwarders to trait methods that have a mismatch between generic and primitives diff --git a/compiler/src/dotty/tools/dotc/core/Definitions.scala b/compiler/src/dotty/tools/dotc/core/Definitions.scala index eee6ba7850b4..4abaf3bc788c 100644 --- a/compiler/src/dotty/tools/dotc/core/Definitions.scala +++ b/compiler/src/dotty/tools/dotc/core/Definitions.scala @@ -280,9 +280,11 @@ class Definitions { def ObjectMethods = List(Object_eq, Object_ne, Object_synchronized, Object_clone, Object_finalize, Object_notify, Object_notifyAll, Object_wait, Object_waitL, Object_waitLI) - /** Dummy method needed by elimByName */ - lazy val dummyApply = enterPolyMethod( - OpsPackageClass, nme.dummyApply, 1, + /** Marker method to indicate an argument to a call-by-name parameter. + * Created by byNameClosures and elimByName, eliminated by Erasure, + */ + lazy val cbnArg = enterPolyMethod( + OpsPackageClass, nme.cbnArg, 1, pt => MethodType(List(FunctionOf(Nil, TypeParamRef(pt, 0))), TypeParamRef(pt, 0))) /** Method representing a throw */ diff --git a/compiler/src/dotty/tools/dotc/core/StdNames.scala b/compiler/src/dotty/tools/dotc/core/StdNames.scala index 92befdacbd9f..bf8a9924780d 100644 --- a/compiler/src/dotty/tools/dotc/core/StdNames.scala +++ b/compiler/src/dotty/tools/dotc/core/StdNames.scala @@ -378,6 +378,7 @@ object StdNames { val build : N = "build" val bytes: N = "bytes" val canEqual_ : N = "canEqual" + val cbnArg: N = "" val checkInitialized: N = "checkInitialized" val ClassManifestFactory: N = "ClassManifestFactory" val classOf: N = "classOf" @@ -391,7 +392,6 @@ object StdNames { val delayedInitArg: N = "delayedInit$body" val drop: N = "drop" val dynamics: N = "dynamics" - val dummyApply: N = "" val elem: N = "elem" val emptyValDef: N = "emptyValDef" val ensureAccessible : N = "ensureAccessible" diff --git a/compiler/src/dotty/tools/dotc/transform/ByNameClosures.scala b/compiler/src/dotty/tools/dotc/transform/ByNameClosures.scala index 7b599e3c1b61..76106e235599 100644 --- a/compiler/src/dotty/tools/dotc/transform/ByNameClosures.scala +++ b/compiler/src/dotty/tools/dotc/transform/ByNameClosures.scala @@ -15,24 +15,23 @@ import core.StdNames.nme /** This phase translates arguments to call-by-name parameters, using the rules * * x ==> x if x is a => parameter - * e.apply() ==> DummyApply(e) if e is pure - * e ==> DummyApply(() => e) for all other arguments + * e.apply() ==> (e) if e is pure + * e ==> (() => e) for all other arguments * * where * - * DummyApply: [T](() => T): T + * : [T](() => T): T * - * is a synthetic method defined in Definitions. Erasure will later strip these DummyApply wrappers. + * is a synthetic method defined in Definitions. Erasure will later strip the wrappers. */ class ByNameClosures extends TransformByNameApply with IdentityDenotTransformer { thisTransformer => import ast.tpd._ - override def phaseName: String = "bynameClosures" + override def phaseName: String = "byNameClosures" override def mkClosure(arg: Tree, argType: Type)(implicit ctx: Context): Tree = { - val inSuper = if (ctx.mode.is(Mode.InSuperCall)) InSuperCall else EmptyFlags val meth = ctx.newSymbol( - ctx.owner, nme.ANON_FUN, Synthetic | Method | inSuper, MethodType(Nil, Nil, argType)) + ctx.owner, nme.ANON_FUN, Synthetic | Method, MethodType(Nil, Nil, argType)) Closure(meth, _ => arg.changeOwnerAfter(ctx.owner, meth, thisTransformer)) } } diff --git a/compiler/src/dotty/tools/dotc/transform/ElimByName.scala b/compiler/src/dotty/tools/dotc/transform/ElimByName.scala index 07790f7e7ec1..9b3008f9147f 100644 --- a/compiler/src/dotty/tools/dotc/transform/ElimByName.scala +++ b/compiler/src/dotty/tools/dotc/transform/ElimByName.scala @@ -15,7 +15,7 @@ import util.Attachment import core.StdNames.nme import ast.Trees._ -/** This phase eliminates ExprTypes `=> T` as types of function parameters, and replaces them by +/** This phase eliminates ExprTypes `=> T` as types of method parameter references, and replaces them b * nullary function types. More precisely: * * For the types of parameter symbols: @@ -25,7 +25,6 @@ import ast.Trees._ * For cbn parameter values * * x ==> x() - * CbnArg(x) ==> DummyApply(x) * * Note: This scheme to have inconsistent types between method types (whose formal types are still * ExprTypes and parameter valdefs (which are now FunctionTypes) is not pretty. There are two @@ -43,10 +42,8 @@ class ElimByName extends TransformByNameApply with InfoTransformer { thisTransfo override def phaseName: String = "elimByName" - override def runsAfter = Set(classOf[HoistSuperArgs]) override def runsAfterGroupsOf = Set(classOf[Splitter]) - // assumes idents and selects have symbols; interferes with splitter distribution - // that's why it's "after group". + // I got errors running this phase in an earlier group, but I did not track them down. /** Map `tree` to `tree.apply()` is `ftree` was of ExprType and becomes now a function */ private def applyIfFunction(tree: Tree, ftree: Tree)(implicit ctx: Context) = diff --git a/compiler/src/dotty/tools/dotc/transform/Erasure.scala b/compiler/src/dotty/tools/dotc/transform/Erasure.scala index 4cee0d0de721..245b153ba9a6 100644 --- a/compiler/src/dotty/tools/dotc/transform/Erasure.scala +++ b/compiler/src/dotty/tools/dotc/transform/Erasure.scala @@ -452,7 +452,7 @@ object Erasure extends TypeTestsCasts{ */ override def typedApply(tree: untpd.Apply, pt: Type)(implicit ctx: Context): Tree = { val Apply(fun, args) = tree - if (fun.symbol == defn.dummyApply) + if (fun.symbol == defn.cbnArg) typedUnadapted(args.head, pt) else typedExpr(fun, FunProto(args, pt, this)) match { case fun1: Apply => // arguments passed in prototype were already passed diff --git a/compiler/src/dotty/tools/dotc/transform/HoistSuperArgs.scala b/compiler/src/dotty/tools/dotc/transform/HoistSuperArgs.scala index 1b8d22a6303b..285b9b36cb9f 100644 --- a/compiler/src/dotty/tools/dotc/transform/HoistSuperArgs.scala +++ b/compiler/src/dotty/tools/dotc/transform/HoistSuperArgs.scala @@ -42,6 +42,11 @@ class HoistSuperArgs extends MiniPhaseTransform with IdentityDenotTransformer { def phaseName = "hoistSuperArgs" + override def runsAfter = Set(classOf[ByNameClosures]) + // By name closures need to be introduced first in order to be hoisted out here. + // There's an interaction with by name closures in that the marker + // application should not be hoisted, but be left at the point of call. + /** Hoist complex arguments in super call `parent` out of the class. * @return A pair consisting of the transformed super call and a list of super argument * defininitions. @@ -81,16 +86,18 @@ class HoistSuperArgs extends MiniPhaseTransform with IdentityDenotTransformer { if (methOwner.isClass) meth.enteredAfter(thisTransform) else meth } + def refNeedsHoist(tp: Type): Boolean = tp match { + case tp: ThisType => !tp.cls.isStaticOwner && tp.cls != cls + case tp: TermRef => refNeedsHoist(tp.prefix) + case _ => false + } + /** Super call argument is complex, needs to be hoisted */ def needsHoist(tree: Tree) = tree match { case _: DefDef => true case _: Template => true - case _: This => !tree.symbol.isStaticOwner case _: New => !tree.tpe.typeSymbol.isStatic - case _: RefTree => - var owner = tree.symbol.effectiveOwner - if (owner.isLocalDummy) owner = owner.owner - tree.isTerm && !owner.isStaticOwner && owner != cls + case _: RefTree | _: This => refNeedsHoist(tree.tpe) case _ => false } @@ -99,8 +106,10 @@ class HoistSuperArgs extends MiniPhaseTransform with IdentityDenotTransformer { * @return The argument after possible hoisting * Might append a method definition to `superArgs` as a side effect. */ - def hoistSuperArg(arg: Tree) = - if (arg.existsSubTree(needsHoist)) { + def hoistSuperArg(arg: Tree): Tree = arg match { + case Apply(fn, arg1 :: Nil) if fn.symbol == defn.cbnArg => + cpy.Apply(arg)(fn, hoistSuperArg(arg1) :: Nil) + case _ if (arg.existsSubTree(needsHoist)) => val superMeth = newSuperArgMethod(arg.tpe) val superArgDef = polyDefDef(superMeth, trefs => vrefss => { val paramSyms = trefs.map(_.typeSymbol) ::: vrefss.flatten.map(_.symbol) @@ -139,8 +148,8 @@ class HoistSuperArgs extends MiniPhaseTransform with IdentityDenotTransformer { .appliedToArgss(termParamRefs(constr.info)) ctx.log(i"hoist $arg, cls = $cls = $res") res - } - else arg + case _ => arg + } def recur(tree: Tree): Tree = tree match { case Apply(fn, args) => cpy.Apply(tree)(recur(fn), args.mapconserve(hoistSuperArg)) diff --git a/compiler/src/dotty/tools/dotc/transform/TransformByNameApply.scala b/compiler/src/dotty/tools/dotc/transform/TransformByNameApply.scala index 17dbf0b3b314..b37489f78f33 100644 --- a/compiler/src/dotty/tools/dotc/transform/TransformByNameApply.scala +++ b/compiler/src/dotty/tools/dotc/transform/TransformByNameApply.scala @@ -13,7 +13,7 @@ import DenotTransformers._ import core.StdNames.nme import ast.Trees._ -/** Abstract base class of ByNameClosures and ELimByName, factoring out the +/** Abstract base class of ByNameClosures and ElimByName, factoring out the * common functionality to transform arguments of by-name parameters. */ abstract class TransformByNameApply extends MiniPhaseTransform { thisTransformer: DenotTransformer => @@ -32,7 +32,7 @@ abstract class TransformByNameApply extends MiniPhaseTransform { thisTransformer origDenot.info.isInstanceOf[ExprType] && exprBecomesFunction(origDenot) } - def mkClosure(arg: Tree, argType: Type)(implicit ctx: Context): Tree = unsupported("mkClosure") + def mkClosure(arg: Tree, argType: Type)(implicit ctx: Context): Tree = unsupported(i"mkClosure($arg)") override def transformApply(tree: Apply)(implicit ctx: Context, info: TransformerInfo): Tree = ctx.traceIndented(s"transforming ${tree.show} at phase ${ctx.phase}", show = true) { @@ -41,13 +41,13 @@ abstract class TransformByNameApply extends MiniPhaseTransform { thisTransformer case formalExpr: ExprType => var argType = arg.tpe.widenIfUnstable if (defn.isBottomType(argType)) argType = formal.widenExpr - def wrap(arg: Tree) = ref(defn.dummyApply).appliedToType(argType).appliedTo(arg) + def wrap(arg: Tree) = ref(defn.cbnArg).appliedToType(argType).appliedTo(arg) arg match { case Apply(Select(qual, nme.apply), Nil) if qual.tpe.derivesFrom(defn.FunctionClass(0)) && isPureExpr(qual) => wrap(qual) case _ => - if (isByNameRef(arg) || arg.symbol == defn.dummyApply) arg + if (isByNameRef(arg) || arg.symbol == defn.cbnArg) arg else wrap(mkClosure(arg, argType)) } case _ => diff --git a/tests/neg/tailcall/t1672b.scala b/tests/neg/tailcall/t1672b.scala index c439d60835ca..24ba9fd91c25 100644 --- a/tests/neg/tailcall/t1672b.scala +++ b/tests/neg/tailcall/t1672b.scala @@ -46,7 +46,7 @@ object Test1772B { else 1 + (try { throw new RuntimeException } catch { - case _: Throwable => bar(i - 1) // error + case _: Throwable => bar(i - 1) // old error }) } } From 04dc2b6adca40eb2dc7ea5bf569721cc22b4ab81 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Fri, 21 Apr 2017 18:05:10 +0200 Subject: [PATCH 08/10] Also hoist complex arguments out of this-calls Also hoist complex arguments out of this-calls of secondary constructors. --- .../tools/dotc/transform/HoistSuperArgs.scala | 225 ++++++++++-------- 1 file changed, 127 insertions(+), 98 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/HoistSuperArgs.scala b/compiler/src/dotty/tools/dotc/transform/HoistSuperArgs.scala index 285b9b36cb9f..74398e50283d 100644 --- a/compiler/src/dotty/tools/dotc/transform/HoistSuperArgs.scala +++ b/compiler/src/dotty/tools/dotc/transform/HoistSuperArgs.scala @@ -15,7 +15,7 @@ import core.Names.TermName import core.NameKinds.SuperArgName import SymUtils._ -/** This phase hoists complex arguments to supercalls out of the enclosing class. +/** This phase hoists complex arguments of supercalls and this-calls out of the enclosing class. * Example: * * class B(y: Int) extends A({ def f(x: Int) = x * x; f(y)}) @@ -47,131 +47,160 @@ class HoistSuperArgs extends MiniPhaseTransform with IdentityDenotTransformer { // There's an interaction with by name closures in that the marker // application should not be hoisted, but be left at the point of call. - /** Hoist complex arguments in super call `parent` out of the class. - * @return A pair consisting of the transformed super call and a list of super argument - * defininitions. + /** Defines methods for hoisting complex supercall arguments out of + * parent super calls and constructor definitions. + * Hoisted superarg methods are collected in `superArgDefs` */ - def hoistSuperArgs(parent: Tree, cls: Symbol)(implicit ctx: Context): (Tree, List[DefDef]) = { - lazy val constr = cls.primaryConstructor - lazy val allParams = // The parameters that can be accessed in the supercall - cls.info.decls.filter(d => d.is(TypeParam) || d.is(TermParamAccessor)) + class Hoister(cls: Symbol)(implicit ctx: Context) { val superArgDefs = new mutable.ListBuffer[DefDef] - /** The parameter references defined by the primary constructor info */ - def allParamRefs(tp: Type): List[ParamRef] = tp match { - case tp: LambdaType => tp.paramRefs ++ allParamRefs(tp.resultType) - case _ => Nil - } + /** If argument is complex, hoist it out into its own method and refer to the + * method instead. + * @param arg The argument that might be hoisted + * @param cdef The definition of the constructor from which the call is made + * @return The argument after possible hoisting + */ + private def hoistSuperArg(arg: Tree, cdef: DefDef): Tree = { + val constr = cdef.symbol + lazy val origParams = // The parameters that can be accessed in the supercall + if (constr == cls.primaryConstructor) + cls.info.decls.filter(d => d.is(TypeParam) || d.is(TermParamAccessor)) + else + (cdef.tparams ::: cdef.vparamss.flatten).map(_.symbol) - /** Splice `restpe` in final result type position of `tp` */ - def replaceResult(tp: Type, restpe: Type): Type = tp match { - case tp: LambdaType => - tp.derivedLambdaType(resType = replaceResult(tp.resultType, restpe)) - case _ => restpe - } + /** The parameter references defined by the constructor info */ + def allParamRefs(tp: Type): List[ParamRef] = tp match { + case tp: LambdaType => tp.paramRefs ++ allParamRefs(tp.resultType) + case _ => Nil + } + + /** Splice `restpe` in final result type position of `tp` */ + def replaceResult(tp: Type, restpe: Type): Type = tp match { + case tp: LambdaType => + tp.derivedLambdaType(resType = replaceResult(tp.resultType, restpe)) + case _ => restpe + } - /** A method representing a hoisted supercall argument */ - def newSuperArgMethod(argType: Type) = { - val (staticFlag, methOwner) = - if (cls.owner.is(Package)) (JavaStatic, cls) else (EmptyFlags, cls.owner) - val argTypeWrtConstr = argType.subst(allParams, allParamRefs(constr.info)) + /** A method representing a hoisted supercall argument */ + def newSuperArgMethod(argType: Type) = { + val (staticFlag, methOwner) = + if (cls.owner.is(Package)) (JavaStatic, cls) else (EmptyFlags, cls.owner) + val argTypeWrtConstr = argType.subst(origParams, allParamRefs(constr.info)) // argType with references to paramRefs of the primary constructor instead of // local parameter accessors - val meth = ctx.newSymbol( + val meth = ctx.newSymbol( owner = methOwner, name = SuperArgName.fresh(cls.name.toTermName), flags = Synthetic | Private | Method | staticFlag, info = replaceResult(constr.info, argTypeWrtConstr), coord = constr.coord) - if (methOwner.isClass) meth.enteredAfter(thisTransform) else meth - } + if (methOwner.isClass) meth.enteredAfter(thisTransform) else meth + } - def refNeedsHoist(tp: Type): Boolean = tp match { - case tp: ThisType => !tp.cls.isStaticOwner && tp.cls != cls - case tp: TermRef => refNeedsHoist(tp.prefix) - case _ => false - } + /** Type of a reference implies that it needs to be hoisted */ + def refNeedsHoist(tp: Type): Boolean = tp match { + case tp: ThisType => !tp.cls.isStaticOwner && tp.cls != cls + case tp: TermRef => refNeedsHoist(tp.prefix) + case _ => false + } - /** Super call argument is complex, needs to be hoisted */ - def needsHoist(tree: Tree) = tree match { - case _: DefDef => true - case _: Template => true - case _: New => !tree.tpe.typeSymbol.isStatic - case _: RefTree | _: This => refNeedsHoist(tree.tpe) - case _ => false + /** Super call argument is complex, needs to be hoisted */ + def needsHoist(tree: Tree) = tree match { + case _: DefDef => true + case _: Template => true + case _: New => !tree.tpe.typeSymbol.isStatic + case _: RefTree | _: This => refNeedsHoist(tree.tpe) + case _ => false + } + + // begin hoistSuperArg + arg match { + case Apply(fn, arg1 :: Nil) if fn.symbol == defn.cbnArg => + cpy.Apply(arg)(fn, hoistSuperArg(arg1, cdef) :: Nil) + case _ if (arg.existsSubTree(needsHoist)) => + val superMeth = newSuperArgMethod(arg.tpe) + val superArgDef = polyDefDef(superMeth, trefs => vrefss => { + val paramSyms = trefs.map(_.typeSymbol) ::: vrefss.flatten.map(_.symbol) + val tmap = new TreeTypeMap( + typeMap = new TypeMap { + def apply(tp: Type) = tp match { + case tp: NamedType + if (tp.symbol.owner == cls || tp.symbol.owner == constr) && + tp.symbol.is(ParamOrAccessor) => + val mappedSym = origParams.zip(paramSyms).toMap.apply(tp.symbol) + if (tp.symbol.isType) mappedSym.typeRef else mappedSym.termRef + case _ => + mapOver(tp) + } + }, + treeMap = { + case tree: RefTree if paramSyms.contains(tree.symbol) => + cpy.Ident(tree)(tree.name).withType(tree.tpe) + case tree => + tree + }) + tmap(arg).changeOwnerAfter(constr, superMeth, thisTransform) + }) + superArgDefs += superArgDef + def termParamRefs(tp: Type, params: List[Symbol]): List[List[Tree]] = tp match { + case tp: PolyType => + termParamRefs(tp.resultType, params) + case tp: MethodType => + val (thisParams, otherParams) = params.splitAt(tp.paramNames.length) + thisParams.map(ref) :: termParamRefs(tp.resultType, otherParams) + case _ => + Nil + } + val (typeParams, termParams) = origParams.span(_.isType) + val res = ref(superMeth) + .appliedToTypes(typeParams.map(_.typeRef)) + .appliedToArgss(termParamRefs(constr.info, termParams)) + ctx.log(i"hoist $arg, cls = $cls = $res") + res + case _ => arg + } } - /** If argument is complex, hoist it out into its own method and refer to the - * method instead. - * @return The argument after possible hoisting - * Might append a method definition to `superArgs` as a side effect. - */ - def hoistSuperArg(arg: Tree): Tree = arg match { - case Apply(fn, arg1 :: Nil) if fn.symbol == defn.cbnArg => - cpy.Apply(arg)(fn, hoistSuperArg(arg1) :: Nil) - case _ if (arg.existsSubTree(needsHoist)) => - val superMeth = newSuperArgMethod(arg.tpe) - val superArgDef = polyDefDef(superMeth, trefs => vrefss => { - val paramSyms = trefs.map(_.typeSymbol) ::: vrefss.flatten.map(_.symbol) - val tmap = new TreeTypeMap( - typeMap = new TypeMap { - def apply(tp: Type) = tp match { - case tp: NamedType if tp.symbol.owner == cls && tp.symbol.is(ParamOrAccessor) => - val mappedSym = allParams.zip(paramSyms).toMap.apply(tp.symbol) - if (tp.symbol.isType) mappedSym.typeRef else mappedSym.termRef - case _ => - mapOver(tp) - } - }, - treeMap = { - case tree: RefTree if paramSyms.contains(tree.symbol) => - cpy.Ident(tree)(tree.name).withType(tree.tpe) - case tree => - tree - } - ) - tmap(arg).changeOwnerAfter(constr, superMeth, thisTransform) - }) - superArgDefs += superArgDef - def termParamRefs(tp: Type): List[List[Tree]] = tp match { - case tp: PolyType => - termParamRefs(tp.resultType) - case tp: MethodType => - def paramRef(name: TermName) = - ref(cls.info.decl(name).suchThat(_.is(ParamAccessor)).symbol) - tp.paramNames.map(paramRef) :: termParamRefs(tp.resultType) - case _ => - Nil - } - val res = ref(superMeth) - .appliedToTypes(cls.typeParams.map(_.typeRef)) - .appliedToArgss(termParamRefs(constr.info)) - ctx.log(i"hoist $arg, cls = $cls = $res") - res - case _ => arg + /** Hoist complex arguments in super call out of the class. */ + def hoistSuperArgsFromCall(superCall: Tree, cdef: DefDef): Tree = superCall match { + case Apply(fn, args) => + cpy.Apply(superCall)(hoistSuperArgsFromCall(fn, cdef), args.mapconserve(hoistSuperArg(_, cdef))) + case _ => + superCall } - def recur(tree: Tree): Tree = tree match { - case Apply(fn, args) => cpy.Apply(tree)(recur(fn), args.mapconserve(hoistSuperArg)) - case _ => tree + /** Hoist complex arguments in this-constructor call of secondary constructor out of the class. */ + def hoistSuperArgsFromConstr(stat: Tree): Tree = stat match { + case stat: DefDef if stat.symbol.isClassConstructor => + cpy.DefDef(stat)(rhs = + stat.rhs match { + case Block(superCall :: stats, expr) => + val superCall1 = hoistSuperArgsFromCall(superCall, stat) + if (superCall1 eq superCall) stat.rhs + else cpy.Block(stat.rhs)(superCall1 :: stats, expr) + case _ => + hoistSuperArgsFromCall(stat.rhs, stat) + }) + case _ => + stat } - (recur(parent), superArgDefs.toList) } override def transformTypeDef(tdef: TypeDef)(implicit ctx: Context, info: TransformerInfo): Tree = tdef.rhs match { - case impl @ Template(_, superCall :: others, _, _) => - val cls = tdef.symbol - val (hoisted, superArgDefs) = hoistSuperArgs(superCall, cls) - if (superArgDefs.isEmpty) tdef + case impl @ Template(cdef, superCall :: others, _, _) => + val hoist = new Hoister(tdef.symbol) + val hoistedSuperCall = hoist.hoistSuperArgsFromCall(superCall, cdef) + val hoistedBody = impl.body.mapconserve(hoist.hoistSuperArgsFromConstr) + if (hoist.superArgDefs.isEmpty) tdef else { val (staticSuperArgDefs, enclSuperArgDefs) = - superArgDefs.partition(_.symbol.is(JavaStatic)) + hoist.superArgDefs.toList.partition(_.symbol.is(JavaStatic)) flatTree( cpy.TypeDef(tdef)( rhs = cpy.Template(impl)( - parents = hoisted :: others, - body = impl.body ++ staticSuperArgDefs)) :: + parents = hoistedSuperCall :: others, + body = hoistedBody ++ staticSuperArgDefs)) :: enclSuperArgDefs) } case _ => From 70171813df95844bc47e1f4bf47f8851341c861b Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Fri, 21 Apr 2017 20:30:37 +0200 Subject: [PATCH 09/10] Add fengyun's test case --- tests/run/supercalls-traits.scala | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/tests/run/supercalls-traits.scala b/tests/run/supercalls-traits.scala index 09e841f46226..241419314e68 100644 --- a/tests/run/supercalls-traits.scala +++ b/tests/run/supercalls-traits.scala @@ -10,6 +10,13 @@ class C extends A with B { override def foo = super[A].foo + super[B].foo } +class Base[A](exp: => Option[A]) + +object Empty extends Base[Nothing](None) + object Test { - def main(args: Array[String]) = assert(new C().foo == 3) + def main(args: Array[String]): Unit = { + assert(new C().foo == 3) + Empty + } } From b14969c50efd5239a8d76236d522f144977a9167 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Fri, 28 Apr 2017 15:56:50 +0200 Subject: [PATCH 10/10] Address reviewers comments --- .../src/dotty/tools/dotc/transform/ByNameClosures.scala | 2 +- .../src/dotty/tools/dotc/transform/HoistSuperArgs.scala | 3 ++- .../dotty/tools/dotc/transform/TransformByNameApply.scala | 6 +++--- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/ByNameClosures.scala b/compiler/src/dotty/tools/dotc/transform/ByNameClosures.scala index 76106e235599..4f8c7cfceac8 100644 --- a/compiler/src/dotty/tools/dotc/transform/ByNameClosures.scala +++ b/compiler/src/dotty/tools/dotc/transform/ByNameClosures.scala @@ -29,7 +29,7 @@ class ByNameClosures extends TransformByNameApply with IdentityDenotTransformer override def phaseName: String = "byNameClosures" - override def mkClosure(arg: Tree, argType: Type)(implicit ctx: Context): Tree = { + override def mkByNameClosure(arg: Tree, argType: Type)(implicit ctx: Context): Tree = { val meth = ctx.newSymbol( ctx.owner, nme.ANON_FUN, Synthetic | Method, MethodType(Nil, Nil, argType)) Closure(meth, _ => arg.changeOwnerAfter(ctx.owner, meth, thisTransformer)) diff --git a/compiler/src/dotty/tools/dotc/transform/HoistSuperArgs.scala b/compiler/src/dotty/tools/dotc/transform/HoistSuperArgs.scala index 74398e50283d..8737c4c9bf49 100644 --- a/compiler/src/dotty/tools/dotc/transform/HoistSuperArgs.scala +++ b/compiler/src/dotty/tools/dotc/transform/HoistSuperArgs.scala @@ -123,11 +123,12 @@ class HoistSuperArgs extends MiniPhaseTransform with IdentityDenotTransformer { val paramSyms = trefs.map(_.typeSymbol) ::: vrefss.flatten.map(_.symbol) val tmap = new TreeTypeMap( typeMap = new TypeMap { + lazy val origToParam = origParams.zip(paramSyms).toMap def apply(tp: Type) = tp match { case tp: NamedType if (tp.symbol.owner == cls || tp.symbol.owner == constr) && tp.symbol.is(ParamOrAccessor) => - val mappedSym = origParams.zip(paramSyms).toMap.apply(tp.symbol) + val mappedSym = origToParam(tp.symbol) if (tp.symbol.isType) mappedSym.typeRef else mappedSym.termRef case _ => mapOver(tp) diff --git a/compiler/src/dotty/tools/dotc/transform/TransformByNameApply.scala b/compiler/src/dotty/tools/dotc/transform/TransformByNameApply.scala index b37489f78f33..b9218163666b 100644 --- a/compiler/src/dotty/tools/dotc/transform/TransformByNameApply.scala +++ b/compiler/src/dotty/tools/dotc/transform/TransformByNameApply.scala @@ -19,7 +19,7 @@ import ast.Trees._ abstract class TransformByNameApply extends MiniPhaseTransform { thisTransformer: DenotTransformer => import ast.tpd._ - /** The info of the tree's symbol at phase Nullarify (i.e. before transformation) */ + /** The info of the tree's symbol before it is potentially transformed in this phase */ private def originalDenotation(tree: Tree)(implicit ctx: Context) = tree.symbol.denot(ctx.withPhase(thisTransformer)) @@ -32,7 +32,7 @@ abstract class TransformByNameApply extends MiniPhaseTransform { thisTransformer origDenot.info.isInstanceOf[ExprType] && exprBecomesFunction(origDenot) } - def mkClosure(arg: Tree, argType: Type)(implicit ctx: Context): Tree = unsupported(i"mkClosure($arg)") + def mkByNameClosure(arg: Tree, argType: Type)(implicit ctx: Context): Tree = unsupported(i"mkClosure($arg)") override def transformApply(tree: Apply)(implicit ctx: Context, info: TransformerInfo): Tree = ctx.traceIndented(s"transforming ${tree.show} at phase ${ctx.phase}", show = true) { @@ -48,7 +48,7 @@ abstract class TransformByNameApply extends MiniPhaseTransform { thisTransformer wrap(qual) case _ => if (isByNameRef(arg) || arg.symbol == defn.cbnArg) arg - else wrap(mkClosure(arg, argType)) + else wrap(mkByNameClosure(arg, argType)) } case _ => arg