diff --git a/compiler/src/dotty/tools/dotc/CompilationUnit.scala b/compiler/src/dotty/tools/dotc/CompilationUnit.scala index 059b0fcc1b7e..c275fb25ca66 100644 --- a/compiler/src/dotty/tools/dotc/CompilationUnit.scala +++ b/compiler/src/dotty/tools/dotc/CompilationUnit.scala @@ -25,15 +25,10 @@ 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. + /** 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. */ - 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. - */ - var containsQuotesOrSplices: Boolean = false + var needsStaging: Boolean = false /** A structure containing a temporary map for generating inline accessors */ val inlineAccessors: InlineAccessors = new InlineAccessors @@ -53,21 +48,18 @@ object CompilationUnit { if (forceTrees) { val force = new Force force.traverse(unit1.tpdTree) - unit1.containsInlineCalls = force.containsInline - unit1.containsQuotesOrSplices = force.containsQuotes + unit1.needsStaging = force.needsStaging } unit1 } /** Force the tree to be loaded */ private class Force extends TreeTraverser { - var containsInline = false - var containsQuotes = false + var needsStaging = false def traverse(tree: Tree)(implicit ctx: Context): Unit = { - if (tree.symbol.isQuote) - containsQuotes = true - if (tpd.isInlineCall(tree)) - containsInline = true + // Note that top-level splices are still inside the inline methods + if (tree.symbol.isQuote || tpd.isInlineCall(tree)) + needsStaging = true traverseChildren(tree) } } diff --git a/compiler/src/dotty/tools/dotc/Compiler.scala b/compiler/src/dotty/tools/dotc/Compiler.scala index c8cbfb93a1eb..60a05d2d26c9 100644 --- a/compiler/src/dotty/tools/dotc/Compiler.scala +++ b/compiler/src/dotty/tools/dotc/Compiler.scala @@ -44,8 +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 InlineCalls) :: // β-reduce inline calls - List(new ReifyQuotes) :: // Turn quoted trees into explicit run-time data structures + List(new Staging) :: // Inline calls, expand macros and 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/core/tasty/TastyFormat.scala b/compiler/src/dotty/tools/dotc/core/tasty/TastyFormat.scala index b59a7fb02ecf..741176aad85f 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TastyFormat.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TastyFormat.scala @@ -86,7 +86,6 @@ 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* @@ -185,7 +184,6 @@ 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 @@ -236,7 +234,7 @@ Standard Section: "Comments" Comment* object TastyFormat { final val header: Array[Int] = Array(0x5C, 0xA1, 0xAB, 0x1F) - val MajorVersion: Int = 11 + val MajorVersion: Int = 12 val MinorVersion: Int = 0 /** Tags used to serialize names */ @@ -289,27 +287,26 @@ object TastyFormat { final val IMPLICIT = 13 final val LAZY = 14 final val OVERRIDE = 15 - 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 + 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 // Cat. 2: tag Nat @@ -383,36 +380,35 @@ object TastyFormat { final val RETURN = 144 final val WHILE = 145 final val TRY = 146 - 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 + 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 // In binary: 101101EI // I = implicit method type @@ -462,7 +458,6 @@ object TastyFormat { | LAZY | OVERRIDE | INLINE - | INLINEPROXY | MACRO | STATIC | OBJECT @@ -520,7 +515,6 @@ object TastyFormat { case LAZY => "LAZY" case OVERRIDE => "OVERRIDE" case INLINE => "INLINE" - case INLINEPROXY => "INLINEPROXY" case MACRO => "MACRO" case STATIC => "STATIC" case OBJECT => "OBJECT" @@ -589,7 +583,6 @@ 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 5217b45200b0..716d0cc5bd44 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala @@ -442,17 +442,6 @@ 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) @@ -619,7 +608,6 @@ 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 f1cd996f4f18..fcabd7184550 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala @@ -604,7 +604,6 @@ 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) @@ -1074,17 +1073,6 @@ 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 eebffcd0bf6e..62627a6b050a 100644 --- a/compiler/src/dotty/tools/dotc/decompiler/TASTYDecompiler.scala +++ b/compiler/src/dotty/tools/dotc/decompiler/TASTYDecompiler.scala @@ -2,7 +2,6 @@ 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. @@ -15,9 +14,7 @@ class TASTYDecompiler extends TASTYCompiler { List(new ReadTastyTreesFromClasses) :: // Load classes from tasty Nil - override protected def picklerPhases: List[List[Phase]] = - List(new InlineCalls) :: // TODO should we really inline for the decompiler? - Nil + override protected def picklerPhases: List[List[Phase]] = Nil override protected def transformPhases: List[List[Phase]] = Nil diff --git a/compiler/src/dotty/tools/dotc/quoted/QuoteCompiler.scala b/compiler/src/dotty/tools/dotc/quoted/QuoteCompiler.scala index 5f57cb7e9ff6..8fca5cdbb0d7 100644 --- a/compiler/src/dotty/tools/dotc/quoted/QuoteCompiler.scala +++ b/compiler/src/dotty/tools/dotc/quoted/QuoteCompiler.scala @@ -13,7 +13,7 @@ import dotty.tools.dotc.core.StdNames.nme import dotty.tools.dotc.core.Symbols.defn import dotty.tools.dotc.core.Types.ExprType import dotty.tools.dotc.core.quoted.PickledQuotes -import dotty.tools.dotc.transform.ReifyQuotes +import dotty.tools.dotc.transform.Staging import dotty.tools.dotc.typer.FrontEnd import dotty.tools.dotc.util.Positions.Position import dotty.tools.dotc.util.SourceFile @@ -30,7 +30,7 @@ class QuoteCompiler extends Compiler { List(List(new QuotedFrontend(putInClass = true))) override protected def picklerPhases: List[List[Phase]] = - List(List(new ReifyQuotes)) + List(List(new Staging)) override def newRun(implicit ctx: Context): ExprRun = { reset() diff --git a/compiler/src/dotty/tools/dotc/transform/InlineCalls.scala b/compiler/src/dotty/tools/dotc/transform/InlineCalls.scala deleted file mode 100644 index 837740bd928f..000000000000 --- a/compiler/src/dotty/tools/dotc/transform/InlineCalls.scala +++ /dev/null @@ -1,45 +0,0 @@ -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.core.Types.MethodicType -import dotty.tools.dotc.transform.SymUtils._ -import dotty.tools.dotc.typer.{ConstFold, Inliner} - -/** β-reduce all calls to inline methods and perform constant folding */ -class InlineCalls extends MacroTransform { thisPhase => - import tpd._ - - override def phaseName: String = InlineCalls.name - - override def run(implicit ctx: Context): Unit = - if (ctx.compilationUnit.containsInlineCalls && !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 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 => - val newTree = super.transform(tree) - 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)) - } - } - -} - -object InlineCalls { - final val name = "inlineCalls" -} diff --git a/compiler/src/dotty/tools/dotc/transform/PostTyper.scala b/compiler/src/dotty/tools/dotc/transform/PostTyper.scala index aa7accbe33a8..3c99b15347c7 100644 --- a/compiler/src/dotty/tools/dotc/transform/PostTyper.scala +++ b/compiler/src/dotty/tools/dotc/transform/PostTyper.scala @@ -173,13 +173,13 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisPhase if (sym.isSplice || sym.isQuote) { markAsMacro(ctx) - ctx.compilationUnit.containsQuotesOrSplices = true + ctx.compilationUnit.needsStaging = true } } private def handleInlineCall(sym: Symbol)(implicit ctx: Context): Unit = { if (sym.is(Inline)) - ctx.compilationUnit.containsInlineCalls = true + ctx.compilationUnit.needsStaging = true } override def transform(tree: Tree)(implicit ctx: Context): Tree = diff --git a/compiler/src/dotty/tools/dotc/transform/Splicer.scala b/compiler/src/dotty/tools/dotc/transform/Splicer.scala index f3c9d5aa19b9..b9bde36415f2 100644 --- a/compiler/src/dotty/tools/dotc/transform/Splicer.scala +++ b/compiler/src/dotty/tools/dotc/transform/Splicer.scala @@ -33,7 +33,7 @@ object Splicer { * and for `~xyz` the tree of `xyz` is interpreted for which the * resulting expression is returned as a `Tree` * - * See: `ReifyQuotes` + * See: `Staging` */ def splice(tree: Tree, pos: SourcePosition, classLoader: ClassLoader)(implicit ctx: Context): Tree = tree match { case Quoted(quotedTree) => quotedTree @@ -63,7 +63,7 @@ object Splicer { * and for `~xyz` the tree of `xyz` is interpreted for which the * resulting expression is returned as a `Tree` * - * See: `ReifyQuotes` + * See: `Staging` */ def canBeSpliced(tree: Tree)(implicit ctx: Context): Boolean = tree match { case Quoted(_) => true diff --git a/compiler/src/dotty/tools/dotc/transform/ReifyQuotes.scala b/compiler/src/dotty/tools/dotc/transform/Staging.scala similarity index 93% rename from compiler/src/dotty/tools/dotc/transform/ReifyQuotes.scala rename to compiler/src/dotty/tools/dotc/transform/Staging.scala index 60cb93008f31..f94b422a5670 100644 --- a/compiler/src/dotty/tools/dotc/transform/ReifyQuotes.scala +++ b/compiler/src/dotty/tools/dotc/transform/Staging.scala @@ -2,7 +2,12 @@ package dotty.tools.dotc package transform import core._ -import Decorators._, Flags._, Types._, Contexts._, Symbols._, Constants._ +import Decorators._ +import Flags._ +import Types._ +import Contexts._ +import Symbols._ +import Constants._ import ast.Trees._ import ast.{TreeTypeMap, untpd} import util.Positions._ @@ -15,11 +20,12 @@ 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 -/** Translates quoted terms and types to `unpickle` method calls. - * Checks that the phase consistency principle (PCP) holds. +/** 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. * * * Transforms top level quote @@ -59,9 +65,9 @@ import dotty.tools.dotc.util.SourcePosition * For macro definitions we assume that we have a single ~ directly as the RHS. * The Splicer is used to check that the RHS will be interpretable (with the `Splicer`) once inlined. */ -class ReifyQuotes extends MacroTransformWithImplicits { - import ast.tpd._ - import ReifyQuotes._ +class Staging extends MacroTransformWithImplicits { + import tpd._ + import Staging._ /** Classloader used for loading macros */ private[this] var myMacroClassLoader: java.lang.ClassLoader = _ @@ -73,7 +79,7 @@ class ReifyQuotes extends MacroTransformWithImplicits { myMacroClassLoader } - override def phaseName: String = ReifyQuotes.name + override def phaseName: String = Staging.name override def checkPostCondition(tree: Tree)(implicit ctx: Context): Unit = { tree match { @@ -88,7 +94,7 @@ class ReifyQuotes extends MacroTransformWithImplicits { } override def run(implicit ctx: Context): Unit = - if (ctx.compilationUnit.containsQuotesOrSplices) super.run + if (ctx.compilationUnit.needsStaging) super.run protected def newTransformer(implicit ctx: Context): Transformer = new Reifier(inQuote = false, null, 0, new LevelInfo, new Embedded, ctx) @@ -414,7 +420,7 @@ class ReifyQuotes extends MacroTransformWithImplicits { else if (body.symbol == defn.DoubleClass) tag("DoubleTag") else pickleAsTasty() } - else ReifyQuotes.toValue(body) match { + else Staging.toValue(body) match { case Some(value) => pickleAsValue(value) case _ => pickleAsTasty() } @@ -437,7 +443,8 @@ class ReifyQuotes 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 evaluatedSplice = Splicer.splice(splice.qualifier, pos, macroClassLoader)(spliceCtx).withPos(splice.pos) + 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) if (ctx.reporter.hasErrors) splice else transform(evaluatedSplice) } else if (!ctx.owner.isInlineMethod) { // level 0 outside an inline method @@ -560,6 +567,9 @@ class ReifyQuotes 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 => @@ -610,7 +620,7 @@ class ReifyQuotes extends MacroTransformWithImplicits { } case _ => markDef(tree) - checkLevel(mapOverTree(enteredSyms)) + ConstFold(checkLevel(mapOverTree(enteredSyms))) } } @@ -634,8 +644,10 @@ class ReifyQuotes extends MacroTransformWithImplicits { } } -object ReifyQuotes { - val name: String = "reifyQuotes" +object Staging { + import tpd._ + + val name: String = "staging" def toValue(tree: tpd.Tree): Option[Any] = tree match { case Literal(Constant(c)) => Some(c) @@ -664,4 +676,20 @@ object ReifyQuotes { /** 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 58849305de45..1432c5405cad 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 = inlineCallPrefix.tpe.tryNormalize match { + val proxyType = inlineCallPrefix.tpe.dealias.tryNormalize match { case typeMatchResult if typeMatchResult.exists => typeMatchResult case _ => tpe.asSeenFrom(inlineCallPrefix.tpe, inlinedMethod.owner).widenIfUnstable } diff --git a/compiler/test/dotc/pos-recompilation.whitelist b/compiler/test/dotc/pos-recompilation.whitelist index eb4c22b9d238..ef14a97f1d25 100644 --- a/compiler/test/dotc/pos-recompilation.whitelist +++ b/compiler/test/dotc/pos-recompilation.whitelist @@ -985,7 +985,6 @@ t8132 t8177d t8177e t8177h -t8207 t8219 t8230a t8237 diff --git a/docs/docs/reference/changed/compiler-plugins.md b/docs/docs/reference/changed/compiler-plugins.md index a10201c4c7d4..6bb1f4d8a417 100644 --- a/docs/docs/reference/changed/compiler-plugins.md +++ b/docs/docs/reference/changed/compiler-plugins.md @@ -60,7 +60,7 @@ import dotty.tools.dotc.core.Decorators._ import dotty.tools.dotc.core.StdNames._ import dotty.tools.dotc.core.Symbols._ import dotty.tools.dotc.plugins.{PluginPhase, StandardPlugin} -import dotty.tools.dotc.transform.{Pickler, ReifyQuotes} +import dotty.tools.dotc.transform.{Pickler, Staging} class DivideZero extends StandardPlugin { val name: String = "divideZero" @@ -75,7 +75,7 @@ class DivideZeroPhase extends PluginPhase { val phaseName = "divideZero" override val runsAfter = Set(Pickler.name) - override val runsBefore = Set(ReifyQuotes.name) + override val runsBefore = Set(Staging.name) override def transformApply(tree: Apply)(implicit ctx: Context): Tree = { tree match { diff --git a/docs/docs/reference/principled-meta-programming.md b/docs/docs/reference/principled-meta-programming.md index 6ef0a8418a5f..bb2617b9dd8c 100644 --- a/docs/docs/reference/principled-meta-programming.md +++ b/docs/docs/reference/principled-meta-programming.md @@ -627,7 +627,7 @@ literals. So it could be done only if symbol literals were abolished. Quotes and splices are primitive forms in the generated abstract syntax trees. They are eliminated in an expansion phase -`ReifyQuotes`. This phase runs after typing and pickling. +`Staging`. This phase runs after typing and pickling. Macro-expansion works outside-in. If the outermost scope is a splice, the spliced AST will be evaluated in an interpreter. A call to a diff --git a/sbt-dotty/sbt-test/sbt-dotty/compiler-plugin/plugin/DivideZero.scala b/sbt-dotty/sbt-test/sbt-dotty/compiler-plugin/plugin/DivideZero.scala index ad7b93a4f40c..e7f07232a8c1 100644 --- a/sbt-dotty/sbt-test/sbt-dotty/compiler-plugin/plugin/DivideZero.scala +++ b/sbt-dotty/sbt-test/sbt-dotty/compiler-plugin/plugin/DivideZero.scala @@ -10,7 +10,7 @@ import transform.MegaPhase.MiniPhase import Decorators._ import Symbols.Symbol import Constants.Constant -import transform.{Pickler, ReifyQuotes} +import transform.{Pickler, Staging} /** Compiler plugin that emits an error when compiling a division by zero */ class DivideZero extends PluginPhase with StandardPlugin { @@ -20,7 +20,7 @@ class DivideZero extends PluginPhase with StandardPlugin { val phaseName = name override val runsAfter = Set(Pickler.name) - override val runsBefore = Set(ReifyQuotes.name) + override val runsBefore = Set(Staging.name) def init(options: List[String]): List[PluginPhase] = this :: Nil 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 02a0925642f4..6e40f84ec7f0 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,11 +2,5 @@ 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 new file mode 100644 index 000000000000..a9daf7d26556 --- /dev/null +++ b/tests/neg-with-compiler/quote-run-in-macro-3/quoted_1.scala @@ -0,0 +1,11 @@ +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 new file mode 100644 index 000000000000..663912caa1e8 --- /dev/null +++ b/tests/neg-with-compiler/quote-run-in-macro-3/quoted_2.scala @@ -0,0 +1,6 @@ +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 new file mode 100644 index 000000000000..a9daf7d26556 --- /dev/null +++ b/tests/neg-with-compiler/quote-run-in-macro-4/quoted_1.scala @@ -0,0 +1,11 @@ +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 new file mode 100644 index 000000000000..f5fa99aa13e6 --- /dev/null +++ b/tests/neg-with-compiler/quote-run-in-macro-4/quoted_2.scala @@ -0,0 +1,9 @@ +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 new file mode 100644 index 000000000000..59c7d29a4378 --- /dev/null +++ b/tests/neg/tasty-macro-assert-2/quoted_1.scala @@ -0,0 +1,69 @@ +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 new file mode 100644 index 000000000000..b43e3cc84dde --- /dev/null +++ b/tests/neg/tasty-macro-assert-2/quoted_2.scala @@ -0,0 +1,10 @@ + +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 b94c98ab6b70..25ef0291b120 100644 --- a/tests/neg/tasty-macro-assert/quoted_2.scala +++ b/tests/neg/tasty-macro-assert/quoted_2.scala @@ -5,8 +5,6 @@ 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/plugins/neg/divideZero/plugin_1.scala b/tests/plugins/neg/divideZero/plugin_1.scala index f9ff7612b119..97b3dfa22ee2 100644 --- a/tests/plugins/neg/divideZero/plugin_1.scala +++ b/tests/plugins/neg/divideZero/plugin_1.scala @@ -8,7 +8,7 @@ import transform.MegaPhase.MiniPhase import Decorators._ import Symbols.Symbol import Constants.Constant -import transform.{Pickler, ReifyQuotes} +import transform.{Pickler, Staging} import StdNames._ class DivideZero extends PluginPhase with StandardPlugin { @@ -18,7 +18,7 @@ class DivideZero extends PluginPhase with StandardPlugin { val phaseName = name override val runsAfter = Set(Pickler.name) - override val runsBefore = Set(ReifyQuotes.name) + override val runsBefore = Set(Staging.name) override def init(options: List[String]): List[PluginPhase] = this :: Nil diff --git a/tests/pos/simpleInline.decompiled b/tests/pos/simpleInline.decompiled index 492a0da6e777..7fa25f3b3720 100644 --- a/tests/pos/simpleInline.decompiled +++ b/tests/pos/simpleInline.decompiled @@ -1,7 +1,5 @@ /** Decompiled from out/posTestFromTasty/pos/simpleInline/Foo.class */ class Foo() { inline def foo: scala.Int = 9 - def bar: scala.Int = { // inlined - 9 - } + def bar: scala.Int = Foo.this.foo } 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 d541fcc56321..690f56b6dba9 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.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))))))) +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()))))))) 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.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))))))) +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()))))))) 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 746b7f8778a7..de27d152d558 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 - x$1.+(x$1) + Test.inlineLambda.apply(x$1) }