From 4f67e612ec8bf7185dfbeac2b0065ad39c2e7325 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sun, 4 Nov 2018 11:30:31 +0100 Subject: [PATCH] Go back to inlining during typing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reverts the following commits: Join containsQuotesOrSplices and containsInlineCalls (reverted from commit 8cf1385fc956a8d4ca12e14527301e3272c96d41) Make InlineCalls an object (reverted from commit ad26554411b9c5b340e96f99c33beb299a7170fa) Remove Inlined and InlineProxy from TASTy (reverted from commit 976e095fd79e7811132b6d85066bbe6253d33bad) Move inlining inside Reify quotes Split some neg tests as now only the first error is emitted. (reverted from commit 21e65f1e4256ae4eb097e84a668ae5ab94cd0744) Erase rhs of erased non inlined val/def in PostTyper (reverted from commit c12e8753f3dccc6b0bc3b84f19e8160434c340c8) Add isInlineCall to TreeInfo (reverted from commit 954c25b3ae85c2f6c68eb4ae209b616f0f987e67) Update doc (reverted from commit 0832e027208830d61e1efba0ca82dfcdad608114) Only run InlineCalls if the tree contains an inline call (reverted from commit f28e2e1ee566bbc5a8a77c1a6345e6b779b311f3) Check if unpickled tree has inline nodes (reverted from commit 927ae4e60c58318336d493b4c4ecc29ec88242e4) Add a bit of documentation (reverted from commit da0c2493cbedf82081ca071363f2713aa5928e89) Fix constant folding during inlining (reverted from commit 32c8798153276a428bd4a1bc7b3e4f924e1f0489) Move inline β-reduction after Pickler (reverted from commit c68dc1f5ace7486339af1d55884677459d0e85fd) Normalize call at Inlined node creation (reverted from commit 746fdd7831a588066f2cfc1c64a000df79d40b63) Move inline β-reduction after post typer (reverted from commit 432eb0a8e2a320e72517cf68c622f0300d221f12) Move inline β-reduction out of typer (reverted from commit 6acaf31eafca43fb14d566b3eef85c5132005b02) --- .../dotty/tools/dotc/CompilationUnit.scala | 9 +- compiler/src/dotty/tools/dotc/Compiler.scala | 2 +- .../src/dotty/tools/dotc/ast/TreeInfo.scala | 10 +- compiler/src/dotty/tools/dotc/ast/Trees.scala | 5 +- .../tools/dotc/core/tasty/TastyFormat.scala | 107 ++++++++++-------- .../tools/dotc/core/tasty/TreePickler.scala | 12 ++ .../tools/dotc/core/tasty/TreeUnpickler.scala | 12 ++ .../dotc/decompiler/TASTYDecompiler.scala | 1 - .../tools/dotc/transform/PostTyper.scala | 38 ++++--- .../dotty/tools/dotc/transform/Staging.scala | 36 +----- .../src/dotty/tools/dotc/typer/Inliner.scala | 11 +- .../src/dotty/tools/dotc/typer/Typer.scala | 7 ++ compiler/test/dotc/pos-from-tasty.blacklist | 5 +- .../test/dotc/pos-recompilation.whitelist | 1 + .../test/dotc/run-test-pickling.blacklist | 1 + .../quote-run-in-macro-2/quoted_2.scala | 6 + .../quote-run-in-macro-3/quoted_1.scala | 11 -- .../quote-run-in-macro-3/quoted_2.scala | 6 - .../quote-run-in-macro-4/quoted_1.scala | 11 -- .../quote-run-in-macro-4/quoted_2.scala | 9 -- tests/neg/tasty-macro-assert-2/quoted_1.scala | 69 ----------- tests/neg/tasty-macro-assert-2/quoted_2.scala | 10 -- tests/neg/tasty-macro-assert/quoted_2.scala | 2 + tests/pos/simpleInline.decompiled | 4 +- .../tasty-extractors-owners.check | 4 +- tests/run-with-compiler/i3876-d.check | 2 +- 26 files changed, 144 insertions(+), 247 deletions(-) delete mode 100644 tests/neg-with-compiler/quote-run-in-macro-3/quoted_1.scala delete mode 100644 tests/neg-with-compiler/quote-run-in-macro-3/quoted_2.scala delete mode 100644 tests/neg-with-compiler/quote-run-in-macro-4/quoted_1.scala delete mode 100644 tests/neg-with-compiler/quote-run-in-macro-4/quoted_2.scala delete mode 100644 tests/neg/tasty-macro-assert-2/quoted_1.scala delete mode 100644 tests/neg/tasty-macro-assert-2/quoted_2.scala diff --git a/compiler/src/dotty/tools/dotc/CompilationUnit.scala b/compiler/src/dotty/tools/dotc/CompilationUnit.scala index c275fb25ca66..00527db565a8 100644 --- a/compiler/src/dotty/tools/dotc/CompilationUnit.scala +++ b/compiler/src/dotty/tools/dotc/CompilationUnit.scala @@ -3,14 +3,12 @@ package dotc import util.SourceFile import ast.{tpd, untpd} -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) { @@ -25,8 +23,8 @@ class CompilationUnit(val source: SourceFile) { /** Pickled TASTY binaries, indexed by class. */ var pickled: Map[ClassSymbol, Array[Byte]] = Map() - /** Will be set to `true` if contains `Quote`, `Splice` or calls to inline methods. - * The information is used in phase `Staging` in order to avoid traversing a quote-less tree. + /** Will be set to `true` if contains `Quote`. + * The information is used in phase `Staging` in order to avoid traversing trees that need no transformations. */ var needsStaging: Boolean = false @@ -57,8 +55,7 @@ object CompilationUnit { private class Force extends TreeTraverser { var needsStaging = false def traverse(tree: Tree)(implicit ctx: Context): Unit = { - // Note that top-level splices are still inside the inline methods - if (tree.symbol.isQuote || tpd.isInlineCall(tree)) + if (tree.symbol.isQuote) needsStaging = true traverseChildren(tree) } diff --git a/compiler/src/dotty/tools/dotc/Compiler.scala b/compiler/src/dotty/tools/dotc/Compiler.scala index 60a05d2d26c9..461b684e0bf4 100644 --- a/compiler/src/dotty/tools/dotc/Compiler.scala +++ b/compiler/src/dotty/tools/dotc/Compiler.scala @@ -44,7 +44,7 @@ class Compiler { /** Phases dealing with TASTY tree pickling and unpickling */ protected def picklerPhases: List[List[Phase]] = List(new Pickler) :: // Generate TASTY info - List(new Staging) :: // Inline calls, expand macros and turn quoted trees into explicit run-time data structures + List(new Staging) :: // Turn quoted trees into explicit run-time data structures Nil /** Phases dealing with the transformation from pickled trees to backend trees */ diff --git a/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala b/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala index 5daee6fc9b46..01f8d21259db 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, Inliner} +import typer.ConstFold import reporting.trace import scala.annotation.tailrec @@ -770,14 +770,6 @@ 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/ast/Trees.scala b/compiler/src/dotty/tools/dotc/ast/Trees.scala index 10b5e93723bf..8206e8dcd763 100644 --- a/compiler/src/dotty/tools/dotc/ast/Trees.scala +++ b/compiler/src/dotty/tools/dotc/ast/Trees.scala @@ -585,8 +585,9 @@ object Trees { /** A tree representing inlined code. * - * @param call Info about the original call that was inlined. - * Only a reference to the toplevel class from which the call was inlined. + * @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 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/core/tasty/TastyFormat.scala b/compiler/src/dotty/tools/dotc/core/tasty/TastyFormat.scala index 741176aad85f..b59a7fb02ecf 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TastyFormat.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TastyFormat.scala @@ -86,6 +86,7 @@ Standard-Section: "ASTs" TopLevelStat* TYPED Length expr_Term ascriptionType_Tern ASSIGN Length lhs_Term rhs_Term BLOCK Length expr_Term Stat* + INLINED Length expr_Term call_Term? ValOrDefDef* LAMBDA Length meth_Term target_Type? IF Length cond_Term then_Term else_Term MATCH Length sel_Term CaseDef* @@ -184,6 +185,7 @@ Standard-Section: "ASTs" TopLevelStat* OVERRIDE INLINE MACRO // inline method containing toplevel splices + INLINEPROXY // symbol of binding representing an inline parameter STATIC // mapped to static Java member OBJECT // an object or its class TRAIT // a trait @@ -234,7 +236,7 @@ Standard Section: "Comments" Comment* object TastyFormat { final val header: Array[Int] = Array(0x5C, 0xA1, 0xAB, 0x1F) - val MajorVersion: Int = 12 + val MajorVersion: Int = 11 val MinorVersion: Int = 0 /** Tags used to serialize names */ @@ -287,26 +289,27 @@ object TastyFormat { final val IMPLICIT = 13 final val LAZY = 14 final val OVERRIDE = 15 - final val INLINE = 16 - final val STATIC = 17 - final val OBJECT = 18 - final val TRAIT = 19 - final val ENUM = 20 - final val LOCAL = 21 - final val SYNTHETIC = 22 - final val ARTIFACT = 23 - final val MUTABLE = 24 - final val LABEL = 25 - final val FIELDaccessor = 26 - final val CASEaccessor = 27 - final val COVARIANT = 28 - final val CONTRAVARIANT = 29 - final val SCALA2X = 30 - final val DEFAULTparameterized = 31 - final val STABLE = 32 - final val MACRO = 33 - final val ERASED = 34 - final val PARAMsetter = 35 + final val INLINEPROXY = 16 + final val INLINE = 17 + final val STATIC = 18 + final val OBJECT = 19 + final val TRAIT = 20 + final val ENUM = 21 + final val LOCAL = 22 + final val SYNTHETIC = 23 + final val ARTIFACT = 24 + final val MUTABLE = 25 + final val LABEL = 26 + final val FIELDaccessor = 27 + final val CASEaccessor = 28 + final val COVARIANT = 29 + final val CONTRAVARIANT = 30 + final val SCALA2X = 31 + final val DEFAULTparameterized = 32 + final val STABLE = 33 + final val MACRO = 34 + final val ERASED = 35 + final val PARAMsetter = 36 // Cat. 2: tag Nat @@ -380,35 +383,36 @@ object TastyFormat { final val RETURN = 144 final val WHILE = 145 final val TRY = 146 - final val SELECTouter = 147 - final val REPEATED = 148 - final val BIND = 149 - final val ALTERNATIVE = 150 - final val UNAPPLY = 151 - final val ANNOTATEDtype = 152 - final val ANNOTATEDtpt = 153 - final val CASEDEF = 154 - final val TEMPLATE = 155 - final val SUPER = 156 - final val SUPERtype = 157 - final val REFINEDtype = 158 - final val REFINEDtpt = 159 - final val APPLIEDtype = 160 - final val APPLIEDtpt = 161 - final val TYPEBOUNDS = 162 - final val TYPEBOUNDStpt = 163 - final val ANDtype = 164 - final val ANDtpt = 165 - final val ORtype = 166 - final val ORtpt = 167 - final val POLYtype = 168 - final val TYPELAMBDAtype = 169 - final val LAMBDAtpt = 170 - final val PARAMtype = 171 - final val ANNOTATION = 172 - final val TERMREFin = 173 - final val TYPEREFin = 174 - final val OBJECTDEF = 175 + final val INLINED = 147 + final val SELECTouter = 148 + final val REPEATED = 149 + final val BIND = 150 + final val ALTERNATIVE = 151 + final val UNAPPLY = 152 + final val ANNOTATEDtype = 153 + final val ANNOTATEDtpt = 154 + final val CASEDEF = 155 + final val TEMPLATE = 156 + final val SUPER = 157 + final val SUPERtype = 158 + final val REFINEDtype = 159 + final val REFINEDtpt = 160 + final val APPLIEDtype = 161 + final val APPLIEDtpt = 162 + final val TYPEBOUNDS = 163 + final val TYPEBOUNDStpt = 164 + final val ANDtype = 165 + final val ANDtpt = 166 + final val ORtype = 167 + final val ORtpt = 168 + final val POLYtype = 169 + final val TYPELAMBDAtype = 170 + final val LAMBDAtpt = 171 + final val PARAMtype = 172 + final val ANNOTATION = 173 + final val TERMREFin = 174 + final val TYPEREFin = 175 + final val OBJECTDEF = 176 // In binary: 101101EI // I = implicit method type @@ -458,6 +462,7 @@ object TastyFormat { | LAZY | OVERRIDE | INLINE + | INLINEPROXY | MACRO | STATIC | OBJECT @@ -515,6 +520,7 @@ object TastyFormat { case LAZY => "LAZY" case OVERRIDE => "OVERRIDE" case INLINE => "INLINE" + case INLINEPROXY => "INLINEPROXY" case MACRO => "MACRO" case STATIC => "STATIC" case OBJECT => "OBJECT" @@ -583,6 +589,7 @@ object TastyFormat { case MATCH => "MATCH" case RETURN => "RETURN" case WHILE => "WHILE" + case INLINED => "INLINED" case SELECTouter => "SELECTouter" case TRY => "TRY" case REPEATED => "REPEATED" diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala b/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala index 5a4cbc312076..a67197786cd2 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala @@ -442,6 +442,17 @@ class TreePickler(pickler: TastyPickler) { case SeqLiteral(elems, elemtpt) => writeByte(REPEATED) withLength { pickleTree(elemtpt); elems.foreach(pickleTree) } + case Inlined(call, bindings, expansion) => + writeByte(INLINED) + bindings.foreach(preRegister) + withLength { + pickleTree(expansion) + if (!call.isEmpty) pickleTree(call) + bindings.foreach { b => + assert(b.isInstanceOf[DefDef] || b.isInstanceOf[ValDef]) + pickleTree(b) + } + } case Bind(name, body) => registerDef(tree.symbol) writeByte(BIND) @@ -608,6 +619,7 @@ class TreePickler(pickler: TastyPickler) { if (flags is Case) writeByte(CASE) if (flags is Override) writeByte(OVERRIDE) if (flags is Inline) writeByte(INLINE) + if (flags is InlineProxy) writeByte(INLINEPROXY) if (flags is Macro) writeByte(MACRO) if (flags is JavaStatic) writeByte(STATIC) if (flags is Module) writeByte(OBJECT) diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala index fcabd7184550..f1cd996f4f18 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala @@ -604,6 +604,7 @@ class TreeUnpickler(reader: TastyReader, case LAZY => addFlag(Lazy) case OVERRIDE => addFlag(Override) case INLINE => addFlag(Inline) + case INLINEPROXY => addFlag(InlineProxy) case MACRO => addFlag(Macro) case STATIC => addFlag(JavaStatic) case OBJECT => addFlag(Module) @@ -1073,6 +1074,17 @@ class TreeUnpickler(reader: TastyReader, val stats = readStats(ctx.owner, end) val expr = exprReader.readTerm() Block(stats, expr) + case INLINED => + val exprReader = fork + skipTree() + def maybeCall = nextUnsharedTag match { + case VALDEF | DEFDEF => EmptyTree + case _ => readTerm() + } + val call = ifBefore(end)(maybeCall, EmptyTree) + val bindings = readStats(ctx.owner, end).asInstanceOf[List[ValOrDefDef]] + val expansion = exprReader.readTerm() // need bindings in scope, so needs to be read before + Inlined(call, bindings, expansion) case IF => If(readTerm(), readTerm(), readTerm()) case LAMBDA => diff --git a/compiler/src/dotty/tools/dotc/decompiler/TASTYDecompiler.scala b/compiler/src/dotty/tools/dotc/decompiler/TASTYDecompiler.scala index 62627a6b050a..4601ff9d1a12 100644 --- a/compiler/src/dotty/tools/dotc/decompiler/TASTYDecompiler.scala +++ b/compiler/src/dotty/tools/dotc/decompiler/TASTYDecompiler.scala @@ -15,7 +15,6 @@ class TASTYDecompiler extends TASTYCompiler { Nil override protected def picklerPhases: List[List[Phase]] = Nil - override protected def transformPhases: List[List[Phase]] = Nil override protected def backendPhases: List[List[Phase]] = diff --git a/compiler/src/dotty/tools/dotc/transform/PostTyper.scala b/compiler/src/dotty/tools/dotc/transform/PostTyper.scala index 3c99b15347c7..355bf11bc3d1 100644 --- a/compiler/src/dotty/tools/dotc/transform/PostTyper.scala +++ b/compiler/src/dotty/tools/dotc/transform/PostTyper.scala @@ -40,7 +40,8 @@ object PostTyper { * * (10) Adds Child annotations to all sealed classes * - * (11) Replace RHS of `erased` (but not `inline`) members by `(???: rhs.type)` + * (11) Minimizes `call` fields of `Inlined` nodes to just point to the toplevel + * class from which code was inlined. * * 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 @@ -177,22 +178,23 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisPhase } } - private def handleInlineCall(sym: Symbol)(implicit ctx: Context): Unit = { - if (sym.is(Inline)) - ctx.compilationUnit.needsStaging = 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 => - 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) @@ -201,7 +203,6 @@ 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) @@ -209,7 +210,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 arg)) + else dropInlines.transform(arg))) else tree methPart(app) match { @@ -222,7 +223,6 @@ 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,6 +236,19 @@ 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) @@ -322,10 +335,9 @@ 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 || sym.isInlineMethod || !rhs.tpe.exists) rhs - else Typed(ref(defn.Predef_undefined), TypeTree(rhs.tpe)) + if (sym.isEffectivelyErased) dropInlines.transform(rhs) else rhs } } diff --git a/compiler/src/dotty/tools/dotc/transform/Staging.scala b/compiler/src/dotty/tools/dotc/transform/Staging.scala index f94b422a5670..90a727e0eec8 100644 --- a/compiler/src/dotty/tools/dotc/transform/Staging.scala +++ b/compiler/src/dotty/tools/dotc/transform/Staging.scala @@ -2,12 +2,7 @@ package dotty.tools.dotc package transform import core._ -import Decorators._ -import Flags._ -import Types._ -import Contexts._ -import Symbols._ -import Constants._ +import Decorators._, Flags._, Types._, Contexts._, Symbols._, Constants._ import ast.Trees._ import ast.{TreeTypeMap, untpd} import util.Positions._ @@ -20,12 +15,11 @@ import typer.Implicits.SearchFailureType import scala.collection.mutable import dotty.tools.dotc.core.StdNames._ import dotty.tools.dotc.core.quoted._ -import dotty.tools.dotc.typer.{ConstFold, Inliner} import dotty.tools.dotc.util.SourcePosition -/** Inline calls to inline methods, evaluates macros, translates quoted terms (and types) - * to `unpickle` method calls and checks that the phase consistency principle (PCP) holds. +/** Translates quoted terms and types to `unpickle` method calls. + * Checks that the phase consistency principle (PCP) holds. * * * Transforms top level quote @@ -443,8 +437,7 @@ class Staging extends MacroTransformWithImplicits { else if (enclosingInlineds.nonEmpty) { // level 0 in an inlined call val spliceCtx = ctx.outer // drop the last `inlineContext` val pos: SourcePosition = Decorators.sourcePos(enclosingInlineds.head.pos)(spliceCtx) - val splicedTree = InlineCalls.transform(splice.qualifier) // inline calls that where inlined at level -1 - val evaluatedSplice = Splicer.splice(splicedTree, pos, macroClassLoader)(spliceCtx).withPos(splice.pos) + val evaluatedSplice = Splicer.splice(splice.qualifier, pos, macroClassLoader)(spliceCtx).withPos(splice.pos) if (ctx.reporter.hasErrors) splice else transform(evaluatedSplice) } else if (!ctx.owner.isInlineMethod) { // level 0 outside an inline method @@ -567,9 +560,6 @@ class Staging extends MacroTransformWithImplicits { enteredSyms = enteredSyms.tail } tree match { - case tree if isInlineCall(tree) && level == 0 && !ctx.reporter.hasErrors && !ctx.settings.YnoInline.value => - val tree2 = super.transform(tree) // transform arguments before inlining (inline arguments and constant fold arguments) - transform(Inliner.inlineCall(tree2, tree.tpe.widen)) case Quoted(quotedTree) => quotation(quotedTree, tree) case tree: TypeTree if tree.tpe.typeSymbol.isSplice => @@ -620,7 +610,7 @@ class Staging extends MacroTransformWithImplicits { } case _ => markDef(tree) - ConstFold(checkLevel(mapOverTree(enteredSyms))) + checkLevel(mapOverTree(enteredSyms)) } } @@ -676,20 +666,4 @@ object Staging { /** Get the list of embedded trees */ def getTrees: List[tpd.Tree] = trees.toList } - - /** β-reduce all calls to inline methods and preform constant folding */ - object InlineCalls extends TreeMap { - override def transform(tree: Tree)(implicit ctx: Context): Tree = tree match { - case tree if isInlineCall(tree) && !ctx.reporter.hasErrors && !ctx.settings.YnoInline.value => - 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) - if (newTree.symbol.exists) - newTree.symbol.defTree = newTree // set for inlined members - newTree - case _ => - ConstFold(super.transform(tree)) - } - } } diff --git a/compiler/src/dotty/tools/dotc/typer/Inliner.scala b/compiler/src/dotty/tools/dotc/typer/Inliner.scala index 1432c5405cad..933e7c10c81f 100644 --- a/compiler/src/dotty/tools/dotc/typer/Inliner.scala +++ b/compiler/src/dotty/tools/dotc/typer/Inliner.scala @@ -455,18 +455,9 @@ 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 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 // different for bindings from arguments and bindings from body. - tpd.Inlined(callTrace, finalBindings, finalExpansion) + tpd.Inlined(call, finalBindings, finalExpansion) } } diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 9c3901128aa2..1c8acf4264d0 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -2452,6 +2452,13 @@ 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") diff --git a/compiler/test/dotc/pos-from-tasty.blacklist b/compiler/test/dotc/pos-from-tasty.blacklist index ace498a34d3f..a48abcd017a0 100644 --- a/compiler/test/dotc/pos-from-tasty.blacklist +++ b/compiler/test/dotc/pos-from-tasty.blacklist @@ -17,7 +17,4 @@ repeatedArgs213.scala default-super.scala # Need to implement printing of match types -matchtype.scala - -# Fails on CI (not locally) -inline-named-typeargs.scala +matchtype.scala \ No newline at end of file diff --git a/compiler/test/dotc/pos-recompilation.whitelist b/compiler/test/dotc/pos-recompilation.whitelist index ef14a97f1d25..eb4c22b9d238 100644 --- a/compiler/test/dotc/pos-recompilation.whitelist +++ b/compiler/test/dotc/pos-recompilation.whitelist @@ -985,6 +985,7 @@ t8132 t8177d t8177e t8177h +t8207 t8219 t8230a t8237 diff --git a/compiler/test/dotc/run-test-pickling.blacklist b/compiler/test/dotc/run-test-pickling.blacklist index 672cee097820..db1a51b342b3 100644 --- a/compiler/test/dotc/run-test-pickling.blacklist +++ b/compiler/test/dotc/run-test-pickling.blacklist @@ -7,3 +7,4 @@ lazy-traits.scala t8133 t8133b tuples1.scala +tuples1a.scala diff --git a/tests/neg-with-compiler/quote-run-in-macro-2/quoted_2.scala b/tests/neg-with-compiler/quote-run-in-macro-2/quoted_2.scala index 6e40f84ec7f0..02a0925642f4 100644 --- a/tests/neg-with-compiler/quote-run-in-macro-2/quoted_2.scala +++ b/tests/neg-with-compiler/quote-run-in-macro-2/quoted_2.scala @@ -2,5 +2,11 @@ import Macros._ object Test { def main(args: Array[String]): Unit = { println(foo(1)) // error + println(foo(1 + 3)) // error + val x = 3 + println(foo { // error + val x = 5 + x + }) } } diff --git a/tests/neg-with-compiler/quote-run-in-macro-3/quoted_1.scala b/tests/neg-with-compiler/quote-run-in-macro-3/quoted_1.scala deleted file mode 100644 index a9daf7d26556..000000000000 --- a/tests/neg-with-compiler/quote-run-in-macro-3/quoted_1.scala +++ /dev/null @@ -1,11 +0,0 @@ -import scala.quoted._ - -import scala.quoted.Toolbox.Default._ - -object Macros { - inline def foo(i: => Int): Int = ~fooImpl('(i)) - def fooImpl(i: Expr[Int]): Expr[Int] = { - val y: Int = i.run - y.toExpr - } -} diff --git a/tests/neg-with-compiler/quote-run-in-macro-3/quoted_2.scala b/tests/neg-with-compiler/quote-run-in-macro-3/quoted_2.scala deleted file mode 100644 index 663912caa1e8..000000000000 --- a/tests/neg-with-compiler/quote-run-in-macro-3/quoted_2.scala +++ /dev/null @@ -1,6 +0,0 @@ -import Macros._ -object Test { - def main(args: Array[String]): Unit = { - println(foo(1 + 3)) // error - } -} diff --git a/tests/neg-with-compiler/quote-run-in-macro-4/quoted_1.scala b/tests/neg-with-compiler/quote-run-in-macro-4/quoted_1.scala deleted file mode 100644 index a9daf7d26556..000000000000 --- a/tests/neg-with-compiler/quote-run-in-macro-4/quoted_1.scala +++ /dev/null @@ -1,11 +0,0 @@ -import scala.quoted._ - -import scala.quoted.Toolbox.Default._ - -object Macros { - inline def foo(i: => Int): Int = ~fooImpl('(i)) - def fooImpl(i: Expr[Int]): Expr[Int] = { - val y: Int = i.run - y.toExpr - } -} diff --git a/tests/neg-with-compiler/quote-run-in-macro-4/quoted_2.scala b/tests/neg-with-compiler/quote-run-in-macro-4/quoted_2.scala deleted file mode 100644 index f5fa99aa13e6..000000000000 --- a/tests/neg-with-compiler/quote-run-in-macro-4/quoted_2.scala +++ /dev/null @@ -1,9 +0,0 @@ -import Macros._ -object Test { - def main(args: Array[String]): Unit = { - println(foo { // error - val x = 5 - x - }) - } -} diff --git a/tests/neg/tasty-macro-assert-2/quoted_1.scala b/tests/neg/tasty-macro-assert-2/quoted_1.scala deleted file mode 100644 index 59c7d29a4378..000000000000 --- a/tests/neg/tasty-macro-assert-2/quoted_1.scala +++ /dev/null @@ -1,69 +0,0 @@ -import scala.quoted._ - -import scala.tasty._ - -object Asserts { - - implicit class Ops[T](left: T) { - def ===(right: T): Boolean = left == right - def !==(right: T): Boolean = left != right - } - - object Ops - - inline def macroAssert(cond: => Boolean): Unit = - ~impl('(cond)) - - def impl(cond: Expr[Boolean])(implicit tasty: Tasty): Expr[Unit] = { - import tasty._ - - val tree = cond.toTasty - - def isOps(tpe: TypeOrBounds): Boolean = tpe match { - case Type.SymRef(IsDefSymbol(sym), _) => sym.name == "Ops" // TODO check that the parent is Asserts - case _ => false - } - - object OpsTree { - def unapply(arg: Term): Option[Term] = arg match { - case Term.Apply(Term.TypeApply(term, _), left :: Nil) if isOps(term.tpe) => - Some(left) - case _ => None - } - } - - tree match { - case Term.Inlined(_, Nil, Term.Apply(Term.Select(OpsTree(left), op, _), right :: Nil)) => - '(assertTrue(~left.toExpr[Boolean])) // Buggy code. To generate the errors - case _ => - '(assertTrue(~cond)) - } - - } - - def assertEquals[T](left: T, right: T): Unit = { - if (left != right) { - println( - s"""Error left did not equal right: - | left = $left - | right = $right""".stripMargin) - } - - } - - def assertNotEquals[T](left: T, right: T): Unit = { - if (left == right) { - println( - s"""Error left was equal to right: - | left = $left - | right = $right""".stripMargin) - } - - } - - def assertTrue(cond: Boolean): Unit = { - if (!cond) - println("Condition was false") - } - -} diff --git a/tests/neg/tasty-macro-assert-2/quoted_2.scala b/tests/neg/tasty-macro-assert-2/quoted_2.scala deleted file mode 100644 index b43e3cc84dde..000000000000 --- a/tests/neg/tasty-macro-assert-2/quoted_2.scala +++ /dev/null @@ -1,10 +0,0 @@ - -import Asserts._ - -object Test { - def main(args: Array[String]): Unit = { - macroAssert(false !== "acb") - macroAssert("acb" !== "acb") // error - } - -} diff --git a/tests/neg/tasty-macro-assert/quoted_2.scala b/tests/neg/tasty-macro-assert/quoted_2.scala index 25ef0291b120..b94c98ab6b70 100644 --- a/tests/neg/tasty-macro-assert/quoted_2.scala +++ b/tests/neg/tasty-macro-assert/quoted_2.scala @@ -5,6 +5,8 @@ object Test { def main(args: Array[String]): Unit = { macroAssert(true === "cde") macroAssert("acb" === "cde") // error + macroAssert(false !== "acb") + macroAssert("acb" !== "acb") // error } } diff --git a/tests/pos/simpleInline.decompiled b/tests/pos/simpleInline.decompiled index 7fa25f3b3720..492a0da6e777 100644 --- a/tests/pos/simpleInline.decompiled +++ b/tests/pos/simpleInline.decompiled @@ -1,5 +1,7 @@ /** Decompiled from out/posTestFromTasty/pos/simpleInline/Foo.class */ class Foo() { inline def foo: scala.Int = 9 - def bar: scala.Int = Foo.this.foo + def bar: scala.Int = { // inlined + 9 + } } diff --git a/tests/run-custom-args/Yretain-trees/tasty-extractors-owners.check b/tests/run-custom-args/Yretain-trees/tasty-extractors-owners.check index 690f56b6dba9..d541fcc56321 100644 --- a/tests/run-custom-args/Yretain-trees/tasty-extractors-owners.check +++ b/tests/run-custom-args/Yretain-trees/tasty-extractors-owners.check @@ -1,5 +1,5 @@ foo -DefDef("main", Nil, List(List(ValDef("args", TypeTree.Applied(TypeTree.Ident("Array"), List(TypeTree.Ident("String"))), None))), TypeTree.Ident("Unit"), Some(Term.Block(Nil, Term.Apply(Term.TypeApply(Term.Ident("printOwners"), List(TypeTree.Synthetic())), List(Term.Block(List(DefDef("foo", Nil, Nil, TypeTree.Synthetic(), Some(Term.Block(List(DefDef("bar", Nil, Nil, TypeTree.Synthetic(), Some(Term.Literal(Constant.Int(1)))), ValDef("bar2", TypeTree.Synthetic(), Some(Term.Literal(Constant.Int(2))))), Term.Typed(Term.Ident("bar"), TypeTree.Synthetic())))), ValDef("foo2", TypeTree.Synthetic(), Some(Term.Block(List(DefDef("baz", Nil, Nil, TypeTree.Synthetic(), Some(Term.Literal(Constant.Int(3)))), ValDef("baz2", TypeTree.Synthetic(), Some(Term.Literal(Constant.Int(4))))), Term.Typed(Term.Ident("baz"), TypeTree.Synthetic())))), ClassDef("A", DefDef("", Nil, List(Nil), TypeTree.Synthetic(), None), List(Term.Apply(Term.Select(Term.New(TypeTree.Synthetic()), "", Some(Signature(Nil, java.lang.Object))), Nil)), None, List(TypeDef("B", TypeTree.Ident("Int")), DefDef("b", Nil, Nil, TypeTree.Synthetic(), Some(Term.Literal(Constant.Int(5)))), ValDef("b2", TypeTree.Synthetic(), Some(Term.Literal(Constant.Int(6))))))), Term.Literal(Constant.Unit()))))))) +DefDef("main", Nil, List(List(ValDef("args", TypeTree.Applied(TypeTree.Ident("Array"), List(TypeTree.Ident("String"))), None))), TypeTree.Ident("Unit"), Some(Term.Block(Nil, Term.Inlined(Some(TypeTree.Ident("Macros$")), Nil, Term.Select(Term.Apply(Term.Apply(Term.TypeApply(Term.Ident("impl"), List(TypeTree.Synthetic())), List(Term.Apply(Term.TypeApply(Term.Ident("apply"), List(TypeTree.Synthetic())), List(Term.Inlined(None, Nil, Term.Block(List(DefDef("foo", Nil, Nil, TypeTree.Synthetic(), Some(Term.Block(List(DefDef("bar", Nil, Nil, TypeTree.Synthetic(), Some(Term.Literal(Constant.Int(1)))), ValDef("bar2", TypeTree.Synthetic(), Some(Term.Literal(Constant.Int(2))))), Term.Typed(Term.Ident("bar"), TypeTree.Synthetic())))), ValDef("foo2", TypeTree.Synthetic(), Some(Term.Block(List(DefDef("baz", Nil, Nil, TypeTree.Synthetic(), Some(Term.Literal(Constant.Int(3)))), ValDef("baz2", TypeTree.Synthetic(), Some(Term.Literal(Constant.Int(4))))), Term.Typed(Term.Ident("baz"), TypeTree.Synthetic())))), ClassDef("A", DefDef("", Nil, List(Nil), TypeTree.Synthetic(), None), List(Term.Apply(Term.Select(Term.New(TypeTree.Synthetic()), "", Some(Signature(Nil, java.lang.Object))), Nil)), None, List(TypeDef("B", TypeTree.Ident("Int")), DefDef("b", Nil, Nil, TypeTree.Synthetic(), Some(Term.Literal(Constant.Int(5)))), ValDef("b2", TypeTree.Synthetic(), Some(Term.Literal(Constant.Int(6))))))), Term.Literal(Constant.Unit()))))))), List(Term.Ident("macroContext"))), "unary_~", Some(Signature(Nil, java.lang.Object))))))) bar DefDef("foo", Nil, Nil, TypeTree.Synthetic(), Some(Term.Block(List(DefDef("bar", Nil, Nil, TypeTree.Synthetic(), Some(Term.Literal(Constant.Int(1)))), ValDef("bar2", TypeTree.Synthetic(), Some(Term.Literal(Constant.Int(2))))), Term.Typed(Term.Ident("bar"), TypeTree.Synthetic())))) @@ -8,7 +8,7 @@ bar2 DefDef("foo", Nil, Nil, TypeTree.Synthetic(), Some(Term.Block(List(DefDef("bar", Nil, Nil, TypeTree.Synthetic(), Some(Term.Literal(Constant.Int(1)))), ValDef("bar2", TypeTree.Synthetic(), Some(Term.Literal(Constant.Int(2))))), Term.Typed(Term.Ident("bar"), TypeTree.Synthetic())))) foo2 -DefDef("main", Nil, List(List(ValDef("args", TypeTree.Applied(TypeTree.Ident("Array"), List(TypeTree.Ident("String"))), None))), TypeTree.Ident("Unit"), Some(Term.Block(Nil, Term.Apply(Term.TypeApply(Term.Ident("printOwners"), List(TypeTree.Synthetic())), List(Term.Block(List(DefDef("foo", Nil, Nil, TypeTree.Synthetic(), Some(Term.Block(List(DefDef("bar", Nil, Nil, TypeTree.Synthetic(), Some(Term.Literal(Constant.Int(1)))), ValDef("bar2", TypeTree.Synthetic(), Some(Term.Literal(Constant.Int(2))))), Term.Typed(Term.Ident("bar"), TypeTree.Synthetic())))), ValDef("foo2", TypeTree.Synthetic(), Some(Term.Block(List(DefDef("baz", Nil, Nil, TypeTree.Synthetic(), Some(Term.Literal(Constant.Int(3)))), ValDef("baz2", TypeTree.Synthetic(), Some(Term.Literal(Constant.Int(4))))), Term.Typed(Term.Ident("baz"), TypeTree.Synthetic())))), ClassDef("A", DefDef("", Nil, List(Nil), TypeTree.Synthetic(), None), List(Term.Apply(Term.Select(Term.New(TypeTree.Synthetic()), "", Some(Signature(Nil, java.lang.Object))), Nil)), None, List(TypeDef("B", TypeTree.Ident("Int")), DefDef("b", Nil, Nil, TypeTree.Synthetic(), Some(Term.Literal(Constant.Int(5)))), ValDef("b2", TypeTree.Synthetic(), Some(Term.Literal(Constant.Int(6))))))), Term.Literal(Constant.Unit()))))))) +DefDef("main", Nil, List(List(ValDef("args", TypeTree.Applied(TypeTree.Ident("Array"), List(TypeTree.Ident("String"))), None))), TypeTree.Ident("Unit"), Some(Term.Block(Nil, Term.Inlined(Some(TypeTree.Ident("Macros$")), Nil, Term.Select(Term.Apply(Term.Apply(Term.TypeApply(Term.Ident("impl"), List(TypeTree.Synthetic())), List(Term.Apply(Term.TypeApply(Term.Ident("apply"), List(TypeTree.Synthetic())), List(Term.Inlined(None, Nil, Term.Block(List(DefDef("foo", Nil, Nil, TypeTree.Synthetic(), Some(Term.Block(List(DefDef("bar", Nil, Nil, TypeTree.Synthetic(), Some(Term.Literal(Constant.Int(1)))), ValDef("bar2", TypeTree.Synthetic(), Some(Term.Literal(Constant.Int(2))))), Term.Typed(Term.Ident("bar"), TypeTree.Synthetic())))), ValDef("foo2", TypeTree.Synthetic(), Some(Term.Block(List(DefDef("baz", Nil, Nil, TypeTree.Synthetic(), Some(Term.Literal(Constant.Int(3)))), ValDef("baz2", TypeTree.Synthetic(), Some(Term.Literal(Constant.Int(4))))), Term.Typed(Term.Ident("baz"), TypeTree.Synthetic())))), ClassDef("A", DefDef("", Nil, List(Nil), TypeTree.Synthetic(), None), List(Term.Apply(Term.Select(Term.New(TypeTree.Synthetic()), "", Some(Signature(Nil, java.lang.Object))), Nil)), None, List(TypeDef("B", TypeTree.Ident("Int")), DefDef("b", Nil, Nil, TypeTree.Synthetic(), Some(Term.Literal(Constant.Int(5)))), ValDef("b2", TypeTree.Synthetic(), Some(Term.Literal(Constant.Int(6))))))), Term.Literal(Constant.Unit()))))))), List(Term.Ident("macroContext"))), "unary_~", Some(Signature(Nil, java.lang.Object))))))) baz ValDef("foo2", TypeTree.Synthetic(), Some(Term.Block(List(DefDef("baz", Nil, Nil, TypeTree.Synthetic(), Some(Term.Literal(Constant.Int(3)))), ValDef("baz2", TypeTree.Synthetic(), Some(Term.Literal(Constant.Int(4))))), Term.Typed(Term.Ident("baz"), TypeTree.Synthetic())))) diff --git a/tests/run-with-compiler/i3876-d.check b/tests/run-with-compiler/i3876-d.check index de27d152d558..746b7f8778a7 100644 --- a/tests/run-with-compiler/i3876-d.check +++ b/tests/run-with-compiler/i3876-d.check @@ -1,5 +1,5 @@ 6 { val x$1: scala.Int = 3 - Test.inlineLambda.apply(x$1) + x$1.+(x$1) }