From 932d7976809b6982771ca0d3f4d5f5da98b945aa Mon Sep 17 00:00:00 2001 From: Jamie Thompson Date: Tue, 8 Oct 2024 16:58:15 +0200 Subject: [PATCH] detect changed selectIn: error when same file, or suspend otherwise --- .../dotty/tools/dotc/core/Annotations.scala | 7 ++++- .../tools/dotc/core/tasty/TreeUnpickler.scala | 30 +++++++++---------- .../dotty/tools/dotc/inlines/Inlines.scala | 15 ++++++++-- 3 files changed, 33 insertions(+), 19 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/Annotations.scala b/compiler/src/dotty/tools/dotc/core/Annotations.scala index d6a99b12e3b3..e47e6b4b7967 100644 --- a/compiler/src/dotty/tools/dotc/core/Annotations.scala +++ b/compiler/src/dotty/tools/dotc/core/Annotations.scala @@ -177,8 +177,13 @@ object Annotations { assert(myTree != null) myTree match { case treeFn: (Context ?=> Tree) @unchecked => + var result: Tree | Null = null myTree = null - myTree = atPhaseBeforeTransforms(treeFn) + try + result = atPhaseBeforeTransforms(treeFn) + myTree = result + finally if result == null then + myTree = ctx ?=> treeFn(using ctx) // 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