Skip to content

Towards Inlining Untyped Code #4589

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 21 commits 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
Original file line number Diff line number Diff line change
Expand Up @@ -258,7 +258,7 @@ class DottyBackendInterface(outputDirectory: AbstractFile, val superCallsMap: Ma
case t: TypeApply if (t.fun.symbol == Predef_classOf) =>
av.visit(name, t.args.head.tpe.classSymbol.denot.info.toTypeKind(bcodeStore)(innerClasesStore).toASMType)
case t: tpd.Select =>
if (t.symbol.denot.owner.is(Flags.Enum)) {
if (t.symbol.denot.owner.is(Flags.JavaEnum)) {
val edesc = innerClasesStore.typeDescriptor(t.tpe.asInstanceOf[bcodeStore.int.Type]) // the class descriptor of the enumeration class.
val evalue = t.symbol.name.mangledString // value the actual enumeration value.
av.visitEnum(name, edesc, evalue)
Expand Down Expand Up @@ -710,7 +710,7 @@ class DottyBackendInterface(outputDirectory: AbstractFile, val superCallsMap: Ma
def isBottomClass: Boolean = (sym ne defn.NullClass) && (sym ne defn.NothingClass)
def isBridge: Boolean = sym is Flags.Bridge
def isArtifact: Boolean = sym is Flags.Artifact
def hasEnumFlag: Boolean = sym is Flags.Enum
def hasEnumFlag: Boolean = sym is Flags.JavaEnum
def hasAccessBoundary: Boolean = sym.accessBoundary(defn.RootClass) ne defn.RootClass
def isVarargsMethod: Boolean = sym is Flags.JavaVarargs
def isDeprecated: Boolean = false
Expand Down
17 changes: 6 additions & 11 deletions compiler/src/dotty/tools/dotc/ast/Desugar.scala
Original file line number Diff line number Diff line change
Expand Up @@ -297,8 +297,8 @@ object desugar {
val isCaseClass = mods.is(Case) && !mods.is(Module)
val isCaseObject = mods.is(Case) && mods.is(Module)
val isImplicit = mods.is(Implicit)
val isEnum = mods.hasMod[Mod.Enum] && !mods.is(Module)
val isEnumCase = mods.hasMod[Mod.EnumCase]
val isEnum = mods.isEnumClass && !mods.is(Module)
def isEnumCase = mods.isEnumCase
val isValueClass = parents.nonEmpty && isAnyVal(parents.head)
// This is not watertight, but `extends AnyVal` will be replaced by `inline` later.

Expand Down Expand Up @@ -641,7 +641,7 @@ object desugar {
val moduleName = checkNotReservedName(mdef).asTermName
val impl = mdef.impl
val mods = mdef.mods
lazy val isEnumCase = mods.hasMod[Mod.EnumCase]
def isEnumCase = mods.isEnumCase
if (mods is Package)
PackageDef(Ident(moduleName), cpy.ModuleDef(mdef)(nme.PACKAGE, impl).withMods(mods &~ Package) :: Nil)
else if (isEnumCase)
Expand Down Expand Up @@ -688,7 +688,7 @@ object desugar {
*/
def patDef(pdef: PatDef)(implicit ctx: Context): Tree = flatTree {
val PatDef(mods, pats, tpt, rhs) = pdef
if (mods.hasMod[Mod.EnumCase])
if (mods.isEnumCase)
pats map {
case id: Ident =>
expandSimpleEnumCase(id.name.asTermName, mods,
Expand Down Expand Up @@ -810,16 +810,11 @@ object desugar {
* ==>
* def $anonfun(params) = body
* Closure($anonfun)
*
* If `inlineable` is true, tag $anonfun with an @inline annotation.
*/
def makeClosure(params: List[ValDef], body: Tree, tpt: Tree = TypeTree(), inlineable: Boolean)(implicit ctx: Context) = {
var mods = synthetic | Artifact
if (inlineable) mods |= Inline
def makeClosure(params: List[ValDef], body: Tree, tpt: Tree = TypeTree())(implicit ctx: Context) =
Block(
DefDef(nme.ANON_FUN, Nil, params :: Nil, tpt, body).withMods(mods),
DefDef(nme.ANON_FUN, Nil, params :: Nil, tpt, body).withMods(synthetic | Artifact),
Closure(Nil, Ident(nme.ANON_FUN), EmptyTree))
}

/** If `nparams` == 1, expand partial function
*
Expand Down
6 changes: 3 additions & 3 deletions compiler/src/dotty/tools/dotc/ast/DesugarEnums.scala
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,8 @@ object DesugarEnums {

/** Is `tree` an (untyped) enum case? */
def isEnumCase(tree: Tree)(implicit ctx: Context): Boolean = tree match {
case tree: MemberDef => tree.mods.hasMod[Mod.EnumCase]
case PatDef(mods, _, _, _) => mods.hasMod[Mod.EnumCase]
case tree: MemberDef => tree.mods.isEnumCase
case PatDef(mods, _, _, _) => mods.isEnumCase
case _ => false
}

Expand Down Expand Up @@ -69,7 +69,7 @@ object DesugarEnums {

/** Add implied flags to an enum class or an enum case */
def addEnumFlags(cdef: TypeDef)(implicit ctx: Context) =
if (cdef.mods.hasMod[Mod.Enum]) cdef.withMods(cdef.mods.withFlags(cdef.mods.flags | Abstract | Sealed))
if (cdef.mods.isEnumClass) cdef.withMods(cdef.mods.withFlags(cdef.mods.flags | Abstract | Sealed))
else if (isEnumCase(cdef)) cdef.withMods(cdef.mods.withFlags(cdef.mods.flags | Final))
else cdef

Expand Down
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/ast/TreeTypeMap.scala
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import core.tasty.TreePickler.Hole

/** A map that applies three functions and a substitution together to a tree and
* makes sure they are coordinated so that the result is well-typed. The functions are
* @param typeMap A function from Type to Type that gets applied to the
* @param typeMap A function from Type to Type that gets applied to the
* type of every tree node and to all locally defined symbols,
* followed by the substitution [substFrom := substTo].
* @param treeMap A transformer that translates all encountered subtrees in
Expand Down
56 changes: 46 additions & 10 deletions compiler/src/dotty/tools/dotc/ast/Trees.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1083,6 +1083,10 @@ object Trees {
case tree: Annotated if (arg eq tree.arg) && (annot eq tree.annot) => tree
case _ => finalize(tree, untpd.Annotated(arg, annot))
}
def UntypedSplice(tree: Tree)(splice: untpd.Tree) = tree match {
case tree: tpd.UntypedSplice if tree.splice `eq` splice => tree
case _ => finalize(tree, tpd.UntypedSplice(splice))
}
def Thicket(tree: Tree)(trees: List[Tree]): Thicket = tree match {
case tree: Thicket if trees eq tree.trees => tree
case _ => finalize(tree, untpd.Thicket(trees))
Expand Down Expand Up @@ -1120,7 +1124,7 @@ object Trees {
*/
protected def inlineContext(call: Tree)(implicit ctx: Context): Context = ctx

abstract class TreeMap(val cpy: TreeCopier = inst.cpy) {
abstract class TreeMap(val cpy: TreeCopier = inst.cpy) { self =>

def transform(tree: Tree)(implicit ctx: Context): Tree = {
Stats.record(s"TreeMap.transform $getClass")
Expand Down Expand Up @@ -1219,8 +1223,8 @@ object Trees {
case Thicket(trees) =>
val trees1 = transform(trees)
if (trees1 eq trees) tree else Thicket(trees1)
case _ if ctx.reporter.errorsReported =>
tree
case _ =>
transformMoreCases(tree)
}
}

Expand All @@ -1232,9 +1236,26 @@ object Trees {
transform(tree).asInstanceOf[Tr]
def transformSub[Tr <: Tree](trees: List[Tr])(implicit ctx: Context): List[Tr] =
transform(trees).asInstanceOf[List[Tr]]

protected def transformMoreCases(tree: Tree)(implicit ctx: Context): Tree = tree match {
case tpd.UntypedSplice(usplice) =>
// For a typed tree map: homomorphism on the untyped part with
// recursive mapping of typed splices.
// The case is overridden in UntypedTreeMap.##
val untpdMap = new untpd.UntypedTreeMap {
override def transform(tree: untpd.Tree)(implicit ctx: Context): untpd.Tree = tree match {
case untpd.TypedSplice(tsplice) =>
untpd.cpy.TypedSplice(tree)(self.transform(tsplice).asInstanceOf[tpd.Tree])
// the cast is safe, since the UntypedSplice case is overridden in UntypedTreeMap.
case _ => super.transform(tree)
}
}
cpy.UntypedSplice(tree)(untpdMap.transform(usplice))
case _ if ctx.reporter.errorsReported => tree
}
}

abstract class TreeAccumulator[X] {
abstract class TreeAccumulator[X] { self =>
// Ties the knot of the traversal: call `foldOver(x, tree))` to dive in the `tree` node.
def apply(x: X, tree: Tree)(implicit ctx: Context): X

Expand Down Expand Up @@ -1329,14 +1350,29 @@ object Trees {
this(this(x, arg), annot)
case Thicket(ts) =>
this(x, ts)
case _ if ctx.reporter.errorsReported || ctx.mode.is(Mode.Interactive) =>
// In interactive mode, errors might come from previous runs.
// In case of errors it may be that typed trees point to untyped ones.
// The IDE can still traverse inside such trees, either in the run where errors
// are reported, or in subsequent ones.
x
case _ =>
foldMoreCases(x, tree)
}
}

def foldMoreCases(x: X, tree: Tree)(implicit ctx: Context): X = tree match {
case tpd.UntypedSplice(usplice) =>
// For a typed tree accumulator: skip the untyped part and fold all typed splices.
// The case is overridden in UntypedTreeAccumulator.
val untpdAcc = new untpd.UntypedTreeAccumulator[X] {
override def apply(x: X, tree: untpd.Tree)(implicit ctx: Context): X = tree match {
case untpd.TypedSplice(tsplice) => self(x, tsplice)
case _ => foldOver(x, tree)
}
}
untpdAcc(x, usplice)
case _ if ctx.reporter.errorsReported || ctx.mode.is(Mode.Interactive) =>
// In interactive mode, errors might come from previous runs.
// In case of errors it may be that typed trees point to untyped ones.
// The IDE can still traverse inside such trees, either in the run where errors
// are reported, or in subsequent ones.
x
}
}

abstract class TreeTraverser extends TreeAccumulator[Unit] {
Expand Down
6 changes: 4 additions & 2 deletions compiler/src/dotty/tools/dotc/ast/tpd.scala
Original file line number Diff line number Diff line change
Expand Up @@ -324,7 +324,7 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo {
case pre: ThisType =>
tp.isType ||
pre.cls.isStaticOwner ||
tp.symbol.isParamOrAccessor && !pre.cls.is(Trait) && ctx.owner.enclosingClass == pre.cls
tp.symbol.isParamOrAccessor && !pre.cls.is(Trait) && ctx.owner.enclosingClass == pre.cls
// was ctx.owner.enclosingClass.derivesFrom(pre.cls) which was not tight enough
// and was spuriously triggered in case inner class would inherit from outer one
// eg anonymous TypeMap inside TypeMap.andThen
Expand Down Expand Up @@ -471,6 +471,8 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo {
} else foldOver(sym, tree)
}

case class UntypedSplice(splice: untpd.Tree) extends Tree

override val cpy: TypedTreeCopier = // Type ascription needed to pick up any new members in TreeCopier (currently there are none)
new TypedTreeCopier

Expand Down Expand Up @@ -640,7 +642,7 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo {

override def skipTransform(tree: Tree)(implicit ctx: Context) = tree.tpe.isError

implicit class TreeOps[ThisTree <: tpd.Tree](val tree: ThisTree) extends AnyVal {
implicit class TreeOps[ThisTree <: tpd.Tree](private val tree: ThisTree) extends AnyVal {

def isValue(implicit ctx: Context): Boolean =
tree.isTerm && tree.tpe.widen.isValueType
Expand Down
64 changes: 41 additions & 23 deletions compiler/src/dotty/tools/dotc/ast/untpd.scala
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo {
/** A typed subtree of an untyped tree needs to be wrapped in a TypedSlice
* @param owner The current owner at the time the tree was defined
*/
abstract case class TypedSplice(tree: tpd.Tree)(val owner: Symbol) extends ProxyTree {
def forwardTo = tree
abstract case class TypedSplice(splice: tpd.Tree)(val owner: Symbol) extends ProxyTree {
def forwardTo = splice
}

object TypedSplice {
Expand Down Expand Up @@ -130,9 +130,7 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo {

case class Inline() extends Mod(Flags.Inline)

case class Enum() extends Mod(Flags.EmptyFlags)

case class EnumCase() extends Mod(Flags.EmptyFlags)
case class Enum() extends Mod(Flags.Enum)
}

/** Modifiers and annotations for definitions
Expand Down Expand Up @@ -163,15 +161,26 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo {
if (this.flags == flags) this
else copy(flags = flags)

def withAddedMod(mod: Mod): Modifiers =
if (mods.exists(_ eq mod)) this
else withMods(mods :+ mod)
def withAddedMod(mod: Mod): Modifiers =
if (mods.exists(_ eq mod)) this
else withMods(mods :+ mod)

def withMods(ms: List[Mod]): Modifiers =
if (mods eq ms) this
else copy(mods = ms)
/** Modifiers with given list of Mods. It is checked that
* all modifiers are already accounted for in `flags` and `privateWithin`.
*/
def withMods(ms: List[Mod]): Modifiers = {
if (mods eq ms) this
else {
if (ms.nonEmpty)
for (m <- ms)
assert(flags.is(m.flags) ||
m.isInstanceOf[Mod.Private] && !privateWithin.isEmpty,
s"unaccounted modifier: $m in $this when adding $ms")
copy(mods = ms)
}
}

def withAddedAnnotation(annot: Tree): Modifiers =
def withAddedAnnotation(annot: Tree): Modifiers =
if (annotations.exists(_ eq annot)) this
else withAnnotations(annotations :+ annot)

Expand All @@ -186,10 +195,11 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo {
def hasFlags = flags != EmptyFlags
def hasAnnotations = annotations.nonEmpty
def hasPrivateWithin = privateWithin != tpnme.EMPTY
def hasMod[T: ClassTag] = {
val cls = implicitly[ClassTag[T]].runtimeClass
mods.exists(mod => cls.isAssignableFrom(mod.getClass))
}

private def isEnum = is(Enum, butNot = JavaDefined)

def isEnumCase = isEnum && is(Case)
def isEnumClass = isEnum && !is(Case)
}

@sharable val EmptyModifiers: Modifiers = new Modifiers()
Expand Down Expand Up @@ -482,10 +492,14 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo {
case tree: PatDef if (mods eq tree.mods) && (pats eq tree.pats) && (tpt eq tree.tpt) && (rhs eq tree.rhs) => tree
case _ => finalize(tree, untpd.PatDef(mods, pats, tpt, rhs))
}
def TypedSplice(tree: Tree)(splice: tpd.Tree)(implicit ctx: Context) = tree match {
case tree: TypedSplice if splice `eq` tree.splice => tree
case _ => finalize(tree, untpd.TypedSplice(splice))
}
}

abstract class UntypedTreeMap(cpy: UntypedTreeCopier = untpd.cpy) extends TreeMap(cpy) {
override def transform(tree: Tree)(implicit ctx: Context): Tree = tree match {
override def transformMoreCases(tree: Tree)(implicit ctx: Context): Tree = tree match {
case ModuleDef(name, impl) =>
cpy.ModuleDef(tree)(name, transformSub(impl))
case ParsedTry(expr, handler, finalizer) =>
Expand Down Expand Up @@ -526,15 +540,17 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo {
cpy.ContextBounds(tree)(transformSub(bounds), transform(cxBounds))
case PatDef(mods, pats, tpt, rhs) =>
cpy.PatDef(tree)(mods, transform(pats), transform(tpt), transform(rhs))
case tpd.UntypedSplice(splice) =>
cpy.UntypedSplice(tree)(transform(splice))
case TypedSplice(_) =>
tree
case _ =>
super.transform(tree)
super.transformMoreCases(tree)
}
}

abstract class UntypedTreeAccumulator[X] extends TreeAccumulator[X] {
override def foldOver(x: X, tree: Tree)(implicit ctx: Context): X = tree match {
abstract class UntypedTreeAccumulator[X] extends TreeAccumulator[X] { self =>
override def foldMoreCases(x: X, tree: Tree)(implicit ctx: Context): X = tree match {
case ModuleDef(name, impl) =>
this(x, impl)
case ParsedTry(expr, handler, finalizer) =>
Expand Down Expand Up @@ -575,10 +591,12 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo {
this(this(x, bounds), cxBounds)
case PatDef(mods, pats, tpt, rhs) =>
this(this(this(x, pats), tpt), rhs)
case TypedSplice(tree) =>
this(x, tree)
case TypedSplice(splice) =>
this(x, splice)
case tpd.UntypedSplice(splice) =>
this(x, splice)
case _ =>
super.foldOver(x, tree)
super.foldMoreCases(x, tree)
}
}

Expand Down
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/config/ScalaSettings.scala
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,6 @@ class ScalaSettings extends Settings.SettingGroup {
val YdebugFlags = BooleanSetting("-Ydebug-flags", "Print all flags of definitions")
val YdebugMissingRefs = BooleanSetting("-Ydebug-missing-refs", "Print a stacktrace when a required symbol is missing")
val YdebugNames = BooleanSetting("-Ydebug-names", "Show internal representation of names")
val YdebugOwners = BooleanSetting("-Ydebug-owners", "Print all owners of definitions (requires -Yprint-syms)")
val YtermConflict = ChoiceSetting("-Yresolve-term-conflict", "strategy", "Resolve term conflicts", List("package", "object", "error"), "error")
val Ylog = PhasesSetting("-Ylog", "Log operations during")
val YemitTasty = BooleanSetting("-Yemit-tasty", "Generate tasty in separate *.tasty file.")
Expand All @@ -113,6 +112,7 @@ class ScalaSettings extends Settings.SettingGroup {
val YplainPrinter = BooleanSetting("-Yplain-printer", "Pretty-print using a plain printer.")
val YprintSyms = BooleanSetting("-Yprint-syms", "when printing trees print info in symbols instead of corresponding info in trees.")
val YprintDebug = BooleanSetting("-Yprint-debug", "when printing trees, print some extra information useful for debugging.")
val YprintDebugOwners = BooleanSetting("-Yprint-debug-owners", "when printing trees, print owners of definitions.")
val YshowPrintErrors = BooleanSetting("-Yshow-print-errors", "don't suppress exceptions thrown during tree printing.")
val YtestPickler = BooleanSetting("-Ytest-pickler", "self-test for pickling functionality; should be used with -Ystop-after:pickler")
val YcheckReentrant = BooleanSetting("-Ycheck-reentrant", "check that compiled program does not contain vars that can be accessed from a global root.")
Expand Down
12 changes: 12 additions & 0 deletions compiler/src/dotty/tools/dotc/core/Flags.scala
Original file line number Diff line number Diff line change
Expand Up @@ -563,6 +563,9 @@ object Flags {
/** An inline parameter */
final val InlineParam = allOf(Inline, Param)

/** An enum case */
final val EnumCase = allOf(Enum, Case)

/** A term parameter or parameter accessor */
final val TermParamOrAccessor = Param | ParamAccessor

Expand Down Expand Up @@ -620,6 +623,15 @@ object Flags {
/** A Java companion object */
final val JavaProtected = allOf(JavaDefined, Protected)

/** A Java enum */
final val JavaEnum = allOf(JavaDefined, Enum)

/** A Java enum trait */
final val JavaEnumTrait = allOf(JavaDefined, Enum)

/** A Java enum value */
final val JavaEnumValue = allOf(Stable, JavaStatic, JavaDefined, Enum)

/** Labeled private[this] */
final val PrivateLocal = allOf(Private, Local)

Expand Down
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/core/Types.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1825,7 +1825,7 @@ object Types {

private def setDenot(denot: Denotation)(implicit ctx: Context): Unit = {
if (ctx.isAfterTyper)
assert(!denot.isOverloaded, this)
assert(!denot.isOverloaded || ctx.mode.is(Mode.Printing), this)
if (Config.checkNoDoubleBindings)
if (ctx.settings.YnoDoubleBindings.value)
checkSymAssign(denot.symbol)
Expand Down
Loading