From 0c57f8e8da5d5216866233f6acc2504dc55ea023 Mon Sep 17 00:00:00 2001
From: Daisy Li
Date: Sun, 9 Feb 2025 11:34:45 -0500
Subject: [PATCH 1/2] Add error-checking when fetching rhs of ValOrDefDefs.
---
.../tools/dotc/transform/init/Semantic.scala | 49 ++++++++++++++-----
.../dotty/tools/dotc/CompilationTests.scala | 23 +++++++++
tests/init/tasty-error/Main.scala | 1 +
tests/init/tasty-error/v0/A.scala | 3 ++
tests/init/tasty-error/v1/A.scala | 3 ++
tests/init/tasty-error/v1/B.scala | 4 ++
6 files changed, 70 insertions(+), 13 deletions(-)
create mode 100644 tests/init/tasty-error/Main.scala
create mode 100644 tests/init/tasty-error/v0/A.scala
create mode 100644 tests/init/tasty-error/v1/A.scala
create mode 100644 tests/init/tasty-error/v1/B.scala
diff --git a/compiler/src/dotty/tools/dotc/transform/init/Semantic.scala b/compiler/src/dotty/tools/dotc/transform/init/Semantic.scala
index 4548dccb598f..5d9b4f0e5ce0 100644
--- a/compiler/src/dotty/tools/dotc/transform/init/Semantic.scala
+++ b/compiler/src/dotty/tools/dotc/transform/init/Semantic.scala
@@ -378,7 +378,7 @@ object Semantic:
// ----- Checker State -----------------------------------
/** The state that threads through the interpreter */
- type Contextual[T] = (Context, Trace, Promoted, Cache.Data, Reporter) ?=> T
+ type Contextual[T] = (Context, Trace, Promoted, Cache.Data, Reporter, TreeCache.CacheData) ?=> T
// ----- Error Handling -----------------------------------
@@ -443,6 +443,29 @@ object Semantic:
inline def reporter(using r: Reporter): Reporter = r
+// ----- Cache for Trees -----------------------------
+
+ object TreeCache:
+ class CacheData:
+ private val emptyTrees = mutable.Set[ValOrDefDef]()
+
+ extension (tree: ValOrDefDef)
+ def getRhs(using Context): Tree =
+ def getTree: Tree =
+ val errorCount = ctx.reporter.errorCount
+ val rhs = tree.rhs
+
+ if (ctx.reporter.errorCount > errorCount)
+ emptyTrees.add(tree)
+ report.warning("Ignoring analyses of " + tree.name + " due to error in reading TASTy.")
+ EmptyTree
+ else
+ rhs
+
+ if (emptyTrees.contains(tree)) EmptyTree
+ else getTree
+ end TreeCache
+
// ----- Operations on domains -----------------------------
extension (a: Value)
def join(b: Value): Value =
@@ -562,7 +585,7 @@ object Semantic:
case ref: Ref =>
val target = if needResolve then resolve(ref.klass, field) else field
if target.is(Flags.Lazy) then
- val rhs = target.defTree.asInstanceOf[ValDef].rhs
+ val rhs = target.defTree.asInstanceOf[ValDef].getRhs
eval(rhs, ref, target.owner.asClass, cacheResult = true)
else if target.exists then
val obj = ref.objekt
@@ -577,7 +600,7 @@ object Semantic:
// return `Hot` here, errors are reported in checking `ThisRef`
Hot
else if target.hasSource then
- val rhs = target.defTree.asInstanceOf[ValOrDefDef].rhs
+ val rhs = target.defTree.asInstanceOf[ValOrDefDef].getRhs
eval(rhs, ref, target.owner.asClass, cacheResult = true)
else
val error = CallUnknown(field)(trace)
@@ -701,7 +724,7 @@ object Semantic:
else
reporter.reportAll(tryReporter.errors)
extendTrace(ddef) {
- eval(ddef.rhs, ref, cls, cacheResult = true)
+ eval(ddef.getRhs, ref, cls, cacheResult = true)
}
else if ref.canIgnoreMethodCall(target) then
Hot
@@ -768,7 +791,7 @@ object Semantic:
val tpl = cls.defTree.asInstanceOf[TypeDef].rhs.asInstanceOf[Template]
extendTrace(cls.defTree) { init(tpl, ref, cls) }
else
- val initCall = ddef.rhs match
+ val initCall = ddef.getRhs match
case Block(call :: _, _) => call
case call => call
extendTrace(ddef) { eval(initCall, ref, cls) }
@@ -787,7 +810,7 @@ object Semantic:
extendTrace(cls.defTree) { eval(tpl, ref, cls, cacheResult = true) }
ref
else
- extendTrace(ddef) { eval(ddef.rhs, ref, cls, cacheResult = true) }
+ extendTrace(ddef) { eval(ddef.getRhs, ref, cls, cacheResult = true) }
else if ref.canIgnoreMethodCall(ctor) then
Hot
else
@@ -897,8 +920,7 @@ object Semantic:
case Cold => Cold
- case ref: Ref => eval(vdef.rhs, ref, enclosingClass, cacheResult = sym.is(Flags.Lazy))
-
+ case ref: Ref => eval(vdef.getRhs, ref, enclosingClass, cacheResult = sym.is(Flags.Lazy))
case _ =>
report.error("[Internal error] unexpected this value when accessing local variable, sym = " + sym.show + ", thisValue = " + thisValue2.show + Trace.show, Trace.position)
Hot
@@ -1105,7 +1127,7 @@ object Semantic:
*
* The class to be checked must be an instantiable concrete class.
*/
- private def checkClass(classSym: ClassSymbol)(using Cache.Data, Context): Unit =
+ private def checkClass(classSym: ClassSymbol)(using Cache.Data, Context, TreeCache.CacheData): Unit =
val thisRef = ThisRef(classSym)
val tpl = classSym.defTree.asInstanceOf[TypeDef].rhs.asInstanceOf[Template]
@@ -1140,6 +1162,7 @@ object Semantic:
*/
def checkClasses(classes: List[ClassSymbol])(using Context): Unit =
given Cache.Data()
+ given TreeCache.CacheData()
for classSym <- classes if isConcreteClass(classSym) do
checkClass(classSym)
@@ -1298,7 +1321,7 @@ object Semantic:
}
case closureDef(ddef) =>
- Fun(ddef.rhs, thisV, klass)
+ Fun(ddef.getRhs, thisV, klass)
case PolyFun(body) =>
Fun(body, thisV, klass)
@@ -1353,7 +1376,7 @@ object Semantic:
case vdef : ValDef =>
// local val definition
- eval(vdef.rhs, thisV, klass)
+ eval(vdef.getRhs, thisV, klass)
case ddef : DefDef =>
// local method
@@ -1571,8 +1594,8 @@ object Semantic:
// class body
if thisV.isThisRef || !thisV.asInstanceOf[Warm].isPopulatingParams then tpl.body.foreach {
- case vdef : ValDef if !vdef.symbol.is(Flags.Lazy) && !vdef.rhs.isEmpty =>
- val res = eval(vdef.rhs, thisV, klass)
+ case vdef : ValDef if !vdef.symbol.is(Flags.Lazy) && !vdef.getRhs.isEmpty =>
+ val res = eval(vdef.getRhs, thisV, klass)
// TODO: Improve promotion to avoid handling enum initialization specially
//
// The failing case is tests/init/pos/i12544.scala due to promotion failure.
diff --git a/compiler/test/dotty/tools/dotc/CompilationTests.scala b/compiler/test/dotty/tools/dotc/CompilationTests.scala
index 1b33c52416ea..e9562b016a74 100644
--- a/compiler/test/dotty/tools/dotc/CompilationTests.scala
+++ b/compiler/test/dotty/tools/dotc/CompilationTests.scala
@@ -235,6 +235,29 @@ class CompilationTests {
tests.foreach(_.delete())
}
+
+ /* This tests for errors in the program's TASTy trees.
+ * The test consists of three files: (a) v1/A, (b) v1/B, and (c) v0/A. (a) and (b) are
+ * compatible, but (b) and (c) are not. If (b) and (c) are compiled together, there should be
+ * an error when reading the files' TASTy trees. */
+ locally {
+ val tastyErrorGroup = TestGroup("checkInit/tasty-error")
+ val tastyErrorOptions = options.without("-Xfatal-warnings")
+
+ val a0Dir = defaultOutputDir + tastyErrorGroup + "/A/v0/A"
+ val a1Dir = defaultOutputDir + tastyErrorGroup + "/A/v1/A"
+ val b1Dir = defaultOutputDir + tastyErrorGroup + "/B/v1/B"
+
+ val tests = List(
+ compileFile("tests/init/tasty-error/v1/A.scala", tastyErrorOptions)(tastyErrorGroup),
+ compileFile("tests/init/tasty-error/v1/B.scala", tastyErrorOptions.withClasspath(a1Dir))(tastyErrorGroup),
+ compileFile("tests/init/tasty-error/v0/A.scala", tastyErrorOptions)(tastyErrorGroup),
+ ).map(_.keepOutput.checkCompile())
+
+ compileFile("tests/init/tasty-error/Main.scala", tastyErrorOptions.withClasspath(a0Dir).withClasspath(b1Dir))(tastyErrorGroup).checkExpectedErrors()
+
+ tests.foreach(_.delete())
+ }
}
// parallel backend tests
diff --git a/tests/init/tasty-error/Main.scala b/tests/init/tasty-error/Main.scala
new file mode 100644
index 000000000000..2b27dd2b0d1f
--- /dev/null
+++ b/tests/init/tasty-error/Main.scala
@@ -0,0 +1 @@
+class Main extends B{} // anypos-error
\ No newline at end of file
diff --git a/tests/init/tasty-error/v0/A.scala b/tests/init/tasty-error/v0/A.scala
new file mode 100644
index 000000000000..be8c7d214378
--- /dev/null
+++ b/tests/init/tasty-error/v0/A.scala
@@ -0,0 +1,3 @@
+class A {
+ def fail(a: Int, b: Int): Int = a
+}
\ No newline at end of file
diff --git a/tests/init/tasty-error/v1/A.scala b/tests/init/tasty-error/v1/A.scala
new file mode 100644
index 000000000000..fa2956d1de7e
--- /dev/null
+++ b/tests/init/tasty-error/v1/A.scala
@@ -0,0 +1,3 @@
+class A {
+ def fail(a: Int): Int = a
+}
\ No newline at end of file
diff --git a/tests/init/tasty-error/v1/B.scala b/tests/init/tasty-error/v1/B.scala
new file mode 100644
index 000000000000..3059f487db64
--- /dev/null
+++ b/tests/init/tasty-error/v1/B.scala
@@ -0,0 +1,4 @@
+class B {
+ val a: A = new A
+ val x = a.fail(0)
+}
\ No newline at end of file
From 074e40ea0571a7e6cb25c0efae07c57b4e1d9e6c Mon Sep 17 00:00:00 2001
From: Tomasz Godzik
Date: Wed, 23 Apr 2025 18:03:44 +0200
Subject: [PATCH 2/2] Add error-checking when fetching rhs of ValOrDefDefs.
[Cherry-picked e80888176d5645b3c26075423e93454dc766b8e1][modified]