Skip to content

Commit bdeda22

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.
1 parent 17ac1ec commit bdeda22

File tree

6 files changed

+48
-40
lines changed

6 files changed

+48
-40
lines changed

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

+17-8
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,9 @@ object Inliner {
3737
def hasBodyToInline(sym: SymDenotation)(implicit ctx: Context): Boolean =
3838
sym.isInlineMethod && sym.hasAnnotation(defn.BodyAnnot)
3939

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

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

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

427-
val inlineTyper = new InlineTyper
428+
val inlineTyper = new InlineTyper(ctx.reporter.errorCount)
428429

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

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

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

10391040
override def typedApply(tree: untpd.Apply, pt: Type)(implicit ctx: Context): Tree = {
10401041
constToLiteral(betaReduce(super.typedApply(tree, pt))) match {
1041-
case res: Apply if res.symbol == defn.InternalQuoted_exprSplice && level == 0 && call.symbol.is(Macro) =>
1042+
case res: Apply
1043+
if res.symbol == defn.InternalQuoted_exprSplice &&
1044+
level == 0 &&
1045+
call.symbol.is(Macro) &&
1046+
!suppressInline =>
10421047
expandMacro(res.args.head, tree.span)
10431048
case res => res
10441049
}
@@ -1088,7 +1093,11 @@ class Inliner(call: tpd.Tree, rhsToInline: tpd.Tree)(implicit ctx: Context) {
10881093
}
10891094
}
10901095

1091-
override def newLikeThis: Typer = new InlineTyper
1096+
override def newLikeThis: Typer = new InlineTyper(initialErrorCount)
1097+
1098+
/** Suppress further inlining if this inline typer has already issued errors */
1099+
override def suppressInline given (ctx: Context) =
1100+
ctx.reporter.errorCount > initialErrorCount || super.suppressInline
10921101
}
10931102

