Skip to content

Commit 952c334

Browse files
authored
Check for tasty error in template trees. (#22867)
2 parents 9956adf + 884436f commit 952c334

File tree

12 files changed

+74
-10
lines changed

12 files changed

+74
-10
lines changed

compiler/src/dotty/tools/dotc/transform/init/Semantic.scala

+23-2
Original file line numberDiff line numberDiff line change
@@ -448,6 +448,18 @@ object Semantic:
448448
object TreeCache:
449449
class CacheData:
450450
private val emptyTrees = mutable.Set[ValOrDefDef]()
451+
private val templatesToSkip = mutable.Set[Template]()
452+
453+
def checkTemplateBodyValidity(tpl: Template, className: String)(using Context): Unit =
454+
if (templatesToSkip.contains(tpl))
455+
throw new TastyTreeException(className)
456+
457+
val errorCount = ctx.reporter.errorCount
458+
tpl.forceFields()
459+
460+
if (ctx.reporter.errorCount > errorCount)
461+
templatesToSkip.add(tpl)
462+
throw new TastyTreeException(className)
451463

452464
extension (tree: ValOrDefDef)
453465
def getRhs(using Context): Tree =
@@ -465,7 +477,9 @@ object Semantic:
465477
if (emptyTrees.contains(tree)) EmptyTree
466478
else getTree
467479
end TreeCache
468-
480+
481+
inline def treeCache(using t: TreeCache.CacheData): TreeCache.CacheData = t
482+
469483
// ----- Operations on domains -----------------------------
470484
extension (a: Value)
471485
def join(b: Value): Value =
@@ -654,6 +668,8 @@ object Semantic:
654668
val methodType = atPhaseBeforeTransforms { meth.info.stripPoly }
655669
var allArgsHot = true
656670
val allParamTypes = methodType.paramInfoss.flatten.map(_.repeatedToSingle)
671+
if(allParamTypes.size != args.size)
672+
report.warning("[Internal error] Number of parameters do not match number of arguments in " + meth.name)
657673
val errors = allParamTypes.zip(args).flatMap { (info, arg) =>
658674
val tryReporter = Reporter.errorsIn { arg.promote }
659675
allArgsHot = allArgsHot && tryReporter.errors.isEmpty
@@ -1173,7 +1189,10 @@ object Semantic:
11731189
given Cache.Data()
11741190
given TreeCache.CacheData()
11751191
for classSym <- classes if isConcreteClass(classSym) && !classSym.isStaticObject do
1176-
checkClass(classSym)
1192+
try
1193+
checkClass(classSym)
1194+
catch
1195+
case TastyTreeException(className) => report.warning("Skipping the analysis of " + classSym.show + " due to an error reading the body of " + className + "'s TASTy.")
11771196

11781197
// ----- Semantic definition --------------------------------
11791198
type ArgInfo = TraceValue[Value]
@@ -1520,6 +1539,8 @@ object Semantic:
15201539
* @param klass The class to which the template belongs.
15211540
*/
15221541
def init(tpl: Template, thisV: Ref, klass: ClassSymbol): Contextual[Value] = log("init " + klass.show, printer, (_: Value).show) {
1542+
treeCache.checkTemplateBodyValidity(tpl, klass.show)
1543+
15231544
val paramsMap = tpl.constr.termParamss.flatten.map { vdef =>
15241545
vdef.name -> thisV.objekt.field(vdef.symbol)
15251546
}.toMap

compiler/src/dotty/tools/dotc/transform/init/Util.scala

+5
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@ import config.Printers.init as printer
1515
import Trace.*
1616

1717
object Util:
18+
/** Exception used for errors encountered when reading TASTy. */
19+
case class TastyTreeException(msg: String) extends RuntimeException(msg)
20+
1821
/** Utility definition used for better error-reporting of argument errors */
1922
case class TraceValue[T](value: T, trace: Trace)
2023

@@ -43,6 +46,8 @@ object Util:
4346
case Apply(fn, args) =>
4447
val argTps = fn.tpe.widen match
4548
case mt: MethodType => mt.paramInfos
49+
if (args.size != argTps.size)
50+
report.warning("[Internal error] Number of arguments do not match number of argument types in " + tree.symbol.name)
4651
val normArgs: List[Arg] = args.zip(argTps).map {
4752
case (arg, _: ExprType) => ByNameArg(arg)
4853
case (arg, _) => arg

compiler/test/dotty/tools/dotc/CompilationTests.scala

+33-8
Original file line numberDiff line numberDiff line change
@@ -275,20 +275,45 @@ class CompilationTests {
275275
* compatible, but (b) and (c) are not. If (b) and (c) are compiled together, there should be
276276
* an error when reading the files' TASTy trees. */
277277
locally {
278-
val tastyErrorGroup = TestGroup("checkInit/tasty-error")
278+
val tastyErrorGroup = TestGroup("checkInit/tasty-error/val-or-defdef")
279279
val tastyErrorOptions = options.without("-Xfatal-warnings")
280280

281-
val a0Dir = defaultOutputDir + tastyErrorGroup + "/A/v0/A"
282-
val a1Dir = defaultOutputDir + tastyErrorGroup + "/A/v1/A"
283-
val b1Dir = defaultOutputDir + tastyErrorGroup + "/B/v1/B"
281+
val classA0 = defaultOutputDir + tastyErrorGroup + "/A/v0/A"
282+
val classA1 = defaultOutputDir + tastyErrorGroup + "/A/v1/A"
283+
val classB1 = defaultOutputDir + tastyErrorGroup + "/B/v1/B"
284284

285285
val tests = List(
286-
compileFile("tests/init/tasty-error/v1/A.scala", tastyErrorOptions)(tastyErrorGroup),
287-
compileFile("tests/init/tasty-error/v1/B.scala", tastyErrorOptions.withClasspath(a1Dir))(tastyErrorGroup),
288-
compileFile("tests/init/tasty-error/v0/A.scala", tastyErrorOptions)(tastyErrorGroup),
286+
compileFile("tests/init/tasty-error/val-or-defdef/v1/A.scala", tastyErrorOptions)(tastyErrorGroup),
287+
compileFile("tests/init/tasty-error/val-or-defdef/v1/B.scala", tastyErrorOptions.withClasspath(classA1))(tastyErrorGroup),
288+
compileFile("tests/init/tasty-error/val-or-defdef/v0/A.scala", tastyErrorOptions)(tastyErrorGroup),
289289
).map(_.keepOutput.checkCompile())
290290

291-
compileFile("tests/init/tasty-error/Main.scala", tastyErrorOptions.withClasspath(a0Dir).withClasspath(b1Dir))(tastyErrorGroup).checkExpectedErrors()
291+
compileFile("tests/init/tasty-error/val-or-defdef/Main.scala", tastyErrorOptions.withClasspath(classA0).withClasspath(classB1))(tastyErrorGroup).checkExpectedErrors()
292+
293+
tests.foreach(_.delete())
294+
}
295+
296+
/* This tests for errors in the program's TASTy trees.
297+
* 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
298+
* compatible, but v1/B and v0/A are not. If v1/B and v0/A are compiled together, there should be
299+
* an error when reading the files' TASTy trees. This fact is demonstrated by the compilation of Main. */
300+
locally {
301+
val tastyErrorGroup = TestGroup("checkInit/tasty-error/typedef")
302+
val tastyErrorOptions = options.without("-Xfatal-warnings").without("-Ycheck:all")
303+
304+
val classC = defaultOutputDir + tastyErrorGroup + "/C/typedef/C"
305+
val classA0 = defaultOutputDir + tastyErrorGroup + "/A/v0/A"
306+
val classA1 = defaultOutputDir + tastyErrorGroup + "/A/v1/A"
307+
val classB1 = defaultOutputDir + tastyErrorGroup + "/B/v1/B"
308+
309+
val tests = List(
310+
compileFile("tests/init/tasty-error/typedef/C.scala", tastyErrorOptions)(tastyErrorGroup),
311+
compileFile("tests/init/tasty-error/typedef/v1/A.scala", tastyErrorOptions.withClasspath(classC))(tastyErrorGroup),
312+
compileFile("tests/init/tasty-error/typedef/v1/B.scala", tastyErrorOptions.withClasspath(classC).withClasspath(classA1))(tastyErrorGroup),
313+
compileFile("tests/init/tasty-error/typedef/v0/A.scala", tastyErrorOptions.withClasspath(classC))(tastyErrorGroup),
314+
).map(_.keepOutput.checkCompile())
315+
316+
compileFile("tests/init/tasty-error/typedef/Main.scala", tastyErrorOptions.withClasspath(classC).withClasspath(classA0).withClasspath(classB1))(tastyErrorGroup).checkExpectedErrors()
292317

293318
tests.foreach(_.delete())
294319
}
+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
class C
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
class Main extends B{} // anypos-error
2+
class ExtendsB extends B{}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
class A(c1: C) {
2+
def fail(a: Int, b: Int): Int = a
3+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
class A(c1: C, c2: C) {
2+
def fail(a: Int, b: Int): Int = a
3+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
class B extends C{
2+
new A(this, this).fail(0,0)
3+
val x = 5
4+
}

0 commit comments

Comments
 (0)