From 90371d2ae66ad0606e9eb6144634f013f7d12c2e Mon Sep 17 00:00:00 2001 From: Liu Fengyun Date: Thu, 21 Mar 2019 12:01:18 +0100 Subject: [PATCH 1/3] Add typeChecks to tasty reflect --- .../tools/dotc/tastyreflect/KernelImpl.scala | 20 +++++++++++++++++++ library/src/scala/tasty/Reflection.scala | 9 +++++++++ library/src/scala/tasty/reflect/Kernel.scala | 13 +++++++++++- .../reflect-typeChecks/assert_1.scala | 16 +++++++++++++++ .../reflect-typeChecks/test_2.scala | 15 ++++++++++++++ 5 files changed, 72 insertions(+), 1 deletion(-) create mode 100644 tests/run-with-compiler/reflect-typeChecks/assert_1.scala create mode 100644 tests/run-with-compiler/reflect-typeChecks/test_2.scala diff --git a/compiler/src/dotty/tools/dotc/tastyreflect/KernelImpl.scala b/compiler/src/dotty/tools/dotc/tastyreflect/KernelImpl.scala index f976ee86e132..f79980f84b63 100644 --- a/compiler/src/dotty/tools/dotc/tastyreflect/KernelImpl.scala +++ b/compiler/src/dotty/tools/dotc/tastyreflect/KernelImpl.scala @@ -3,6 +3,7 @@ package tastyreflect import dotty.tools.dotc.ast.{Trees, tpd, untpd} import dotty.tools.dotc.ast.tpd.TreeOps +import dotty.tools.dotc.typer.Typer import dotty.tools.dotc.core._ import dotty.tools.dotc.core.Flags._ import dotty.tools.dotc.core.StdNames.nme @@ -10,6 +11,8 @@ import dotty.tools.dotc.core.quoted.PickledQuotes import dotty.tools.dotc.core.Symbols._ import dotty.tools.dotc.core.Decorators._ import dotty.tools.dotc.tastyreflect.FromSymbol.{definitionFromSym, packageDefFromSym} +import dotty.tools.dotc.parsing.Parsers.Parser +import dotty.tools.dotc.util.SourceFile import scala.tasty.reflect.Kernel @@ -47,6 +50,23 @@ class KernelImpl(val rootContext: core.Contexts.Context, val rootPosition: util. def Settings_color(self: Settings): Boolean = self.color.value(rootContext) == "always" + // + // MISC + // + /** Whether the code type checks in the given context? + * + * @param code The code to be type checked + * + * The code should be a sequence of expressions or statements that may appear in a block. + */ + def typeChecks(code: String)(implicit ctx: Context): Boolean = { + val tree = new Parser(SourceFile.virtual("tasty-reflect", code)).block() + + val ctx2 = ctx.fresh.setNewTyperState().setTyper(new Typer) + ctx2.typer.typed(tree)(ctx2) + !ctx2.reporter.hasErrors + } + // // TREES // diff --git a/library/src/scala/tasty/Reflection.scala b/library/src/scala/tasty/Reflection.scala index b7fecff9a9d7..548ca805bdf2 100644 --- a/library/src/scala/tasty/Reflection.scala +++ b/library/src/scala/tasty/Reflection.scala @@ -27,6 +27,15 @@ abstract class Reflection def typeOf[T: scala.quoted.Type]: Type = implicitly[scala.quoted.Type[T]].unseal.tpe + + /** Whether the code type checks in the given context? + * + * @param code The code to be type checked + * + * The code should be a sequence of expressions or statements that may appear in a block. + */ + def typeChecks(code: String)(implicit ctx: Context): Boolean = kernel.typeChecks(code)(ctx) + val util: reflect.utils.TreeUtils { val reflect: self.type } = new reflect.utils.TreeUtils { val reflect: self.type = self } diff --git a/library/src/scala/tasty/reflect/Kernel.scala b/library/src/scala/tasty/reflect/Kernel.scala index 44d8e163bd4b..b07ace18853e 100644 --- a/library/src/scala/tasty/reflect/Kernel.scala +++ b/library/src/scala/tasty/reflect/Kernel.scala @@ -153,6 +153,17 @@ trait Kernel { def Settings_color(self: Settings): Boolean + // + // MISC + // + /** Whether the code type checks in the given context? + * + * @param code The code to be type checked + * + * The code should be a sequence of expressions or statements that may appear in a block. + */ + def typeChecks(code: String)(implicit ctx: Context): Boolean + // // TREES // @@ -695,7 +706,7 @@ trait Kernel { // // PATTERNS // - + /** Pattern tree of the pattern part of a CaseDef */ type Pattern <: AnyRef diff --git a/tests/run-with-compiler/reflect-typeChecks/assert_1.scala b/tests/run-with-compiler/reflect-typeChecks/assert_1.scala new file mode 100644 index 000000000000..b1439fbde4a0 --- /dev/null +++ b/tests/run-with-compiler/reflect-typeChecks/assert_1.scala @@ -0,0 +1,16 @@ +import scala.quoted._ +import scala.tasty._ + +object scalatest { + + inline def assertCompiles(inline code: String): Unit = ${ assertImpl(code, true) } + inline def assertNotCompile(inline code: String): Unit = ${ assertImpl(code, false) } + + def assertImpl(code: String, expect: Boolean)(implicit refl: Reflection): Expr[Unit] = { + import refl._ + + val actual = typeChecks(code) + + '{ assert(${expect.toExpr} == ${actual.toExpr}) } + } +} diff --git a/tests/run-with-compiler/reflect-typeChecks/test_2.scala b/tests/run-with-compiler/reflect-typeChecks/test_2.scala new file mode 100644 index 000000000000..94b3e61fdc9d --- /dev/null +++ b/tests/run-with-compiler/reflect-typeChecks/test_2.scala @@ -0,0 +1,15 @@ +object Test { + import scalatest._ + + trait Eq[T] + implicit val eq: Eq[Int] = new Eq[Int] {} + + implicit class AnyOps[T](x: T) { + def === (y: T)(implicit c: Eq[T]) = x == y + } + + def main(args: Array[String]): Unit = { + assertCompiles("5 === 5") + assertNotCompile("5.6 === 7.7") + } +} From 4652b7b118a08f2357b90c7cb8d7f46ec2002423 Mon Sep 17 00:00:00 2001 From: Liu Fengyun Date: Fri, 22 Mar 2019 13:17:54 +0100 Subject: [PATCH 2/3] Address review --- .../tools/dotc/tastyreflect/KernelImpl.scala | 10 ++++--- library/src/scala/tasty/Reflection.scala | 19 +++++++------ .../reflect-typeChecks/assert_1.scala | 4 +-- .../reflect-typeChecks/test_2.scala | 27 ++++++++++++++++++- 4 files changed, 45 insertions(+), 15 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/tastyreflect/KernelImpl.scala b/compiler/src/dotty/tools/dotc/tastyreflect/KernelImpl.scala index f79980f84b63..eace2b70194f 100644 --- a/compiler/src/dotty/tools/dotc/tastyreflect/KernelImpl.scala +++ b/compiler/src/dotty/tools/dotc/tastyreflect/KernelImpl.scala @@ -60,11 +60,13 @@ class KernelImpl(val rootContext: core.Contexts.Context, val rootPosition: util. * The code should be a sequence of expressions or statements that may appear in a block. */ def typeChecks(code: String)(implicit ctx: Context): Boolean = { - val tree = new Parser(SourceFile.virtual("tasty-reflect", code)).block() - val ctx2 = ctx.fresh.setNewTyperState().setTyper(new Typer) - ctx2.typer.typed(tree)(ctx2) - !ctx2.reporter.hasErrors + val tree = new Parser(SourceFile.virtual("tasty-reflect", code))(ctx2).block() + + !ctx2.reporter.hasErrors && { + ctx2.typer.typed(tree)(ctx2) + !ctx2.reporter.hasErrors + } } // diff --git a/library/src/scala/tasty/Reflection.scala b/library/src/scala/tasty/Reflection.scala index 548ca805bdf2..61ad3db52d3e 100644 --- a/library/src/scala/tasty/Reflection.scala +++ b/library/src/scala/tasty/Reflection.scala @@ -27,14 +27,17 @@ abstract class Reflection def typeOf[T: scala.quoted.Type]: Type = implicitly[scala.quoted.Type[T]].unseal.tpe - - /** Whether the code type checks in the given context? - * - * @param code The code to be type checked - * - * The code should be a sequence of expressions or statements that may appear in a block. - */ - def typeChecks(code: String)(implicit ctx: Context): Boolean = kernel.typeChecks(code)(ctx) + object typing { + /** Whether the code type checks in the given context? + * + * @param code The code to be type checked + * + * @return false if the code has syntax error or type error in the given context, otherwise returns true. + * + * The code should be a sequence of expressions or statements that may appear in a block. + */ + def typeChecks(code: String)(implicit ctx: Context): Boolean = kernel.typeChecks(code)(ctx) + } val util: reflect.utils.TreeUtils { val reflect: self.type } = new reflect.utils.TreeUtils { val reflect: self.type = self diff --git a/tests/run-with-compiler/reflect-typeChecks/assert_1.scala b/tests/run-with-compiler/reflect-typeChecks/assert_1.scala index b1439fbde4a0..b9ab4733bbfe 100644 --- a/tests/run-with-compiler/reflect-typeChecks/assert_1.scala +++ b/tests/run-with-compiler/reflect-typeChecks/assert_1.scala @@ -3,13 +3,13 @@ import scala.tasty._ object scalatest { - inline def assertCompiles(inline code: String): Unit = ${ assertImpl(code, true) } + inline def assertCompile(inline code: String): Unit = ${ assertImpl(code, true) } inline def assertNotCompile(inline code: String): Unit = ${ assertImpl(code, false) } def assertImpl(code: String, expect: Boolean)(implicit refl: Reflection): Expr[Unit] = { import refl._ - val actual = typeChecks(code) + val actual = typing.typeChecks(code) '{ assert(${expect.toExpr} == ${actual.toExpr}) } } diff --git a/tests/run-with-compiler/reflect-typeChecks/test_2.scala b/tests/run-with-compiler/reflect-typeChecks/test_2.scala index 94b3e61fdc9d..e83eb0c9503b 100644 --- a/tests/run-with-compiler/reflect-typeChecks/test_2.scala +++ b/tests/run-with-compiler/reflect-typeChecks/test_2.scala @@ -9,7 +9,32 @@ object Test { } def main(args: Array[String]): Unit = { - assertCompiles("5 === 5") + assertCompile("5 === 5") assertNotCompile("5.6 === 7.7") + + val x: Int = 5 + assertCompile("x + 3") + assertNotCompile("y + 3") + import scala.util.Left + assertCompile("Left(3)") + assertNotCompile("Rigth(3)") + + def f(x: Int): Int = x * x + assertCompile("f(3)") + assertNotCompile("g(3)") + + type T + assertCompile("def foo(x: T): T = x") + assertNotCompile("foo(???)") + assertNotCompile("def foo(x: S): S = x") + + assertNotCompile("def test(x: Int) =") + + assertCompile( + """ + class EqString extends Eq[String] + new EqString + """ + ) } } From a942a9ca0409b00c31076cba1f11e05237a86660 Mon Sep 17 00:00:00 2001 From: Liu Fengyun Date: Mon, 25 Mar 2019 09:37:31 +0100 Subject: [PATCH 3/3] Address review --- compiler/src/dotty/tools/dotc/tastyreflect/KernelImpl.scala | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/compiler/src/dotty/tools/dotc/tastyreflect/KernelImpl.scala b/compiler/src/dotty/tools/dotc/tastyreflect/KernelImpl.scala index eace2b70194f..85c4cc64c9e3 100644 --- a/compiler/src/dotty/tools/dotc/tastyreflect/KernelImpl.scala +++ b/compiler/src/dotty/tools/dotc/tastyreflect/KernelImpl.scala @@ -63,7 +63,8 @@ class KernelImpl(val rootContext: core.Contexts.Context, val rootPosition: util. val ctx2 = ctx.fresh.setNewTyperState().setTyper(new Typer) val tree = new Parser(SourceFile.virtual("tasty-reflect", code))(ctx2).block() - !ctx2.reporter.hasErrors && { + if (ctx2.reporter.hasErrors) false + else { ctx2.typer.typed(tree)(ctx2) !ctx2.reporter.hasErrors }