Skip to content

Inliner improvements #6218

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

Merged
merged 17 commits into from
Apr 14, 2019
Merged
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
1 change: 1 addition & 0 deletions compiler/src/dotty/tools/dotc/config/Printers.scala
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ object Printers {
val quotePickling: Printer = noPrinter
val plugins: Printer = noPrinter
val simplify: Printer = noPrinter
val staging: Printer = noPrinter
val subtyping: Printer = noPrinter
val tailrec: Printer = noPrinter
val transforms: Printer = noPrinter
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package transform

import dotty.tools.dotc.ast.Trees._
import dotty.tools.dotc.ast.{TreeMapWithImplicits, TreeTypeMap, tpd, untpd}
import dotty.tools.dotc.config.Printers.staging
import dotty.tools.dotc.core.Constants._
import dotty.tools.dotc.core.Decorators._
import dotty.tools.dotc.core.Flags._
Expand Down Expand Up @@ -71,7 +72,7 @@ abstract class TreeMapWithStages(@constructorOnly ictx: Context) extends TreeMap
override def transform(tree: Tree)(implicit ctx: Context): Tree = {
if (tree.source != ctx.source && tree.source.exists)
transform(tree)(ctx.withSource(tree.source))
else reporting.trace(i"StagingTransformer.transform $tree at $level", show = true) {
else reporting.trace(i"StagingTransformer.transform $tree at $level", staging, show = true) {
def mapOverTree(lastEntered: List[Symbol]) =
try super.transform(tree)
finally
Expand Down
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/typer/ErrorReporting.scala
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ object ErrorReporting {
if (tree.tpe.widen.exists)
i"${exprStr(tree)} does not take ${kind}parameters"
else {
new FatalError("").printStackTrace()
//new FatalError("").printStackTrace() //DEBUG
i"undefined: $tree # ${tree.uniqueId}: ${tree.tpe.toString} at ${ctx.phase}"
}

Expand Down
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/typer/Implicits.scala
Original file line number Diff line number Diff line change
Expand Up @@ -773,7 +773,7 @@ trait Implicits { self: Typer =>
success(Literal(c))
case TypeRef(_, sym) if sym == defn.UnitClass =>
success(Literal(Constant(())))
case n: NamedType =>
case n: TermRef =>
success(ref(n))
case tp =>
EmptyTree
Expand Down
153 changes: 77 additions & 76 deletions compiler/src/dotty/tools/dotc/typer/Inliner.scala
Original file line number Diff line number Diff line change
Expand Up @@ -237,7 +237,7 @@ class Inliner(call: tpd.Tree, rhsToInline: tpd.Tree)(implicit ctx: Context) {
/** A buffer for bindings that define proxies for actual arguments */
private val bindingsBuf = new mutable.ListBuffer[ValOrDefDef]

private def newSym(name: Name, flags: FlagSet, info: Type): Symbol =
private def newSym(name: Name, flags: FlagSet, info: Type)(implicit ctx: Context): Symbol =
ctx.newSymbol(ctx.owner, name, flags, info, coord = call.span)

/** A binding for the parameter of an inline method. This is a `val` def for
Expand Down Expand Up @@ -730,35 +730,40 @@ class Inliner(call: tpd.Tree, rhsToInline: tpd.Tree)(implicit ctx: Context) {
val gadtSyms = typer.gadtSyms(scrutType)

/** Try to match pattern `pat` against scrutinee reference `scrut`. If successful add
* bindings for variables bound in this pattern to `bindingsBuf`.
* bindings for variables bound in this pattern to `caseBindingMap`.
*/
def reducePattern(
bindingsBuf: mutable.ListBuffer[ValOrDefDef],
fromBuf: mutable.ListBuffer[TypeSymbol],
toBuf: mutable.ListBuffer[TypeSymbol],
caseBindingMap: mutable.ListBuffer[(Symbol, MemberDef)],
scrut: TermRef,
pat: Tree
)(implicit ctx: Context): Boolean = {

/** Create a binding of a pattern bound variable with matching part of
* scrutinee as RHS and type that corresponds to RHS.
*/
def newBinding(sym: TermSymbol, rhs: Tree): Unit = {
sym.info = rhs.tpe.widenTermRefExpr
bindingsBuf += ValDef(sym, constToLiteral(rhs)).withSpan(sym.span)
def newTermBinding(sym: TermSymbol, rhs: Tree): Unit = {
val copied = sym.copy(info = rhs.tpe.widenTermRefExpr, coord = sym.coord).asTerm
caseBindingMap += ((sym, ValDef(copied, constToLiteral(rhs)).withSpan(sym.span)))
}

def newTypeBinding(sym: TypeSymbol, alias: Type): Unit = {
val copied = sym.copy(info = TypeAlias(alias), coord = sym.coord).asType
caseBindingMap += ((sym, TypeDef(copied)))
}

def searchImplicit(sym: TermSymbol, tpt: Tree) = {
val evTyper = new Typer
val evidence = evTyper.inferImplicitArg(tpt.tpe, tpt.span)(ctx.fresh.setTyper(evTyper))
val evCtx = ctx.fresh.setTyper(evTyper)
val evidence = evTyper.inferImplicitArg(tpt.tpe, tpt.span)(evCtx)
evidence.tpe match {
case fail: Implicits.AmbiguousImplicits =>
ctx.error(evTyper.missingArgMsg(evidence, tpt.tpe, ""), tpt.sourcePos)
true // hard error: return true to stop implicit search here
case fail: Implicits.SearchFailureType =>
false
case _ =>
newBinding(sym, evidence)
//inliner.println(i"inferred implicit $sym: ${sym.info} with $evidence: ${evidence.tpe.widen}, ${evCtx.gadt.constraint}, ${evCtx.typerState.constraint}")
newTermBinding(sym, evidence)
true
}
}
Expand Down Expand Up @@ -808,27 +813,25 @@ class Inliner(call: tpd.Tree, rhsToInline: tpd.Tree)(implicit ctx: Context) {
extractBindVariance(SimpleIdentityMap.Empty, tpt.tpe)
}

def addTypeBindings(typeBinds: TypeBindsMap)(implicit ctx: Context): Unit =
typeBinds.foreachBinding { case (sym, shouldBeMinimized) =>
newTypeBinding(sym, ctx.gadt.approximation(sym, fromBelow = shouldBeMinimized))
}

def registerAsGadtSyms(typeBinds: TypeBindsMap)(implicit ctx: Context): Unit =
typeBinds.foreachBinding { case (sym, _) =>
val TypeBounds(lo, hi) = sym.info.bounds
ctx.gadt.addBound(sym, lo, isUpper = false)
ctx.gadt.addBound(sym, hi, isUpper = true)
}

def addTypeBindings(typeBinds: TypeBindsMap)(implicit ctx: Context): Unit =
typeBinds.foreachBinding { case (sym, shouldBeMinimized) =>
val copied = sym.copy(info = TypeAlias(ctx.gadt.approximation(sym, fromBelow = shouldBeMinimized))).asType
fromBuf += sym
toBuf += copied
}

pat match {
case Typed(pat1, tpt) =>
val typeBinds = getTypeBindsMap(pat1, tpt)
registerAsGadtSyms(typeBinds)
scrut <:< tpt.tpe && {
addTypeBindings(typeBinds)
reducePattern(bindingsBuf, fromBuf, toBuf, scrut, pat1)
reducePattern(caseBindingMap, scrut, pat1)
}
case pat @ Bind(name: TermName, Typed(_, tpt)) if isImplicit =>
val typeBinds = getTypeBindsMap(tpt, tpt)
Expand All @@ -838,8 +841,8 @@ class Inliner(call: tpd.Tree, rhsToInline: tpd.Tree)(implicit ctx: Context) {
true
}
case pat @ Bind(name: TermName, body) =>
reducePattern(bindingsBuf, fromBuf, toBuf, scrut, body) && {
if (name != nme.WILDCARD) newBinding(pat.symbol.asTerm, ref(scrut))
reducePattern(caseBindingMap, scrut, body) && {
if (name != nme.WILDCARD) newTermBinding(pat.symbol.asTerm, ref(scrut))
true
}
case Ident(nme.WILDCARD) =>
Expand All @@ -862,8 +865,8 @@ class Inliner(call: tpd.Tree, rhsToInline: tpd.Tree)(implicit ctx: Context) {
case (Nil, Nil) => true
case (pat :: pats1, selector :: selectors1) =>
val elem = newSym(InlineBinderName.fresh(), Synthetic, selector.tpe.widenTermRefExpr).asTerm
newBinding(elem, selector)
reducePattern(bindingsBuf, fromBuf, toBuf, elem.termRef, pat) &&
caseBindingMap += ((NoSymbol, ValDef(elem, constToLiteral(selector)).withSpan(elem.span)))
reducePattern(caseBindingMap, elem.termRef, pat) &&
reduceSubPatterns(pats1, selectors1)
case _ => false
}
Expand All @@ -890,7 +893,7 @@ class Inliner(call: tpd.Tree, rhsToInline: tpd.Tree)(implicit ctx: Context) {
false
}
case Inlined(EmptyTree, Nil, ipat) =>
reducePattern(bindingsBuf, fromBuf, toBuf, scrut, ipat)
reducePattern(caseBindingMap, scrut, ipat)
case _ => false
}
}
Expand All @@ -900,32 +903,34 @@ class Inliner(call: tpd.Tree, rhsToInline: tpd.Tree)(implicit ctx: Context) {
val scrutineeBinding = normalizeBinding(ValDef(scrutineeSym, scrutinee))

def reduceCase(cdef: CaseDef): MatchRedux = {
val caseBindingsBuf = new mutable.ListBuffer[ValOrDefDef]()
def guardOK(implicit ctx: Context) = cdef.guard.isEmpty || {
val guardCtx = ctx.fresh.setNewScope
caseBindingsBuf.foreach(binding => guardCtx.enter(binding.symbol))
typer.typed(cdef.guard, defn.BooleanType)(guardCtx) match {
case ConstantValue(true) => true
case _ => false
val caseBindingMap = new mutable.ListBuffer[(Symbol, MemberDef)]()

def substBindings(
bindings: List[(Symbol, MemberDef)],
bbuf: mutable.ListBuffer[MemberDef],
from: List[Symbol], to: List[Symbol]): (List[MemberDef], List[Symbol], List[Symbol]) =
bindings match {
case (sym, binding) :: rest =>
bbuf += binding.subst(from, to).asInstanceOf[MemberDef]
if (sym.exists) substBindings(rest, bbuf, sym :: from, binding.symbol :: to)
else substBindings(rest, bbuf, from, to)
case Nil => (bbuf.toList, from, to)
}
}
if (!isImplicit) caseBindingsBuf += scrutineeBinding

if (!isImplicit) caseBindingMap += ((NoSymbol, scrutineeBinding))
val gadtCtx = typer.gadtContext(gadtSyms).addMode(Mode.GADTflexible)
val fromBuf = mutable.ListBuffer.empty[TypeSymbol]
val toBuf = mutable.ListBuffer.empty[TypeSymbol]
if (reducePattern(caseBindingsBuf, fromBuf, toBuf, scrutineeSym.termRef, cdef.pat)(gadtCtx) && guardOK) {
val caseBindings = caseBindingsBuf.toList
val from = fromBuf.toList
val to = toBuf.toList
if (from.isEmpty) Some((caseBindings, cdef.body))
else {
val Block(stats, expr) = tpd.Block(caseBindings, cdef.body).subst(from, to)
val typeDefs = to.collect { case sym if sym.name != tpnme.WILDCARD => tpd.TypeDef(sym).withSpan(sym.span) }
Some((typeDefs ::: stats.asInstanceOf[List[MemberDef]], expr))
if (reducePattern(caseBindingMap, scrutineeSym.termRef, cdef.pat)(gadtCtx)) {
val (caseBindings, from, to) = substBindings(caseBindingMap.toList, mutable.ListBuffer(), Nil, Nil)
val guardOK = cdef.guard.isEmpty || {
typer.typed(cdef.guard.subst(from, to), defn.BooleanType) match {
case ConstantValue(true) => true
case _ => false
}
}
if (guardOK) Some((caseBindings, cdef.body.subst(from, to)))
else None
}
else
None
else None
}

def recur(cases: List[CaseDef]): MatchRedux = cases match {
Expand Down Expand Up @@ -1051,8 +1056,29 @@ class Inliner(call: tpd.Tree, rhsToInline: tpd.Tree)(implicit ctx: Context) {
*/
def dropUnusedDefs(bindings: List[MemberDef], tree: Tree)(implicit ctx: Context): (List[MemberDef], Tree) = {
// inlining.println(i"drop unused $bindings%, % in $tree")

def inlineTermBindings(termBindings: List[MemberDef], tree: Tree)(implicit ctx: Context): (List[MemberDef], Tree) = {
val (termBindings, typeBindings) = bindings.partition(_.symbol.isTerm)
if (typeBindings.nonEmpty) {
val typeBindingsSet = typeBindings.foldLeft[SimpleIdentitySet[Symbol]](SimpleIdentitySet.empty)(_ + _.symbol)
val inlineTypeBindings = new TreeTypeMap(
typeMap = new TypeMap() {
override def apply(tp: Type): Type = tp match {
case tr: TypeRef if tr.prefix.eq(NoPrefix) && typeBindingsSet.contains(tr.symbol) =>
val TypeAlias(res) = tr.info
res
case tp => mapOver(tp)
}
},
treeMap = {
case ident: Ident if ident.isType && typeBindingsSet.contains(ident.symbol) =>
val TypeAlias(r) = ident.symbol.info
TypeTree(r).withSpan(ident.span)
case tree => tree
}
)
val Block(termBindings1, tree1) = inlineTypeBindings(Block(termBindings, tree))
dropUnusedDefs(termBindings1.asInstanceOf[List[ValOrDefDef]], tree1)
}
else {
val refCount = newMutableSymbolMap[Int]
val bindingOfSym = newMutableSymbolMap[MemberDef]

Expand All @@ -1061,7 +1087,7 @@ class Inliner(call: tpd.Tree, rhsToInline: tpd.Tree)(implicit ctx: Context) {
case vdef @ ValDef(_, _, _) => isPureExpr(vdef.rhs)
case _ => false
}
for (binding <- termBindings if isInlineable(binding)) {
for (binding <- bindings if isInlineable(binding)) {
refCount(binding.symbol) = 0
bindingOfSym(binding.symbol) = binding
}
Expand Down Expand Up @@ -1119,40 +1145,15 @@ class Inliner(call: tpd.Tree, rhsToInline: tpd.Tree)(implicit ctx: Context) {
}
}

val retained = termBindings.filterConserve(binding => retain(binding.symbol))
if (retained `eq` termBindings) {
(termBindings, tree)
val retained = bindings.filterConserve(binding => retain(binding.symbol))
if (retained `eq` bindings) {
(bindings, tree)
}
else {
val expanded = inlineBindings.transform(tree)
dropUnusedDefs(retained, expanded)
}
}

val (termBindings, typeBindings) = bindings.partition(_.symbol.isTerm)
if (typeBindings.isEmpty) inlineTermBindings(termBindings, tree)
else {
val typeBindingsSet = typeBindings.foldLeft[SimpleIdentitySet[Symbol]](SimpleIdentitySet.empty)(_ + _.symbol)
val inlineTypeBindings = new TreeTypeMap(
typeMap = new TypeMap() {
override def apply(tp: Type): Type = tp match {
case tr: TypeRef if tr.prefix.eq(NoPrefix) && typeBindingsSet.contains(tr.symbol) =>
val TypeAlias(res) = tr.info
res
case tp => mapOver(tp)
}
},
treeMap = {
case ident: Ident if ident.isType && typeBindingsSet.contains(ident.symbol) =>
val TypeAlias(r) = ident.symbol.info
TypeTree(r).withSpan(ident.span)
case tree => tree
}
)

val Block(termBindings1, tree1) = inlineTypeBindings(Block(termBindings, tree))
inlineTermBindings(termBindings1.asInstanceOf[List[ValOrDefDef]], tree1)
}
}

private def expandMacro(body: Tree, span: Span)(implicit ctx: Context) = {
Expand Down
6 changes: 3 additions & 3 deletions compiler/src/dotty/tools/dotc/typer/RefChecks.scala
Original file line number Diff line number Diff line change
Expand Up @@ -156,8 +156,8 @@ object RefChecks {
* 1.8.1 M's type is a subtype of O's type, or
* 1.8.2 M is of type []S, O is of type ()T and S <: T, or
* 1.8.3 M is of type ()S, O is of type []T and S <: T, or
* 1.9.1 If M or O are erased, they must both be erased
* 1.9.2 If M or O are extension methods, they must both be extension methods
* 1.9.1 If M is erased, O is erased. If O is erased, M is erased or inline.
* 1.9.2 If M or O are extension methods, they must both be extension methods.
* 1.10 If M is an inline or Scala-2 macro method, O cannot be deferred unless
* there's also a concrete method that M overrides.
* 1.11. If O is a Scala-2 macro, M must be a Scala-2 macro.
Expand Down Expand Up @@ -394,7 +394,7 @@ object RefChecks {
overrideError("must be declared lazy to override a lazy value")
} else if (member.is(Erased) && !other.is(Erased)) { // (1.9.1)
overrideError("is erased, cannot override non-erased member")
} else if (other.is(Erased) && !member.is(Erased)) { // (1.9.1)
} else if (other.is(Erased) && !member.is(Erased | Inline)) { // (1.9.1)
overrideError("is not erased, cannot override erased member")
} else if (member.is(Extension) && !other.is(Extension)) { // (1.9.2)
overrideError("is an extension method, cannot override a normal method")
Expand Down
1 change: 1 addition & 0 deletions compiler/test/dotc/run-test-pickling.blacklist
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ tuples1a.scala
typeclass-derivation1.scala
typeclass-derivation2.scala
typeclass-derivation2a.scala
typeclass-derivation2c.scala
typeclass-derivation3.scala
derive-generic.scala
deriving-interesting-prefixes.scala
Expand Down
5 changes: 5 additions & 0 deletions tests/neg/i6241.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
object Test extends App {
inline def v[T] = valueOf[T] // error

println(v[String])
}
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
// Implicit matches that bind parameters don't work yet.
object `implicit-match` {
object invariant {
case class Box[T](value: T)
Expand Down
Loading