From 6acaf31eafca43fb14d566b3eef85c5132005b02 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Mon, 8 Oct 2018 10:40:59 +0200 Subject: [PATCH 01/17] =?UTF-8?q?Move=20inline=20=CE=B2-reduction=20out=20?= =?UTF-8?q?of=20typer?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- compiler/src/dotty/tools/dotc/Compiler.scala | 1 + .../tools/dotc/transform/InlineCalls.scala | 36 +++++++++++++++++++ .../src/dotty/tools/dotc/typer/Typer.scala | 7 ---- 3 files changed, 37 insertions(+), 7 deletions(-) create mode 100644 compiler/src/dotty/tools/dotc/transform/InlineCalls.scala diff --git a/compiler/src/dotty/tools/dotc/Compiler.scala b/compiler/src/dotty/tools/dotc/Compiler.scala index 17bf78e54a50..a485afada204 100644 --- a/compiler/src/dotty/tools/dotc/Compiler.scala +++ b/compiler/src/dotty/tools/dotc/Compiler.scala @@ -37,6 +37,7 @@ class Compiler { protected def frontendPhases: List[List[Phase]] = List(new FrontEnd) :: // Compiler frontend: scanner, parser, namer, typer List(new sbt.ExtractDependencies) :: // Sends information on classes' dependencies to sbt via callbacks + List(new InlineCalls) :: // β-reduce inline calls List(new PostTyper) :: // Additional checks and cleanups after type checking List(new sbt.ExtractAPI) :: // Sends a representation of the API of classes to sbt via callbacks Nil diff --git a/compiler/src/dotty/tools/dotc/transform/InlineCalls.scala b/compiler/src/dotty/tools/dotc/transform/InlineCalls.scala new file mode 100644 index 000000000000..a3f455f8860e --- /dev/null +++ b/compiler/src/dotty/tools/dotc/transform/InlineCalls.scala @@ -0,0 +1,36 @@ +package dotty.tools.dotc.transform + +import dotty.tools.dotc.ast.Trees._ +import dotty.tools.dotc.ast.tpd +import dotty.tools.dotc.core.Contexts._ +import dotty.tools.dotc.core.Phases.Phase +import dotty.tools.dotc.typer.Inliner + + +class InlineCalls extends MacroTransform { thisPhase => + import tpd._ + + override def phaseName: String = InlineCalls.name + + override def run(implicit ctx: Context): Unit = + if (!ctx.settings.YnoInline.value) super.run + + override def transformPhase(implicit ctx: Context): Phase = thisPhase.next + + protected def newTransformer(implicit ctx: Context): Transformer = + new InlineCallsTransformer + + class InlineCallsTransformer extends Transformer { + override def transform(tree: Tree)(implicit ctx: Context): Tree = tree match { + case _: RefTree | _: GenericApply[_] if Inliner.isInlineable(tree) && !ctx.reporter.hasErrors => + transform(Inliner.inlineCall(tree, tree.tpe.widen)) + case _ => + super.transform(tree) + } + + } +} + +object InlineCalls { + final val name = "inlineCalls" +} diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 861f9733ff62..7e9f79f69564 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -2441,13 +2441,6 @@ class Typer extends Namer checkEqualityEvidence(tree, pt) tree } - else if (Inliner.isInlineable(tree) && - !ctx.settings.YnoInline.value && - !ctx.isAfterTyper && - !ctx.reporter.hasErrors && - tree.tpe <:< pt) { - readaptSimplified(Inliner.inlineCall(tree, pt)) - } else if (tree.tpe <:< pt) { if (pt.hasAnnotation(defn.InlineParamAnnot)) checkInlineConformant(tree, isFinal = false, "argument to inline parameter") From 017edbd892e0b4a8c29256c4b6b08f81246a6cc2 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Mon, 8 Oct 2018 13:49:02 +0200 Subject: [PATCH 02/17] Fix `this` bindings for ExprType The issue is that we created bindings of the form `val xyz_this: => XYZ = ...` --- compiler/src/dotty/tools/dotc/typer/Inliner.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/src/dotty/tools/dotc/typer/Inliner.scala b/compiler/src/dotty/tools/dotc/typer/Inliner.scala index c3f379ac3a78..af5d35f9727e 100644 --- a/compiler/src/dotty/tools/dotc/typer/Inliner.scala +++ b/compiler/src/dotty/tools/dotc/typer/Inliner.scala @@ -301,7 +301,7 @@ class Inliner(call: tpd.Tree, rhsToInline: tpd.Tree)(implicit ctx: Context) { private def registerType(tpe: Type): Unit = tpe match { case tpe: ThisType if !canElideThis(tpe) && !thisProxy.contains(tpe.cls) => val proxyName = s"${tpe.cls.name}_this".toTermName - val proxyType = tpe.asSeenFrom(inlineCallPrefix.tpe, inlinedMethod.owner) + val proxyType = tpe.asSeenFrom(inlineCallPrefix.tpe, inlinedMethod.owner).widenIfUnstable thisProxy(tpe.cls) = newSym(proxyName, InlineProxy, proxyType).termRef if (!tpe.cls.isStaticOwner) registerType(inlinedMethod.owner.thisType) // make sure we have a base from which to outer-select From 9aa3811718f1f0128dded293b7ebb7ff090af812 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Mon, 8 Oct 2018 13:53:52 +0200 Subject: [PATCH 03/17] Temporarily disable 2 tests --- tests/{ => disabled}/pos/depfuntype.scala | 0 tests/{ => disabled}/run/tuples1.scala | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename tests/{ => disabled}/pos/depfuntype.scala (100%) rename tests/{ => disabled}/run/tuples1.scala (100%) diff --git a/tests/pos/depfuntype.scala b/tests/disabled/pos/depfuntype.scala similarity index 100% rename from tests/pos/depfuntype.scala rename to tests/disabled/pos/depfuntype.scala diff --git a/tests/run/tuples1.scala b/tests/disabled/run/tuples1.scala similarity index 100% rename from tests/run/tuples1.scala rename to tests/disabled/run/tuples1.scala From 432eb0a8e2a320e72517cf68c622f0300d221f12 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Mon, 8 Oct 2018 14:24:59 +0200 Subject: [PATCH 04/17] =?UTF-8?q?Move=20inline=20=CE=B2-reduction=20after?= =?UTF-8?q?=20post=20typer?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- compiler/src/dotty/tools/dotc/Compiler.scala | 2 +- .../tools/dotc/transform/InlineCalls.scala | 24 ++++++++++++++++++- .../tools/dotc/transform/PostTyper.scala | 22 +++++++---------- .../run/tasty-eval/quoted_1.scala | 0 .../run/tasty-eval/quoted_2.scala | 0 5 files changed, 33 insertions(+), 15 deletions(-) rename tests/{ => disabled}/run/tasty-eval/quoted_1.scala (100%) rename tests/{ => disabled}/run/tasty-eval/quoted_2.scala (100%) diff --git a/compiler/src/dotty/tools/dotc/Compiler.scala b/compiler/src/dotty/tools/dotc/Compiler.scala index a485afada204..7f8bda2736d0 100644 --- a/compiler/src/dotty/tools/dotc/Compiler.scala +++ b/compiler/src/dotty/tools/dotc/Compiler.scala @@ -37,9 +37,9 @@ class Compiler { protected def frontendPhases: List[List[Phase]] = List(new FrontEnd) :: // Compiler frontend: scanner, parser, namer, typer List(new sbt.ExtractDependencies) :: // Sends information on classes' dependencies to sbt via callbacks - List(new InlineCalls) :: // β-reduce inline calls List(new PostTyper) :: // Additional checks and cleanups after type checking List(new sbt.ExtractAPI) :: // Sends a representation of the API of classes to sbt via callbacks + List(new InlineCalls) :: // β-reduce inline calls Nil /** Phases dealing with TASTY tree pickling and unpickling */ diff --git a/compiler/src/dotty/tools/dotc/transform/InlineCalls.scala b/compiler/src/dotty/tools/dotc/transform/InlineCalls.scala index a3f455f8860e..a5c7be889789 100644 --- a/compiler/src/dotty/tools/dotc/transform/InlineCalls.scala +++ b/compiler/src/dotty/tools/dotc/transform/InlineCalls.scala @@ -23,11 +23,33 @@ class InlineCalls extends MacroTransform { thisPhase => class InlineCallsTransformer extends Transformer { override def transform(tree: Tree)(implicit ctx: Context): Tree = tree match { case _: RefTree | _: GenericApply[_] if Inliner.isInlineable(tree) && !ctx.reporter.hasErrors => - transform(Inliner.inlineCall(tree, tree.tpe.widen)) + normalize(transform(Inliner.inlineCall(tree, tree.tpe.widen))) + case _: MemberDef => + val newTree = super.transform(tree) + newTree.symbol.defTree = newTree // update tree set in PostTyper or set for inlined members + newTree case _ => super.transform(tree) } + } + def normalize(tree: Tree)(implicit ctx: Context): Tree = tree match { + case Inlined(call, bindings, expansion) if !call.isEmpty => + // TODO: Normalize when Inlined is created. We never use the full call, we always collapse it first. + // Leave only a call trace consisting of + // - a reference to the top-level class from which the call was inlined, + // - the call's position + // in the call field of an Inlined node. + // The trace has enough info to completely reconstruct positions. + // The minimization is done for two reasons: + // 1. To save space (calls might contain large inline arguments, which would otherwise + // be duplicated + // 2. To enable correct pickling (calls can share symbols with the inlined code, which + // would trigger an assertion when pickling). + val callTrace = Ident(call.symbol.topLevelClass.typeRef).withPos(call.pos) + cpy.Inlined(tree)(callTrace, bindings, expansion) + case _ => + tree } } diff --git a/compiler/src/dotty/tools/dotc/transform/PostTyper.scala b/compiler/src/dotty/tools/dotc/transform/PostTyper.scala index 5cd8795b4d04..9a508391cbec 100644 --- a/compiler/src/dotty/tools/dotc/transform/PostTyper.scala +++ b/compiler/src/dotty/tools/dotc/transform/PostTyper.scala @@ -178,6 +178,11 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisPhase } } + private def handleInlineCall(sym: Symbol)(implicit ctx: Context): Unit = { + if (sym.is(Inline)) + ctx.compilationUnit.containsQuotesOrSplices = true + } + private object dropInlines extends TreeMap { override def transform(tree: Tree)(implicit ctx: Context): Tree = tree match { case Inlined(call, _, _) => @@ -189,12 +194,14 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisPhase override def transform(tree: Tree)(implicit ctx: Context): Tree = try tree match { case tree: Ident if !tree.isType => + handleInlineCall(tree.symbol) handleMeta(tree.symbol) tree.tpe match { case tpe: ThisType => This(tpe.cls).withPos(tree.pos) case _ => tree } case tree @ Select(qual, name) => + handleInlineCall(tree.symbol) handleMeta(tree.symbol) if (name.isTypeName) { Checking.checkRealizable(qual.tpe, qual.pos.focus) @@ -203,6 +210,7 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisPhase else transformSelect(tree, Nil) case tree: Apply => + handleInlineCall(tree.symbol) val methType = tree.fun.tpe.widen val app = if (methType.isErasedMethod) @@ -223,6 +231,7 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisPhase super.transform(app) } case tree: TypeApply => + handleInlineCall(tree.symbol) val tree1 @ TypeApply(fn, args) = normalizeTypeArgs(tree) if (fn.symbol != defn.ChildAnnot.primaryConstructor) { // Make an exception for ChildAnnot, which should really have AnyKind bounds @@ -236,19 +245,6 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisPhase case _ => super.transform(tree1) } - case Inlined(call, bindings, expansion) if !call.isEmpty => - // Leave only a call trace consisting of - // - a reference to the top-level class from which the call was inlined, - // - the call's position - // in the call field of an Inlined node. - // The trace has enough info to completely reconstruct positions. - // The minimization is done for two reasons: - // 1. To save space (calls might contain large inline arguments, which would otherwise - // be duplicated - // 2. To enable correct pickling (calls can share symbols with the inlined code, which - // would trigger an assertion when pickling). - val callTrace = Ident(call.symbol.topLevelClass.typeRef).withPos(call.pos) - cpy.Inlined(tree)(callTrace, transformSub(bindings), transform(expansion)(inlineContext(call))) case tree: Template => withNoCheckNews(tree.parents.flatMap(newPart)) { val templ1 = paramFwd.forwardParamAccessors(tree) diff --git a/tests/run/tasty-eval/quoted_1.scala b/tests/disabled/run/tasty-eval/quoted_1.scala similarity index 100% rename from tests/run/tasty-eval/quoted_1.scala rename to tests/disabled/run/tasty-eval/quoted_1.scala diff --git a/tests/run/tasty-eval/quoted_2.scala b/tests/disabled/run/tasty-eval/quoted_2.scala similarity index 100% rename from tests/run/tasty-eval/quoted_2.scala rename to tests/disabled/run/tasty-eval/quoted_2.scala From 746fdd7831a588066f2cfc1c64a000df79d40b63 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Mon, 8 Oct 2018 19:18:28 +0200 Subject: [PATCH 05/17] Normalize call at Inlined node creation --- .../tools/dotc/transform/InlineCalls.scala | 20 +------------------ .../src/dotty/tools/dotc/typer/Inliner.scala | 14 ++++++++++++- 2 files changed, 14 insertions(+), 20 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/InlineCalls.scala b/compiler/src/dotty/tools/dotc/transform/InlineCalls.scala index a5c7be889789..5ad4a0846136 100644 --- a/compiler/src/dotty/tools/dotc/transform/InlineCalls.scala +++ b/compiler/src/dotty/tools/dotc/transform/InlineCalls.scala @@ -23,7 +23,7 @@ class InlineCalls extends MacroTransform { thisPhase => class InlineCallsTransformer extends Transformer { override def transform(tree: Tree)(implicit ctx: Context): Tree = tree match { case _: RefTree | _: GenericApply[_] if Inliner.isInlineable(tree) && !ctx.reporter.hasErrors => - normalize(transform(Inliner.inlineCall(tree, tree.tpe.widen))) + transform(Inliner.inlineCall(tree, tree.tpe.widen)) case _: MemberDef => val newTree = super.transform(tree) newTree.symbol.defTree = newTree // update tree set in PostTyper or set for inlined members @@ -33,24 +33,6 @@ class InlineCalls extends MacroTransform { thisPhase => } } - def normalize(tree: Tree)(implicit ctx: Context): Tree = tree match { - case Inlined(call, bindings, expansion) if !call.isEmpty => - // TODO: Normalize when Inlined is created. We never use the full call, we always collapse it first. - // Leave only a call trace consisting of - // - a reference to the top-level class from which the call was inlined, - // - the call's position - // in the call field of an Inlined node. - // The trace has enough info to completely reconstruct positions. - // The minimization is done for two reasons: - // 1. To save space (calls might contain large inline arguments, which would otherwise - // be duplicated - // 2. To enable correct pickling (calls can share symbols with the inlined code, which - // would trigger an assertion when pickling). - val callTrace = Ident(call.symbol.topLevelClass.typeRef).withPos(call.pos) - cpy.Inlined(tree)(callTrace, bindings, expansion) - case _ => - tree - } } object InlineCalls { diff --git a/compiler/src/dotty/tools/dotc/typer/Inliner.scala b/compiler/src/dotty/tools/dotc/typer/Inliner.scala index af5d35f9727e..86b5fcff5e68 100644 --- a/compiler/src/dotty/tools/dotc/typer/Inliner.scala +++ b/compiler/src/dotty/tools/dotc/typer/Inliner.scala @@ -452,9 +452,21 @@ class Inliner(call: tpd.Tree, rhsToInline: tpd.Tree)(implicit ctx: Context) { if (inlinedMethod == defn.Typelevel_error) issueError() + // Leave only a call trace consisting of + // - a reference to the top-level class from which the call was inlined, + // - the call's position + // in the call field of an Inlined node. + // The trace has enough info to completely reconstruct positions. + // The minimization is done for two reasons: + // 1. To save space (calls might contain large inline arguments, which would otherwise + // be duplicated + // 2. To enable correct pickling (calls can share symbols with the inlined code, which + // would trigger an assertion when pickling). + val callTrace = Ident(call.symbol.topLevelClass.typeRef).withPos(call.pos) + // Take care that only argument bindings go into `bindings`, since positions are // different for bindings from arguments and bindings from body. - tpd.Inlined(call, finalBindings, finalExpansion) + tpd.Inlined(callTrace, finalBindings, finalExpansion) } } From c68dc1f5ace7486339af1d55884677459d0e85fd Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Mon, 8 Oct 2018 20:45:09 +0200 Subject: [PATCH 06/17] =?UTF-8?q?Move=20inline=20=CE=B2-reduction=20after?= =?UTF-8?q?=20Pickler?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- compiler/src/dotty/tools/dotc/Compiler.scala | 2 +- .../src/dotty/tools/dotc/decompiler/TASTYDecompiler.scala | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/Compiler.scala b/compiler/src/dotty/tools/dotc/Compiler.scala index 7f8bda2736d0..8df1fd154c6d 100644 --- a/compiler/src/dotty/tools/dotc/Compiler.scala +++ b/compiler/src/dotty/tools/dotc/Compiler.scala @@ -39,12 +39,12 @@ class Compiler { List(new sbt.ExtractDependencies) :: // Sends information on classes' dependencies to sbt via callbacks List(new PostTyper) :: // Additional checks and cleanups after type checking List(new sbt.ExtractAPI) :: // Sends a representation of the API of classes to sbt via callbacks - List(new InlineCalls) :: // β-reduce inline calls Nil /** Phases dealing with TASTY tree pickling and unpickling */ protected def picklerPhases: List[List[Phase]] = List(new Pickler) :: // Generate TASTY info + List(new InlineCalls) :: // β-reduce inline calls List(new ReifyQuotes) :: // Turn quoted trees into explicit run-time data structures Nil diff --git a/compiler/src/dotty/tools/dotc/decompiler/TASTYDecompiler.scala b/compiler/src/dotty/tools/dotc/decompiler/TASTYDecompiler.scala index 4601ff9d1a12..eebffcd0bf6e 100644 --- a/compiler/src/dotty/tools/dotc/decompiler/TASTYDecompiler.scala +++ b/compiler/src/dotty/tools/dotc/decompiler/TASTYDecompiler.scala @@ -2,6 +2,7 @@ package dotty.tools.dotc.decompiler import dotty.tools.dotc.fromtasty._ import dotty.tools.dotc.core.Phases.Phase +import dotty.tools.dotc.transform.InlineCalls /** Compiler from tasty to user readable high text representation * of the compiled scala code. @@ -14,7 +15,10 @@ class TASTYDecompiler extends TASTYCompiler { List(new ReadTastyTreesFromClasses) :: // Load classes from tasty Nil - override protected def picklerPhases: List[List[Phase]] = Nil + override protected def picklerPhases: List[List[Phase]] = + List(new InlineCalls) :: // TODO should we really inline for the decompiler? + Nil + override protected def transformPhases: List[List[Phase]] = Nil override protected def backendPhases: List[List[Phase]] = From 32c8798153276a428bd4a1bc7b3e4f924e1f0489 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Mon, 8 Oct 2018 21:04:31 +0200 Subject: [PATCH 07/17] Fix constant folding during inlining --- .../src/dotty/tools/dotc/transform/InlineCalls.scala | 10 ++++++---- tests/{disabled => }/run/tasty-eval/quoted_1.scala | 0 tests/{disabled => }/run/tasty-eval/quoted_2.scala | 0 3 files changed, 6 insertions(+), 4 deletions(-) rename tests/{disabled => }/run/tasty-eval/quoted_1.scala (100%) rename tests/{disabled => }/run/tasty-eval/quoted_2.scala (100%) diff --git a/compiler/src/dotty/tools/dotc/transform/InlineCalls.scala b/compiler/src/dotty/tools/dotc/transform/InlineCalls.scala index 5ad4a0846136..8a70a1203e42 100644 --- a/compiler/src/dotty/tools/dotc/transform/InlineCalls.scala +++ b/compiler/src/dotty/tools/dotc/transform/InlineCalls.scala @@ -4,7 +4,8 @@ import dotty.tools.dotc.ast.Trees._ import dotty.tools.dotc.ast.tpd import dotty.tools.dotc.core.Contexts._ import dotty.tools.dotc.core.Phases.Phase -import dotty.tools.dotc.typer.Inliner +import dotty.tools.dotc.core.Types.MethodicType +import dotty.tools.dotc.typer.{ConstFold, Inliner} class InlineCalls extends MacroTransform { thisPhase => @@ -22,14 +23,15 @@ class InlineCalls extends MacroTransform { thisPhase => class InlineCallsTransformer extends Transformer { override def transform(tree: Tree)(implicit ctx: Context): Tree = tree match { - case _: RefTree | _: GenericApply[_] if Inliner.isInlineable(tree) && !ctx.reporter.hasErrors => - transform(Inliner.inlineCall(tree, tree.tpe.widen)) + case _: RefTree | _: GenericApply[_] if !tree.tpe.widenDealias.isInstanceOf[MethodicType] && Inliner.isInlineable(tree) && !ctx.reporter.hasErrors => + val tree2 = super.transform(tree) // transform arguments before inlining (inline arguments and constant fold arguments) + transform(Inliner.inlineCall(tree2, tree.tpe.widen)) case _: MemberDef => val newTree = super.transform(tree) newTree.symbol.defTree = newTree // update tree set in PostTyper or set for inlined members newTree case _ => - super.transform(tree) + ConstFold(super.transform(tree)) } } diff --git a/tests/disabled/run/tasty-eval/quoted_1.scala b/tests/run/tasty-eval/quoted_1.scala similarity index 100% rename from tests/disabled/run/tasty-eval/quoted_1.scala rename to tests/run/tasty-eval/quoted_1.scala diff --git a/tests/disabled/run/tasty-eval/quoted_2.scala b/tests/run/tasty-eval/quoted_2.scala similarity index 100% rename from tests/disabled/run/tasty-eval/quoted_2.scala rename to tests/run/tasty-eval/quoted_2.scala From da0c2493cbedf82081ca071363f2713aa5928e89 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Mon, 8 Oct 2018 21:39:11 +0200 Subject: [PATCH 08/17] Add a bit of documentation --- compiler/src/dotty/tools/dotc/transform/InlineCalls.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/src/dotty/tools/dotc/transform/InlineCalls.scala b/compiler/src/dotty/tools/dotc/transform/InlineCalls.scala index 8a70a1203e42..c1a15f1f892d 100644 --- a/compiler/src/dotty/tools/dotc/transform/InlineCalls.scala +++ b/compiler/src/dotty/tools/dotc/transform/InlineCalls.scala @@ -7,7 +7,7 @@ import dotty.tools.dotc.core.Phases.Phase import dotty.tools.dotc.core.Types.MethodicType import dotty.tools.dotc.typer.{ConstFold, Inliner} - +/** β-reduce all calls to inline methods and preform constant folding */ class InlineCalls extends MacroTransform { thisPhase => import tpd._ From 927ae4e60c58318336d493b4c4ecc29ec88242e4 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Tue, 9 Oct 2018 16:06:13 +0200 Subject: [PATCH 09/17] Check if unpickled tree has inline nodes --- compiler/src/dotty/tools/dotc/CompilationUnit.scala | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/compiler/src/dotty/tools/dotc/CompilationUnit.scala b/compiler/src/dotty/tools/dotc/CompilationUnit.scala index 4cbd228c02a2..e9a20b162436 100644 --- a/compiler/src/dotty/tools/dotc/CompilationUnit.scala +++ b/compiler/src/dotty/tools/dotc/CompilationUnit.scala @@ -3,12 +3,14 @@ package dotc import util.SourceFile import ast.{tpd, untpd} -import tpd.{ Tree, TreeTraverser } +import dotty.tools.dotc.ast.Trees +import tpd.{Tree, TreeTraverser} import typer.PrepareInlineable.InlineAccessors import dotty.tools.dotc.core.Contexts.Context import dotty.tools.dotc.core.SymDenotations.ClassDenotation import dotty.tools.dotc.core.Symbols._ import dotty.tools.dotc.transform.SymUtils._ +import dotty.tools.dotc.typer.Inliner class CompilationUnit(val source: SourceFile) { @@ -57,6 +59,11 @@ object CompilationUnit { def traverse(tree: Tree)(implicit ctx: Context): Unit = { if (tree.symbol.isQuote) containsQuotes = true + tree match { + case _: tpd.RefTree | _: Trees.GenericApply[_] if Inliner.isInlineable(tree) => + containsQuotes = true // May inline a quote + case _ => + } traverseChildren(tree) } } From c6c53893bab339fa5ed260643ac577bb49601b45 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Tue, 9 Oct 2018 17:19:14 +0200 Subject: [PATCH 10/17] Fix tuples inlining When inlining a `this` binding that referes to a result typed with a match type, this type must be normalized and used. --- compiler/src/dotty/tools/dotc/typer/Inliner.scala | 5 ++++- tests/{disabled => }/run/tuples1.scala | 0 tests/run/tuples1a.scala | 9 +++++++++ 3 files changed, 13 insertions(+), 1 deletion(-) rename tests/{disabled => }/run/tuples1.scala (100%) create mode 100644 tests/run/tuples1a.scala diff --git a/compiler/src/dotty/tools/dotc/typer/Inliner.scala b/compiler/src/dotty/tools/dotc/typer/Inliner.scala index 86b5fcff5e68..901e684c86ff 100644 --- a/compiler/src/dotty/tools/dotc/typer/Inliner.scala +++ b/compiler/src/dotty/tools/dotc/typer/Inliner.scala @@ -301,7 +301,10 @@ class Inliner(call: tpd.Tree, rhsToInline: tpd.Tree)(implicit ctx: Context) { private def registerType(tpe: Type): Unit = tpe match { case tpe: ThisType if !canElideThis(tpe) && !thisProxy.contains(tpe.cls) => val proxyName = s"${tpe.cls.name}_this".toTermName - val proxyType = tpe.asSeenFrom(inlineCallPrefix.tpe, inlinedMethod.owner).widenIfUnstable + val proxyType = inlineCallPrefix.tpe.tryNormalize match { + case typeMatchResult if typeMatchResult.exists => typeMatchResult + case _ => tpe.asSeenFrom(inlineCallPrefix.tpe, inlinedMethod.owner).widenIfUnstable + } thisProxy(tpe.cls) = newSym(proxyName, InlineProxy, proxyType).termRef if (!tpe.cls.isStaticOwner) registerType(inlinedMethod.owner.thisType) // make sure we have a base from which to outer-select diff --git a/tests/disabled/run/tuples1.scala b/tests/run/tuples1.scala similarity index 100% rename from tests/disabled/run/tuples1.scala rename to tests/run/tuples1.scala diff --git a/tests/run/tuples1a.scala b/tests/run/tuples1a.scala new file mode 100644 index 000000000000..ee1e5b9aa360 --- /dev/null +++ b/tests/run/tuples1a.scala @@ -0,0 +1,9 @@ +object Test extends App { + val t7 = '5' *: 4 *: "C" *: () + + val t7a = t7.tail + val t7b = t7a.tail + val t7c: Unit = (t7.tail: (Int, String)).tail + val t7d: Unit = (t7.tail: Int *: String *: Unit).tail + val t7e: Unit = t7.tail.tail +} From 9e14dee4c448306a65055313c78d4b007f5ed425 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Tue, 9 Oct 2018 17:27:37 +0200 Subject: [PATCH 11/17] Fix dependent function type test Previously the `implicitly[C]` was refining its return type of `ev` by inlining. As we are now inlining after typer we refine the return type explicitly to `ev.type`. --- tests/{disabled => }/pos/depfuntype.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename tests/{disabled => }/pos/depfuntype.scala (92%) diff --git a/tests/disabled/pos/depfuntype.scala b/tests/pos/depfuntype.scala similarity index 92% rename from tests/disabled/pos/depfuntype.scala rename to tests/pos/depfuntype.scala index 308b7aecc58d..15a30d28da07 100644 --- a/tests/disabled/pos/depfuntype.scala +++ b/tests/pos/depfuntype.scala @@ -19,7 +19,7 @@ object Test { // Reproduced here because the one from DottyPredef is lacking a Tasty tree and // therefore can't be inlined when testing non-bootstrapped. // But inlining `implicitly` is vital to make the definition of `ifun` below work. - inline final def implicitly[T](implicit ev: T): T = ev + inline final def implicitly[T](implicit ev: T): ev.type = ev type IDF = implicit (x: C) => x.M From f28e2e1ee566bbc5a8a77c1a6345e6b779b311f3 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Tue, 9 Oct 2018 17:41:29 +0200 Subject: [PATCH 12/17] Only run InlineCalls if the tree contains an inline call --- compiler/src/dotty/tools/dotc/CompilationUnit.scala | 9 ++++++++- .../src/dotty/tools/dotc/transform/InlineCalls.scala | 5 ++++- compiler/src/dotty/tools/dotc/transform/PostTyper.scala | 2 +- 3 files changed, 13 insertions(+), 3 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/CompilationUnit.scala b/compiler/src/dotty/tools/dotc/CompilationUnit.scala index e9a20b162436..bedcfb7ddbbb 100644 --- a/compiler/src/dotty/tools/dotc/CompilationUnit.scala +++ b/compiler/src/dotty/tools/dotc/CompilationUnit.scala @@ -25,6 +25,11 @@ class CompilationUnit(val source: SourceFile) { /** Pickled TASTY binaries, indexed by class. */ var pickled: Map[ClassSymbol, Array[Byte]] = Map() + /** Will be reset to `true` if `tpdTree` contains a call to an inline method. The information + * is used in phase InlineCalls in order to avoid traversing an inline-less tree. + */ + var containsInlineCalls: Boolean = false + /** Will be reset to `true` if `untpdTree` contains `Quote` trees. The information * is used in phase ReifyQuotes in order to avoid traversing a quote-less tree. */ @@ -48,6 +53,7 @@ object CompilationUnit { if (forceTrees) { val force = new Force force.traverse(unit1.tpdTree) + unit1.containsInlineCalls = force.containsInline unit1.containsQuotesOrSplices = force.containsQuotes } unit1 @@ -55,13 +61,14 @@ object CompilationUnit { /** Force the tree to be loaded */ private class Force extends TreeTraverser { + var containsInline = false var containsQuotes = false def traverse(tree: Tree)(implicit ctx: Context): Unit = { if (tree.symbol.isQuote) containsQuotes = true tree match { case _: tpd.RefTree | _: Trees.GenericApply[_] if Inliner.isInlineable(tree) => - containsQuotes = true // May inline a quote + containsInline = true case _ => } traverseChildren(tree) diff --git a/compiler/src/dotty/tools/dotc/transform/InlineCalls.scala b/compiler/src/dotty/tools/dotc/transform/InlineCalls.scala index c1a15f1f892d..ddd9c618da7d 100644 --- a/compiler/src/dotty/tools/dotc/transform/InlineCalls.scala +++ b/compiler/src/dotty/tools/dotc/transform/InlineCalls.scala @@ -5,6 +5,7 @@ import dotty.tools.dotc.ast.tpd import dotty.tools.dotc.core.Contexts._ import dotty.tools.dotc.core.Phases.Phase import dotty.tools.dotc.core.Types.MethodicType +import dotty.tools.dotc.transform.SymUtils._ import dotty.tools.dotc.typer.{ConstFold, Inliner} /** β-reduce all calls to inline methods and preform constant folding */ @@ -14,7 +15,7 @@ class InlineCalls extends MacroTransform { thisPhase => override def phaseName: String = InlineCalls.name override def run(implicit ctx: Context): Unit = - if (!ctx.settings.YnoInline.value) super.run + if (ctx.compilationUnit.containsInlineCalls && !ctx.settings.YnoInline.value) super.run override def transformPhase(implicit ctx: Context): Phase = thisPhase.next @@ -31,6 +32,8 @@ class InlineCalls extends MacroTransform { thisPhase => newTree.symbol.defTree = newTree // update tree set in PostTyper or set for inlined members newTree case _ => + if (tree.symbol.isQuote || tree.symbol.isSplice) + ctx.compilationUnit.containsQuotesOrSplices = true ConstFold(super.transform(tree)) } } diff --git a/compiler/src/dotty/tools/dotc/transform/PostTyper.scala b/compiler/src/dotty/tools/dotc/transform/PostTyper.scala index 9a508391cbec..965a38905c4e 100644 --- a/compiler/src/dotty/tools/dotc/transform/PostTyper.scala +++ b/compiler/src/dotty/tools/dotc/transform/PostTyper.scala @@ -180,7 +180,7 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisPhase private def handleInlineCall(sym: Symbol)(implicit ctx: Context): Unit = { if (sym.is(Inline)) - ctx.compilationUnit.containsQuotesOrSplices = true + ctx.compilationUnit.containsInlineCalls = true } private object dropInlines extends TreeMap { From 0832e027208830d61e1efba0ca82dfcdad608114 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Tue, 9 Oct 2018 17:48:13 +0200 Subject: [PATCH 13/17] Update doc --- compiler/src/dotty/tools/dotc/ast/Trees.scala | 5 ++--- compiler/src/dotty/tools/dotc/typer/Inliner.scala | 7 ++----- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/ast/Trees.scala b/compiler/src/dotty/tools/dotc/ast/Trees.scala index 453516880ab9..a4c6c25c6dd8 100644 --- a/compiler/src/dotty/tools/dotc/ast/Trees.scala +++ b/compiler/src/dotty/tools/dotc/ast/Trees.scala @@ -583,9 +583,8 @@ object Trees { /** A tree representing inlined code. * - * @param call Info about the original call that was inlined - * Until PostTyper, this is the full call, afterwards only - * a reference to the toplevel class from which the call was inlined. + * @param call Info about the original call that was inlined. + * Only a reference to the toplevel class from which the call was inlined. * @param bindings Bindings for proxies to be used in the inlined code * @param expansion The inlined tree, minus bindings. * diff --git a/compiler/src/dotty/tools/dotc/typer/Inliner.scala b/compiler/src/dotty/tools/dotc/typer/Inliner.scala index 901e684c86ff..58849305de45 100644 --- a/compiler/src/dotty/tools/dotc/typer/Inliner.scala +++ b/compiler/src/dotty/tools/dotc/typer/Inliner.scala @@ -460,11 +460,8 @@ class Inliner(call: tpd.Tree, rhsToInline: tpd.Tree)(implicit ctx: Context) { // - the call's position // in the call field of an Inlined node. // The trace has enough info to completely reconstruct positions. - // The minimization is done for two reasons: - // 1. To save space (calls might contain large inline arguments, which would otherwise - // be duplicated - // 2. To enable correct pickling (calls can share symbols with the inlined code, which - // would trigger an assertion when pickling). + // The minimization is done for the following reason: + // * To save space (calls might contain large inline arguments, which would otherwise be duplicated val callTrace = Ident(call.symbol.topLevelClass.typeRef).withPos(call.pos) // Take care that only argument bindings go into `bindings`, since positions are From 894eceec4e88d2f3a4b7403a1a3a822d12f99293 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Tue, 16 Oct 2018 16:55:11 +0200 Subject: [PATCH 14/17] Update comment --- tests/pos/depfuntype.scala | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/tests/pos/depfuntype.scala b/tests/pos/depfuntype.scala index 15a30d28da07..b132b843c240 100644 --- a/tests/pos/depfuntype.scala +++ b/tests/pos/depfuntype.scala @@ -16,9 +16,7 @@ object Test { val z = depfun3(d) val z1: d.M = z - // Reproduced here because the one from DottyPredef is lacking a Tasty tree and - // therefore can't be inlined when testing non-bootstrapped. - // But inlining `implicitly` is vital to make the definition of `ifun` below work. + // Reproduced here because the one from DottyPredef is lacking a parameter dependency of the return type `ev.type` inline final def implicitly[T](implicit ev: T): ev.type = ev type IDF = implicit (x: C) => x.M From 72f3ab60e7f2d822e295578e7d53223d2f81ed43 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Thu, 18 Oct 2018 07:58:36 +0200 Subject: [PATCH 15/17] Fix typo --- compiler/src/dotty/tools/dotc/transform/InlineCalls.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/src/dotty/tools/dotc/transform/InlineCalls.scala b/compiler/src/dotty/tools/dotc/transform/InlineCalls.scala index ddd9c618da7d..577716cea5d0 100644 --- a/compiler/src/dotty/tools/dotc/transform/InlineCalls.scala +++ b/compiler/src/dotty/tools/dotc/transform/InlineCalls.scala @@ -8,7 +8,7 @@ import dotty.tools.dotc.core.Types.MethodicType import dotty.tools.dotc.transform.SymUtils._ import dotty.tools.dotc.typer.{ConstFold, Inliner} -/** β-reduce all calls to inline methods and preform constant folding */ +/** β-reduce all calls to inline methods and perform constant folding */ class InlineCalls extends MacroTransform { thisPhase => import tpd._ From 954c25b3ae85c2f6c68eb4ae209b616f0f987e67 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Thu, 18 Oct 2018 08:00:45 +0200 Subject: [PATCH 16/17] Add isInlineCall to TreeInfo --- compiler/src/dotty/tools/dotc/CompilationUnit.scala | 7 ++----- compiler/src/dotty/tools/dotc/ast/TreeInfo.scala | 10 +++++++++- .../src/dotty/tools/dotc/transform/InlineCalls.scala | 2 +- 3 files changed, 12 insertions(+), 7 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/CompilationUnit.scala b/compiler/src/dotty/tools/dotc/CompilationUnit.scala index bedcfb7ddbbb..059b0fcc1b7e 100644 --- a/compiler/src/dotty/tools/dotc/CompilationUnit.scala +++ b/compiler/src/dotty/tools/dotc/CompilationUnit.scala @@ -66,11 +66,8 @@ object CompilationUnit { def traverse(tree: Tree)(implicit ctx: Context): Unit = { if (tree.symbol.isQuote) containsQuotes = true - tree match { - case _: tpd.RefTree | _: Trees.GenericApply[_] if Inliner.isInlineable(tree) => - containsInline = true - case _ => - } + if (tpd.isInlineCall(tree)) + containsInline = true traverseChildren(tree) } } diff --git a/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala b/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala index 4f59b51a9a53..0b1bf71a18db 100644 --- a/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala +++ b/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala @@ -5,7 +5,7 @@ package ast import core._ import Flags._, Trees._, Types._, Contexts._ import Names._, StdNames._, NameOps._, Symbols._ -import typer.ConstFold +import typer.{ConstFold, Inliner} import reporting.trace import scala.annotation.tailrec @@ -759,6 +759,14 @@ trait TypedTreeInfo extends TreeInfo[Type] { self: Trees.Instance[Type] => false } + /** Is this call a call to a method that is marked as Inline */ + def isInlineCall(arg: Tree)(implicit ctx: Context): Boolean = arg match { + case _: RefTree | _: GenericApply[_] => + !arg.tpe.widenDealias.isInstanceOf[MethodicType] && Inliner.isInlineable(arg) + case _ => + false + } + /** Structural tree comparison (since == on trees is reference equality). * For the moment, only Ident, Select, Literal, Apply and TypeApply are supported */ diff --git a/compiler/src/dotty/tools/dotc/transform/InlineCalls.scala b/compiler/src/dotty/tools/dotc/transform/InlineCalls.scala index 577716cea5d0..837740bd928f 100644 --- a/compiler/src/dotty/tools/dotc/transform/InlineCalls.scala +++ b/compiler/src/dotty/tools/dotc/transform/InlineCalls.scala @@ -24,7 +24,7 @@ class InlineCalls extends MacroTransform { thisPhase => class InlineCallsTransformer extends Transformer { override def transform(tree: Tree)(implicit ctx: Context): Tree = tree match { - case _: RefTree | _: GenericApply[_] if !tree.tpe.widenDealias.isInstanceOf[MethodicType] && Inliner.isInlineable(tree) && !ctx.reporter.hasErrors => + case tree if isInlineCall(tree) && !ctx.reporter.hasErrors => val tree2 = super.transform(tree) // transform arguments before inlining (inline arguments and constant fold arguments) transform(Inliner.inlineCall(tree2, tree.tpe.widen)) case _: MemberDef => From c12e8753f3dccc6b0bc3b84f19e8160434c340c8 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Thu, 18 Oct 2018 08:27:56 +0200 Subject: [PATCH 17/17] Erase rhs of erased non inlined val/def in PostTyper --- .../tools/dotc/transform/PostTyper.scala | 20 ++++++------------- compiler/test/dotc/pos-from-tasty.blacklist | 5 ++++- 2 files changed, 10 insertions(+), 15 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/PostTyper.scala b/compiler/src/dotty/tools/dotc/transform/PostTyper.scala index 965a38905c4e..8add8b11c4e1 100644 --- a/compiler/src/dotty/tools/dotc/transform/PostTyper.scala +++ b/compiler/src/dotty/tools/dotc/transform/PostTyper.scala @@ -40,8 +40,7 @@ object PostTyper { * * (10) Adds Child annotations to all sealed classes * - * (11) Minimizes `call` fields of `Inlined` nodes to just point to the toplevel - * class from which code was inlined. + * (11) Replace RHS of `erased` (but not `inline`) members by `(???: rhs.type)` * * 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 @@ -183,14 +182,6 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisPhase ctx.compilationUnit.containsInlineCalls = true } - private object dropInlines extends TreeMap { - override def transform(tree: Tree)(implicit ctx: Context): Tree = tree match { - case Inlined(call, _, _) => - cpy.Inlined(tree)(call, Nil, Typed(ref(defn.Predef_undefined), TypeTree(tree.tpe))) - case _ => super.transform(tree) - } - } - override def transform(tree: Tree)(implicit ctx: Context): Tree = try tree match { case tree: Ident if !tree.isType => @@ -218,7 +209,7 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisPhase tree.fun, tree.args.map(arg => if (methType.isImplicitMethod && arg.pos.isSynthetic) ref(defn.Predef_undefined) - else dropInlines.transform(arg))) + else arg)) else tree methPart(app) match { @@ -328,9 +319,10 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisPhase } /** Transforms the rhs tree into a its default tree if it is in an `erased` val/def. - * Performed to shrink the tree that is known to be erased later. - */ + * Performed to shrink the tree that is known to be erased later. + */ private def normalizeErasedRhs(rhs: Tree, sym: Symbol)(implicit ctx: Context) = - if (sym.isEffectivelyErased) dropInlines.transform(rhs) else rhs + if (!sym.isEffectivelyErased || sym.isInlineMethod || !rhs.tpe.exists) rhs + else Typed(ref(defn.Predef_undefined), TypeTree(rhs.tpe)) } } diff --git a/compiler/test/dotc/pos-from-tasty.blacklist b/compiler/test/dotc/pos-from-tasty.blacklist index a48abcd017a0..ace498a34d3f 100644 --- a/compiler/test/dotc/pos-from-tasty.blacklist +++ b/compiler/test/dotc/pos-from-tasty.blacklist @@ -17,4 +17,7 @@ repeatedArgs213.scala default-super.scala # Need to implement printing of match types -matchtype.scala \ No newline at end of file +matchtype.scala + +# Fails on CI (not locally) +inline-named-typeargs.scala