diff options
author | Martin Odersky <odersky@gmail.com> | 2014-07-13 14:12:00 +0200 |
---|---|---|
committer | Martin Odersky <odersky@gmail.com> | 2014-07-17 11:02:03 +0200 |
commit | 3f3581c1fdefbebf06d4b3d12c6474c2997ec52d (patch) | |
tree | d0d6dc101647a0f78e30bee1b298757b96c0890e | |
parent | 65a85700146674d64f02aed1f2ec185a8ff41c7a (diff) | |
download | dotty-3f3581c1fdefbebf06d4b3d12c6474c2997ec52d.tar.gz dotty-3f3581c1fdefbebf06d4b3d12c6474c2997ec52d.tar.bz2 dotty-3f3581c1fdefbebf06d4b3d12c6474c2997ec52d.zip |
Make ExtensionMethods not rely on TailRec
This acommit allows TailRec to run after ExtensionMethods. This is
due to the following changes:
1) Extension methods now "rewire" calls to other extension methods of the
same class, so that they go directly to other extension methods
instead of to the original method in the class.
2) Tailrec annotations get removed from original method and get
added to the extension method instead.
With this commit all tests can be re-enabled again.
-rw-r--r-- | src/dotty/tools/dotc/Compiler.scala | 2 | ||||
-rw-r--r-- | src/dotty/tools/dotc/transform/ExtensionMethods.scala | 64 | ||||
-rw-r--r-- | test/dotc/tests.scala | 4 |
3 files changed, 56 insertions, 14 deletions
diff --git a/src/dotty/tools/dotc/Compiler.scala b/src/dotty/tools/dotc/Compiler.scala index dfed8ce9d..95f74e290 100644 --- a/src/dotty/tools/dotc/Compiler.scala +++ b/src/dotty/tools/dotc/Compiler.scala @@ -20,9 +20,9 @@ class Compiler { List( List(new FrontEnd), List(new Companions, new ElimRepeated /*, new ElimLocals*/), - List(new TailRec), List(new SuperAccessors), List(new ExtensionMethods), + List(new TailRec), List(new PatternMatcher, new LazyValTranformContext().transformer, new Splitter), diff --git a/src/dotty/tools/dotc/transform/ExtensionMethods.scala b/src/dotty/tools/dotc/transform/ExtensionMethods.scala index 3e5932cfe..917db17a1 100644 --- a/src/dotty/tools/dotc/transform/ExtensionMethods.scala +++ b/src/dotty/tools/dotc/transform/ExtensionMethods.scala @@ -23,7 +23,7 @@ import Decorators._ * Perform Step 1 in the inline classes SIP: Creates extension methods for all * methods in a value class, except parameter or super accessors, or constructors. */ -class ExtensionMethods extends MacroTransform with InfoTransformer { thisTransformer => +class ExtensionMethods extends MacroTransform with DenotTransformer { thisTransformer => import tpd._ @@ -32,22 +32,30 @@ class ExtensionMethods extends MacroTransform with InfoTransformer { thisTransfo override def runsAfter: Set[String] = Set("elimrepeated") // TODO: add tailrec - override def transformInfo(tp: Type, sym: Symbol)(implicit ctx: Context): Type = tp match { - case tp: ClassInfo if sym is ModuleClass => - sym.linkedClass match { + override def transform(ref: SingleDenotation)(implicit ctx: Context): SingleDenotation = ref match { + case ref: ClassDenotation if ref is ModuleClass => + ref.linkedClass match { case origClass: ClassSymbol if isDerivedValueClass(origClass) => - val decls1 = tp.decls.cloneScope + val cinfo = ref.classInfo + val decls1 = cinfo.decls.cloneScope ctx.atPhase(thisTransformer.next) { implicit ctx => for (decl <- origClass.classInfo.decls) { if (isMethodWithExtension(decl)) - decls1.enter(createExtensionMethod(decl, sym)) + decls1.enter(createExtensionMethod(decl, ref.symbol)) } } - tp.derivedClassInfo(decls = decls1) - case _ => tp + if (decls1.isEmpty) ref + else ref.copySymDenotation(info = cinfo.derivedClassInfo(decls = decls1)) + case _ => + ref } + case ref: SymDenotation + if isMethodWithExtension(ref) && ref.hasAnnotation(defn.TailrecAnnotationClass) => + val ref1 = ref.copySymDenotation() + ref1.removeAnnotation(defn.TailrecAnnotationClass) + ref1 case _ => - tp + ref } def newTransformer(implicit ctx: Context): Transformer = new Extender @@ -120,7 +128,8 @@ class ExtensionMethods extends MacroTransform with InfoTransformer { thisTransfo imeth.flags | Final &~ (Override | Protected | AbsOverride), imeth.info.toStatic(imeth.owner.asClass), privateWithin = imeth.privateWithin, coord = imeth.coord) - extensionMeth.addAnnotations(from = imeth) + extensionMeth.addAnnotations(from = imeth)(ctx.withPhase(thisTransformer)) + // need to change phase to add tailrec annotation which gets removed from original method in the same phase. extensionMeth } @@ -169,8 +178,41 @@ class ExtensionMethods extends MacroTransform with InfoTransformer { thisTransfo ctx.log(s"Value class $origClass spawns extension method.\n Old: ${origMeth.showDcl}\n New: ${extensionMeth.showDcl}") + def needsRewiring(sym: Symbol) = isMethodWithExtension(sym) && sym.owner == origClass + extensionDefs(staticClass) += polyDefDef(extensionMeth, trefs => vrefss => { val thisRef :: argRefs = vrefss.flatten + def rewireToStatic(tree: Tree, targs: List[Tree])(implicit ctx: Context): Tree = { + def rewireCall(thisArg: Tree): Tree = { + val sym = tree.symbol + if (needsRewiring(sym)) { + val base = thisArg.tpe.baseTypeWithArgs(origClass) + assert(base.exists) + ref(extensionMethod(sym).termRef) + .appliedToTypeTrees(targs ++ base.argInfos.map(TypeTree(_))) + .appliedTo(thisArg) + } else EmptyTree + } + tree match { + case Ident(_) => rewireCall(thisRef) + case Select(qual, _) => rewireCall(qual) + case tree @ TypeApply(fn, targs1) => + assert(targs.isEmpty) + rewireToStatic(fn, targs1) + case Block(stats, expr) => + val expr1 = rewireToStatic(expr, targs) + if (expr1.isEmpty) EmptyTree else Block(stats, expr1) withPos tree.pos + case _ => EmptyTree + } + } + val rewireType = new TypeMap { + def apply(tp: Type) = tp match { + case ref: TermRef if needsRewiring(ref.symbol) => + extensionMethod(ref.symbol).termRef + case _ => + mapOver(tp) + } + } new TreeTypeMap( typeMap = _ .subst(origTParams, trefs) @@ -179,7 +221,7 @@ class ExtensionMethods extends MacroTransform with InfoTransformer { thisTransfo ownerMap = (sym => if (sym eq origMeth) extensionMeth else sym), treeMap = { case tree: This if tree.symbol == origClass => thisRef - case tree => tree + case tree => rewireToStatic(tree, Nil) orElse tree } ).transform(rhs) }) diff --git a/test/dotc/tests.scala b/test/dotc/tests.scala index ac4e915c2..f1896cbdf 100644 --- a/test/dotc/tests.scala +++ b/test/dotc/tests.scala @@ -14,7 +14,7 @@ class tests extends CompilerTest { "-pagewidth", "160") implicit val defaultOptions = noCheckOptions ++ List( - "-Yskip:tailrec, -Ycheck:extmethods"//, "-Ystop-before:terminal" + "-Ycheck:extmethods"//, "-Ystop-before:terminal" ) val twice = List("#runs", "2", "-YnoDoubleBindings") @@ -78,7 +78,7 @@ class tests extends CompilerTest { @Test def neg_t1192_legalPrefix = compileFile(negDir, "t1192", xerrors = 1) @Test def neg_tailcall_t1672b = compileFile(negDir, "tailcall/t1672b", xerrors = 6) @Test def neg_tailcall_t3275 = compileFile(negDir, "tailcall/t3275", xerrors = 1) - @Test def neg_tailcall_t6574 = compileFile(negDir, "tailcall/t6574", xerrors = 4) + @Test def neg_tailcall_t6574 = compileFile(negDir, "tailcall/t6574", xerrors = 2) @Test def neg_tailcall = compileFile(negDir, "tailcall/tailrec", xerrors = 7) @Test def neg_tailcall2 = compileFile(negDir, "tailcall/tailrec-2", xerrors = 2) @Test def neg_tailcall3 = compileFile(negDir, "tailcall/tailrec-3", xerrors = 2) |