Skip to content

Commit 17fa485

Browse files
Internals for the typechecker for testing
1 parent cc8c77b commit 17fa485

File tree

5 files changed

+86
-19
lines changed

5 files changed

+86
-19
lines changed

compiler/src/dotty/tools/dotc/ast/tpd.scala

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -868,6 +868,10 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo {
868868
def appliedToArgs(args: List[Tree])(implicit ctx: Context): Apply =
869869
Apply(tree, args)
870870

871+
/** An applied node that accepts only varargs as arguments */
872+
def appliedToVarargs(args: List[Tree], tpt: Tree)(given Context): Tree =
873+
appliedTo(repeated(args, tpt))
874+
871875
/** The current tree applied to given argument lists:
872876
* `tree (argss(0)) ... (argss(argss.length -1))`
873877
*/
@@ -885,6 +889,10 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo {
885889
def appliedToTypes(targs: List[Type])(implicit ctx: Context): Tree =
886890
appliedToTypeTrees(targs map (TypeTree(_)))
887891

892+
/** The current tree applied to given type argument: `tree[targ]` */
893+
def appliedToTypeTree(targ: Tree)(implicit ctx: Context): Tree =
894+
appliedToTypeTrees(targ :: Nil)
895+
888896
/** The current tree applied to given type argument list: `tree[targs(0), ..., targs(targs.length - 1)]` */
889897
def appliedToTypeTrees(targs: List[Tree])(implicit ctx: Context): Tree =
890898
if (targs.isEmpty) tree else TypeApply(tree, targs)
@@ -1357,5 +1365,24 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo {
13571365
val targs = atp.argTypes
13581366
tpd.applyOverloaded(New(atp.typeConstructor), nme.CONSTRUCTOR, args, targs, atp)
13591367
}
1360-
}
13611368

1369+
/** Convert a list of trees to a vararg-compatible tree.
1370+
* Used to make arguments for methods that accept varargs.
1371+
*/
1372+
def repeated(trees: List[Tree], tpt: Tree)(given ctx: Context): Tree =
1373+
ctx.typeAssigner.arrayToRepeated(JavaSeqLiteral(trees, tpt))
1374+
1375+
/** Create a tree representing a list containing all
1376+
* the elements of the argument list. A "list of tree to
1377+
* tree of list" conversion.
1378+
*
1379+
* @param trees the elements the list represented by
1380+
* the resulting tree should contain.
1381+
* @param tpe the type of the elements of the resulting list.
1382+
*
1383+
*/
1384+
def mkList(trees: List[Tree], tpe: Tree)(given Context): Tree =
1385+
ref(defn.ListModule).select(nme.apply)
1386+
.appliedToTypeTree(tpe)
1387+
.appliedToVarargs(trees, tpe)
1388+
}

