From db47f147fef833f35ac667859ea9087566ff6f22 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Mon, 8 Jan 2018 17:32:11 +0100 Subject: [PATCH 01/14] Fix compiling quote in file with no splices --- compiler/src/dotty/tools/dotc/transform/PostTyper.scala | 4 +++- compiler/test/dotty/tools/dotc/FromTastyTests.scala | 1 + tests/pos/quote-no-splices.scala | 9 +++++++++ 3 files changed, 13 insertions(+), 1 deletion(-) create mode 100644 tests/pos/quote-no-splices.scala diff --git a/compiler/src/dotty/tools/dotc/transform/PostTyper.scala b/compiler/src/dotty/tools/dotc/transform/PostTyper.scala index 46fb723e2391..3485f119c7c1 100644 --- a/compiler/src/dotty/tools/dotc/transform/PostTyper.scala +++ b/compiler/src/dotty/tools/dotc/transform/PostTyper.scala @@ -203,7 +203,9 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisPhase // might be a type constructor. Checking.checkInstantiable(tree.tpe, nu.pos) withNoCheckNews(nu :: Nil)(super.transform(tree)) - case _ => + case meth => + if (meth.symbol.name.eq(nme.QUOTE) && meth.symbol.owner.eq(defn.OpsPackageClass)) + ctx.compilationUnit.containsQuotesOrSplices = true super.transform(tree) } case tree: TypeApply => diff --git a/compiler/test/dotty/tools/dotc/FromTastyTests.scala b/compiler/test/dotty/tools/dotc/FromTastyTests.scala index db6ab211257f..23131e3a06d4 100644 --- a/compiler/test/dotty/tools/dotc/FromTastyTests.scala +++ b/compiler/test/dotty/tools/dotc/FromTastyTests.scala @@ -49,6 +49,7 @@ class FromTastyTests extends ParallelTesting { "t8023.scala", "tcpoly_ticket2096.scala", "t247.scala", + "quote-no-splices.scala", ) ) step1.checkCompile() // Compile all files to generate the class files with tasty diff --git a/tests/pos/quote-no-splices.scala b/tests/pos/quote-no-splices.scala new file mode 100644 index 000000000000..7f67c60492ce --- /dev/null +++ b/tests/pos/quote-no-splices.scala @@ -0,0 +1,9 @@ +class Foo { + def foo: Unit = { + val expr ='{ + val a = 3 + println("foo") + 2 + a + } + } +} From 385a87f89441c6d516a6c40efc1dde959d2b7497 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Thu, 14 Dec 2017 17:14:39 +0100 Subject: [PATCH 02/14] Implement run/show for quoted expressions --- .../dotty/tools/dotc/CompilationUnit.scala | 8 ++- .../dotc/quoted/ExprCompilationUnit.scala | 11 ++++ .../tools/dotc/quoted/ExprCompiler.scala | 36 +++++++++++ .../tools/dotc/quoted/ExprDecompiler.scala | 15 +++++ .../tools/dotc/quoted/ExprFrontend.scala | 55 +++++++++++++++++ .../src/dotty/tools/dotc/quoted/ExprRun.scala | 13 ++++ .../dotty/tools/dotc/quoted/QuoteDriver.scala | 61 +++++++++++++++++++ .../tools/dotc/quoted/QuotePrinter.scala | 17 ++++++ .../src/dotty/tools/dotc/quoted/Runners.scala | 10 +++ .../dotty/tools/dotc/transform/Splicer.scala | 2 +- compiler/test/dotty/Jars.scala | 8 +++ .../dotty/tools/dotc/CompilationTests.scala | 4 +- .../test/dotty/tools/vulpix/ChildJVMMain.java | 1 + .../tools/vulpix/TestConfiguration.scala | 1 + dist/bin/dotr | 12 +++- library/src/scala/quoted/Expr.scala | 7 ++- library/src/scala/runtime/quoted/Runner.scala | 9 +++ library/src/scala/runtime/quoted/Show.scala | 9 +++ project/Build.scala | 27 +++++--- project/scripts/sbtTests | 10 +++ tests/pos/quote-0.scala | 1 + tests/pos/quote-assert/quoted_2.scala | 1 + tests/pos/quote-stagedInterpreter.scala | 1 + tests/run-special/quote-run-2.check | 18 ++++++ tests/run-special/quote-run-2.scala | 19 ++++++ tests/run-special/quote-run.check | 16 +++++ tests/run-special/quote-run.scala | 43 +++++++++++++ 27 files changed, 401 insertions(+), 14 deletions(-) create mode 100644 compiler/src/dotty/tools/dotc/quoted/ExprCompilationUnit.scala create mode 100644 compiler/src/dotty/tools/dotc/quoted/ExprCompiler.scala create mode 100644 compiler/src/dotty/tools/dotc/quoted/ExprDecompiler.scala create mode 100644 compiler/src/dotty/tools/dotc/quoted/ExprFrontend.scala create mode 100644 compiler/src/dotty/tools/dotc/quoted/ExprRun.scala create mode 100644 compiler/src/dotty/tools/dotc/quoted/QuoteDriver.scala create mode 100644 compiler/src/dotty/tools/dotc/quoted/QuotePrinter.scala create mode 100644 compiler/src/dotty/tools/dotc/quoted/Runners.scala create mode 100644 library/src/scala/runtime/quoted/Runner.scala create mode 100644 library/src/scala/runtime/quoted/Show.scala create mode 100644 tests/run-special/quote-run-2.check create mode 100644 tests/run-special/quote-run-2.scala create mode 100644 tests/run-special/quote-run.check create mode 100644 tests/run-special/quote-run.scala diff --git a/compiler/src/dotty/tools/dotc/CompilationUnit.scala b/compiler/src/dotty/tools/dotc/CompilationUnit.scala index c3d9326f180c..f93320a7ff90 100644 --- a/compiler/src/dotty/tools/dotc/CompilationUnit.scala +++ b/compiler/src/dotty/tools/dotc/CompilationUnit.scala @@ -31,9 +31,13 @@ class CompilationUnit(val source: SourceFile) { object CompilationUnit { /** Make a compilation unit for top class `clsd` with the contends of the `unpickled` */ - def mkCompilationUnit(clsd: ClassDenotation, unpickled: Tree, forceTrees: Boolean)(implicit ctx: Context): CompilationUnit = { + def mkCompilationUnit(clsd: ClassDenotation, unpickled: Tree, forceTrees: Boolean)(implicit ctx: Context): CompilationUnit = + mkCompilationUnit(new SourceFile(clsd.symbol.associatedFile, Seq()), unpickled, forceTrees) + + /** Make a compilation unit the given unpickled tree */ + def mkCompilationUnit(source: SourceFile, unpickled: Tree, forceTrees: Boolean)(implicit ctx: Context): CompilationUnit = { assert(!unpickled.isEmpty, unpickled) - val unit1 = new CompilationUnit(new SourceFile(clsd.symbol.associatedFile, Seq())) + val unit1 = new CompilationUnit(source) unit1.tpdTree = unpickled if (forceTrees) force.traverse(unit1.tpdTree) diff --git a/compiler/src/dotty/tools/dotc/quoted/ExprCompilationUnit.scala b/compiler/src/dotty/tools/dotc/quoted/ExprCompilationUnit.scala new file mode 100644 index 000000000000..620ca7065f57 --- /dev/null +++ b/compiler/src/dotty/tools/dotc/quoted/ExprCompilationUnit.scala @@ -0,0 +1,11 @@ +package dotty.tools.dotc.quoted + +import dotty.tools.dotc.CompilationUnit +import dotty.tools.dotc.util.NoSource + +import scala.quoted.Expr + +/* Compilation unit containing the contents of a quoted expression */ +class ExprCompilationUnit(val expr: Expr[_]) extends CompilationUnit(NoSource) { + override def toString = s"Expr($expr)" +} diff --git a/compiler/src/dotty/tools/dotc/quoted/ExprCompiler.scala b/compiler/src/dotty/tools/dotc/quoted/ExprCompiler.scala new file mode 100644 index 000000000000..03372afc39e3 --- /dev/null +++ b/compiler/src/dotty/tools/dotc/quoted/ExprCompiler.scala @@ -0,0 +1,36 @@ +package dotty.tools.dotc +package quoted + +import dotty.tools.backend.jvm.GenBCode +import dotty.tools.dotc.core.Contexts.Context +import dotty.tools.dotc.core.{Mode, Phases} +import dotty.tools.dotc.core.Phases.Phase +import dotty.tools.dotc.transform.Pickler +import dotty.tools.io.VirtualDirectory + +/** Compiler that takes the contents of a quoted expression `expr` and produces + * a class file with `class ' { def apply: Object = expr }`. + */ +class ExprCompiler(directory: VirtualDirectory) extends Compiler { + + /** A GenBCode phase that outputs to a virtual directory */ + private class ExprGenBCode extends GenBCode { + override def phaseName = "genBCode" + override def outputDir(implicit ctx: Context) = directory + } + + override def phases: List[List[Phase]] = { + val backendPhases = super.phases.dropWhile { + case List(_: Pickler) => false + case _ => true + }.tail + + List(new ExprFrontend(putInClass = true)) :: + Phases.replace(classOf[GenBCode], _ => new ExprGenBCode :: Nil, backendPhases) + } + + override def newRun(implicit ctx: Context): ExprRun = { + reset() + new ExprRun(this, ctx.addMode(Mode.ReadPositions)) + } +} diff --git a/compiler/src/dotty/tools/dotc/quoted/ExprDecompiler.scala b/compiler/src/dotty/tools/dotc/quoted/ExprDecompiler.scala new file mode 100644 index 000000000000..ea91109ec0a3 --- /dev/null +++ b/compiler/src/dotty/tools/dotc/quoted/ExprDecompiler.scala @@ -0,0 +1,15 @@ +package dotty.tools.dotc.quoted + +import java.io.PrintStream + +import dotty.tools.dotc.core.Phases.Phase + +/** Compiler that takes the contents of a quoted expression `expr` and produces outputs + * the pretty printed code. + */ +class ExprDecompiler(out: PrintStream) extends ExprCompiler(null) { + override def phases: List[List[Phase]] = List( + List(new ExprFrontend(putInClass = false)), // Create class from Expr + List(new QuotePrinter(out)) // Print all loaded classes + ) +} diff --git a/compiler/src/dotty/tools/dotc/quoted/ExprFrontend.scala b/compiler/src/dotty/tools/dotc/quoted/ExprFrontend.scala new file mode 100644 index 000000000000..e068a739bcfc --- /dev/null +++ b/compiler/src/dotty/tools/dotc/quoted/ExprFrontend.scala @@ -0,0 +1,55 @@ +package dotty.tools.dotc.quoted + +import dotty.tools.dotc.CompilationUnit +import dotty.tools.dotc.ast.tpd +import dotty.tools.dotc.core.Contexts.Context +import dotty.tools.dotc.core.Flags._ +import dotty.tools.dotc.core.Scopes._ +import dotty.tools.dotc.core.StdNames._ +import dotty.tools.dotc.core.Symbols._ +import dotty.tools.dotc.core.Types._ +import dotty.tools.dotc.core.quoted.PickledQuotes +import dotty.tools.dotc.typer.FrontEnd +import dotty.tools.dotc.util.Positions._ +import dotty.tools.dotc.util.SourceFile +import dotty.tools.io._ + +import scala.quoted.Expr + +/** Frontend that receives scala.quoted.Expr as input */ +class ExprFrontend(putInClass: Boolean) extends FrontEnd { + import tpd._ + + override def isTyper = false + + override def runOn(units: List[CompilationUnit])(implicit ctx: Context): List[CompilationUnit] = { + units.map { + case exprUnit: ExprCompilationUnit => + val tree = + if (putInClass) inClass(exprUnit.expr) + else PickledQuotes.quotedToTree(exprUnit.expr) + val source = new SourceFile("", Seq()) + CompilationUnit.mkCompilationUnit(source, tree, forceTrees = true) + } + } + + /** Places the contents of expr in a compilable tree for a class + * with the following format. + * `package __root__ { class ' { def apply: Any = } }` + */ + private def inClass(expr: Expr[_])(implicit ctx: Context): Tree = { + val pos = Position(0) + val assocFile = new PlainFile(Path("")) + + val cls = ctx.newCompleteClassSymbol(defn.RootClass, nme.QUOTE.toTypeName, EmptyFlags, + defn.ObjectType :: Nil, newScope, coord = pos, assocFile = assocFile).entered.asClass + cls.enter(ctx.newDefaultConstructor(cls), EmptyScope) + val meth = ctx.newSymbol(cls, nme.apply, Method, ExprType(defn.AnyType), coord = pos).entered + + val quoted = PickledQuotes.quotedToTree(expr)(ctx.withOwner(meth)) + + val run = DefDef(meth, quoted) + val classTree = ClassDef(cls, DefDef(cls.primaryConstructor.asTerm), run :: Nil) + PackageDef(ref(defn.RootPackage).asInstanceOf[Ident], classTree :: Nil).withPos(pos) + } +} diff --git a/compiler/src/dotty/tools/dotc/quoted/ExprRun.scala b/compiler/src/dotty/tools/dotc/quoted/ExprRun.scala new file mode 100644 index 000000000000..9230ce877382 --- /dev/null +++ b/compiler/src/dotty/tools/dotc/quoted/ExprRun.scala @@ -0,0 +1,13 @@ +package dotty.tools.dotc +package quoted + +import dotty.tools.dotc.core.Contexts._ + +import scala.quoted._ + +class ExprRun(comp: Compiler, ictx: Context) extends Run(comp, ictx) { + def compileExpr(expr: Expr[_]): Unit = { + val units = new ExprCompilationUnit(expr) :: Nil + compileUnits(units) + } +} diff --git a/compiler/src/dotty/tools/dotc/quoted/QuoteDriver.scala b/compiler/src/dotty/tools/dotc/quoted/QuoteDriver.scala new file mode 100644 index 000000000000..20fb3532a90d --- /dev/null +++ b/compiler/src/dotty/tools/dotc/quoted/QuoteDriver.scala @@ -0,0 +1,61 @@ +package dotty.tools.dotc.quoted + +import dotty.tools.dotc.Driver +import dotty.tools.dotc.core.Contexts.Context +import dotty.tools.dotc.core.StdNames._ +import dotty.tools.io.VirtualDirectory + +import dotty.tools.repl.AbstractFileClassLoader + +import scala.quoted.Expr + +import java.io.ByteArrayOutputStream +import java.io.PrintStream +import java.nio.charset.StandardCharsets + +class QuoteDriver extends Driver { + + def run[T](expr: Expr[T]): T = { + val ctx: Context = initCtx.fresh + // TODO enable optimisation? + // ctx.settings.optimise.update(true)(ctx) + + val outDir = new VirtualDirectory("(memory)", None) + + new ExprCompiler(outDir).newRun(ctx).compileExpr(expr) + + val classLoader = new AbstractFileClassLoader(outDir, this.getClass.getClassLoader) + + val clazz = classLoader.loadClass(nme.QUOTE.toString) + val method = clazz.getMethod("apply") + val instance = clazz.newInstance() + + method.invoke(instance).asInstanceOf[T] + } + + def show(expr: Expr[_]): String = { + val ctx: Context = initCtx.fresh + ctx.settings.color.update("never")(ctx) // TODO support colored show + val baos = new ByteArrayOutputStream + var ps: PrintStream = null + try { + ps = new PrintStream(baos, true, "utf-8") + + new ExprDecompiler(ps).newRun(ctx).compileExpr(expr) + + new String(baos.toByteArray, StandardCharsets.UTF_8) + } + finally if (ps != null) ps.close() + } + + override def initCtx: Context = { + val ictx = super.initCtx.fresh + val compilerClasspath = System.getProperty("dotty.tools.dotc.classpath") + assert(compilerClasspath ne null, "System property `dotty.tools.dotc.classpath` is not set.") + val classpath = System.getProperty("java.class.path") + val scalaLib = classpath.split(":").filter(_.contains("scala-library")).mkString(":") + ictx.settings.classpath.update(compilerClasspath + ":" + scalaLib)(ictx) + ictx + } + +} diff --git a/compiler/src/dotty/tools/dotc/quoted/QuotePrinter.scala b/compiler/src/dotty/tools/dotc/quoted/QuotePrinter.scala new file mode 100644 index 000000000000..96ac9e8390e1 --- /dev/null +++ b/compiler/src/dotty/tools/dotc/quoted/QuotePrinter.scala @@ -0,0 +1,17 @@ +package dotty.tools.dotc.quoted + +import java.io.PrintStream + +import dotty.tools.dotc.core.Contexts._ +import dotty.tools.dotc.core.Phases.Phase + +/** Pretty prints the compilation unit to an output stream */ +class QuotePrinter(out: PrintStream) extends Phase { + + override def phaseName: String = "quotePrinter" + + override def run(implicit ctx: Context): Unit = { + val unit = ctx.compilationUnit + out.print(unit.tpdTree.show) + } +} diff --git a/compiler/src/dotty/tools/dotc/quoted/Runners.scala b/compiler/src/dotty/tools/dotc/quoted/Runners.scala new file mode 100644 index 000000000000..6b63acd7c076 --- /dev/null +++ b/compiler/src/dotty/tools/dotc/quoted/Runners.scala @@ -0,0 +1,10 @@ +package dotty.tools.dotc.quoted + +import scala.quoted.Expr +import scala.runtime.quoted._ + +/** Default runners for quoted expressions */ +object Runners { + implicit def runner[T]: Runner[T] = (expr: Expr[T]) => new QuoteDriver().run(expr) + implicit def show[T]: Show[T] = (expr: Expr[T]) => new QuoteDriver().show(expr) +} diff --git a/compiler/src/dotty/tools/dotc/transform/Splicer.scala b/compiler/src/dotty/tools/dotc/transform/Splicer.scala index 271c807a8ec1..6e05b9d85873 100644 --- a/compiler/src/dotty/tools/dotc/transform/Splicer.scala +++ b/compiler/src/dotty/tools/dotc/transform/Splicer.scala @@ -24,7 +24,7 @@ object Splicer { /** Splice the Tree for a Quoted expression which is constructed via a reflective call to the given method */ private def reflectiveSplice(tree: Tree)(implicit ctx: Context): Tree = { val interpreter = new Interpreter - interpreter.interpretTree[quoted.Expr[_]](tree).map(PickledQuotes.quotedToTree(_)).getOrElse(tree) + interpreter.interpretTree[scala.quoted.Expr[_]](tree).map(PickledQuotes.quotedToTree(_)).getOrElse(tree) } } diff --git a/compiler/test/dotty/Jars.scala b/compiler/test/dotty/Jars.scala index dd06dc2a6fee..5294943d2554 100644 --- a/compiler/test/dotty/Jars.scala +++ b/compiler/test/dotty/Jars.scala @@ -14,6 +14,10 @@ object Jars { val dottyInterfaces: String = sys.env.get("DOTTY_INTERFACE") .getOrElse(Properties.dottyInterfaces) + /** Scala asm Jar */ + lazy val scalaAsm: String = + findJarFromRuntime("scala-asm-6.0.0-scala-1") + /** Dotty extras classpath from env or properties */ val dottyExtras: List[String] = sys.env.get("DOTTY_EXTRAS") .map(_.split(":").toList).getOrElse(Properties.dottyExtras) @@ -25,6 +29,10 @@ object Jars { val dottyTestDeps: List[String] = dottyLib :: dottyCompiler :: dottyInterfaces :: dottyExtras + /** Dotty runtime with compiler dependencies, used for quoted.Expr.run */ + val dottyRunWithCompiler: List[String] = + dottyLib :: dottyCompiler :: dottyInterfaces :: scalaAsm :: Nil + def scalaLibrary: String = sys.env.get("DOTTY_SCALA_LIBRARY") .getOrElse(findJarFromRuntime("scala-library-2.")) diff --git a/compiler/test/dotty/tools/dotc/CompilationTests.scala b/compiler/test/dotty/tools/dotc/CompilationTests.scala index 82bb6ef523f1..c82e2c273806 100644 --- a/compiler/test/dotty/tools/dotc/CompilationTests.scala +++ b/compiler/test/dotty/tools/dotc/CompilationTests.scala @@ -197,7 +197,9 @@ class CompilationTests extends ParallelTesting { @Test def runAll: Unit = { implicit val testGroup: TestGroup = TestGroup("runAll") compileFilesInDir("../tests/run", defaultOptions) + - compileFilesInDir("../tests/run-no-optimise", defaultOptions) + compileFilesInDir("../tests/run-no-optimise", defaultOptions) + + compileFile("../tests/run-special/quote-run.scala", defaultRunWithCompilerOptions) + + compileFile("../tests/run-special/quote-run-2.scala", defaultRunWithCompilerOptions) }.checkRuns() // Generic java signatures tests --------------------------------------------- diff --git a/compiler/test/dotty/tools/vulpix/ChildJVMMain.java b/compiler/test/dotty/tools/vulpix/ChildJVMMain.java index 90b7958989e3..2982662eb9c9 100644 --- a/compiler/test/dotty/tools/vulpix/ChildJVMMain.java +++ b/compiler/test/dotty/tools/vulpix/ChildJVMMain.java @@ -12,6 +12,7 @@ public class ChildJVMMain { static final String MessageEnd = "##THIS IS THE END FOR ME, GOODBYE##"; private static void runMain(String dir) throws Exception { + System.setProperty("dotty.tools.dotc.classpath", dir); ArrayList cp = new ArrayList<>(); for (String path : dir.split(":")) cp.add(new File(path).toURI().toURL()); diff --git a/compiler/test/dotty/tools/vulpix/TestConfiguration.scala b/compiler/test/dotty/tools/vulpix/TestConfiguration.scala index b45af357ec3d..845908be2e0f 100644 --- a/compiler/test/dotty/tools/vulpix/TestConfiguration.scala +++ b/compiler/test/dotty/tools/vulpix/TestConfiguration.scala @@ -51,6 +51,7 @@ object TestConfiguration { val defaultUnoptimised = TestFlags(classPath, runClassPath, basicDefaultOptions) val defaultOptimised = defaultUnoptimised and "-optimise" val defaultOptions = defaultUnoptimised + val defaultRunWithCompilerOptions = defaultOptions.withRunClasspath(Jars.dottyRunWithCompiler.mkString(":")) val allowDeepSubtypes = defaultOptions without "-Yno-deep-subtypes" val allowDoubleBindings = defaultOptions without "-Yno-double-bindings" val picklingOptions = defaultUnoptimised and ( diff --git a/dist/bin/dotr b/dist/bin/dotr index bb3d06330fdb..b993375544d5 100755 --- a/dist/bin/dotr +++ b/dist/bin/dotr @@ -30,7 +30,9 @@ fi source "$PROG_HOME/bin/common" declare -a residual_args +declare -a system_properties run_repl=false +with_compiler=false CLASS_PATH="" while [[ $# -gt 0 ]]; do @@ -44,6 +46,10 @@ while [[ $# -gt 0 ]]; do shift shift ;; + -with-compiler) + with_compiler=true + shift + ;; -d) DEBUG="$DEBUG_STR" shift @@ -69,5 +75,9 @@ else else cp_arg+="$PSEP$CLASS_PATH" fi - eval exec "\"$JAVACMD\"" "$DEBUG" "-classpath \"$cp_arg\"" "${residual_args[@]}" + if [ $with_compiler == true ]; then + cp_arg+="$PSEP$DOTTY_COMP$PSEP$DOTTY_INTF$PSEP$SCALA_ASM" + system_properties+=("-Ddotty.tools.dotc.classpath=$DOTTY_COMP$PSEP$DOTTY_LIB$PSEP$DOTTY_INTF$PSEP$SCALA_ASM") + fi + eval exec "\"$JAVACMD\"" "$DEBUG" "-classpath \"$cp_arg\"" "${system_properties[@]}" "${residual_args[@]}" fi diff --git a/library/src/scala/quoted/Expr.scala b/library/src/scala/quoted/Expr.scala index 58241792ddc1..07dd03b94b01 100644 --- a/library/src/scala/quoted/Expr.scala +++ b/library/src/scala/quoted/Expr.scala @@ -1,8 +1,11 @@ package scala.quoted +import scala.runtime.quoted.{Runner, Show} + abstract class Expr[T] extends Quoted { - def unary_~ : T = throw new Error("~ should have been compiled away") - def run: T = ??? + final def unary_~ : T = throw new Error("~ should have been compiled away") + final def run(implicit runner: Runner[T]): T = runner.run(this) + final def show(implicit runner: Show[T]): String = runner.run(this) } object Expr { diff --git a/library/src/scala/runtime/quoted/Runner.scala b/library/src/scala/runtime/quoted/Runner.scala new file mode 100644 index 000000000000..4f39c86adb78 --- /dev/null +++ b/library/src/scala/runtime/quoted/Runner.scala @@ -0,0 +1,9 @@ +package scala.runtime.quoted + +import scala.annotation.implicitNotFound +import scala.quoted.Expr + +@implicitNotFound("Could not find implicit Runner. Default runner can must be imported with `import dotty.tools.dotc.quoted.Runners._`") +trait Runner[T] { + def run(expr: Expr[T]): T +} diff --git a/library/src/scala/runtime/quoted/Show.scala b/library/src/scala/runtime/quoted/Show.scala new file mode 100644 index 000000000000..ec055a656277 --- /dev/null +++ b/library/src/scala/runtime/quoted/Show.scala @@ -0,0 +1,9 @@ +package scala.runtime.quoted + +import scala.annotation.implicitNotFound +import scala.quoted.Expr + +@implicitNotFound("Could not find implicit Show. Default runner can must be imported with `import dotty.tools.dotc.quoted.Runners._`") +trait Show[T] { + def run(expr: Expr[T]): String +} diff --git a/project/Build.scala b/project/Build.scala index f5ebbd12e3da..ec28a9acf7ac 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -571,22 +571,35 @@ object Build { val java: String = Process("which" :: "java" :: Nil).!! val attList = (dependencyClasspath in Runtime).value val _ = packageAll.value - val scalaLib = attList + + def findLib(name: String) = attList .map(_.data.getAbsolutePath) - .find(_.contains("scala-library")) + .find(_.contains(name)) .toList.mkString(":") + val scalaLib = findLib("scala-library") + val dottyLib = packageAll.value("dotty-library") + + def run(args: List[String]): Unit = { + val fullArgs = insertClasspathInArgs(args, s".:$dottyLib:$scalaLib") + s"$java ${fullArgs.mkString(" ")}".! + } + if (args.isEmpty) { println("Couldn't run `dotr` without args. Use `repl` to run the repl or add args to run the dotty application") } else if (java == "") { println("Couldn't find java executable on path, please install java to a default location") } else if (scalaLib == "") { println("Couldn't find scala-library on classpath, please run using script in bin dir instead") - } else { - val dottyLib = packageAll.value("dotty-library") - val fullArgs = insertClasspathInArgs(args, s".:$dottyLib:$scalaLib") - s"$java ${fullArgs.mkString(" ")}".! - } + } else if (args.contains("-with-compiler")) { + val args1 = args.filter(_ != "-with-compiler") + val asm = findLib("scala-asm") + val dottyCompiler = packageAll.value("dotty-compiler") + val dottyInterfaces = packageAll.value("dotty-interfaces") + val deps = s"$dottyCompiler:$dottyInterfaces:$asm" + val args2 = s"-Ddotty.tools.dotc.classpath=$deps:$dottyLib" :: insertClasspathInArgs(args1, deps) + run(args2) + } else run(args) }, run := dotc.evaluated, dotc := runCompilerMain().evaluated, diff --git a/project/scripts/sbtTests b/project/scripts/sbtTests index 7d7e68ee77d9..17641aea4c56 100755 --- a/project/scripts/sbtTests +++ b/project/scripts/sbtTests @@ -56,3 +56,13 @@ else echo "failed output check" exit -1 fi + +echo "testing scala.quoted.Expr.run from sbt dotr" +./project/scripts/sbt ";dotc -classpath compiler/target/scala-2.12/classes tests/run-special/quote-run.scala; dotr -with-compiler Test" > sbtdot5.out +cat sbtdot5.out +if grep -e "val a: Int = 3" sbtdot5.out; then + echo "output ok" +else + echo "failed output check" + exit -1 +fi diff --git a/tests/pos/quote-0.scala b/tests/pos/quote-0.scala index 3155c353ca33..8372c6414cde 100644 --- a/tests/pos/quote-0.scala +++ b/tests/pos/quote-0.scala @@ -1,4 +1,5 @@ import scala.quoted._ +import dotty.tools.dotc.quoted.Runners._ class Test { diff --git a/tests/pos/quote-assert/quoted_2.scala b/tests/pos/quote-assert/quoted_2.scala index 1b6cde45c700..7d73cdc65a4a 100644 --- a/tests/pos/quote-assert/quoted_2.scala +++ b/tests/pos/quote-assert/quoted_2.scala @@ -1,3 +1,4 @@ +import dotty.tools.dotc.quoted.Runners.runner import scala.quoted._ import Macros._ diff --git a/tests/pos/quote-stagedInterpreter.scala b/tests/pos/quote-stagedInterpreter.scala index 2ba57be2b004..863aa6c3ae1e 100644 --- a/tests/pos/quote-stagedInterpreter.scala +++ b/tests/pos/quote-stagedInterpreter.scala @@ -1,4 +1,5 @@ import scala.quoted._ +import dotty.tools.dotc.quoted.Runners._ enum Exp { case Num(n: Int) diff --git a/tests/run-special/quote-run-2.check b/tests/run-special/quote-run-2.check new file mode 100644 index 000000000000..d9052f8c4d3d --- /dev/null +++ b/tests/run-special/quote-run-2.check @@ -0,0 +1,18 @@ +1.0 +5.0 +{ + { + val y: Double = 5.0.*(5.0) + y + } +} +{ + 5.0.*( + { + { + val y: Double = 5.0.*(5.0) + y + } + } + ) +} diff --git a/tests/run-special/quote-run-2.scala b/tests/run-special/quote-run-2.scala new file mode 100644 index 000000000000..8361539c1442 --- /dev/null +++ b/tests/run-special/quote-run-2.scala @@ -0,0 +1,19 @@ + +import dotty.tools.dotc.quoted.Runners._ + +import scala.quoted._ + +object Test { + def main(args: Array[String]): Unit = { + def powerCode(n: Int, x: Expr[Double]): Expr[Double] = + if (n == 0) '(1.0) + else if (n == 1) x + else if (n % 2 == 0) '{ { val y = ~x * ~x; ~powerCode(n / 2, '(y)) } } + else '{ ~x * ~powerCode(n - 1, x) } + + println(powerCode(0, '(5)).show) + println(powerCode(1, '(5)).show) + println(powerCode(2, '(5)).show) + println(powerCode(3, '(5)).show) + } +} diff --git a/tests/run-special/quote-run.check b/tests/run-special/quote-run.check new file mode 100644 index 000000000000..fefd8facd664 --- /dev/null +++ b/tests/run-special/quote-run.check @@ -0,0 +1,16 @@ +foo +5 +foo +5 +{ + val a: Int = 3 + println("foo") + 2.+(a) +} + +lambda(4) +lambda(5) +Foo +false +Bar +class '$A$1 diff --git a/tests/run-special/quote-run.scala b/tests/run-special/quote-run.scala new file mode 100644 index 000000000000..ad8c4fe35f7b --- /dev/null +++ b/tests/run-special/quote-run.scala @@ -0,0 +1,43 @@ + +import dotty.tools.dotc.quoted.Runners._ + +import scala.quoted._ + +object Test { + def main(args: Array[String]): Unit = { + val expr = '{ + val a = 3 + println("foo") + 2 + a + } + println(expr.run) + println(expr.run) + println(expr.show) + + val lambdaExpr = '{ + (x: Int) => println("lambda(" + x + ")") + } + println() + + val lambda = lambdaExpr.run + lambda(4) + lambda(5) + + val classExpr = '{ + class A { + override def toString: String = "Foo" + } + new A + } + val classExpr2 = '{ + class A { + override def toString: String = "Bar" + } + new A + } + println(classExpr.run) + println(classExpr.run.getClass == classExpr.run.getClass) + println(classExpr2.run) + println(classExpr2.run.getClass) + } +} From 3df0b196a3da24e20bb192ce2bc659a8002652d7 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Thu, 21 Dec 2017 18:33:15 +0100 Subject: [PATCH 03/14] Simplify compiler classpath for dotr --- compiler/src/dotty/tools/dotc/quoted/QuoteDriver.scala | 5 +---- compiler/test/dotty/tools/vulpix/ChildJVMMain.java | 4 +++- dist/bin/dotr | 4 +--- project/Build.scala | 4 +--- 4 files changed, 6 insertions(+), 11 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/quoted/QuoteDriver.scala b/compiler/src/dotty/tools/dotc/quoted/QuoteDriver.scala index 20fb3532a90d..d19899331eb7 100644 --- a/compiler/src/dotty/tools/dotc/quoted/QuoteDriver.scala +++ b/compiler/src/dotty/tools/dotc/quoted/QuoteDriver.scala @@ -50,11 +50,8 @@ class QuoteDriver extends Driver { override def initCtx: Context = { val ictx = super.initCtx.fresh - val compilerClasspath = System.getProperty("dotty.tools.dotc.classpath") - assert(compilerClasspath ne null, "System property `dotty.tools.dotc.classpath` is not set.") val classpath = System.getProperty("java.class.path") - val scalaLib = classpath.split(":").filter(_.contains("scala-library")).mkString(":") - ictx.settings.classpath.update(compilerClasspath + ":" + scalaLib)(ictx) + ictx.settings.classpath.update(classpath)(ictx) ictx } diff --git a/compiler/test/dotty/tools/vulpix/ChildJVMMain.java b/compiler/test/dotty/tools/vulpix/ChildJVMMain.java index 2982662eb9c9..ce9aebf719e1 100644 --- a/compiler/test/dotty/tools/vulpix/ChildJVMMain.java +++ b/compiler/test/dotty/tools/vulpix/ChildJVMMain.java @@ -12,7 +12,9 @@ public class ChildJVMMain { static final String MessageEnd = "##THIS IS THE END FOR ME, GOODBYE##"; private static void runMain(String dir) throws Exception { - System.setProperty("dotty.tools.dotc.classpath", dir); + String jcp = System.getProperty("java.class.path"); + System.setProperty("java.class.path", jcp == null ? dir : dir + ":" + jcp); + ArrayList cp = new ArrayList<>(); for (String path : dir.split(":")) cp.add(new File(path).toURI().toURL()); diff --git a/dist/bin/dotr b/dist/bin/dotr index b993375544d5..34766208c84d 100755 --- a/dist/bin/dotr +++ b/dist/bin/dotr @@ -30,7 +30,6 @@ fi source "$PROG_HOME/bin/common" declare -a residual_args -declare -a system_properties run_repl=false with_compiler=false CLASS_PATH="" @@ -77,7 +76,6 @@ else fi if [ $with_compiler == true ]; then cp_arg+="$PSEP$DOTTY_COMP$PSEP$DOTTY_INTF$PSEP$SCALA_ASM" - system_properties+=("-Ddotty.tools.dotc.classpath=$DOTTY_COMP$PSEP$DOTTY_LIB$PSEP$DOTTY_INTF$PSEP$SCALA_ASM") fi - eval exec "\"$JAVACMD\"" "$DEBUG" "-classpath \"$cp_arg\"" "${system_properties[@]}" "${residual_args[@]}" + eval exec "\"$JAVACMD\"" "$DEBUG" "-classpath \"$cp_arg\"" "${residual_args[@]}" fi diff --git a/project/Build.scala b/project/Build.scala index ec28a9acf7ac..46f3a9316648 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -596,9 +596,7 @@ object Build { val asm = findLib("scala-asm") val dottyCompiler = packageAll.value("dotty-compiler") val dottyInterfaces = packageAll.value("dotty-interfaces") - val deps = s"$dottyCompiler:$dottyInterfaces:$asm" - val args2 = s"-Ddotty.tools.dotc.classpath=$deps:$dottyLib" :: insertClasspathInArgs(args1, deps) - run(args2) + run(insertClasspathInArgs(args1, s"$dottyCompiler:$dottyInterfaces:$asm")) } else run(args) }, run := dotc.evaluated, From 3e5625cc70113e07e028ad2a335ee16fc8d66fe6 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Thu, 21 Dec 2017 18:58:07 +0100 Subject: [PATCH 04/14] Add -with-compiler to dotc --- dist/bin/dotc | 2 ++ project/Build.scala | 10 ++++++---- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/dist/bin/dotc b/dist/bin/dotc index 0ada07560cfc..a91f70fcc06a 100755 --- a/dist/bin/dotc +++ b/dist/bin/dotc @@ -29,6 +29,7 @@ source "$PROG_HOME/bin/common" default_java_opts="-Xmx768m -Xms768m" bootcp=true +withCompiler=true CompilerMain=dotty.tools.dotc.Main DecompilerMain=dotty.tools.dotc.decompiler.Main @@ -89,6 +90,7 @@ case "$1" in -nobootcp) unset bootcp && shift ;; -colors) colors=true && shift ;; -no-colors) unset colors && shift ;; + -with-compiler) jvm_cp_args="$PSEP$DOTTY_COMP" && shift ;; # break out -D and -J options and add them to JAVA_OPTS as well # so they reach the JVM in time to do some good. The -D options diff --git a/project/Build.scala b/project/Build.scala index 46f3a9316648..ccc3d29cb700 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -639,10 +639,12 @@ object Build { def runCompilerMain(repl: Boolean = false) = Def.inputTaskDyn { val dottyLib = packageAll.value("dotty-library") + lazy val dottyCompiler = packageAll.value("dotty-compiler") val args0: List[String] = spaceDelimited("").parsed.toList val decompile = args0.contains("-decompile") val debugFromTasty = args0.contains("-Ythrough-tasty") - val args = args0.filter(arg => arg != "-repl" && arg != "-decompile" && arg != "-Ythrough-tasty") + val args = args0.filter(arg => arg != "-repl" && arg != "-decompile" && + arg != "-with-compiler" && arg != "-Ythrough-tasty") val main = if (repl) "dotty.tools.repl.Main" @@ -650,9 +652,9 @@ object Build { else if (debugFromTasty) "dotty.tools.dotc.fromtasty.Debug" else "dotty.tools.dotc.Main" - val extraClasspath = - if (decompile && !args.contains("-classpath")) dottyLib + ":." - else dottyLib + var extraClasspath = dottyLib + if (decompile && !args.contains("-classpath")) extraClasspath += ":." + if (args0.contains("-with-compiler")) extraClasspath += s":$dottyCompiler" val fullArgs = main :: insertClasspathInArgs(args, extraClasspath) From 9e54e73f5598a3ff4d7eb398195106fb57297d17 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Fri, 22 Dec 2017 09:15:44 +0100 Subject: [PATCH 05/14] Make staged interpreter a run test --- .../dotty/tools/dotc/CompilationTests.scala | 3 +- tests/pos/quote-stagedInterpreter.scala | 36 ------------ .../quote-run-staged-interpreter.check | 21 +++++++ .../quote-run-staged-interpreter.scala | 55 +++++++++++++++++++ 4 files changed, 78 insertions(+), 37 deletions(-) delete mode 100644 tests/pos/quote-stagedInterpreter.scala create mode 100644 tests/run-special/quote-run-staged-interpreter.check create mode 100644 tests/run-special/quote-run-staged-interpreter.scala diff --git a/compiler/test/dotty/tools/dotc/CompilationTests.scala b/compiler/test/dotty/tools/dotc/CompilationTests.scala index c82e2c273806..390398732a12 100644 --- a/compiler/test/dotty/tools/dotc/CompilationTests.scala +++ b/compiler/test/dotty/tools/dotc/CompilationTests.scala @@ -199,7 +199,8 @@ class CompilationTests extends ParallelTesting { compileFilesInDir("../tests/run", defaultOptions) + compileFilesInDir("../tests/run-no-optimise", defaultOptions) + compileFile("../tests/run-special/quote-run.scala", defaultRunWithCompilerOptions) + - compileFile("../tests/run-special/quote-run-2.scala", defaultRunWithCompilerOptions) + compileFile("../tests/run-special/quote-run-2.scala", defaultRunWithCompilerOptions) + + compileFile("../tests/run-special/quote-run-staged-interpreter.scala", defaultRunWithCompilerOptions) }.checkRuns() // Generic java signatures tests --------------------------------------------- diff --git a/tests/pos/quote-stagedInterpreter.scala b/tests/pos/quote-stagedInterpreter.scala deleted file mode 100644 index 863aa6c3ae1e..000000000000 --- a/tests/pos/quote-stagedInterpreter.scala +++ /dev/null @@ -1,36 +0,0 @@ -import scala.quoted._ -import dotty.tools.dotc.quoted.Runners._ - -enum Exp { - case Num(n: Int) - case Plus(e1: Exp, e2: Exp) - case Var(x: String) - case Let(x: String, e: Exp, in: Exp) -} - -object Test { - import Exp._ - - val keepLets = true - - val exp = Plus(Plus(Num(2), Var("x")), Num(4)) - - val letExp = Let("x", Num(3), exp) - - def compile(e: Exp, env: Map[String, Expr[Int]]): Expr[Int] = e match { - case Num(n) => n - case Plus(e1, e2) => '(~compile(e1, env) + ~compile(e2, env)) - case Var(x) => env(x) - case Let(x, e, body) => - if (keepLets) - '{ val y = ~compile(e, env); ~compile(body, env + (x -> '(y))) } - else - compile(body, env + (x -> compile(e, env))) - } - - val res1 = '{ (x: Int) => ~compile(exp, Map("x" -> '(x))) } - - val res2 = compile(letExp, Map()) - - res1.run -} diff --git a/tests/run-special/quote-run-staged-interpreter.check b/tests/run-special/quote-run-staged-interpreter.check new file mode 100644 index 000000000000..bacb3969c81b --- /dev/null +++ b/tests/run-special/quote-run-staged-interpreter.check @@ -0,0 +1,21 @@ +{ + { + def $anonfun(x: Int): Int = + { + 2.+(x).+(4) + } + closure($anonfun) + } +} +6 +8 +9 +--- +2.+(3).+(4) +9 +--- +{ + val y: Int = 3 + 2.+(y).+(4) +} +9 diff --git a/tests/run-special/quote-run-staged-interpreter.scala b/tests/run-special/quote-run-staged-interpreter.scala new file mode 100644 index 000000000000..91429b7c86eb --- /dev/null +++ b/tests/run-special/quote-run-staged-interpreter.scala @@ -0,0 +1,55 @@ +import scala.quoted._ +import dotty.tools.dotc.quoted.Runners._ + +enum Exp { + case Num(n: Int) + case Plus(e1: Exp, e2: Exp) + case Var(x: String) + case Let(x: String, e: Exp, in: Exp) +} + +object Test { + import Exp._ + + def compile(e: Exp, env: Map[String, Expr[Int]], keepLets: Boolean): Expr[Int] = { + def compileImpl(e: Exp, env: Map[String, Expr[Int]]): Expr[Int] = e match { + case Num(n) => n + case Plus(e1, e2) => '(~compileImpl(e1, env) + ~compileImpl(e2, env)) + case Var(x) => env(x) + case Let(x, e, body) => + if (keepLets) + '{ val y = ~compileImpl(e, env); ~compileImpl(body, env + (x -> '(y))) } + else + compileImpl(body, env + (x -> compileImpl(e, env))) + } + compileImpl(e, env) + } + + + def main(args: Array[String]): Unit = { + val exp = Plus(Plus(Num(2), Var("x")), Num(4)) + val letExp = Let("x", Num(3), exp) + + val res1 = '{ (x: Int) => ~compile(exp, Map("x" -> '(x)), false) } + + + println(res1.show) + + val fn = res1.run + println(fn(0)) + println(fn(2)) + println(fn(3)) + + println("---") + + val res2 = compile(letExp, Map(), false) + println(res2.show) + println(res2.run) + + println("---") + + val res3 = compile(letExp, Map(), true) + println(res3.show) + println(res3.run) + } +} From 7d9c6e4ace33c41093b7355d95114011e8c7f836 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Sat, 23 Dec 2017 10:40:07 +0100 Subject: [PATCH 06/14] Simplify Runners --- compiler/src/dotty/tools/dotc/quoted/Runners.scala | 10 ++++++++-- library/src/scala/quoted/Expr.scala | 4 ++-- library/src/scala/runtime/quoted/Runner.scala | 1 + library/src/scala/runtime/quoted/Show.scala | 9 --------- 4 files changed, 11 insertions(+), 13 deletions(-) delete mode 100644 library/src/scala/runtime/quoted/Show.scala diff --git a/compiler/src/dotty/tools/dotc/quoted/Runners.scala b/compiler/src/dotty/tools/dotc/quoted/Runners.scala index 6b63acd7c076..c38a13c80c1c 100644 --- a/compiler/src/dotty/tools/dotc/quoted/Runners.scala +++ b/compiler/src/dotty/tools/dotc/quoted/Runners.scala @@ -5,6 +5,12 @@ import scala.runtime.quoted._ /** Default runners for quoted expressions */ object Runners { - implicit def runner[T]: Runner[T] = (expr: Expr[T]) => new QuoteDriver().run(expr) - implicit def show[T]: Show[T] = (expr: Expr[T]) => new QuoteDriver().show(expr) + implicit def runner[T]: Runner[T] = new Runner[T] { + + def run(expr: Expr[T]): T = + new QuoteDriver().run(expr) + + def show(expr: Expr[T]): String = + new QuoteDriver().show(expr) + } } diff --git a/library/src/scala/quoted/Expr.scala b/library/src/scala/quoted/Expr.scala index 07dd03b94b01..07f2e6e91751 100644 --- a/library/src/scala/quoted/Expr.scala +++ b/library/src/scala/quoted/Expr.scala @@ -1,11 +1,11 @@ package scala.quoted -import scala.runtime.quoted.{Runner, Show} +import scala.runtime.quoted.Runner abstract class Expr[T] extends Quoted { final def unary_~ : T = throw new Error("~ should have been compiled away") final def run(implicit runner: Runner[T]): T = runner.run(this) - final def show(implicit runner: Show[T]): String = runner.run(this) + final def show(implicit runner: Runner[T]): String = runner.show(this) } object Expr { diff --git a/library/src/scala/runtime/quoted/Runner.scala b/library/src/scala/runtime/quoted/Runner.scala index 4f39c86adb78..178c80c9d255 100644 --- a/library/src/scala/runtime/quoted/Runner.scala +++ b/library/src/scala/runtime/quoted/Runner.scala @@ -6,4 +6,5 @@ import scala.quoted.Expr @implicitNotFound("Could not find implicit Runner. Default runner can must be imported with `import dotty.tools.dotc.quoted.Runners._`") trait Runner[T] { def run(expr: Expr[T]): T + def show(expr: Expr[T]): String } diff --git a/library/src/scala/runtime/quoted/Show.scala b/library/src/scala/runtime/quoted/Show.scala deleted file mode 100644 index ec055a656277..000000000000 --- a/library/src/scala/runtime/quoted/Show.scala +++ /dev/null @@ -1,9 +0,0 @@ -package scala.runtime.quoted - -import scala.annotation.implicitNotFound -import scala.quoted.Expr - -@implicitNotFound("Could not find implicit Show. Default runner can must be imported with `import dotty.tools.dotc.quoted.Runners._`") -trait Show[T] { - def run(expr: Expr[T]): String -} From efff78b5e68347b20f82ca75516fcffdcca6df8c Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Sat, 23 Dec 2017 10:45:53 +0100 Subject: [PATCH 07/14] Avoid running the compiler for ConstantExpr --- compiler/src/dotty/tools/dotc/quoted/Runners.scala | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/quoted/Runners.scala b/compiler/src/dotty/tools/dotc/quoted/Runners.scala index c38a13c80c1c..2e7f4e5aeeb6 100644 --- a/compiler/src/dotty/tools/dotc/quoted/Runners.scala +++ b/compiler/src/dotty/tools/dotc/quoted/Runners.scala @@ -1,16 +1,22 @@ package dotty.tools.dotc.quoted import scala.quoted.Expr +import scala.quoted.Liftable.ConstantExpr import scala.runtime.quoted._ /** Default runners for quoted expressions */ object Runners { + implicit def runner[T]: Runner[T] = new Runner[T] { - def run(expr: Expr[T]): T = - new QuoteDriver().run(expr) + def run(expr: Expr[T]): T = expr match { + case expr: ConstantExpr[T] => expr.value + case _ => new QuoteDriver().run(expr) + } - def show(expr: Expr[T]): String = - new QuoteDriver().show(expr) + def show(expr: Expr[T]): String = expr match { + case expr: ConstantExpr[T] => expr.value.toString + case _ => new QuoteDriver().show(expr) + } } } From bb5b114ef2823f8a2bba494fdd2eb019036b12c8 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Sat, 23 Dec 2017 20:32:17 +0100 Subject: [PATCH 08/14] Fix typo --- library/src/scala/runtime/quoted/Runner.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/src/scala/runtime/quoted/Runner.scala b/library/src/scala/runtime/quoted/Runner.scala index 178c80c9d255..e78517bb4f28 100644 --- a/library/src/scala/runtime/quoted/Runner.scala +++ b/library/src/scala/runtime/quoted/Runner.scala @@ -3,7 +3,7 @@ package scala.runtime.quoted import scala.annotation.implicitNotFound import scala.quoted.Expr -@implicitNotFound("Could not find implicit Runner. Default runner can must be imported with `import dotty.tools.dotc.quoted.Runners._`") +@implicitNotFound("Could not find implicit Runner. Default runner can be imported with `import dotty.tools.dotc.quoted.Runners._`") trait Runner[T] { def run(expr: Expr[T]): T def show(expr: Expr[T]): String From 1cfc13654d253e0ae61641ce97c268aab9b65645 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Mon, 8 Jan 2018 20:53:55 +0100 Subject: [PATCH 09/14] Update quote.Expr API in docs --- docs/docs/reference/symmetric-meta-programming.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/docs/reference/symmetric-meta-programming.md b/docs/docs/reference/symmetric-meta-programming.md index 004315516cfd..c198a08c1fa5 100644 --- a/docs/docs/reference/symmetric-meta-programming.md +++ b/docs/docs/reference/symmetric-meta-programming.md @@ -400,7 +400,8 @@ to `T` but only `~` is subject to the PCP, whereas `run` is just a normal method abstract class Expr[T] { def unary_~: T - def run: T // run staged code + def run(implicit runner: Runner[T]): T // run staged code + def show(implicit runner: Runner[T]): String // show staged code } ### Limitations to Splicing From 98c72284aded6c3081c9783212f566dd2a49c579 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Tue, 9 Jan 2018 13:14:46 +0100 Subject: [PATCH 10/14] Fix format of ConstantExpr.show --- .../dotty/tools/dotc/quoted/QuoteDriver.scala | 4 +- .../src/dotty/tools/dotc/quoted/Runners.scala | 10 ++++- .../dotty/tools/dotc/CompilationTests.scala | 1 + tests/run-special/quote-run-constants.check | 28 +++++++++++++ tests/run-special/quote-run-constants.scala | 42 +++++++++++++++++++ 5 files changed, 81 insertions(+), 4 deletions(-) create mode 100644 tests/run-special/quote-run-constants.check create mode 100644 tests/run-special/quote-run-constants.scala diff --git a/compiler/src/dotty/tools/dotc/quoted/QuoteDriver.scala b/compiler/src/dotty/tools/dotc/quoted/QuoteDriver.scala index d19899331eb7..f79302494793 100644 --- a/compiler/src/dotty/tools/dotc/quoted/QuoteDriver.scala +++ b/compiler/src/dotty/tools/dotc/quoted/QuoteDriver.scala @@ -1,14 +1,12 @@ package dotty.tools.dotc.quoted import dotty.tools.dotc.Driver -import dotty.tools.dotc.core.Contexts.Context +import dotty.tools.dotc.core.Contexts.{Context, FreshContext} import dotty.tools.dotc.core.StdNames._ import dotty.tools.io.VirtualDirectory - import dotty.tools.repl.AbstractFileClassLoader import scala.quoted.Expr - import java.io.ByteArrayOutputStream import java.io.PrintStream import java.nio.charset.StandardCharsets diff --git a/compiler/src/dotty/tools/dotc/quoted/Runners.scala b/compiler/src/dotty/tools/dotc/quoted/Runners.scala index 2e7f4e5aeeb6..d5f9b18f7dc5 100644 --- a/compiler/src/dotty/tools/dotc/quoted/Runners.scala +++ b/compiler/src/dotty/tools/dotc/quoted/Runners.scala @@ -1,5 +1,9 @@ package dotty.tools.dotc.quoted +import dotty.tools.dotc.ast.Trees.Literal +import dotty.tools.dotc.core.Constants.Constant +import dotty.tools.dotc.printing.RefinedPrinter + import scala.quoted.Expr import scala.quoted.Liftable.ConstantExpr import scala.runtime.quoted._ @@ -15,7 +19,11 @@ object Runners { } def show(expr: Expr[T]): String = expr match { - case expr: ConstantExpr[T] => expr.value.toString + case expr: ConstantExpr[T] => + val ctx = new QuoteDriver().initCtx + ctx.settings.color.update("never")(ctx) + val printer = new RefinedPrinter(ctx) + printer.toText(Literal(Constant(expr.value))).mkString(Int.MaxValue, false) case _ => new QuoteDriver().show(expr) } } diff --git a/compiler/test/dotty/tools/dotc/CompilationTests.scala b/compiler/test/dotty/tools/dotc/CompilationTests.scala index 390398732a12..87c3e13d2c39 100644 --- a/compiler/test/dotty/tools/dotc/CompilationTests.scala +++ b/compiler/test/dotty/tools/dotc/CompilationTests.scala @@ -198,6 +198,7 @@ class CompilationTests extends ParallelTesting { implicit val testGroup: TestGroup = TestGroup("runAll") compileFilesInDir("../tests/run", defaultOptions) + compileFilesInDir("../tests/run-no-optimise", defaultOptions) + + compileFile("../tests/run-special/quote-run-constants.scala", defaultRunWithCompilerOptions) + compileFile("../tests/run-special/quote-run.scala", defaultRunWithCompilerOptions) + compileFile("../tests/run-special/quote-run-2.scala", defaultRunWithCompilerOptions) + compileFile("../tests/run-special/quote-run-staged-interpreter.scala", defaultRunWithCompilerOptions) diff --git a/tests/run-special/quote-run-constants.check b/tests/run-special/quote-run-constants.check new file mode 100644 index 000000000000..19284ac517a1 --- /dev/null +++ b/tests/run-special/quote-run-constants.check @@ -0,0 +1,28 @@ +true +a + + +" +' +\ +1 +2 +3 +4.0 +5.0 +xyz +====== +true +'a' +'\n' +'\"' +'\'' +'\\' +1 +2 +3L +4.0 +5.0 +"xyz" +"\n\\\"\'" +"abc\n xyz" diff --git a/tests/run-special/quote-run-constants.scala b/tests/run-special/quote-run-constants.scala new file mode 100644 index 000000000000..af9449767a4f --- /dev/null +++ b/tests/run-special/quote-run-constants.scala @@ -0,0 +1,42 @@ + +import dotty.tools.dotc.quoted.Runners._ + +import scala.quoted._ + +object Test { + def main(args: Array[String]): Unit = { + def run[T](expr: Expr[T]): Unit = println(expr.run) + def show[T](expr: Expr[T]): Unit = println(expr.show) + + run(true) + run('a') + run('\n') + run('"') + run('\'') + run('\\') + run(1) + run(2) + run(3L) + run(4.0f) + run(5.0d) + run("xyz") + + println("======") + + show(true) + show('a') + show('\n') + show('"') + show('\'') + show('\\') + show(1) + show(2) + show(3L) + show(4.0f) + show(5.0d) + show("xyz") + show("\n\\\"'") + show("""abc + xyz""") + } +} From 71d8c789fa64e9c80d71552de55160fa5f234135 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Fri, 12 Jan 2018 07:36:01 +0100 Subject: [PATCH 11/14] Move ExprFrontend and ExprRun into ExprCompiler --- .../tools/dotc/quoted/ExprCompiler.scala | 62 ++++++++++++++++++- .../tools/dotc/quoted/ExprFrontend.scala | 55 ---------------- .../src/dotty/tools/dotc/quoted/ExprRun.scala | 13 ---- 3 files changed, 61 insertions(+), 69 deletions(-) delete mode 100644 compiler/src/dotty/tools/dotc/quoted/ExprFrontend.scala delete mode 100644 compiler/src/dotty/tools/dotc/quoted/ExprRun.scala diff --git a/compiler/src/dotty/tools/dotc/quoted/ExprCompiler.scala b/compiler/src/dotty/tools/dotc/quoted/ExprCompiler.scala index 03372afc39e3..98bf4b1d4129 100644 --- a/compiler/src/dotty/tools/dotc/quoted/ExprCompiler.scala +++ b/compiler/src/dotty/tools/dotc/quoted/ExprCompiler.scala @@ -2,16 +2,30 @@ package dotty.tools.dotc package quoted import dotty.tools.backend.jvm.GenBCode +import dotty.tools.dotc.ast.tpd + import dotty.tools.dotc.core.Contexts.Context +import dotty.tools.dotc.core.Flags.{EmptyFlags, Method} import dotty.tools.dotc.core.{Mode, Phases} import dotty.tools.dotc.core.Phases.Phase +import dotty.tools.dotc.core.Scopes.{EmptyScope, newScope} +import dotty.tools.dotc.core.StdNames.nme +import dotty.tools.dotc.core.Symbols.defn +import dotty.tools.dotc.core.Types.ExprType +import dotty.tools.dotc.core.quoted.PickledQuotes import dotty.tools.dotc.transform.Pickler -import dotty.tools.io.VirtualDirectory +import dotty.tools.dotc.typer.FrontEnd +import dotty.tools.dotc.util.Positions.Position +import dotty.tools.dotc.util.SourceFile +import dotty.tools.io.{Path, PlainFile, VirtualDirectory} + +import scala.quoted.Expr /** Compiler that takes the contents of a quoted expression `expr` and produces * a class file with `class ' { def apply: Object = expr }`. */ class ExprCompiler(directory: VirtualDirectory) extends Compiler { + import tpd._ /** A GenBCode phase that outputs to a virtual directory */ private class ExprGenBCode extends GenBCode { @@ -33,4 +47,50 @@ class ExprCompiler(directory: VirtualDirectory) extends Compiler { reset() new ExprRun(this, ctx.addMode(Mode.ReadPositions)) } + + /** Frontend that receives scala.quoted.Expr as input */ + class ExprFrontend(putInClass: Boolean) extends FrontEnd { + import tpd._ + + override def isTyper = false + + override def runOn(units: List[CompilationUnit])(implicit ctx: Context): List[CompilationUnit] = { + units.map { + case exprUnit: ExprCompilationUnit => + val tree = + if (putInClass) inClass(exprUnit.expr) + else PickledQuotes.quotedToTree(exprUnit.expr) + val source = new SourceFile("", Seq()) + CompilationUnit.mkCompilationUnit(source, tree, forceTrees = true) + } + } + + /** Places the contents of expr in a compilable tree for a class + * with the following format. + * `package __root__ { class ' { def apply: Any = } }` + */ + private def inClass(expr: Expr[_])(implicit ctx: Context): Tree = { + val pos = Position(0) + val assocFile = new PlainFile(Path("")) + + val cls = ctx.newCompleteClassSymbol(defn.RootClass, nme.QUOTE.toTypeName, EmptyFlags, + defn.ObjectType :: Nil, newScope, coord = pos, assocFile = assocFile).entered.asClass + cls.enter(ctx.newDefaultConstructor(cls), EmptyScope) + val meth = ctx.newSymbol(cls, nme.apply, Method, ExprType(defn.AnyType), coord = pos).entered + + val quoted = PickledQuotes.quotedToTree(expr)(ctx.withOwner(meth)) + + val run = DefDef(meth, quoted) + val classTree = ClassDef(cls, DefDef(cls.primaryConstructor.asTerm), run :: Nil) + PackageDef(ref(defn.RootPackage).asInstanceOf[Ident], classTree :: Nil).withPos(pos) + } + } + + class ExprRun(comp: Compiler, ictx: Context) extends Run(comp, ictx) { + def compileExpr(expr: Expr[_]): Unit = { + val units = new ExprCompilationUnit(expr) :: Nil + compileUnits(units) + } + } + } diff --git a/compiler/src/dotty/tools/dotc/quoted/ExprFrontend.scala b/compiler/src/dotty/tools/dotc/quoted/ExprFrontend.scala deleted file mode 100644 index e068a739bcfc..000000000000 --- a/compiler/src/dotty/tools/dotc/quoted/ExprFrontend.scala +++ /dev/null @@ -1,55 +0,0 @@ -package dotty.tools.dotc.quoted - -import dotty.tools.dotc.CompilationUnit -import dotty.tools.dotc.ast.tpd -import dotty.tools.dotc.core.Contexts.Context -import dotty.tools.dotc.core.Flags._ -import dotty.tools.dotc.core.Scopes._ -import dotty.tools.dotc.core.StdNames._ -import dotty.tools.dotc.core.Symbols._ -import dotty.tools.dotc.core.Types._ -import dotty.tools.dotc.core.quoted.PickledQuotes -import dotty.tools.dotc.typer.FrontEnd -import dotty.tools.dotc.util.Positions._ -import dotty.tools.dotc.util.SourceFile -import dotty.tools.io._ - -import scala.quoted.Expr - -/** Frontend that receives scala.quoted.Expr as input */ -class ExprFrontend(putInClass: Boolean) extends FrontEnd { - import tpd._ - - override def isTyper = false - - override def runOn(units: List[CompilationUnit])(implicit ctx: Context): List[CompilationUnit] = { - units.map { - case exprUnit: ExprCompilationUnit => - val tree = - if (putInClass) inClass(exprUnit.expr) - else PickledQuotes.quotedToTree(exprUnit.expr) - val source = new SourceFile("", Seq()) - CompilationUnit.mkCompilationUnit(source, tree, forceTrees = true) - } - } - - /** Places the contents of expr in a compilable tree for a class - * with the following format. - * `package __root__ { class ' { def apply: Any = } }` - */ - private def inClass(expr: Expr[_])(implicit ctx: Context): Tree = { - val pos = Position(0) - val assocFile = new PlainFile(Path("")) - - val cls = ctx.newCompleteClassSymbol(defn.RootClass, nme.QUOTE.toTypeName, EmptyFlags, - defn.ObjectType :: Nil, newScope, coord = pos, assocFile = assocFile).entered.asClass - cls.enter(ctx.newDefaultConstructor(cls), EmptyScope) - val meth = ctx.newSymbol(cls, nme.apply, Method, ExprType(defn.AnyType), coord = pos).entered - - val quoted = PickledQuotes.quotedToTree(expr)(ctx.withOwner(meth)) - - val run = DefDef(meth, quoted) - val classTree = ClassDef(cls, DefDef(cls.primaryConstructor.asTerm), run :: Nil) - PackageDef(ref(defn.RootPackage).asInstanceOf[Ident], classTree :: Nil).withPos(pos) - } -} diff --git a/compiler/src/dotty/tools/dotc/quoted/ExprRun.scala b/compiler/src/dotty/tools/dotc/quoted/ExprRun.scala deleted file mode 100644 index 9230ce877382..000000000000 --- a/compiler/src/dotty/tools/dotc/quoted/ExprRun.scala +++ /dev/null @@ -1,13 +0,0 @@ -package dotty.tools.dotc -package quoted - -import dotty.tools.dotc.core.Contexts._ - -import scala.quoted._ - -class ExprRun(comp: Compiler, ictx: Context) extends Run(comp, ictx) { - def compileExpr(expr: Expr[_]): Unit = { - val units = new ExprCompilationUnit(expr) :: Nil - compileUnits(units) - } -} From d8d38becf9ac4337bf001c70937416a80b3754f5 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Fri, 12 Jan 2018 07:43:48 +0100 Subject: [PATCH 12/14] Do not overide phases directly --- .../dotty/tools/dotc/quoted/ExprCompiler.scala | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/quoted/ExprCompiler.scala b/compiler/src/dotty/tools/dotc/quoted/ExprCompiler.scala index 98bf4b1d4129..24de4011e541 100644 --- a/compiler/src/dotty/tools/dotc/quoted/ExprCompiler.scala +++ b/compiler/src/dotty/tools/dotc/quoted/ExprCompiler.scala @@ -13,7 +13,7 @@ import dotty.tools.dotc.core.StdNames.nme import dotty.tools.dotc.core.Symbols.defn import dotty.tools.dotc.core.Types.ExprType import dotty.tools.dotc.core.quoted.PickledQuotes -import dotty.tools.dotc.transform.Pickler +import dotty.tools.dotc.transform.ReifyQuotes import dotty.tools.dotc.typer.FrontEnd import dotty.tools.dotc.util.Positions.Position import dotty.tools.dotc.util.SourceFile @@ -33,15 +33,14 @@ class ExprCompiler(directory: VirtualDirectory) extends Compiler { override def outputDir(implicit ctx: Context) = directory } - override def phases: List[List[Phase]] = { - val backendPhases = super.phases.dropWhile { - case List(_: Pickler) => false - case _ => true - }.tail + override protected def frontendPhases: List[List[Phase]] = + List(List(new ExprFrontend(putInClass = true))) - List(new ExprFrontend(putInClass = true)) :: - Phases.replace(classOf[GenBCode], _ => new ExprGenBCode :: Nil, backendPhases) - } + override protected def picklerPhases: List[List[Phase]] = + List(List(new ReifyQuotes)) + + override protected def backendPhases: List[List[Phase]] = + List(List(new ExprGenBCode)) override def newRun(implicit ctx: Context): ExprRun = { reset() From 88b7f46570e4abffeee7446d73a735ad7c612ca8 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Fri, 12 Jan 2018 07:48:23 +0100 Subject: [PATCH 13/14] Use isQuote --- compiler/src/dotty/tools/dotc/transform/PostTyper.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/src/dotty/tools/dotc/transform/PostTyper.scala b/compiler/src/dotty/tools/dotc/transform/PostTyper.scala index 3485f119c7c1..1bdf66b50158 100644 --- a/compiler/src/dotty/tools/dotc/transform/PostTyper.scala +++ b/compiler/src/dotty/tools/dotc/transform/PostTyper.scala @@ -204,7 +204,7 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisPhase Checking.checkInstantiable(tree.tpe, nu.pos) withNoCheckNews(nu :: Nil)(super.transform(tree)) case meth => - if (meth.symbol.name.eq(nme.QUOTE) && meth.symbol.owner.eq(defn.OpsPackageClass)) + if (meth.symbol.isQuote) ctx.compilationUnit.containsQuotesOrSplices = true super.transform(tree) } From 610ad9c853dc42296835b2f0fc4ebed5b6ee7870 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Mon, 8 Jan 2018 17:32:11 +0100 Subject: [PATCH 14/14] Fix compiling quote in file with no splices --- .../src/dotty/tools/dotc/CompilationUnit.scala | 15 ++++++++++++--- .../dotty/tools/dotc/transform/PostTyper.scala | 5 ++--- .../test/dotty/tools/dotc/FromTastyTests.scala | 5 ----- 3 files changed, 14 insertions(+), 11 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/CompilationUnit.scala b/compiler/src/dotty/tools/dotc/CompilationUnit.scala index f93320a7ff90..6adb73d083af 100644 --- a/compiler/src/dotty/tools/dotc/CompilationUnit.scala +++ b/compiler/src/dotty/tools/dotc/CompilationUnit.scala @@ -8,6 +8,7 @@ import dotty.tools.dotc.ast.tpd.{ Tree, TreeTraverser } import dotty.tools.dotc.core.Contexts.Context import dotty.tools.dotc.core.SymDenotations.ClassDenotation import dotty.tools.dotc.core.Symbols._ +import dotty.tools.dotc.transform.SymUtils._ class CompilationUnit(val source: SourceFile) { @@ -39,13 +40,21 @@ object CompilationUnit { assert(!unpickled.isEmpty, unpickled) val unit1 = new CompilationUnit(source) unit1.tpdTree = unpickled - if (forceTrees) + if (forceTrees) { + val force = new Force force.traverse(unit1.tpdTree) + unit1.containsQuotesOrSplices = force.containsQuotes + } unit1 } /** Force the tree to be loaded */ - private object force extends TreeTraverser { - def traverse(tree: Tree)(implicit ctx: Context): Unit = traverseChildren(tree) + private class Force extends TreeTraverser { + var containsQuotes = false + def traverse(tree: Tree)(implicit ctx: Context): Unit = { + if (tree.symbol.isQuote) + containsQuotes = true + traverseChildren(tree) + } } } diff --git a/compiler/src/dotty/tools/dotc/transform/PostTyper.scala b/compiler/src/dotty/tools/dotc/transform/PostTyper.scala index 1bdf66b50158..3807a25b09c2 100644 --- a/compiler/src/dotty/tools/dotc/transform/PostTyper.scala +++ b/compiler/src/dotty/tools/dotc/transform/PostTyper.scala @@ -180,6 +180,7 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisPhase override def transform(tree: Tree)(implicit ctx: Context): Tree = try tree match { case tree: Ident if !tree.isType => + handleMeta(tree.symbol) tree.tpe match { case tpe: ThisType => This(tpe.cls).withPos(tree.pos) case _ => tree @@ -203,9 +204,7 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisPhase // might be a type constructor. Checking.checkInstantiable(tree.tpe, nu.pos) withNoCheckNews(nu :: Nil)(super.transform(tree)) - case meth => - if (meth.symbol.isQuote) - ctx.compilationUnit.containsQuotesOrSplices = true + case _ => super.transform(tree) } case tree: TypeApply => diff --git a/compiler/test/dotty/tools/dotc/FromTastyTests.scala b/compiler/test/dotty/tools/dotc/FromTastyTests.scala index 23131e3a06d4..547b418b9ce1 100644 --- a/compiler/test/dotty/tools/dotc/FromTastyTests.scala +++ b/compiler/test/dotty/tools/dotc/FromTastyTests.scala @@ -37,10 +37,6 @@ class FromTastyTests extends ParallelTesting { "i3000.scala", "i536.scala", "i974.scala", - "quote-liftable.scala", - "quote-0.scala", - "quote-1.scala", - "quote-stagedInterpreter.scala", "t1203a.scala", "t2260.scala", "t3612.scala", // Test never finishes @@ -49,7 +45,6 @@ class FromTastyTests extends ParallelTesting { "t8023.scala", "tcpoly_ticket2096.scala", "t247.scala", - "quote-no-splices.scala", ) ) step1.checkCompile() // Compile all files to generate the class files with tasty