Skip to content

Commit ccd9c22

Browse files
committed
Check for tasty error in Template trees.
Add asserts for argument sizes.
1 parent a5e029a commit ccd9c22

File tree

12 files changed

+76
-14
lines changed

12 files changed

+76
-14
lines changed

Diff for: compiler/src/dotty/tools/dotc/transform/init/Semantic.scala

+24-4
Original file line numberDiff line numberDiff line change
@@ -9,15 +9,14 @@ import Types.*
99
import StdNames.*
1010
import NameKinds.OuterSelectName
1111
import NameKinds.SuperAccessorName
12-
1312
import ast.tpd.*
1413
import config.Printers.init as printer
1514
import reporting.trace as log
16-
1715
import Errors.*
1816
import Trace.*
1917
import Util.*
2018
import Cache.*
19+
import dotty.tools.dotc.sbt.ExtractDependencies.internalError
2120

2221
import scala.collection.mutable
2322
import scala.annotation.tailrec
@@ -448,6 +447,18 @@ object Semantic:
448447
object TreeCache:
449448
class CacheData:
450449
private val emptyTrees = mutable.Set[ValOrDefDef]()
450+
private val templatesToSkip = mutable.Set[Template]()
451+
452+
def checkTemplateBodyValidity(tpl: Template, className: String)(using Context): Unit =
453+
if (templatesToSkip.contains(tpl))
454+
throw new TastyTreeException(className)
455+
456+
val errorCount = ctx.reporter.errorCount
457+
tpl.forceFields()
458+
459+
if (ctx.reporter.errorCount > errorCount)
460+
templatesToSkip.add(tpl)
461+
throw new TastyTreeException(className)
451462

452463
extension (tree: ValOrDefDef)
453464
def getRhs(using Context): Tree =
@@ -465,7 +476,9 @@ object Semantic:
465476
if (emptyTrees.contains(tree)) EmptyTree
466477
else getTree
467478
end TreeCache
468-
479+
480+
inline def treeCache(using t: TreeCache.CacheData): TreeCache.CacheData = t
481+
469482
// ----- Operations on domains -----------------------------
470483
extension (a: Value)
471484
def join(b: Value): Value =
@@ -654,6 +667,8 @@ object Semantic:
654667
val methodType = atPhaseBeforeTransforms { meth.info.stripPoly }
655668
var allArgsHot = true
656669
val allParamTypes = methodType.paramInfoss.flatten.map(_.repeatedToSingle)
670+
if(allParamTypes.size != args.size)
671+
internalError("Number of parameters do not match number of arguments in " + meth.name)
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

Diff for: compiler/src/dotty/tools/dotc/transform/init/Util.scala

+6-2
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,15 @@ import Types.*
88
import Symbols.*
99
import StdNames.*
1010
import ast.tpd.*
11-
1211
import reporting.trace as log
1312
import config.Printers.init as printer
14-
1513
import Trace.*
14+
import dotty.tools.dotc.sbt.ExtractDependencies.internalError
1615

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

@@ -43,6 +45,8 @@ object Util:
4345
case Apply(fn, args) =>
4446
val argTps = fn.tpe.widen match
4547
case mt: MethodType => mt.paramInfos
48+
if (args.size != argTps.size)
49+
internalError("Number of arguments do not match number of argument types in " + tree.symbol.name)
4650
val normArgs: List[Arg] = args.zip(argTps).map {
4751
case (arg, _: ExprType) => ByNameArg(arg)
4852
case (arg, _) => arg

Diff for: 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
}

Diff for: tests/init/tasty-error/typedef/C.scala

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
class C

Diff for: tests/init/tasty-error/typedef/Main.scala

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
class Main extends B{} // anypos-error
2+
class ExtendsB extends B{}

Diff for: tests/init/tasty-error/typedef/v0/A.scala

+3
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+
}

Diff for: tests/init/tasty-error/typedef/v1/A.scala

+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+
}

Diff for: tests/init/tasty-error/typedef/v1/B.scala

+4
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+
}
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.

0 commit comments

Comments
 (0)