10941103
/** Drop any side-effect-free bindings that are unused in expansion or other reachable bindings.

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

-2
Original file line numberDiff line numberDiff line change
@@ -806,8 +806,6 @@ class Namer { typer: Typer =>
806806

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

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

+21-21
Original file line numberDiff line numberDiff line change
@@ -222,42 +222,43 @@ object PrepareInlineable {
222222
val inlineCtx = ctx
223223
inlined.updateAnnotation(LazyBodyAnnotation { _ =>
224224
implicit val ctx = inlineCtx
225-
val rawBody = treeExpr(ctx)
226-
val typedBody =
227-
if (ctx.reporter.hasErrors) rawBody
228-
else ctx.compilationUnit.inlineAccessors.makeInlineable(rawBody)
229-
checkInlineMethod(inlined, typedBody)
230-
val inlineableBody = typedBody
231-
inlining.println(i"Body to inline for $inlined: $inlineableBody")
232-
inlineableBody
225+
val initialErrorCount = ctx.reporter.errorCount
226+
var inlinedBody = treeExpr(ctx)
227+
if (ctx.reporter.errorCount == initialErrorCount) {
228+
inlinedBody = ctx.compilationUnit.inlineAccessors.makeInlineable(inlinedBody)
229+
checkInlineMethod(inlined, inlinedBody)
230+
if (ctx.reporter.errorCount != initialErrorCount)
231+
inlinedBody = EmptyTree
232+
}
233+
inlining.println(i"Body to inline for $inlined: $inlinedBody")
234+
inlinedBody
233235
})
234236
}
235237
}
236238
}
237239

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

247-
def checkInlineMacro(sym: Symbol, rhs: Tree, pos: SourcePosition)(implicit ctx: Context) = {
248-
if (sym.is(Macro) && !ctx.isAfterTyper) {
249-
def isValidMacro(tree: Tree)(implicit ctx: Context): Unit = tree match {
251+
def checkMacro(tree: Tree): Unit = tree match {
250252
case Spliced(code) =>
251253
if (code.symbol.flags.is(Inline))
252254
ctx.error("Macro cannot be implemented with an `inline` method", code.sourcePos)
253255
Splicer.checkValidMacroBody(code)
254-
new PCPCheckAndHeal(freshStagingContext).transform(rhs) // Ignore output, only check PCP
255-
256-
case Block(List(stat), Literal(Constants.Constant(()))) => isValidMacro(stat)
257-
case Block(Nil, expr) => isValidMacro(expr)
258-
case Typed(expr, _) => isValidMacro(expr)
256+
new PCPCheckAndHeal(freshStagingContext).transform(body) // Ignore output, only check PCP
257+
case Block(List(stat), Literal(Constants.Constant(()))) => checkMacro(stat)
258+
case Block(Nil, expr) => checkMacro(expr)
259+
case Typed(expr, _) => checkMacro(expr)
259260
case Block(DefDef(nme.ANON_FUN, _, _, _, _) :: Nil, Closure(_, fn, _)) if fn.symbol.info.isImplicitMethod =>
260-
// TODO Suppot this pattern
261+
// TODO Support this pattern
261262
ctx.error(
262263
"""Macros using a return type of the form `foo(): given X => Y` are not yet supported.
263264
|
@@ -272,10 +273,9 @@ object PrepareInlineable {
272273
|
273274
| * The contents of the splice must call a static method
274275
| * All arguments must be quoted or inline
275-
""".stripMargin, pos)
276+
""".stripMargin, inlined.sourcePos)
276277
}
277-
isValidMacro(rhs)
278+
checkMacro(body)
278279
}
279280
}
280-
281281
}

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

+9-6
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`.
@@ -1601,10 +1602,8 @@ class Typer extends Namer
16011602
if (sym.isInlineMethod) rhsCtx.addMode(Mode.InlineableBody)
16021603
val rhs1 = typedExpr(ddef.rhs, tpt1.tpe.widenExpr)(rhsCtx)
16031604

1604-
if (sym.isInlineMethod) {
1605-
PrepareInlineable.checkInlineMacro(sym, rhs1, ddef.sourcePos)
1605+
if (sym.isInlineMethod)
16061606
PrepareInlineable.registerInlineInfo(sym, _ => rhs1)
1607-
}
16081607

16091608
if (sym.isConstructor && !sym.isPrimaryConstructor) {
16101609
for (param <- tparams1 ::: vparamss1.flatten)
@@ -2734,10 +2733,11 @@ class Typer extends Namer
27342733
}
27352734
else if (Inliner.isInlineable(tree) &&
27362735
!ctx.settings.YnoInline.value &&
2737-
!ctx.isAfterTyper &&
2738-
!ctx.reporter.hasErrors) {
2736+
!suppressInline) {
27392737
tree.tpe <:< wildApprox(pt)
2740-
readaptSimplified(Inliner.inlineCall(tree))
2738+
val errorCount = ctx.reporter.errorCount
2739+
val inlined = Inliner.inlineCall(tree)
2740+
if (errorCount == ctx.reporter.errorCount) readaptSimplified(inlined) else inlined
27412741
}
27422742
else if (tree.symbol.isScala2Macro) {
27432743
if (tree.symbol eq defn.StringContext_f) {
@@ -3047,6 +3047,9 @@ class Typer extends Namer
30473047
}
30483048
}
30493049

3050+
// Overridden in InlineTyper
3051+
def suppressInline given (ctx: Context): Boolean = ctx.isAfterTyper
3052+
30503053
/** Does the "contextuality" of the method type `methType` match the one of the prototype `pt`?
30513054
* This is the case if
30523055
* - both are contextual, or

tests/neg/GenericNumLits/Test_2.scala

+1
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,5 @@ object Test extends App {
22

33
val e1: Even = 1234
44
val e2: Even = 123 // error: 123 is odd
5+
val e3: Even = 123456789101111 // error: number too large
56
}

tests/neg/GenericNumLits/Test_3.scala

-3
This file was deleted.

0 commit comments

Comments
 (0)