From 8f48a3befb3b23ec6cb9fbfca1bc3e148c0d893b Mon Sep 17 00:00:00 2001 From: Jan Chyb Date: Fri, 11 Apr 2025 16:34:40 +0200 Subject: [PATCH] Fix issue with certain synthetics missing in compiletime.typechecks Previously, instead of using the PostTyper phase directly, a typed tree Transformer was used. However, it did not transform the ClassInfo values using PostTyper.transformInfo, which lead to RefChecks not finding certain synthesized methods, throwing an incorrect error about a missing implementation. This is fixed by running the full suite of PostTyper transforms. A minor refactor is included, to limit the repeated CompilationUnit construction and error checking. --- .../dotty/tools/dotc/CompilationUnit.scala | 4 +- .../dotty/tools/dotc/inlines/Inlines.scala | 38 ++++++++++--------- .../test/dotc/run-test-pickling.excludelist | 2 +- tests/run/i22968.check | 1 + tests/run/i22968.scala | 7 ++++ 5 files changed, 32 insertions(+), 20 deletions(-) create mode 100644 tests/run/i22968.check create mode 100644 tests/run/i22968.scala diff --git a/compiler/src/dotty/tools/dotc/CompilationUnit.scala b/compiler/src/dotty/tools/dotc/CompilationUnit.scala index 9c985ecd84b3..b627e149e5fb 100644 --- a/compiler/src/dotty/tools/dotc/CompilationUnit.scala +++ b/compiler/src/dotty/tools/dotc/CompilationUnit.scala @@ -158,10 +158,10 @@ object CompilationUnit { unit1 } - /** Create a compilation unit corresponding to an in-memory String. + /** Create a compilation unit corresponding to an in-memory String. * Used for `compiletime.testing.typeChecks`. */ - def apply(name: String, source: String)(using Context): CompilationUnit = { + def apply(name: String, source: String): CompilationUnit = { val src = SourceFile.virtual(name = name, content = source, maybeIncomplete = false) new CompilationUnit(src, null) } diff --git a/compiler/src/dotty/tools/dotc/inlines/Inlines.scala b/compiler/src/dotty/tools/dotc/inlines/Inlines.scala index 85bea871b955..a7269c83bccb 100644 --- a/compiler/src/dotty/tools/dotc/inlines/Inlines.scala +++ b/compiler/src/dotty/tools/dotc/inlines/Inlines.scala @@ -395,6 +395,11 @@ object Inlines: case ConstantType(Constant(code: String)) => val unitName = "tasty-reflect" val source2 = SourceFile.virtual(unitName, code) + def compilationUnits(untpdTree: untpd.Tree, tpdTree: Tree): List[CompilationUnit] = + val compilationUnit = CompilationUnit(unitName, code) + compilationUnit.tpdTree = tpdTree + compilationUnit.untpdTree = untpdTree + List(compilationUnit) // We need a dummy owner, as the actual one does not have a computed denotation yet, // but might be inspected in a transform phase, leading to cyclic errors val dummyOwner = newSymbol(ctx.owner, "$dummySymbol$".toTermName, Private, defn.AnyType, NoSymbol) @@ -407,31 +412,30 @@ object Inlines: .withOwner(dummyOwner) inContext(newContext) { - val tree2 = new Parser(source2).block() - if ctx.reporter.allErrors.nonEmpty then + def noErrors = ctx.reporter.allErrors.isEmpty + val untpdTree = new Parser(source2).block() + if !noErrors then ctx.reporter.allErrors.map((ErrorKind.Parser, _)) else - val tree3 = ctx.typer.typed(tree2) + val tpdTree1 = ctx.typer.typed(untpdTree) ctx.base.postTyperPhase match - case postTyper: PostTyper if ctx.reporter.allErrors.isEmpty => - val tree4 = atPhase(postTyper) { postTyper.newTransformer.transform(tree3) } + case postTyper: PostTyper if noErrors => + val tpdTree2 = + atPhase(postTyper) { postTyper.runOn(compilationUnits(untpdTree, tpdTree1)).head.tpdTree } ctx.base.setRootTreePhase match - case setRootTree => - val tree5 = - val compilationUnit = CompilationUnit(unitName, code) - compilationUnit.tpdTree = tree4 - compilationUnit.untpdTree = tree2 - var units = List(compilationUnit) - atPhase(setRootTree)(setRootTree.runOn(units).head.tpdTree) + case setRootTree if noErrors => // might be noPhase, if -Yretain-trees is not used + val tpdTree3 = + atPhase(setRootTree)(setRootTree.runOn(compilationUnits(untpdTree, tpdTree2)).head.tpdTree) ctx.base.inliningPhase match - case inlining: Inlining if ctx.reporter.allErrors.isEmpty => - val tree6 = atPhase(inlining) { inlining.newTransformer.transform(tree5) } - if ctx.reporter.allErrors.isEmpty && reconstructedTransformPhases.nonEmpty then - var transformTree = tree6 + case inlining: Inlining if noErrors => + val tpdTree4 = atPhase(inlining) { inlining.newTransformer.transform(tpdTree3) } + if noErrors && reconstructedTransformPhases.nonEmpty then + var transformTree = tpdTree4 for phase <- reconstructedTransformPhases do - if ctx.reporter.allErrors.isEmpty then + if noErrors then transformTree = atPhase(phase.end + 1)(phase.transformUnit(transformTree)) case _ => + case _ => case _ => ctx.reporter.allErrors.map((ErrorKind.Typer, _)) } diff --git a/compiler/test/dotc/run-test-pickling.excludelist b/compiler/test/dotc/run-test-pickling.excludelist index c880a4b78f23..1597487da668 100644 --- a/compiler/test/dotc/run-test-pickling.excludelist +++ b/compiler/test/dotc/run-test-pickling.excludelist @@ -49,4 +49,4 @@ named-tuples-strawman-2.scala # typecheckErrors method unpickling typeCheckErrors.scala i18150.scala - +i22968.scala diff --git a/tests/run/i22968.check b/tests/run/i22968.check new file mode 100644 index 000000000000..375518e921cd --- /dev/null +++ b/tests/run/i22968.check @@ -0,0 +1 @@ +List() diff --git a/tests/run/i22968.scala b/tests/run/i22968.scala new file mode 100644 index 000000000000..28ed954d1d86 --- /dev/null +++ b/tests/run/i22968.scala @@ -0,0 +1,7 @@ +import scala.compiletime.testing.typeCheckErrors + +object Test { + def main(args: Array[String]): Unit = { + println(typeCheckErrors("enum Foo { case A }")) + } +}