Skip to content

Commit fe86aee

Browse files
committed
Check for tasty error in Template trees.
Check for tasty error in Template trees.
1 parent a5e029a commit fe86aee

File tree

12 files changed

+72
-10
lines changed

12 files changed

+72
-10
lines changed

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

+22-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,7 @@ object Semantic:
654668
val methodType = atPhaseBeforeTransforms { meth.info.stripPoly }
655669
var allArgsHot = true
656670
val allParamTypes = methodType.paramInfoss.flatten.map(_.repeatedToSingle)
671+
assert(allParamTypes.size == args.size)
657672
val errors = allParamTypes.zip(args).flatMap { (info, arg) =>
658673
val tryReporter = Reporter.errorsIn { arg.promote }
659674
allArgsHot = allArgsHot && tryReporter.errors.isEmpty
@@ -1173,7 +1188,10 @@ object Semantic:
11731188
given Cache.Data()
11741189
given TreeCache.CacheData()
11751190
for classSym <- classes if isConcreteClass(classSym) && !classSym.isStaticObject do
1176-
checkClass(classSym)
1191+
try
1192+
checkClass(classSym)
1193+
catch
1194+
case TastyTreeException(className) => report.warning("Skipping the analysis of " + classSym.show + " due to an error reading the body of " + className + "'s TASTy.")
11771195

11781196
// ----- Semantic definition --------------------------------
11791197
type ArgInfo = TraceValue[Value]
@@ -1520,6 +1538,8 @@ object Semantic:
15201538
* @param klass The class to which the template belongs.
15211539
*/
15221540
def init(tpl: Template, thisV: Ref, klass: ClassSymbol): Contextual[Value] = log("init " + klass.show, printer, (_: Value).show) {
1541+
treeCache.checkTemplateBodyValidity(tpl, klass.show)
1542+
15231543
val paramsMap = tpl.constr.termParamss.flatten.map { vdef =>
15241544
vdef.name -> thisV.objekt.field(vdef.symbol)
15251545
}.toMap

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

+4
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,7 @@ object Util:
4346
case Apply(fn, args) =>
4447
val argTps = fn.tpe.widen match
4548
case mt: MethodType => mt.paramInfos
49+
assert(args.size == argTps.size)
4650
val normArgs: List[Arg] = args.zip(argTps).map {
4751
case (arg, _: ExprType) => ByNameArg(arg)
4852
case (arg, _) => arg

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

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

284-
val a0Dir = defaultOutputDir + tastyErrorGroup + "/A/v0/A"
285-
val a1Dir = defaultOutputDir + tastyErrorGroup + "/A/v1/A"
286-
val b1Dir = defaultOutputDir + tastyErrorGroup + "/B/v1/B"
284+
val classA0 = defaultOutputDir + tastyErrorGroup + "/A/v0/A"
285+
val classA1 = defaultOutputDir + tastyErrorGroup + "/A/v1/A"
286+
val classB1 = defaultOutputDir + tastyErrorGroup + "/B/v1/B"
287287

288288
val tests = List(
289-
compileFile("tests/init/tasty-error/v1/A.scala", tastyErrorOptions)(tastyErrorGroup),
290-
compileFile("tests/init/tasty-error/v1/B.scala", tastyErrorOptions.withClasspath(a1Dir))(tastyErrorGroup),
291-
compileFile("tests/init/tasty-error/v0/A.scala", tastyErrorOptions)(tastyErrorGroup),
289+
compileFile("tests/init/tasty-error/val-or-defdef/v1/A.scala", tastyErrorOptions)(tastyErrorGroup),
290+
compileFile("tests/init/tasty-error/val-or-defdef/v1/B.scala", tastyErrorOptions.withClasspath(classA1))(tastyErrorGroup),
291+
compileFile("tests/init/tasty-error/val-or-defdef/v0/A.scala", tastyErrorOptions)(tastyErrorGroup),
292292
).map(_.keepOutput.checkCompile())
293293

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

296321
tests.foreach(_.delete())
297322
}
+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)