Skip to content

Commit f4ba907

Browse files
committed
Intrinsify scala.compiletime.testing.typeChecks
As know from the start, this funtionallity should not have been added to the reflection API. Nevertheless it is still possible to use its result in a macro.
1 parent 6a08e09 commit f4ba907

File tree

10 files changed

+54
-66
lines changed

10 files changed

+54
-66
lines changed

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,8 @@ class Definitions {
221221
@tu lazy val Compiletime_constValue : Symbol = CompiletimePackageObject.requiredMethod("constValue")
222222
@tu lazy val Compiletime_constValueOpt: Symbol = CompiletimePackageObject.requiredMethod("constValueOpt")
223223
@tu lazy val Compiletime_code : Symbol = CompiletimePackageObject.requiredMethod("code")
224+
@tu lazy val CompiletimeTestingPackageObject: Symbol = ctx.requiredModule("scala.compiletime.testing.package")
225+
@tu lazy val CompiletimeTesting_typeChecks : Symbol = CompiletimeTestingPackageObject.requiredMethod("typeChecks")
224226

225227
/** The `scalaShadowing` package is used to safely modify classes and
226228
* objects in scala so that they can be used from dotty. They will

compiler/src/dotty/tools/dotc/tastyreflect/ReflectionCompilerInterface.scala

Lines changed: 0 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ import dotty.tools.dotc.core.Symbols._
1212
import dotty.tools.dotc.core.Decorators._
1313
import dotty.tools.dotc.core.Types.SingletonType
1414
import dotty.tools.dotc.tastyreflect.FromSymbol.{definitionFromSym, packageDefFromSym}
15-
import dotty.tools.dotc.parsing.Parsers.Parser
1615
import dotty.tools.dotc.typer.Implicits.{AmbiguousImplicits, DivergingImplicit, NoMatchingImplicits, SearchFailure, SearchFailureType}
1716
import dotty.tools.dotc.util.{SourceFile, SourcePosition, Spans}
1817

@@ -82,26 +81,6 @@ class ReflectionCompilerInterface(val rootContext: core.Contexts.Context) extend
8281

8382
def Settings_color(self: Settings): Boolean = self.color.value(rootContext) == "always"
8483

85-
//
86-
// MISC
87-
//
88-
/** Whether the code type checks in the given context?
89-
*
90-
* @param code The code to be type checked
91-
*
92-
* The code should be a sequence of expressions or statements that may appear in a block.
93-
*/
94-
def typeChecks(code: String) given (ctx: Context): Boolean = {
95-
val ctx2 = ctx.fresh.setNewTyperState().setTyper(new Typer)
96-
val tree = new Parser(SourceFile.virtual("tasty-reflect", code))(ctx2).block()
97-
98-
if (ctx2.reporter.hasErrors) false
99-
else {
100-
ctx2.typer.typed(tree)(ctx2)
101-
!ctx2.reporter.hasErrors
102-
}
103-
}
104-
10584
//
10685
// TREES
10786
//

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

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import config.Printers.inlining
2323
import ErrorReporting.errorTree
2424
import dotty.tools.dotc.tastyreflect.ReflectionImpl
2525
import dotty.tools.dotc.util.{SimpleIdentityMap, SimpleIdentitySet, SourceFile, SourcePosition}
26+
import dotty.tools.dotc.parsing.Parsers.Parser
2627

2728
import collection.mutable
2829
import reporting.trace
@@ -63,6 +64,7 @@ object Inliner {
6364
* and body that replace it.
6465
*/
6566
def inlineCall(tree: Tree)(implicit ctx: Context): Tree = {
67+
if (tree.symbol == defn.CompiletimeTesting_typeChecks) return Intrinsics.typeChecks(tree)
6668

6769
/** Set the position of all trees logically contained in the expansion of
6870
* inlined call `call` to the position of `call`. This transform is necessary
@@ -189,6 +191,32 @@ object Inliner {
189191
if (callSym.is(Macro)) ref(callSym.topLevelClass.owner).select(callSym.topLevelClass.name).withSpan(pos.span)
190192
else Ident(callSym.topLevelClass.typeRef).withSpan(pos.span)
191193
}
194+
195+
object Intrinsics {
196+
197+
/** Expand call to scala.compiletime.testing.typeChecks */
198+
def typeChecks(tree: Tree)(implicit ctx: Context): Tree = {
199+
assert(tree.symbol == defn.CompiletimeTesting_typeChecks)
200+
def getCodeArgValue(t: Tree): String = t match {
201+
case Literal(Constant(code: String)) => code
202+
case Typed(t2, _) => getCodeArgValue(t2)
203+
case Inlined(_, Nil, t2) => getCodeArgValue(t2)
204+
case Block(Nil, t2) => getCodeArgValue(t2)
205+
}
206+
val Apply(_, codeArg :: Nil) = tree
207+
val code = getCodeArgValue(codeArg.underlyingArgument)
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))
217+
}
218+
219+
}
192220
}
193221

