Skip to content

Commit

Permalink
Allow inlining after errors
Browse files Browse the repository at this point in the history
But avoid inlining:

 - if the typechecking the body to inline generated errors
 - if checking inlined method generated errors
 - if we are in an inline typer and the same inline typer
   already generated errors.
  • Loading branch information
odersky committed Jul 24, 2019
1 parent 17ac1ec commit bdeda22
Show file tree
Hide file tree
Showing 6 changed files with 48 additions and 40 deletions.
25 changes: 17 additions & 8 deletions compiler/src/dotty/tools/dotc/typer/Inliner.scala
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,9 @@ object Inliner {
def hasBodyToInline(sym: SymDenotation)(implicit ctx: Context): Boolean =
sym.isInlineMethod && sym.hasAnnotation(defn.BodyAnnot)

/** The body to inline for method `sym`.
/** The body to inline for method `sym`, or `EmptyTree` if none exists.
* Note: definitions coming from Scala2x class files might be `@forceInline`,
* but still lack that body.
* @pre hasBodyToInline(sym)
*/
def bodyToInline(sym: SymDenotation)(implicit ctx: Context): Tree =
Expand All @@ -48,7 +50,7 @@ object Inliner {

/** Should call to method `meth` be inlined in this context? */
def isInlineable(meth: Symbol)(implicit ctx: Context): Boolean =
meth.is(Inline) && hasBodyToInline(meth) && !ctx.inInlineMethod
meth.is(Inline) && !ctx.inInlineMethod && !bodyToInline(meth).isEmpty

/** Should call be inlined in this context? */
def isInlineable(tree: Tree)(implicit ctx: Context): Boolean = tree match {
Expand Down Expand Up @@ -108,8 +110,7 @@ object Inliner {
cpy.Block(tree)(bindings.toList, inlineCall(tree1))
else if (enclosingInlineds.length < ctx.settings.XmaxInlines.value) {
val body = bodyToInline(tree.symbol) // can typecheck the tree and thereby produce errors
if (ctx.reporter.hasErrors) tree
else new Inliner(tree, body).inlined(tree.sourcePos)
new Inliner(tree, body).inlined(tree.sourcePos)
}
else
errorTree(
Expand Down Expand Up @@ -424,7 +425,7 @@ class Inliner(call: tpd.Tree, rhsToInline: tpd.Tree)(implicit ctx: Context) {
// Compute bindings for all this-proxies, appending them to bindingsBuf
computeThisBindings()

val inlineTyper = new InlineTyper
val inlineTyper = new InlineTyper(ctx.reporter.errorCount)

val inlineCtx = inlineContext(call).fresh.setTyper(inlineTyper).setNewScope

Expand Down Expand Up @@ -991,7 +992,7 @@ class Inliner(call: tpd.Tree, rhsToInline: tpd.Tree)(implicit ctx: Context) {
* 4. Make sure inlined code is type-correct.
* 5. Make sure that the tree's typing is idempotent (so that future -Ycheck passes succeed)
*/
class InlineTyper extends ReTyper {
class InlineTyper(initialErrorCount: Int) extends ReTyper {
import reducer._

override def ensureAccessible(tpe: Type, superAccess: Boolean, pos: SourcePosition)(implicit ctx: Context): Type = {
Expand Down Expand Up @@ -1038,7 +1039,11 @@ class Inliner(call: tpd.Tree, rhsToInline: tpd.Tree)(implicit ctx: Context) {

override def typedApply(tree: untpd.Apply, pt: Type)(implicit ctx: Context): Tree = {
constToLiteral(betaReduce(super.typedApply(tree, pt))) match {
case res: Apply if res.symbol == defn.InternalQuoted_exprSplice && level == 0 && call.symbol.is(Macro) =>
case res: Apply
if res.symbol == defn.InternalQuoted_exprSplice &&
level == 0 &&
call.symbol.is(Macro) &&
!suppressInline =>
expandMacro(res.args.head, tree.span)
case res => res
}
Expand Down Expand Up @@ -1088,7 +1093,11 @@ class Inliner(call: tpd.Tree, rhsToInline: tpd.Tree)(implicit ctx: Context) {
}
}

override def newLikeThis: Typer = new InlineTyper
override def newLikeThis: Typer = new InlineTyper(initialErrorCount)

/** Suppress further inlining if this inline typer has already issued errors */
override def suppressInline given (ctx: Context) =
ctx.reporter.errorCount > initialErrorCount || super.suppressInline
}

/** Drop any side-effect-free bindings that are unused in expansion or other reachable bindings.
Expand Down
2 changes: 0 additions & 2 deletions compiler/src/dotty/tools/dotc/typer/Namer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -806,8 +806,6 @@ class Namer { typer: Typer =>

private def addInlineInfo(sym: Symbol) = original match {
case original: untpd.DefDef if sym.isInlineMethod =>
if (sym.owner.isClass && sym.owner.seesOpaques)
ctx.error(em"Implementation restriction: No inline methods allowed where opaque type aliases are in scope", sym.sourcePos)
PrepareInlineable.registerInlineInfo(
sym,
implicit ctx => typedAheadExpr(original).asInstanceOf[tpd.DefDef].rhs
Expand Down
42 changes: 21 additions & 21 deletions compiler/src/dotty/tools/dotc/typer/PrepareInlineable.scala
Original file line number Diff line number Diff line change
Expand Up @@ -222,42 +222,43 @@ object PrepareInlineable {
val inlineCtx = ctx
inlined.updateAnnotation(LazyBodyAnnotation { _ =>
implicit val ctx = inlineCtx
val rawBody = treeExpr(ctx)
val typedBody =
if (ctx.reporter.hasErrors) rawBody
else ctx.compilationUnit.inlineAccessors.makeInlineable(rawBody)
checkInlineMethod(inlined, typedBody)
val inlineableBody = typedBody
inlining.println(i"Body to inline for $inlined: $inlineableBody")
inlineableBody
val initialErrorCount = ctx.reporter.errorCount
var inlinedBody = treeExpr(ctx)
if (ctx.reporter.errorCount == initialErrorCount) {
inlinedBody = ctx.compilationUnit.inlineAccessors.makeInlineable(inlinedBody)
checkInlineMethod(inlined, inlinedBody)
if (ctx.reporter.errorCount != initialErrorCount)
inlinedBody = EmptyTree
}
inlining.println(i"Body to inline for $inlined: $inlinedBody")
inlinedBody
})
}
}
}

def checkInlineMethod(inlined: Symbol, body: Tree)(implicit ctx: Context): Unit = {
if (inlined.owner.isClass && inlined.owner.seesOpaques)
ctx.error(em"Implementation restriction: No inline methods allowed where opaque type aliases are in scope", inlined.sourcePos)
if (ctx.outer.inInlineMethod)
ctx.error(ex"implementation restriction: nested inline methods are not supported", inlined.sourcePos)
if (inlined.name.isUnapplyName && tupleArgs(body).isEmpty)
ctx.warning(
em"inline unapply method can be rewritten only if its right hand side is a tuple (e1, ..., eN)",
body.sourcePos)
}
if (inlined.is(Macro) && !ctx.isAfterTyper) {

def checkInlineMacro(sym: Symbol, rhs: Tree, pos: SourcePosition)(implicit ctx: Context) = {
if (sym.is(Macro) && !ctx.isAfterTyper) {
def isValidMacro(tree: Tree)(implicit ctx: Context): Unit = tree match {
def checkMacro(tree: Tree): Unit = tree match {
case Spliced(code) =>
if (code.symbol.flags.is(Inline))
ctx.error("Macro cannot be implemented with an `inline` method", code.sourcePos)
Splicer.checkValidMacroBody(code)
new PCPCheckAndHeal(freshStagingContext).transform(rhs) // Ignore output, only check PCP

case Block(List(stat), Literal(Constants.Constant(()))) => isValidMacro(stat)
case Block(Nil, expr) => isValidMacro(expr)
case Typed(expr, _) => isValidMacro(expr)
new PCPCheckAndHeal(freshStagingContext).transform(body) // Ignore output, only check PCP
case Block(List(stat), Literal(Constants.Constant(()))) => checkMacro(stat)
case Block(Nil, expr) => checkMacro(expr)
case Typed(expr, _) => checkMacro(expr)
case Block(DefDef(nme.ANON_FUN, _, _, _, _) :: Nil, Closure(_, fn, _)) if fn.symbol.info.isImplicitMethod =>
// TODO Suppot this pattern
// TODO Support this pattern
ctx.error(
"""Macros using a return type of the form `foo(): given X => Y` are not yet supported.
|
Expand All @@ -272,10 +273,9 @@ object PrepareInlineable {
|
| * The contents of the splice must call a static method
| * All arguments must be quoted or inline
""".stripMargin, pos)
""".stripMargin, inlined.sourcePos)
}
isValidMacro(rhs)
checkMacro(body)
}
}

}
15 changes: 9 additions & 6 deletions compiler/src/dotty/tools/dotc/typer/Typer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@ class Typer extends Namer
*/
private[this] var foundUnderScala2: Type = NoType

// Overridden in derived typers
def newLikeThis: Typer = new Typer

/** Find the type of an identifier with given `name` in given context `ctx`.
Expand Down Expand Up @@ -1601,10 +1602,8 @@ class Typer extends Namer
if (sym.isInlineMethod) rhsCtx.addMode(Mode.InlineableBody)
val rhs1 = typedExpr(ddef.rhs, tpt1.tpe.widenExpr)(rhsCtx)

if (sym.isInlineMethod) {
PrepareInlineable.checkInlineMacro(sym, rhs1, ddef.sourcePos)
if (sym.isInlineMethod)
PrepareInlineable.registerInlineInfo(sym, _ => rhs1)
}

if (sym.isConstructor && !sym.isPrimaryConstructor) {
for (param <- tparams1 ::: vparamss1.flatten)
Expand Down Expand Up @@ -2734,10 +2733,11 @@ class Typer extends Namer
}
else if (Inliner.isInlineable(tree) &&
!ctx.settings.YnoInline.value &&
!ctx.isAfterTyper &&
!ctx.reporter.hasErrors) {
!suppressInline) {
tree.tpe <:< wildApprox(pt)
readaptSimplified(Inliner.inlineCall(tree))
val errorCount = ctx.reporter.errorCount
val inlined = Inliner.inlineCall(tree)
if (errorCount == ctx.reporter.errorCount) readaptSimplified(inlined) else inlined
}
else if (tree.symbol.isScala2Macro) {
if (tree.symbol eq defn.StringContext_f) {
Expand Down Expand Up @@ -3047,6 +3047,9 @@ class Typer extends Namer
}
}

// Overridden in InlineTyper
def suppressInline given (ctx: Context): Boolean = ctx.isAfterTyper

/** Does the "contextuality" of the method type `methType` match the one of the prototype `pt`?
* This is the case if
* - both are contextual, or
Expand Down
1 change: 1 addition & 0 deletions tests/neg/GenericNumLits/Test_2.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@ object Test extends App {

val e1: Even = 1234
val e2: Even = 123 // error: 123 is odd
val e3: Even = 123456789101111 // error: number too large
}
3 changes: 0 additions & 3 deletions tests/neg/GenericNumLits/Test_3.scala

This file was deleted.

0 comments on commit bdeda22

Please sign in to comment.