Skip to content

Go back to inlining during typing #5382

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 3 additions & 6 deletions compiler/src/dotty/tools/dotc/CompilationUnit.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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) {

Expand All @@ -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

Expand Down Expand Up @@ -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)
}
Expand Down
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/Compiler.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
Expand Down
10 changes: 1 addition & 9 deletions compiler/src/dotty/tools/dotc/ast/TreeInfo.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
*/
Expand Down
5 changes: 3 additions & 2 deletions compiler/src/dotty/tools/dotc/ast/Trees.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it be possible to keep the original call somewhere? This information is important:

  • for incremental compilation, because the parameters of an inlined call may introduce dependencies that are not visible in the resulting inlined code
  • for the IDE, because the user works with trees before inlining.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A potential problem is that this could increase bytecode size significantly. E.g. looking at the trace calls in the compiler, which surround large bodies of code. If we keep the call that code would be duplicated in the Tasty tree.

Currently, we get the calls for trees that are in an edit buffer, but not for others. So findReferences would only show those references that survive in the inlined code, but not other references that are not inlined.

We should think a bit more how we can reconcile these concerns.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Regarding bytecode size: if we have several nested trace-like calls the blowup could be exponential. You can see this sometimes when you print at phase frontend without -Yshow-no-inlines.

* @param bindings Bindings for proxies to be used in the inlined code
* @param expansion The inlined tree, minus bindings.
*
Expand Down
107 changes: 57 additions & 50 deletions compiler/src/dotty/tools/dotc/core/tasty/TastyFormat.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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*
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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 */
Expand Down Expand Up @@ -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

Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -458,6 +462,7 @@ object TastyFormat {
| LAZY
| OVERRIDE
| INLINE
| INLINEPROXY
| MACRO
| STATIC
| OBJECT
Expand Down Expand Up @@ -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"
Expand Down Expand Up @@ -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"
Expand Down
12 changes: 12 additions & 0 deletions compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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)
Expand Down
12 changes: 12 additions & 0 deletions compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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 =>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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]] =
Expand Down
Loading