From 884436fd2c641cd32c5d68b06dc5c13fb1dcc1d3 Mon Sep 17 00:00:00 2001
From: Daisy Li
Date: Mon, 24 Mar 2025 22:28:18 -0400
Subject: [PATCH] Report warning for argument parameter number mismatch.
---
.../tools/dotc/transform/init/Semantic.scala | 25 ++++++++++-
.../tools/dotc/transform/init/Util.scala | 5 +++
.../dotty/tools/dotc/CompilationTests.scala | 41 +++++++++++++++----
tests/init/tasty-error/typedef/C.scala | 1 +
tests/init/tasty-error/typedef/Main.scala | 2 +
tests/init/tasty-error/typedef/v0/A.scala | 3 ++
tests/init/tasty-error/typedef/v1/A.scala | 3 ++
tests/init/tasty-error/typedef/v1/B.scala | 4 ++
.../{ => val-or-defdef}/Main.scala | 0
.../{ => val-or-defdef}/v0/A.scala | 0
.../{ => val-or-defdef}/v1/A.scala | 0
.../{ => val-or-defdef}/v1/B.scala | 0
12 files changed, 74 insertions(+), 10 deletions(-)
create mode 100644 tests/init/tasty-error/typedef/C.scala
create mode 100644 tests/init/tasty-error/typedef/Main.scala
create mode 100644 tests/init/tasty-error/typedef/v0/A.scala
create mode 100644 tests/init/tasty-error/typedef/v1/A.scala
create mode 100644 tests/init/tasty-error/typedef/v1/B.scala
rename tests/init/tasty-error/{ => val-or-defdef}/Main.scala (100%)
rename tests/init/tasty-error/{ => val-or-defdef}/v0/A.scala (100%)
rename tests/init/tasty-error/{ => val-or-defdef}/v1/A.scala (100%)
rename tests/init/tasty-error/{ => val-or-defdef}/v1/B.scala (100%)
diff --git a/compiler/src/dotty/tools/dotc/transform/init/Semantic.scala b/compiler/src/dotty/tools/dotc/transform/init/Semantic.scala
index adb2370bb1e0..a8a855b6ae5b 100644
--- a/compiler/src/dotty/tools/dotc/transform/init/Semantic.scala
+++ b/compiler/src/dotty/tools/dotc/transform/init/Semantic.scala
@@ -448,6 +448,18 @@ object Semantic:
object TreeCache:
class CacheData:
private val emptyTrees = mutable.Set[ValOrDefDef]()
+ private val templatesToSkip = mutable.Set[Template]()
+
+ def checkTemplateBodyValidity(tpl: Template, className: String)(using Context): Unit =
+ if (templatesToSkip.contains(tpl))
+ throw new TastyTreeException(className)
+
+ val errorCount = ctx.reporter.errorCount
+ tpl.forceFields()
+
+ if (ctx.reporter.errorCount > errorCount)
+ templatesToSkip.add(tpl)
+ throw new TastyTreeException(className)
extension (tree: ValOrDefDef)
def getRhs(using Context): Tree =
@@ -465,7 +477,9 @@ object Semantic:
if (emptyTrees.contains(tree)) EmptyTree
else getTree
end TreeCache
-
+
+ inline def treeCache(using t: TreeCache.CacheData): TreeCache.CacheData = t
+
// ----- Operations on domains -----------------------------
extension (a: Value)
def join(b: Value): Value =
@@ -654,6 +668,8 @@ object Semantic:
val methodType = atPhaseBeforeTransforms { meth.info.stripPoly }
var allArgsHot = true
val allParamTypes = methodType.paramInfoss.flatten.map(_.repeatedToSingle)
+ if(allParamTypes.size != args.size)
+ report.warning("[Internal error] Number of parameters do not match number of arguments in " + meth.name)
val errors = allParamTypes.zip(args).flatMap { (info, arg) =>
val tryReporter = Reporter.errorsIn { arg.promote }
allArgsHot = allArgsHot && tryReporter.errors.isEmpty
@@ -1173,7 +1189,10 @@ object Semantic:
given Cache.Data()
given TreeCache.CacheData()
for classSym <- classes if isConcreteClass(classSym) && !classSym.isStaticObject do
- checkClass(classSym)
+ try
+ checkClass(classSym)
+ catch
+ case TastyTreeException(className) => report.warning("Skipping the analysis of " + classSym.show + " due to an error reading the body of " + className + "'s TASTy.")
// ----- Semantic definition --------------------------------
type ArgInfo = TraceValue[Value]
@@ -1520,6 +1539,8 @@ object Semantic:
* @param klass The class to which the template belongs.
*/
def init(tpl: Template, thisV: Ref, klass: ClassSymbol): Contextual[Value] = log("init " + klass.show, printer, (_: Value).show) {
+ treeCache.checkTemplateBodyValidity(tpl, klass.show)
+
val paramsMap = tpl.constr.termParamss.flatten.map { vdef =>
vdef.name -> thisV.objekt.field(vdef.symbol)
}.toMap
diff --git a/compiler/src/dotty/tools/dotc/transform/init/Util.scala b/compiler/src/dotty/tools/dotc/transform/init/Util.scala
index ca30e2d32a4d..3280c289f926 100644
--- a/compiler/src/dotty/tools/dotc/transform/init/Util.scala
+++ b/compiler/src/dotty/tools/dotc/transform/init/Util.scala
@@ -15,6 +15,9 @@ import config.Printers.init as printer
import Trace.*
object Util:
+ /** Exception used for errors encountered when reading TASTy. */
+ case class TastyTreeException(msg: String) extends RuntimeException(msg)
+
/** Utility definition used for better error-reporting of argument errors */
case class TraceValue[T](value: T, trace: Trace)
@@ -43,6 +46,8 @@ object Util:
case Apply(fn, args) =>
val argTps = fn.tpe.widen match
case mt: MethodType => mt.paramInfos
+ if (args.size != argTps.size)
+ report.warning("[Internal error] Number of arguments do not match number of argument types in " + tree.symbol.name)
val normArgs: List[Arg] = args.zip(argTps).map {
case (arg, _: ExprType) => ByNameArg(arg)
case (arg, _) => arg
diff --git a/compiler/test/dotty/tools/dotc/CompilationTests.scala b/compiler/test/dotty/tools/dotc/CompilationTests.scala
index 47ed2aa6564d..f533c02175bd 100644
--- a/compiler/test/dotty/tools/dotc/CompilationTests.scala
+++ b/compiler/test/dotty/tools/dotc/CompilationTests.scala
@@ -278,20 +278,45 @@ class CompilationTests {
* 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 tastyErrorGroup = TestGroup("checkInit/tasty-error/val-or-defdef")
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 classA0 = defaultOutputDir + tastyErrorGroup + "/A/v0/A"
+ val classA1 = defaultOutputDir + tastyErrorGroup + "/A/v1/A"
+ val classB1 = 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),
+ compileFile("tests/init/tasty-error/val-or-defdef/v1/A.scala", tastyErrorOptions)(tastyErrorGroup),
+ compileFile("tests/init/tasty-error/val-or-defdef/v1/B.scala", tastyErrorOptions.withClasspath(classA1))(tastyErrorGroup),
+ compileFile("tests/init/tasty-error/val-or-defdef/v0/A.scala", tastyErrorOptions)(tastyErrorGroup),
).map(_.keepOutput.checkCompile())
- compileFile("tests/init/tasty-error/Main.scala", tastyErrorOptions.withClasspath(a0Dir).withClasspath(b1Dir))(tastyErrorGroup).checkExpectedErrors()
+ compileFile("tests/init/tasty-error/val-or-defdef/Main.scala", tastyErrorOptions.withClasspath(classA0).withClasspath(classB1))(tastyErrorGroup).checkExpectedErrors()
+
+ tests.foreach(_.delete())
+ }
+
+ /* This tests for errors in the program's TASTy trees.
+ * The test consists of five files: Main, C, v1/A, v1/B, and v0/A. The files v1/A, v1/B, and v0/A all depend on C. v1/A and v1/B are
+ * compatible, but v1/B and v0/A are not. If v1/B and v0/A are compiled together, there should be
+ * an error when reading the files' TASTy trees. This fact is demonstrated by the compilation of Main. */
+ locally {
+ val tastyErrorGroup = TestGroup("checkInit/tasty-error/typedef")
+ val tastyErrorOptions = options.without("-Xfatal-warnings").without("-Ycheck:all")
+
+ val classC = defaultOutputDir + tastyErrorGroup + "/C/typedef/C"
+ val classA0 = defaultOutputDir + tastyErrorGroup + "/A/v0/A"
+ val classA1 = defaultOutputDir + tastyErrorGroup + "/A/v1/A"
+ val classB1 = defaultOutputDir + tastyErrorGroup + "/B/v1/B"
+
+ val tests = List(
+ compileFile("tests/init/tasty-error/typedef/C.scala", tastyErrorOptions)(tastyErrorGroup),
+ compileFile("tests/init/tasty-error/typedef/v1/A.scala", tastyErrorOptions.withClasspath(classC))(tastyErrorGroup),
+ compileFile("tests/init/tasty-error/typedef/v1/B.scala", tastyErrorOptions.withClasspath(classC).withClasspath(classA1))(tastyErrorGroup),
+ compileFile("tests/init/tasty-error/typedef/v0/A.scala", tastyErrorOptions.withClasspath(classC))(tastyErrorGroup),
+ ).map(_.keepOutput.checkCompile())
+
+ compileFile("tests/init/tasty-error/typedef/Main.scala", tastyErrorOptions.withClasspath(classC).withClasspath(classA0).withClasspath(classB1))(tastyErrorGroup).checkExpectedErrors()
tests.foreach(_.delete())
}
diff --git a/tests/init/tasty-error/typedef/C.scala b/tests/init/tasty-error/typedef/C.scala
new file mode 100644
index 000000000000..9dccdd5e5954
--- /dev/null
+++ b/tests/init/tasty-error/typedef/C.scala
@@ -0,0 +1 @@
+class C
\ No newline at end of file
diff --git a/tests/init/tasty-error/typedef/Main.scala b/tests/init/tasty-error/typedef/Main.scala
new file mode 100644
index 000000000000..232a7c34c4e1
--- /dev/null
+++ b/tests/init/tasty-error/typedef/Main.scala
@@ -0,0 +1,2 @@
+class Main extends B{} // anypos-error
+class ExtendsB extends B{}
\ No newline at end of file
diff --git a/tests/init/tasty-error/typedef/v0/A.scala b/tests/init/tasty-error/typedef/v0/A.scala
new file mode 100644
index 000000000000..9c86f47e2c77
--- /dev/null
+++ b/tests/init/tasty-error/typedef/v0/A.scala
@@ -0,0 +1,3 @@
+class A(c1: C) {
+ def fail(a: Int, b: Int): Int = a
+}
diff --git a/tests/init/tasty-error/typedef/v1/A.scala b/tests/init/tasty-error/typedef/v1/A.scala
new file mode 100644
index 000000000000..a79930e5dd30
--- /dev/null
+++ b/tests/init/tasty-error/typedef/v1/A.scala
@@ -0,0 +1,3 @@
+class A(c1: C, c2: C) {
+ def fail(a: Int, b: Int): Int = a
+}
diff --git a/tests/init/tasty-error/typedef/v1/B.scala b/tests/init/tasty-error/typedef/v1/B.scala
new file mode 100644
index 000000000000..801efcb5fdf0
--- /dev/null
+++ b/tests/init/tasty-error/typedef/v1/B.scala
@@ -0,0 +1,4 @@
+class B extends C{
+ new A(this, this).fail(0,0)
+ val x = 5
+}
\ No newline at end of file
diff --git a/tests/init/tasty-error/Main.scala b/tests/init/tasty-error/val-or-defdef/Main.scala
similarity index 100%
rename from tests/init/tasty-error/Main.scala
rename to tests/init/tasty-error/val-or-defdef/Main.scala
diff --git a/tests/init/tasty-error/v0/A.scala b/tests/init/tasty-error/val-or-defdef/v0/A.scala
similarity index 100%
rename from tests/init/tasty-error/v0/A.scala
rename to tests/init/tasty-error/val-or-defdef/v0/A.scala
diff --git a/tests/init/tasty-error/v1/A.scala b/tests/init/tasty-error/val-or-defdef/v1/A.scala
similarity index 100%
rename from tests/init/tasty-error/v1/A.scala
rename to tests/init/tasty-error/val-or-defdef/v1/A.scala
diff --git a/tests/init/tasty-error/v1/B.scala b/tests/init/tasty-error/val-or-defdef/v1/B.scala
similarity index 100%
rename from tests/init/tasty-error/v1/B.scala
rename to tests/init/tasty-error/val-or-defdef/v1/B.scala