diff --git a/compiler/src/dotty/tools/dotc/core/Annotations.scala b/compiler/src/dotty/tools/dotc/core/Annotations.scala index d6a99b12e3b3..608ae470cfa9 100644 --- a/compiler/src/dotty/tools/dotc/core/Annotations.scala +++ b/compiler/src/dotty/tools/dotc/core/Annotations.scala @@ -177,8 +177,14 @@ object Annotations { assert(myTree != null) myTree match { case treeFn: (Context ?=> Tree) @unchecked => + val retainedFn = treeFn // for some reason treeFn is null if exception is thrown + var result: Tree | Null = null myTree = null - myTree = atPhaseBeforeTransforms(treeFn) + try + result = atPhaseBeforeTransforms(treeFn) + myTree = result + finally if result == null then + myTree = retainedFn // reset, if unit is suspended then it will re-enter this annotation case _ => } myTree.asInstanceOf[Tree] diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala index c6a334a33473..74e5ef08b874 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala @@ -170,11 +170,13 @@ class TreeUnpickler(reader: TastyReader, case ex: Exception => fail(ex) } - class TreeReader(val reader: TastyReader) { + class TreeReader(val reader: TastyReader, inInlineBody: Boolean = false) { import reader.* - def forkAt(start: Addr): TreeReader = new TreeReader(subReader(start, endAddr)) - def fork: TreeReader = forkAt(currentAddr) + def forkAt(start: Addr, inInlineBody: Boolean = false): TreeReader = + new TreeReader(subReader(start, endAddr), inInlineBody) + + def fork: TreeReader = forkAt(currentAddr, inInlineBody) def skipParentTree(tag: Int): Unit = { if tag == SPLITCLAUSE then () @@ -694,7 +696,7 @@ class TreeUnpickler(reader: TastyReader, val ctx1 = localContext(sym)(using ctx0).addMode(Mode.ReadPositions) inContext(sourceChangeContext(Addr(0))(using ctx1)) { // avoids space leaks by not capturing the current context - forkAt(rhsStart).readTree() + forkAt(rhsStart, inInlineBody = true).readTree() } }) goto(start) @@ -1580,21 +1582,14 @@ class TreeUnpickler(reader: TastyReader, val d = ownerTpe.decl(name).atSignature(sig, target) (if !d.exists then lookupInSuper else d).asSeenFrom(prefix) - val denot0 = inContext(ctx.addMode(Mode.ResolveFromTASTy)): + val denot = inContext(ctx.addMode(Mode.ResolveFromTASTy)): searchDenot // able to resolve SourceInvisible members - val denot = - if - denot0.symbol.exists - && denot0.symbol.is(SourceInvisible) - && denot0.symbol.isDefinedInSource - then - searchDenot // fallback - else - denot0 - - makeSelect(qual, name, denot) + val sel = makeSelect(qual, name, denot) + if denot == NoDenotation && inInlineBody && sel.denot.symbol.exists && sel.symbol.isDefinedInCurrentRun then + throw new ChangedMethodDenot(sel.denot.symbol) + sel case REPEATED => val elemtpt = readTpt() SeqLiteral(until(end)(readTree()), elemtpt) @@ -1901,6 +1896,9 @@ class TreeUnpickler(reader: TastyReader, object TreeUnpickler { + /** Specifically thrown when a SELECTin was written to TASTy, i.e. is expected to resolve, and then doesn't. */ + private[dotc] final class ChangedMethodDenot(val resolved: Symbol) extends Exception + /** Define the expected format of the tasty bytes * - TopLevel: Tasty that contains a full class nested in its package * - Term: Tasty that contains only a term tree diff --git a/compiler/src/dotty/tools/dotc/inlines/Inlines.scala b/compiler/src/dotty/tools/dotc/inlines/Inlines.scala index aeecd9c376e3..a09874ff56ee 100644 --- a/compiler/src/dotty/tools/dotc/inlines/Inlines.scala +++ b/compiler/src/dotty/tools/dotc/inlines/Inlines.scala @@ -19,6 +19,7 @@ import staging.StagingLevel import collection.mutable import reporting.{NotConstant, trace} import util.Spans.Span +import dotty.tools.dotc.core.tasty.TreeUnpickler /** Support for querying inlineable methods and for inlining calls to such methods */ object Inlines: @@ -158,8 +159,18 @@ object Inlines: else if enclosingInlineds.length < ctx.settings.XmaxInlines.value && !reachedInlinedTreesLimit then val body = try bodyToInline(tree.symbol) // can typecheck the tree and thereby produce errors - catch case _: MissingInlineInfo => - throw CyclicReference(ctx.owner) + catch + case _: MissingInlineInfo => throw CyclicReference(ctx.owner) + case err: TreeUnpickler.ChangedMethodDenot => + // tested in sbt-test/tasty-compat/add-param-unroll2/a_v3/A.scala + if err.resolved.source == ctx.source then + report.error(em"""cannot inline ${tree.symbol}: + | The definition of ${err.resolved.showLocated}, defined in the current file, has changed incompatibly. + | Try inlining from a different file.""", tree.srcPos) + EmptyTree + else + // Tested in sbt-test/tasty-compat/add-param-unroll2/a_v3_2/C.scala + ctx.compilationUnit.suspend("suspending in case of possible generated methods") new InlineCall(tree).expand(body) else ctx.base.stopInlining = true