194222
/** Produces an inlined version of `call` via its `inlined` method.
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
package scala.compiletime
2+
3+
import scala.quoted._
4+
5+
package object testing {
6+
7+
/** Whether the code type checks in the current context?
8+
*
9+
* @param code The code to be type checked
10+
*
11+
* @return false if the code has syntax error or type error in the current context, otherwise returns true.
12+
*
13+
* The code should be a sequence of expressions or statements that may appear in a block.
14+
*/
15+
inline def typeChecks(inline code: String): Boolean =
16+
error("`typeChecks` was not checked by the compiler")
17+
18+
}

library/src-bootstrapped/scala/compiletime/testing/typeChecks.scala

Lines changed: 0 additions & 10 deletions
This file was deleted.

library/src/scala/tasty/Reflection.scala

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -28,17 +28,4 @@ class Reflection(private[scala] val internal: CompilerInterface)
2828
def typeOf[T: scala.quoted.Type]: Type =
2929
implicitly[scala.quoted.Type[T]].unseal.tpe
3030

31-
// TODO move out of Reflection
32-
object typing {
33-
/** Whether the code type checks in the given context?
34-
*
35-
* @param code The code to be type checked
36-
*
37-
* @return false if the code has syntax error or type error in the given context, otherwise returns true.
38-
*
39-
* The code should be a sequence of expressions or statements that may appear in a block.
40-
*/
41-
def typeChecks(code: String)(implicit ctx: Context): Boolean = internal.typeChecks(code)
42-
}
43-
4431
}

library/src/scala/tasty/reflect/CompilerInterface.scala

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -186,17 +186,6 @@ trait CompilerInterface {
186186

187187
def Settings_color(self: Settings): Boolean
188188

189-
//
190-
// MISC
191-
//
192-
/** Whether the code type checks in the given context?
193-
*
194-
* @param code The code to be type checked
195-
*
196-
* The code should be a sequence of expressions or statements that may appear in a block.
197-
*/
198-
def typeChecks(code: String) given (ctx: Context): Boolean
199-
200189
//
201190
// TREES
202191
//

tests/run-macros/reflect-inline/assert_1.scala

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,9 @@ object api {
88
x.stripMargin.toExpr
99

1010
inline def typeChecks(inline x: String): Boolean =
11-
${ typeChecksImpl(x) }
11+
${ typeChecksImpl(scala.compiletime.testing.typeChecks(x)) }
1212

13-
private def typeChecksImpl(x: String) given (qctx: QuoteContext): Expr[Boolean] = {
14-
import qctx.tasty._
15-
if (qctx.tasty.typing.typeChecks(x)) true.toExpr else false.toExpr
13+
private def typeChecksImpl(b: Boolean) given (qctx: QuoteContext): Expr[Boolean] = {
14+
if (b) true.toExpr else false.toExpr
1615
}
1716
}

tests/run-macros/reflect-typeChecks/assert_1.scala

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,10 @@ import scala.quoted._
22

33
object scalatest {
44

5-
inline def assertCompile(inline code: String): Unit = ${ assertImpl(code, true) }
6-
inline def assertNotCompile(inline code: String): Unit = ${ assertImpl(code, false) }
7-
8-
def assertImpl(code: String, expect: Boolean) given (qctx: QuoteContext): Expr[Unit] = {
9-
import qctx.tasty._
10-
11-
val actual = typing.typeChecks(code)
5+
inline def assertCompile(inline code: String): Unit = ${ assertImpl(code, compiletime.testing.typeChecks(code), true) }
6+
inline def assertNotCompile(inline code: String): Unit = ${ assertImpl(code, compiletime.testing.typeChecks(code), false) }
127

8+
def assertImpl(code: String, actual: Boolean, expect: Boolean) given (qctx: QuoteContext): Expr[Unit] = {
139
'{ assert(${expect.toExpr} == ${actual.toExpr}) }
1410
}
1511
}

0 commit comments

Comments
 (0)