Skip to content

Commit

Permalink
Merge pull request scala#6218 from dotty-staging/inliner-fixes
Browse files Browse the repository at this point in the history
Inliner improvements
  • Loading branch information
milessabin authored Apr 14, 2019
2 parents c3e88b7 + 1929522 commit 25a3773
Show file tree
Hide file tree
Showing 12 changed files with 1,079 additions and 82 deletions.
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 @@ -239,7 +239,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 @@ -732,35 +732,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 @@ -810,27 +815,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 @@ -840,8 +843,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 @@ -864,8 +867,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 @@ -892,7 +895,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 @@ -902,32 +905,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 @@ -1053,8 +1058,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 @@ -1063,7 +1089,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 @@ -1121,40 +1147,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

0 comments on commit 25a3773

Please sign in to comment.