Skip to content

Commit 932d797

Browse files
committed
detect changed selectIn: error when same file, or suspend otherwise
1 parent 020617f commit 932d797

File tree

3 files changed

+33
-19
lines changed

3 files changed

+33
-19
lines changed

compiler/src/dotty/tools/dotc/core/Annotations.scala

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -177,8 +177,13 @@ object Annotations {
177177
assert(myTree != null)
178178
myTree match {
179179
case treeFn: (Context ?=> Tree) @unchecked =>
180+
var result: Tree | Null = null
180181
myTree = null
181-
myTree = atPhaseBeforeTransforms(treeFn)
182+
try
183+
result = atPhaseBeforeTransforms(treeFn)
184+
myTree = result
185+
finally if result == null then
186+
myTree = ctx ?=> treeFn(using ctx) // reset, if unit is suspended then it will re-enter this annotation
182187
case _ =>
183188
}
184189
myTree.asInstanceOf[Tree]

compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala

Lines changed: 14 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -170,11 +170,13 @@ class TreeUnpickler(reader: TastyReader,
170170
case ex: Exception => fail(ex)
171171
}
172172

173-
class TreeReader(val reader: TastyReader) {
173+
class TreeReader(val reader: TastyReader, inInlineBody: Boolean = false) {
174174
import reader.*
175175

176-
def forkAt(start: Addr): TreeReader = new TreeReader(subReader(start, endAddr))
177-
def fork: TreeReader = forkAt(currentAddr)
176+
def forkAt(start: Addr, inInlineBody: Boolean = false): TreeReader =
177+
new TreeReader(subReader(start, endAddr), inInlineBody)
178+
179+
def fork: TreeReader = forkAt(currentAddr, inInlineBody)
178180

179181
def skipParentTree(tag: Int): Unit = {
180182
if tag == SPLITCLAUSE then ()
@@ -694,7 +696,7 @@ class TreeUnpickler(reader: TastyReader,
694696
val ctx1 = localContext(sym)(using ctx0).addMode(Mode.ReadPositions)
695697
inContext(sourceChangeContext(Addr(0))(using ctx1)) {
696698
// avoids space leaks by not capturing the current context
697-
forkAt(rhsStart).readTree()
699+
forkAt(rhsStart, inInlineBody = true).readTree()
698700
}
699701
})
700702
goto(start)
@@ -1580,21 +1582,14 @@ class TreeUnpickler(reader: TastyReader,
15801582
val d = ownerTpe.decl(name).atSignature(sig, target)
15811583
(if !d.exists then lookupInSuper else d).asSeenFrom(prefix)
15821584

1583-
val denot0 = inContext(ctx.addMode(Mode.ResolveFromTASTy)):
1585+
val denot = inContext(ctx.addMode(Mode.ResolveFromTASTy)):
15841586
searchDenot // able to resolve SourceInvisible members
15851587

1586-
val denot =
1587-
if
1588-
denot0.symbol.exists
1589-
&& denot0.symbol.is(SourceInvisible)
1590-
&& denot0.symbol.isDefinedInSource
1591-
then
1592-
searchDenot // fallback
1593-
else
1594-
denot0
1595-
15961588

1597-
makeSelect(qual, name, denot)
1589+
val sel = makeSelect(qual, name, denot)
1590+
if denot == NoDenotation && inInlineBody && sel.denot.symbol.exists && sel.symbol.isDefinedInCurrentRun then
1591+
throw new ChangedMethodDenot(sel.denot.symbol)
1592+
sel
15981593
case REPEATED =>
15991594
val elemtpt = readTpt()
16001595
SeqLiteral(until(end)(readTree()), elemtpt)
@@ -1901,6 +1896,9 @@ class TreeUnpickler(reader: TastyReader,
19011896

19021897
object TreeUnpickler {
19031898

1899+
/** Specifically thrown when a SELECTin was written to TASTy, i.e. is expected to resolve, and then doesn't. */
1900+
private[dotc] final class ChangedMethodDenot(val resolved: Symbol) extends Exception
1901+
19041902
/** Define the expected format of the tasty bytes
19051903
* - TopLevel: Tasty that contains a full class nested in its package
19061904
* - Term: Tasty that contains only a term tree

compiler/src/dotty/tools/dotc/inlines/Inlines.scala

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import staging.StagingLevel
1919
import collection.mutable
2020
import reporting.{NotConstant, trace}
2121
import util.Spans.Span
22+
import dotty.tools.dotc.core.tasty.TreeUnpickler
2223

2324
/** Support for querying inlineable methods and for inlining calls to such methods */
2425
object Inlines:
@@ -158,8 +159,18 @@ object Inlines:
158159
else if enclosingInlineds.length < ctx.settings.XmaxInlines.value && !reachedInlinedTreesLimit then
159160
val body =
160161
try bodyToInline(tree.symbol) // can typecheck the tree and thereby produce errors
161-
catch case _: MissingInlineInfo =>
162-
throw CyclicReference(ctx.owner)
162+
catch
163+
case _: MissingInlineInfo => throw CyclicReference(ctx.owner)
164+
case err: TreeUnpickler.ChangedMethodDenot =>
165+
// tested in sbt-test/tasty-compat/add-param-unroll2/a_v3/A.scala
166+
if err.resolved.source == ctx.source then
167+
report.error(em"""cannot inline ${tree.symbol}:
168+
| The definition of ${err.resolved.showLocated}, defined in the current file, has changed incompatibly.
169+
| Try inlining from a different file.""", tree.srcPos)
170+
EmptyTree
171+
else
172+
// Tested in sbt-test/tasty-compat/add-param-unroll2/a_v3_2/C.scala
173+
ctx.compilationUnit.suspend("suspending in case of possible generated methods")
163174
new InlineCall(tree).expand(body)
164175
else
165176
ctx.base.stopInlining = true

0 commit comments

Comments
 (0)