Skip to content

Commit 8cf938d

Browse files
committed
Allow inlining after errors
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. As part of this commit, merge hasBodyToInline and bodyToInline. An erroneous body can suppress inlining by returing an EmptyTree.
1 parent 17dfdc9 commit 8cf938d

File tree

4 files changed

+36
-24
lines changed

4 files changed

+36
-24
lines changed

compiler/src/dotty/tools/dotc/sbt/ExtractAPI.scala

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -591,16 +591,16 @@ private class ExtractAPICollector(implicit val ctx: Context) extends ThunkHolder
591591

592592
def apiAnnotations(s: Symbol): List[api.Annotation] = {
593593
val annots = new mutable.ListBuffer[api.Annotation]
594-
595-
if (Inliner.hasBodyToInline(s)) {
594+
val inlineBody = Inliner.bodyToInline(s)
595+
if (!inlineBody.isEmpty) {
596596
// FIXME: If the body of an inlineable method changes, all the reverse
597597
// dependencies of this method need to be recompiled. sbt has no way
598598
// of tracking method bodies, so as a hack we include the pretty-printed
599599
// typed tree of the method as part of the signature we send to sbt.
600600
// To do this properly we would need a way to hash trees and types in
601601
// dotty itself.
602602
val printTypesCtx = ctx.fresh.setSetting(ctx.settings.XprintTypes, true)
603-
annots += marker(Inliner.bodyToInline(s).show(printTypesCtx))
603+
annots += marker(inlineBody.show(printTypesCtx))
604604
}
605605

606606
// In the Scala2 ExtractAPI phase we only extract annotations that extend

compiler/src/dotty/tools/dotc/typer/Inliner.scala

Lines changed: 23 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -38,15 +38,20 @@ object Inliner {
3838
def hasBodyToInline(sym: SymDenotation)(implicit ctx: Context): Boolean =
3939
sym.isInlineMethod && sym.hasAnnotation(defn.BodyAnnot)
4040

41-
/** The body to inline for method `sym`.
41+
/** The body to inline for method `sym`, or `EmptyTree` if none exists.
42+
* Note: definitions coming from Scala2x class files might be `@forceInline`,
43+
* but still lack that body.
4244
* @pre hasBodyToInline(sym)
4345
*/
4446
def bodyToInline(sym: SymDenotation)(implicit ctx: Context): Tree =
45-
sym.unforcedAnnotation(defn.BodyAnnot).get.tree
47+
if (sym.isInlineMethod && sym.hasAnnotation(defn.BodyAnnot))
48+
sym.getAnnotation(defn.BodyAnnot).get.tree
49+
else
50+
EmptyTree
4651

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

5156
/** Should call be inlined in this context? */
5257
def isInlineable(tree: Tree)(implicit ctx: Context): Boolean = tree match {
@@ -105,8 +110,7 @@ object Inliner {
105110
cpy.Block(tree)(bindings.toList, inlineCall(tree1))
106111
else if (enclosingInlineds.length < ctx.settings.XmaxInlines.value) {
107112
val body = bodyToInline(tree.symbol) // can typecheck the tree and thereby produce errors
108-
if (ctx.reporter.hasErrors) tree
109-
else new Inliner(tree, body).inlined(tree.sourcePos)
113+
new Inliner(tree, body).inlined(tree.sourcePos)
110114
}
111115
else
112116
errorTree(
@@ -204,7 +208,7 @@ class Inliner(call: tpd.Tree, rhsToInline: tpd.Tree)(implicit ctx: Context) {
204208
private val inlineCallPrefix =
205209
qualifier(methPart).orElse(This(inlinedMethod.enclosingClass.asClass))
206210

207-
inlining.println("-----------------------\nInlining $call\nWith RHS $rhsToInline")
211+
inlining.println(i"-----------------------\nInlining $call\nWith RHS $rhsToInline")
208212

209213
// Make sure all type arguments to the call are fully determined
210214
for (targ <- callTypeArgs) fullyDefinedType(targ.tpe, "inlined type argument", targ.span)
@@ -421,7 +425,7 @@ class Inliner(call: tpd.Tree, rhsToInline: tpd.Tree)(implicit ctx: Context) {
421425
// Compute bindings for all this-proxies, appending them to bindingsBuf
422426
computeThisBindings()
423427

424-
val inlineTyper = new InlineTyper
428+
val inlineTyper = new InlineTyper(ctx.reporter.errorCount)
425429

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

@@ -986,7 +990,7 @@ class Inliner(call: tpd.Tree, rhsToInline: tpd.Tree)(implicit ctx: Context) {
986990
* 4. Make sure inlined code is type-correct.
987991
* 5. Make sure that the tree's typing is idempotent (so that future -Ycheck passes succeed)
988992
*/
989-
class InlineTyper extends ReTyper {
993+
class InlineTyper(initialErrorCount: Int) extends ReTyper {
990994
import reducer._
991995

992996
override def ensureAccessible(tpe: Type, superAccess: Boolean, pos: SourcePosition)(implicit ctx: Context): Type = {
@@ -1033,7 +1037,11 @@ class Inliner(call: tpd.Tree, rhsToInline: tpd.Tree)(implicit ctx: Context) {
10331037

10341038
override def typedApply(tree: untpd.Apply, pt: Type)(implicit ctx: Context): Tree =
10351039
constToLiteral(betaReduce(super.typedApply(tree, pt))) match {
1036-
case res: Apply if res.symbol == defn.InternalQuoted_exprSplice && level == 0 && call.symbol.is(Macro) =>
1040+
case res: Apply
1041+
if res.symbol == defn.InternalQuoted_exprSplice &&
1042+
level == 0 &&
1043+
call.symbol.is(Macro) &&
1044+
!suppressInline =>
10371045
expandMacro(res.args.head, tree.span)
10381046
case res => res
10391047
}
@@ -1082,7 +1090,11 @@ class Inliner(call: tpd.Tree, rhsToInline: tpd.Tree)(implicit ctx: Context) {
10821090
}
10831091
}
10841092

1085-
override def newLikeThis: Typer = new InlineTyper
1093+
override def newLikeThis: Typer = new InlineTyper(initialErrorCount)
1094+
1095+
/** Suppress further inlining if this inline typer has already issued errors */
1096+
override def suppressInline given (ctx: Context) =
1097+
ctx.reporter.errorCount > initialErrorCount || super.suppressInline
10861098
}
10871099

10881100
/** Drop any side-effect-free bindings that are unused in expansion or other reachable bindings.
@@ -1202,8 +1214,7 @@ class Inliner(call: tpd.Tree, rhsToInline: tpd.Tree)(implicit ctx: Context) {
12021214
}
12031215
}
12041216
val normalizedSplice = inlinedNormailizer.transform(evaluatedSplice)
1205-
1206-
if (ctx.reporter.hasErrors) EmptyTree
1217+
if (normalizedSplice.isEmpty) normalizedSplice
12071218
else normalizedSplice.withSpan(span)
12081219
}
12091220
}

compiler/src/dotty/tools/dotc/typer/Namer.scala

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -805,8 +805,6 @@ class Namer { typer: Typer =>
805805

806806
private def addInlineInfo(sym: Symbol) = original match {
807807
case original: untpd.DefDef if sym.isInlineMethod =>
808-
if (sym.owner.isClass && sym.owner.seesOpaques)
809-
ctx.error(em"Implementation restriction: No inline methods allowed where opaque type aliases are in scope", sym.sourcePos)
810808
PrepareInlineable.registerInlineInfo(
811809
sym,
812810
implicit ctx => typedAheadExpr(original).asInstanceOf[tpd.DefDef].rhs

compiler/src/dotty/tools/dotc/typer/Typer.scala

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,7 @@ class Typer extends Namer
111111
*/
112112
private[this] var foundUnderScala2: Type = NoType
113113

114+
// Overridden in derived typers
114115
def newLikeThis: Typer = new Typer
115116

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

1539-
if (sym.isInlineMethod) {
1540-
PrepareInlineable.checkInlineMacro(sym, rhs1, ddef.sourcePos)
1540+
if (sym.isInlineMethod)
15411541
PrepareInlineable.registerInlineInfo(sym, _ => rhs1)
1542-
}
15431542

15441543
if (sym.isConstructor && !sym.isPrimaryConstructor) {
15451544
for (param <- tparams1 ::: vparamss1.flatten)
@@ -2119,7 +2118,7 @@ class Typer extends Namer
21192118
traverse(xtree :: rest)
21202119
case none =>
21212120
typed(mdef) match {
2122-
case mdef1: DefDef if Inliner.hasBodyToInline(mdef1.symbol) =>
2121+
case mdef1: DefDef if !Inliner.bodyToInline(mdef1.symbol).isEmpty =>
21232122
buf += inlineExpansion(mdef1)
21242123
// replace body with expansion, because it will be used as inlined body
21252124
// from separately compiled files - the original BodyAnnotation is not kept.
@@ -2666,10 +2665,11 @@ class Typer extends Namer
26662665
}
26672666
else if (Inliner.isInlineable(tree) &&
26682667
!ctx.settings.YnoInline.value &&
2669-
!ctx.isAfterTyper &&
2670-
!ctx.reporter.hasErrors) {
2668+
!suppressInline) {
26712669
tree.tpe <:< wildApprox(pt)
2672-
readaptSimplified(Inliner.inlineCall(tree))
2670+
val errorCount = ctx.reporter.errorCount
2671+
val inlined = Inliner.inlineCall(tree)
2672+
if (errorCount == ctx.reporter.errorCount) readaptSimplified(inlined) else inlined
26732673
}
26742674
else if (tree.symbol.isScala2Macro &&
26752675
// raw and s are eliminated by the StringInterpolatorOpt phase
@@ -2978,6 +2978,9 @@ class Typer extends Namer
29782978
}
29792979
}
29802980

2981+
// Overridden in InlineTyper
2982+
def suppressInline given (ctx: Context): Boolean = ctx.isAfterTyper
2983+
29812984
/** Does the "contextuality" of the method type `methType` match the one of the prototype `pt`?
29822985
* This is the case if
29832986
* - both are contextual, or

0 commit comments

Comments
 (0)