compiler/src/dotty/tools/dotc/core/Definitions.scala

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -225,7 +225,14 @@ class Definitions {
225225
@tu lazy val Compiletime_code : Symbol = CompiletimePackageObject.requiredMethod("code")
226226
@tu lazy val Compiletime_summonFrom : Symbol = CompiletimePackageObject.requiredMethod("summonFrom")
227227
@tu lazy val CompiletimeTestingPackageObject: Symbol = ctx.requiredModule("scala.compiletime.testing.package")
228-
@tu lazy val CompiletimeTesting_typeChecks : Symbol = CompiletimeTestingPackageObject.requiredMethod("typeChecks")
228+
@tu lazy val CompiletimeTesting_typeChecks: Symbol = CompiletimeTestingPackageObject.requiredMethod("typeChecks")
229+
@tu lazy val CompiletimeTesting_typeCheckErrors: Symbol = CompiletimeTestingPackageObject.requiredMethod("typeCheckErrors")
230+
@tu lazy val CompiletimeTesting_ErrorClass: ClassSymbol = ctx.requiredClass("scala.compiletime.testing.Error")
231+
@tu lazy val CompiletimeTesting_Error: Symbol = ctx.requiredModule("scala.compiletime.testing.Error")
232+
@tu lazy val CompiletimeTesting_Error_apply = CompiletimeTesting_Error.requiredMethod(nme.apply)
233+
@tu lazy val CompiletimeTesting_ErrorKind: Symbol = ctx.requiredModule("scala.compiletime.testing.ErrorKind")
234+
@tu lazy val CompiletimeTesting_ErrorKind_Parser: Symbol = CompiletimeTesting_ErrorKind.requiredMethod("Parser")
235+
@tu lazy val CompiletimeTesting_ErrorKind_Typer: Symbol = CompiletimeTesting_ErrorKind.requiredMethod("Typer")
229236

230237
/** The `scalaShadowing` package is used to safely modify classes and
231238
* objects in scala so that they can be used from dotty. They will
@@ -389,6 +396,7 @@ class Definitions {
389396
methodNames.map(getWrapVarargsArrayModule.requiredMethod(_))
390397
})
391398

399+
@tu lazy val ListModule: Symbol = ctx.requiredModule("scala.collection.immutable.List")
392400
@tu lazy val NilModule: Symbol = ctx.requiredModule("scala.collection.immutable.Nil")
393401

394402
@tu lazy val SingletonClass: ClassSymbol =

compiler/src/dotty/tools/dotc/typer/Inliner.scala

Lines changed: 48 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,9 @@ object Inliner {
6666
* and body that replace it.
6767
*/
6868
def inlineCall(tree: Tree)(implicit ctx: Context): Tree = {
69-
if (tree.symbol == defn.CompiletimeTesting_typeChecks) return Intrinsics.typeChecks(tree)
69+
if tree.symbol.owner.companionModule == defn.CompiletimeTestingPackageObject
70+
if (tree.symbol == defn.CompiletimeTesting_typeChecks) return Intrinsics.typeChecks(tree)
71+
if (tree.symbol == defn.CompiletimeTesting_typeCheckErrors) return Intrinsics.typeCheckErrors(tree)
7072

7173
/** Set the position of all trees logically contained in the expansion of
7274
* inlined call `call` to the position of `call`. This transform is necessary
@@ -193,10 +195,12 @@ object Inliner {
193195
}
194196

195197
object Intrinsics {
198+
import dotty.tools.dotc.reporting.diagnostic.messages.Error
199+
private enum ErrorKind
200+
case Parser, Typer
196201

197-
/** Expand call to scala.compiletime.testing.typeChecks */
198-
def typeChecks(tree: Tree)(implicit ctx: Context): Tree = {
199-
assert(tree.symbol == defn.CompiletimeTesting_typeChecks)
202+
private def compileForErrors(tree: Tree, stopAfterParser: Boolean)(given ctx: Context): List[(ErrorKind, Error)] =
203+
assert(tree.symbol == defn.CompiletimeTesting_typeChecks || tree.symbol == defn.CompiletimeTesting_typeCheckErrors)
200204
def stripTyped(t: Tree): Tree = t match {
201205
case Typed(t2, _) => stripTyped(t2)
202206
case _ => t
@@ -205,20 +209,49 @@ object Inliner {
205209
val Apply(_, codeArg :: Nil) = tree
206210
ConstFold(stripTyped(codeArg.underlyingArgument)).tpe.widenTermRefExpr match {
207211
case ConstantType(Constant(code: String)) =>
208-
val ctx2 = ctx.fresh.setNewTyperState().setTyper(new Typer)
209-
val tree2 = new Parser(SourceFile.virtual("tasty-reflect", code))(ctx2).block()
210-
val res =
211-
if (ctx2.reporter.hasErrors) false
212-
else {
213-
ctx2.typer.typed(tree2)(ctx2)
214-
!ctx2.reporter.hasErrors
215-
}
216-
Literal(Constant(res))
212+
val source2 = SourceFile.virtual("tasty-reflect", code)
213+
val ctx2 = ctx.fresh.setNewTyperState().setTyper(new Typer).setSource(source2)
214+
val tree2 = new Parser(source2)(ctx2).block()
215+
val res = collection.mutable.ListBuffer.empty[(ErrorKind, Error)]
216+
217+
val parseErrors = ctx2.reporter.allErrors.toList
218+
res ++= parseErrors.map(e => ErrorKind.Parser -> e)
219+
if !stopAfterParser || res.isEmpty
220+
ctx2.typer.typed(tree2)(ctx2)
221+
val typerErrors = ctx2.reporter.allErrors.filterNot(parseErrors.contains)
222+
res ++= typerErrors.map(e => ErrorKind.Typer -> e)
223+
res.toList
217224
case t =>
218225
assert(ctx.reporter.hasErrors) // at least: argument to inline parameter must be a known value
219-
EmptyTree
226+
Nil
220227
}
221-
}
228+
229+
private def packError(kind: ErrorKind, error: Error)(given Context): Tree =
230+
def lit(x: Any) = Literal(Constant(x))
231+
val constructor: Tree = ref(defn.CompiletimeTesting_Error_apply)
232+
val parserErrorKind: Tree = ref(defn.CompiletimeTesting_ErrorKind_Parser)
233+
val typerErrorKind: Tree = ref(defn.CompiletimeTesting_ErrorKind_Typer)
234+
235+
constructor.appliedTo(
236+
lit(error.message),
237+
lit(error.pos.lineContent.reverse.dropWhile("\n ".contains).reverse),
238+
lit(error.pos.column),
239+
if kind == ErrorKind.Parser then parserErrorKind else typerErrorKind)
240+
241+
private def packErrors(errors: List[(ErrorKind, Error)])(given Context): Tree =
242+
val individualErrors: List[Tree] = errors.map(packError)
243+
val errorTpt = ref(defn.CompiletimeTesting_ErrorClass)
244+
mkList(individualErrors, errorTpt)
245+
246+
/** Expand call to scala.compiletime.testing.typeChecks */
247+
def typeChecks(tree: Tree)(given Context): Tree =
248+
val errors = compileForErrors(tree, true)
249+
Literal(Constant(errors.isEmpty))
250+
251+
/** Expand call to scala.compiletime.testing.typeCheckErrors */
252+
def typeCheckErrors(tree: Tree)(given Context): Tree =
253+
val errors = compileForErrors(tree, false)
254+
packErrors(errors)
222255
}
223256
}
224257

compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -368,7 +368,7 @@ trait QuotesAndSplices {
368368
val quoteClass = if (tree.quoted.isTerm) defn.QuotedExprClass else defn.QuotedTypeClass
369369
val quotedPattern =
370370
if (tree.quoted.isTerm) ref(defn.InternalQuoted_exprQuote.termRef).appliedToType(defn.AnyType).appliedTo(shape).select(nme.apply).appliedTo(qctx)
371-
else ref(defn.InternalQuoted_typeQuote.termRef).appliedToTypeTrees(shape :: Nil)
371+
else ref(defn.InternalQuoted_typeQuote.termRef).appliedToTypeTree(shape)
372372
UnApply(
373373
fun = ref(unapplySym.termRef).appliedToTypeTrees(typeBindingsTuple :: TypeTree(patType) :: Nil),
374374
implicits = quotedPattern :: Literal(Constant(typeBindings.nonEmpty)) :: qctx :: Nil,

library/src/scala/compiletime/testing/package.scala

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,5 +28,4 @@ package object testing {
2828
*/
2929
inline def typeCheckErrors(inline code: String): List[Error] =
3030
error("`typeCheckErrors` was not checked by the compiler")
31-
3231
}

0 commit comments

Comments
 (0)