From f8cf9fd689a65f789bb4f3dc65894511b505b130 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Thu, 26 Sep 2019 19:08:44 +0200 Subject: [PATCH 01/31] Suspend callers of macros compiled in the same run Allows macros to be defined and called in the same project. The idea is to suspend the callers of a macro (and their upstream dependencies), compile the sourcefile(s) containing the macro(s), and then compile the suspended files in a second run. The scheme detects and flags as errors calls to macros in the same file and mutual dependcies between different files which would not work with suspensions. It needs to be complements by another mechanism that detects that class files loaded by a macro class loader do not have assiciated source files that are currently compiled. --- .../dotty/tools/dotc/CompilationUnit.scala | 10 ++++++ compiler/src/dotty/tools/dotc/Driver.scala | 21 ++++++++---- compiler/src/dotty/tools/dotc/Run.scala | 16 +++++---- .../src/dotty/tools/dotc/typer/FrontEnd.scala | 34 ++++++++++++++----- .../src/dotty/tools/dotc/typer/Inliner.scala | 4 +++ .../src/dotty/tools/dotc/typer/Namer.scala | 20 ++++++++--- tests/neg/macro-cycle1.scala | 8 +++++ tests/run/macros-in-same-project1.check | 3 ++ tests/run/macros-in-same-project1/Foo.scala | 9 +++++ tests/run/macros-in-same-project1/Test.scala | 3 ++ 10 files changed, 102 insertions(+), 26 deletions(-) create mode 100644 tests/neg/macro-cycle1.scala create mode 100644 tests/run/macros-in-same-project1.check create mode 100644 tests/run/macros-in-same-project1/Foo.scala create mode 100644 tests/run/macros-in-same-project1/Test.scala diff --git a/compiler/src/dotty/tools/dotc/CompilationUnit.scala b/compiler/src/dotty/tools/dotc/CompilationUnit.scala index bce33813fa70..c4efcc7f0e68 100644 --- a/compiler/src/dotty/tools/dotc/CompilationUnit.scala +++ b/compiler/src/dotty/tools/dotc/CompilationUnit.scala @@ -31,10 +31,20 @@ class CompilationUnit protected (val source: SourceFile) { /** A structure containing a temporary map for generating inline accessors */ val inlineAccessors: InlineAccessors = new InlineAccessors + + var suspended: Boolean = false + + def suspend()(given ctx: Context): Nothing = + if !suspended then + suspended = true + ctx.run.suspendedUnits += this + throw CompilationUnit.SuspendException() } object CompilationUnit { + class SuspendException extends Exception + /** Make a compilation unit for top class `clsd` with the contents of the `unpickled` tree */ def apply(clsd: ClassDenotation, unpickled: Tree, forceTrees: Boolean)(implicit ctx: Context): CompilationUnit = apply(new SourceFile(clsd.symbol.associatedFile, Array.empty[Char]), unpickled, forceTrees) diff --git a/compiler/src/dotty/tools/dotc/Driver.scala b/compiler/src/dotty/tools/dotc/Driver.scala index 81f79ecdb094..790383060267 100644 --- a/compiler/src/dotty/tools/dotc/Driver.scala +++ b/compiler/src/dotty/tools/dotc/Driver.scala @@ -30,23 +30,30 @@ class Driver { protected def doCompile(compiler: Compiler, fileNames: List[String])(implicit ctx: Context): Reporter = if (fileNames.nonEmpty) - try { + try val run = compiler.newRun run.compile(fileNames) - run.printSummary() - } - catch { + + def finish(run: Run): Unit = + run.printSummary() + if !ctx.reporter.errorsReported && run.suspendedUnits.nonEmpty then + val run1 = compiler.newRun + for unit <- run.suspendedUnits do unit.suspended = false + run1.compileUnits(run.suspendedUnits.toList) + finish(run1) + + finish(run) + catch case ex: FatalError => ctx.error(ex.getMessage) // signals that we should fail compilation. - ctx.reporter case ex: TypeError => println(s"${ex.toMessage} while compiling ${fileNames.mkString(", ")}") throw ex case ex: Throwable => println(s"$ex while compiling ${fileNames.mkString(", ")}") throw ex - } - else ctx.reporter + ctx.reporter + end doCompile protected def initCtx: Context = (new ContextBase).initialCtx diff --git a/compiler/src/dotty/tools/dotc/Run.scala b/compiler/src/dotty/tools/dotc/Run.scala index 91be70512d3c..954daf240e65 100644 --- a/compiler/src/dotty/tools/dotc/Run.scala +++ b/compiler/src/dotty/tools/dotc/Run.scala @@ -78,17 +78,20 @@ class Run(comp: Compiler, ictx: Context) extends ImplicitRunInfo with Constraint */ def units: List[CompilationUnit] = myUnits + var suspendedUnits: mutable.ListBuffer[CompilationUnit] = mutable.ListBuffer() + private def units_=(us: List[CompilationUnit]): Unit = myUnits = us - /** The files currently being compiled, this may return different results over time. - * These files do not have to be source files since it's possible to compile - * from TASTY. - */ + /** The files currently being compiled (active or suspended). + * This may return different results over time. + * These files do not have to be source files since it's possible to compile + * from TASTY. + */ def files: Set[AbstractFile] = { if (myUnits ne myUnitsCached) { myUnitsCached = myUnits - myFiles = myUnits.map(_.source.file).toSet + myFiles = (myUnits ++ suspendedUnits).map(_.source.file).toSet } myFiles } @@ -247,11 +250,10 @@ class Run(comp: Compiler, ictx: Context) extends ImplicitRunInfo with Constraint } /** Print summary; return # of errors encountered */ - def printSummary(): Reporter = { + def printSummary(): Unit = { printMaxConstraint() val r = ctx.reporter r.printSummary - r } override def reset(): Unit = { diff --git a/compiler/src/dotty/tools/dotc/typer/FrontEnd.scala b/compiler/src/dotty/tools/dotc/typer/FrontEnd.scala index f466a6956e62..8af839c8ba2e 100644 --- a/compiler/src/dotty/tools/dotc/typer/FrontEnd.scala +++ b/compiler/src/dotty/tools/dotc/typer/FrontEnd.scala @@ -6,6 +6,7 @@ import core._ import Phases._ import Contexts._ import Symbols._ +import Decorators._ import dotty.tools.dotc.parsing.JavaParsers.JavaParser import parsing.Parsers.Parser import config.Config @@ -71,11 +72,15 @@ class FrontEnd extends Phase { } def typeCheck(implicit ctx: Context): Unit = monitor("typechecking") { - val unit = ctx.compilationUnit - unit.tpdTree = ctx.typer.typedExpr(unit.untpdTree) - typr.println("typed: " + unit.source) - record("retained untyped trees", unit.untpdTree.treeSize) - record("retained typed trees after typer", unit.tpdTree.treeSize) + try + val unit = ctx.compilationUnit + if !unit.suspended then + unit.tpdTree = ctx.typer.typedExpr(unit.untpdTree) + typr.println("typed: " + unit.source) + record("retained untyped trees", unit.untpdTree.treeSize) + record("retained typed trees after typer", unit.tpdTree.treeSize) + catch + case ex: CompilationUnit.SuspendException => } private def firstTopLevelDef(trees: List[tpd.Tree])(implicit ctx: Context): Symbol = trees match { @@ -86,14 +91,14 @@ class FrontEnd extends Phase { } protected def discardAfterTyper(unit: CompilationUnit)(implicit ctx: Context): Boolean = - unit.isJava + unit.isJava || unit.suspended override def runOn(units: List[CompilationUnit])(implicit ctx: Context): List[CompilationUnit] = { val unitContexts = for (unit <- units) yield { ctx.inform(s"compiling ${unit.source}") ctx.fresh.setCompilationUnit(unit) } - unitContexts foreach (parse(_)) + unitContexts.foreach(parse(_)) record("parsedTrees", ast.Trees.ntrees) remaining = unitContexts while (remaining.nonEmpty) { @@ -108,7 +113,20 @@ class FrontEnd extends Phase { unitContexts.foreach(typeCheck(_)) record("total trees after typer", ast.Trees.ntrees) - unitContexts.map(_.compilationUnit).filterNot(discardAfterTyper) + val newUnits = unitContexts.map(_.compilationUnit).filterNot(discardAfterTyper) + val suspendedUnits = ctx.run.suspendedUnits + if newUnits.isEmpty && suspendedUnits.nonEmpty && !ctx.reporter.errorsReported then + val where = + if suspendedUnits.size == 1 then i"in ${suspendedUnits.head}." + else i"""among + | + | ${suspendedUnits.toList}%, % + |""" + ctx.error(em"""Cyclic macro dependencies $where + |Compilation stopped since no further progress can be made. + | + |To fix this, place macros in one set of files and their callers in another.""") + newUnits } def run(implicit ctx: Context): Unit = unsupported("run") diff --git a/compiler/src/dotty/tools/dotc/typer/Inliner.scala b/compiler/src/dotty/tools/dotc/typer/Inliner.scala index 70f6718de2da..2f31a412d041 100644 --- a/compiler/src/dotty/tools/dotc/typer/Inliner.scala +++ b/compiler/src/dotty/tools/dotc/typer/Inliner.scala @@ -70,6 +70,10 @@ object Inliner { */ def inlineCall(tree: Tree)(implicit ctx: Context): Tree = { if (tree.symbol == defn.CompiletimeTesting_typeChecks) return Intrinsics.typeChecks(tree) + if tree.symbol.is(Macro) && tree.symbol.isDefinedInCurrentRun then + if ctx.compilationUnit.source.file == tree.symbol.associatedFile then + ctx.error("Cannot call macro defined in the same source file", tree.sourcePos) + ctx.compilationUnit.suspend() /** Set the position of all trees logically contained in the expansion of * inlined call `call` to the position of `call`. This transform is necessary diff --git a/compiler/src/dotty/tools/dotc/typer/Namer.scala b/compiler/src/dotty/tools/dotc/typer/Namer.scala index e185ce523e5f..8dd2a799aa33 100644 --- a/compiler/src/dotty/tools/dotc/typer/Namer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Namer.scala @@ -809,10 +809,15 @@ class Namer { typer: Typer => assert(ctx.mode.is(Mode.Interactive), s"completing $denot in wrong run ${ctx.runId}, was created in ${creationContext.runId}") denot.info = UnspecifiedErrorType } - else { - completeInCreationContext(denot) - if (denot.isCompleted) registerIfChild(denot) - } + else + try + completeInCreationContext(denot) + if (denot.isCompleted) registerIfChild(denot) + catch + case ex: CompilationUnit.SuspendException => + val completer = SuspendCompleter() + denot.info = completer + completer.complete(denot) } protected def addAnnotations(sym: Symbol): Unit = original match { @@ -1199,6 +1204,13 @@ class Namer { typer: Typer => } } + class SuspendCompleter extends LazyType, SymbolLoaders.SecondCompleter { + + final override def complete(denot: SymDenotation)(implicit ctx: Context): Unit = + denot.resetFlag(Touched) // allow one more completion + ctx.compilationUnit.suspend() + } + /** Typecheck `tree` during completion using `typed`, and remember result in TypedAhead map */ def typedAhead(tree: Tree, typed: untpd.Tree => tpd.Tree)(implicit ctx: Context): tpd.Tree = { val xtree = expanded(tree) diff --git a/tests/neg/macro-cycle1.scala b/tests/neg/macro-cycle1.scala new file mode 100644 index 000000000000..b772005e35f3 --- /dev/null +++ b/tests/neg/macro-cycle1.scala @@ -0,0 +1,8 @@ +import scala.quoted.{Expr, QuoteContext} +object Test { + def fooImpl(given QuoteContext): Expr[Unit] = '{println("hi")} + + inline def foo: Unit = ${fooImpl} + + foo +} diff --git a/tests/run/macros-in-same-project1.check b/tests/run/macros-in-same-project1.check new file mode 100644 index 000000000000..81c22b90899a --- /dev/null +++ b/tests/run/macros-in-same-project1.check @@ -0,0 +1,3 @@ +object Test extends App { + Foo.myMacro() +} \ No newline at end of file diff --git a/tests/run/macros-in-same-project1/Foo.scala b/tests/run/macros-in-same-project1/Foo.scala new file mode 100644 index 000000000000..524ccde401f8 --- /dev/null +++ b/tests/run/macros-in-same-project1/Foo.scala @@ -0,0 +1,9 @@ +import scala.quoted._ + +object Foo { + + inline def myMacro(): Unit = ${ aMacroImplementation } + + def aMacroImplementation(given QuoteContext): Expr[Unit] = '{ println("Hello") } + +} \ No newline at end of file diff --git a/tests/run/macros-in-same-project1/Test.scala b/tests/run/macros-in-same-project1/Test.scala new file mode 100644 index 000000000000..81c22b90899a --- /dev/null +++ b/tests/run/macros-in-same-project1/Test.scala @@ -0,0 +1,3 @@ +object Test extends App { + Foo.myMacro() +} \ No newline at end of file From 068af6ae120619491458cae58dce965c480f78bb Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Fri, 27 Sep 2019 12:27:30 +0200 Subject: [PATCH 02/31] Make macroDependencies compilation-order independent Using a Set risks giving different results in different runs. --- compiler/src/dotty/tools/dotc/typer/Inliner.scala | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/typer/Inliner.scala b/compiler/src/dotty/tools/dotc/typer/Inliner.scala index 2f31a412d041..05ee07b86973 100644 --- a/compiler/src/dotty/tools/dotc/typer/Inliner.scala +++ b/compiler/src/dotty/tools/dotc/typer/Inliner.scala @@ -1276,13 +1276,13 @@ class Inliner(call: tpd.Tree, rhsToInline: tpd.Tree)(implicit ctx: Context) { * This corresponds to the symbols that will need to be interpreted. */ private def macroDependencies(tree: Tree)(implicit ctx: Context) = - new TreeAccumulator[Set[Symbol]] { + new TreeAccumulator[List[Symbol]] { private var level = -1 - override def apply(syms: Set[Symbol], tree: tpd.Tree)(implicit ctx: Context): Set[Symbol] = + override def apply(syms: List[Symbol], tree: tpd.Tree)(implicit ctx: Context): List[Symbol] = if (level != -1) foldOver(syms, tree) else tree match { case tree: RefTree if level == -1 && tree.symbol.isDefinedInCurrentRun && !tree.symbol.isLocal => - foldOver(syms + tree.symbol, tree) + foldOver(tree.symbol :: syms, tree) case Quoted(body) => level += 1 try apply(syms, body) @@ -1294,6 +1294,6 @@ class Inliner(call: tpd.Tree, rhsToInline: tpd.Tree)(implicit ctx: Context) { case _ => foldOver(syms, tree) } - }.apply(Set.empty, tree) + }.apply(Nil, tree) } From 4e8ab2a282e57fdf0100e9b05ffb2b862f341de7 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Fri, 27 Sep 2019 15:07:57 +0200 Subject: [PATCH 03/31] Track splice calls instead of inline calls The real thing that needs to be tracked for cyclic dependencies are calls to spliced methods. Move the suspend machinery from inline calls to these. --- .../src/dotty/tools/dotc/typer/Inliner.scala | 42 +++++++------------ 1 file changed, 14 insertions(+), 28 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/typer/Inliner.scala b/compiler/src/dotty/tools/dotc/typer/Inliner.scala index 05ee07b86973..0a5034b46995 100644 --- a/compiler/src/dotty/tools/dotc/typer/Inliner.scala +++ b/compiler/src/dotty/tools/dotc/typer/Inliner.scala @@ -70,10 +70,6 @@ object Inliner { */ def inlineCall(tree: Tree)(implicit ctx: Context): Tree = { if (tree.symbol == defn.CompiletimeTesting_typeChecks) return Intrinsics.typeChecks(tree) - if tree.symbol.is(Macro) && tree.symbol.isDefinedInCurrentRun then - if ctx.compilationUnit.source.file == tree.symbol.associatedFile then - ctx.error("Cannot call macro defined in the same source file", tree.sourcePos) - ctx.compilationUnit.suspend() /** Set the position of all trees logically contained in the expansion of * inlined call `call` to the position of `call`. This transform is necessary @@ -1241,35 +1237,25 @@ class Inliner(call: tpd.Tree, rhsToInline: tpd.Tree)(implicit ctx: Context) { assert(level == 0) val inlinedFrom = enclosingInlineds.last val ctx1 = tastyreflect.MacroExpansion.context(inlinedFrom) - val dependencies = macroDependencies(body) - if (dependencies.nonEmpty) { - var location = inlinedFrom.symbol - if (location.isLocalDummy) location = location.owner - - val msg = - em"""Failed to expand macro. This macro depends on an implementation that is defined in the same project and not yet compiled. - |In particular ${inlinedFrom.symbol} depends on ${dependencies.map(_.show).mkString(", ")}. - | - |Moving ${dependencies.map(_.show).mkString(", ")} to a different project would fix this. - |""".stripMargin - ctx.error(msg, inlinedFrom.sourcePos) - EmptyTree - } - else { - val evaluatedSplice = Splicer.splice(body, inlinedFrom.sourcePos, MacroClassLoader.fromContext)(ctx1) + if dependencies.nonEmpty then + for sym <- dependencies do + if ctx.compilationUnit.source.file == sym.associatedFile then + ctx.error(em"Cannot call macro $sym defined in the same source file", body.sourcePos) + ctx.compilationUnit.suspend() // this throws a SuspendException - val inlinedNormailizer = new TreeMap { - override def transform(tree: tpd.Tree)(implicit ctx: Context): tpd.Tree = tree match { - case Inlined(EmptyTree, Nil, expr) if enclosingInlineds.isEmpty => transform(expr) - case _ => super.transform(tree) - } + val evaluatedSplice = Splicer.splice(body, inlinedFrom.sourcePos, MacroClassLoader.fromContext)(ctx1) + + val inlinedNormailizer = new TreeMap { + override def transform(tree: tpd.Tree)(implicit ctx: Context): tpd.Tree = tree match { + case Inlined(EmptyTree, Nil, expr) if enclosingInlineds.isEmpty => transform(expr) + case _ => super.transform(tree) } - val normalizedSplice = inlinedNormailizer.transform(evaluatedSplice) - if (normalizedSplice.isEmpty) normalizedSplice - else normalizedSplice.withSpan(span) } + val normalizedSplice = inlinedNormailizer.transform(evaluatedSplice) + if (normalizedSplice.isEmpty) normalizedSplice + else normalizedSplice.withSpan(span) } /** Return the set of symbols that are refered at level -1 by the tree and defined in the current run. From 3a13d292927a07f06627d7992fab6bbb5d4b102c Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Fri, 27 Sep 2019 15:13:36 +0200 Subject: [PATCH 04/31] Fix tests Drop reflect-inline, since it did not do what one thought (called stdlib stripMargin instead of locally defined one). Move macros-in-same-project1 to run-macros Fix error marker for macro-cycle1.scala. --- compiler/test/dotc/run-test-pickling.blacklist | 1 + tests/neg/macro-cycle1.scala | 2 +- .../macros-in-same-project1/Foo.scala | 0 .../macros-in-same-project1/Test.scala | 0 tests/run-macros/reflect-inline/assert_1.scala | 16 ---------------- tests/run-macros/reflect-inline/test_2.scala | 10 ---------- 6 files changed, 2 insertions(+), 27 deletions(-) rename tests/{run => run-macros}/macros-in-same-project1/Foo.scala (100%) rename tests/{run => run-macros}/macros-in-same-project1/Test.scala (100%) delete mode 100644 tests/run-macros/reflect-inline/assert_1.scala delete mode 100644 tests/run-macros/reflect-inline/test_2.scala diff --git a/compiler/test/dotc/run-test-pickling.blacklist b/compiler/test/dotc/run-test-pickling.blacklist index f6dab8b29bcd..f3e4c7ec847c 100644 --- a/compiler/test/dotc/run-test-pickling.blacklist +++ b/compiler/test/dotc/run-test-pickling.blacklist @@ -16,6 +16,7 @@ derive-generic.scala mixin-forwarder-overload t8905 t10889 +macros-in-same-project1 i5257.scala enum-java tuple-ops.scala diff --git a/tests/neg/macro-cycle1.scala b/tests/neg/macro-cycle1.scala index b772005e35f3..9fca9ef3de12 100644 --- a/tests/neg/macro-cycle1.scala +++ b/tests/neg/macro-cycle1.scala @@ -2,7 +2,7 @@ import scala.quoted.{Expr, QuoteContext} object Test { def fooImpl(given QuoteContext): Expr[Unit] = '{println("hi")} - inline def foo: Unit = ${fooImpl} + inline def foo: Unit = ${fooImpl} // error foo } diff --git a/tests/run/macros-in-same-project1/Foo.scala b/tests/run-macros/macros-in-same-project1/Foo.scala similarity index 100% rename from tests/run/macros-in-same-project1/Foo.scala rename to tests/run-macros/macros-in-same-project1/Foo.scala diff --git a/tests/run/macros-in-same-project1/Test.scala b/tests/run-macros/macros-in-same-project1/Test.scala similarity index 100% rename from tests/run/macros-in-same-project1/Test.scala rename to tests/run-macros/macros-in-same-project1/Test.scala diff --git a/tests/run-macros/reflect-inline/assert_1.scala b/tests/run-macros/reflect-inline/assert_1.scala deleted file mode 100644 index 693a185eee32..000000000000 --- a/tests/run-macros/reflect-inline/assert_1.scala +++ /dev/null @@ -1,16 +0,0 @@ -import scala.quoted._ - -object api { - inline def (inline x: String) stripMargin: String = - ${ stripImpl(x) } - - private def stripImpl(x: String)(given qctx: QuoteContext): Expr[String] = - Expr(x.stripMargin) - - inline def typeChecks(inline x: String): Boolean = - ${ typeChecksImpl(scala.compiletime.testing.typeChecks(x)) } - - private def typeChecksImpl(b: Boolean)(given qctx: QuoteContext): Expr[Boolean] = { - if (b) Expr(true) else Expr(false) - } -} diff --git a/tests/run-macros/reflect-inline/test_2.scala b/tests/run-macros/reflect-inline/test_2.scala deleted file mode 100644 index f99fbacbf0d7..000000000000 --- a/tests/run-macros/reflect-inline/test_2.scala +++ /dev/null @@ -1,10 +0,0 @@ -import api._ - -object Test { - def main(args: Array[String]): Unit = { - val a: String = "5" - assert(typeChecks("|1 + 1".stripMargin)) - assert(scala.compiletime.testing.typeChecks("|1 + 1".stripMargin)) - assert(("|3 + " + a).stripMargin == "3 + 5") - } -} \ No newline at end of file From 81a29f2be8ca36d3a65735f693e3b2d3f1cb79ad Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Fri, 27 Sep 2019 15:14:18 +0200 Subject: [PATCH 05/31] Add `outermost` utility method to SourcePosition. Not needed right now, but might be useful in the future. --- compiler/src/dotty/tools/dotc/util/SourcePosition.scala | 3 +++ 1 file changed, 3 insertions(+) diff --git a/compiler/src/dotty/tools/dotc/util/SourcePosition.scala b/compiler/src/dotty/tools/dotc/util/SourcePosition.scala index 51dde590c475..62744659dde3 100644 --- a/compiler/src/dotty/tools/dotc/util/SourcePosition.scala +++ b/compiler/src/dotty/tools/dotc/util/SourcePosition.scala @@ -61,6 +61,9 @@ extends interfaces.SourcePosition with Showable { def focus : SourcePosition = withSpan(span.focus) def toSynthetic: SourcePosition = withSpan(span.toSynthetic) + def outermost: SourcePosition = + if outer == null || outer == NoSourcePosition then this else outer.outermost + override def toString: String = s"${if (source.exists) source.file.toString else "(no source)"}:$span" From 38784ab8ce35fa5c2971b5713678dd342500cd6d Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Fri, 27 Sep 2019 16:22:53 +0200 Subject: [PATCH 06/31] Add debug output to track down problem compiling ScalaTest --- compiler/src/dotty/tools/dotc/CompilationUnit.scala | 2 ++ compiler/src/dotty/tools/dotc/typer/Inliner.scala | 1 + 2 files changed, 3 insertions(+) diff --git a/compiler/src/dotty/tools/dotc/CompilationUnit.scala b/compiler/src/dotty/tools/dotc/CompilationUnit.scala index c4efcc7f0e68..e1ccc8a4c1c0 100644 --- a/compiler/src/dotty/tools/dotc/CompilationUnit.scala +++ b/compiler/src/dotty/tools/dotc/CompilationUnit.scala @@ -10,6 +10,7 @@ import dotty.tools.dotc.core.SymDenotations.ClassDenotation import dotty.tools.dotc.core.Symbols._ import dotty.tools.dotc.transform.SymUtils._ import util.{NoSource, SourceFile} +import core.Decorators._ class CompilationUnit protected (val source: SourceFile) { @@ -36,6 +37,7 @@ class CompilationUnit protected (val source: SourceFile) { def suspend()(given ctx: Context): Nothing = if !suspended then + println(i"suspended: $this") suspended = true ctx.run.suspendedUnits += this throw CompilationUnit.SuspendException() diff --git a/compiler/src/dotty/tools/dotc/typer/Inliner.scala b/compiler/src/dotty/tools/dotc/typer/Inliner.scala index 0a5034b46995..7b9d391b87bf 100644 --- a/compiler/src/dotty/tools/dotc/typer/Inliner.scala +++ b/compiler/src/dotty/tools/dotc/typer/Inliner.scala @@ -1243,6 +1243,7 @@ class Inliner(call: tpd.Tree, rhsToInline: tpd.Tree)(implicit ctx: Context) { for sym <- dependencies do if ctx.compilationUnit.source.file == sym.associatedFile then ctx.error(em"Cannot call macro $sym defined in the same source file", body.sourcePos) + println(i"suspension triggered by macro call to ${sym.showLocated} in ${sym.associatedFile}") ctx.compilationUnit.suspend() // this throws a SuspendException val evaluatedSplice = Splicer.splice(body, inlinedFrom.sourcePos, MacroClassLoader.fromContext)(ctx1) From 58d7f5e48205a26cc48e2ba18e43e5ee9ea39c29 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Fri, 27 Sep 2019 16:48:30 +0200 Subject: [PATCH 07/31] Add -Xprint-suspension option to print info about suspensions --- compiler/src/dotty/tools/dotc/CompilationUnit.scala | 3 ++- compiler/src/dotty/tools/dotc/Driver.scala | 8 ++++++-- compiler/src/dotty/tools/dotc/config/ScalaSettings.scala | 3 ++- compiler/src/dotty/tools/dotc/typer/FrontEnd.scala | 4 +++- compiler/src/dotty/tools/dotc/typer/Inliner.scala | 3 ++- 5 files changed, 15 insertions(+), 6 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/CompilationUnit.scala b/compiler/src/dotty/tools/dotc/CompilationUnit.scala index e1ccc8a4c1c0..ea0faa19ce6d 100644 --- a/compiler/src/dotty/tools/dotc/CompilationUnit.scala +++ b/compiler/src/dotty/tools/dotc/CompilationUnit.scala @@ -37,7 +37,8 @@ class CompilationUnit protected (val source: SourceFile) { def suspend()(given ctx: Context): Nothing = if !suspended then - println(i"suspended: $this") + if (ctx.settings.XprintSuspension.value) + ctx.echo(i"suspended: $this") suspended = true ctx.run.suspendedUnits += this throw CompilationUnit.SuspendException() diff --git a/compiler/src/dotty/tools/dotc/Driver.scala b/compiler/src/dotty/tools/dotc/Driver.scala index 790383060267..67f16b7208f5 100644 --- a/compiler/src/dotty/tools/dotc/Driver.scala +++ b/compiler/src/dotty/tools/dotc/Driver.scala @@ -10,6 +10,7 @@ import core.{MacroClassLoader, Mode, TypeError} import dotty.tools.dotc.ast.Positioned import dotty.tools.io.File import reporting._ +import core.Decorators._ import scala.util.control.NonFatal import fromtasty.{TASTYCompiler, TastyFileUtil} @@ -37,9 +38,12 @@ class Driver { def finish(run: Run): Unit = run.printSummary() if !ctx.reporter.errorsReported && run.suspendedUnits.nonEmpty then + val suspendedUnits = run.suspendedUnits.toList + if (ctx.settings.XprintSuspension.value) + ctx.echo(i"compiling suspended $suspendedUnits%, %") val run1 = compiler.newRun - for unit <- run.suspendedUnits do unit.suspended = false - run1.compileUnits(run.suspendedUnits.toList) + for unit <- suspendedUnits do unit.suspended = false + run1.compileUnits(suspendedUnits) finish(run1) finish(run) diff --git a/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala b/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala index 3cd5a42c41ed..c52f7a231896 100644 --- a/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala +++ b/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala @@ -77,6 +77,7 @@ class ScalaSettings extends Settings.SettingGroup { val XprintDiff: Setting[Boolean] = BooleanSetting("-Xprint-diff", "Print changed parts of the tree since last print.") val XprintDiffDel: Setting[Boolean] = BooleanSetting("-Xprint-diff-del", "Print changed parts of the tree since last print including deleted parts.") val XprintInline: Setting[Boolean] = BooleanSetting("-Xprint-inline", "Show where inlined code comes from") + val XprintSuspension: Setting[Boolean] = BooleanSetting("-Xprint-suspension", "Show when code is suspended until macros are compiled") val Xprompt: Setting[Boolean] = BooleanSetting("-Xprompt", "Display a prompt after each error (debugging option).") val XnoValueClasses: Setting[Boolean] = BooleanSetting("-Xno-value-classes", "Do not use value classes. Helps debugging.") val XreplLineWidth: Setting[Int] = IntSetting("-Xrepl-line-width", "Maximal number of columns per line for REPL output", 390) @@ -200,7 +201,7 @@ class ScalaSettings extends Settings.SettingGroup { "The source repository of your project", "" ) - + val projectLogo: Setting[String] = StringSetting( "-project-logo", "project logo filename", diff --git a/compiler/src/dotty/tools/dotc/typer/FrontEnd.scala b/compiler/src/dotty/tools/dotc/typer/FrontEnd.scala index 8af839c8ba2e..d533cf01ecc4 100644 --- a/compiler/src/dotty/tools/dotc/typer/FrontEnd.scala +++ b/compiler/src/dotty/tools/dotc/typer/FrontEnd.scala @@ -125,7 +125,9 @@ class FrontEnd extends Phase { ctx.error(em"""Cyclic macro dependencies $where |Compilation stopped since no further progress can be made. | - |To fix this, place macros in one set of files and their callers in another.""") + |To fix this, place macros in one set of files and their callers in another. + | + |Compiling with -XprintSuspension gives more information.""") newUnits } diff --git a/compiler/src/dotty/tools/dotc/typer/Inliner.scala b/compiler/src/dotty/tools/dotc/typer/Inliner.scala index 7b9d391b87bf..387ac09726bb 100644 --- a/compiler/src/dotty/tools/dotc/typer/Inliner.scala +++ b/compiler/src/dotty/tools/dotc/typer/Inliner.scala @@ -1243,7 +1243,8 @@ class Inliner(call: tpd.Tree, rhsToInline: tpd.Tree)(implicit ctx: Context) { for sym <- dependencies do if ctx.compilationUnit.source.file == sym.associatedFile then ctx.error(em"Cannot call macro $sym defined in the same source file", body.sourcePos) - println(i"suspension triggered by macro call to ${sym.showLocated} in ${sym.associatedFile}") + if (ctx.settings.XprintSuspension.value) + ctx.echo(i"suspension triggered by macro call to ${sym.showLocated} in ${sym.associatedFile}", body.sourcePos) ctx.compilationUnit.suspend() // this throws a SuspendException val evaluatedSplice = Splicer.splice(body, inlinedFrom.sourcePos, MacroClassLoader.fromContext)(ctx1) From ce557d6756d96c88e9699cf5f445983860171aa1 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Fri, 27 Sep 2019 17:11:27 +0200 Subject: [PATCH 08/31] Compile scalatest with -Xprint-suspension --- community-build/community-projects/scalatest | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/community-build/community-projects/scalatest b/community-build/community-projects/scalatest index 08f8a5c0f8f3..dbc37c4d36aa 160000 --- a/community-build/community-projects/scalatest +++ b/community-build/community-projects/scalatest @@ -1 +1 @@ -Subproject commit 08f8a5c0f8f3a3e8d25e0320e171b386e0c44bb1 +Subproject commit dbc37c4d36aa5e8b390d654fa990d51740e6734d From b2d2796526cabb99348efd8d9594bd8328c2e6a1 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Fri, 27 Sep 2019 17:11:55 +0200 Subject: [PATCH 09/31] Don't count vals as macro dependencies --- compiler/src/dotty/tools/dotc/typer/Inliner.scala | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/compiler/src/dotty/tools/dotc/typer/Inliner.scala b/compiler/src/dotty/tools/dotc/typer/Inliner.scala index 387ac09726bb..3a414f066a6e 100644 --- a/compiler/src/dotty/tools/dotc/typer/Inliner.scala +++ b/compiler/src/dotty/tools/dotc/typer/Inliner.scala @@ -1269,7 +1269,11 @@ class Inliner(call: tpd.Tree, rhsToInline: tpd.Tree)(implicit ctx: Context) { override def apply(syms: List[Symbol], tree: tpd.Tree)(implicit ctx: Context): List[Symbol] = if (level != -1) foldOver(syms, tree) else tree match { - case tree: RefTree if level == -1 && tree.symbol.isDefinedInCurrentRun && !tree.symbol.isLocal => + case tree: RefTree + if level == -1 + && tree.symbol.isDefinedInCurrentRun + && tree.symbol.isRealMethod + && !tree.symbol.isLocal => foldOver(tree.symbol :: syms, tree) case Quoted(body) => level += 1 From 440e2956802f96a2bfb2df4bea1582f37f22785f Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Sat, 28 Sep 2019 09:14:03 +0200 Subject: [PATCH 10/31] Move tests to pos-macros --- tests/neg-macros/macros-in-same-project-1.check | 8 -------- tests/neg-macros/macros-in-same-project-1/Bar.scala | 3 --- tests/neg-macros/macros-in-same-project-2.check | 8 -------- tests/neg-macros/macros-in-same-project-3.check | 8 -------- tests/pos-macros/macros-in-same-project-1/Bar.scala | 3 +++ .../macros-in-same-project-1/Foo.scala | 0 .../macros-in-same-project-2/Bar.scala | 2 +- .../macros-in-same-project-2/Foo.scala | 0 .../macros-in-same-project-3/Bar.scala | 0 .../macros-in-same-project-3/Baz.scala | 0 .../macros-in-same-project-3/Foo.scala | 0 11 files changed, 4 insertions(+), 28 deletions(-) delete mode 100644 tests/neg-macros/macros-in-same-project-1.check delete mode 100644 tests/neg-macros/macros-in-same-project-1/Bar.scala delete mode 100644 tests/neg-macros/macros-in-same-project-2.check delete mode 100644 tests/neg-macros/macros-in-same-project-3.check create mode 100644 tests/pos-macros/macros-in-same-project-1/Bar.scala rename tests/{neg-macros => pos-macros}/macros-in-same-project-1/Foo.scala (100%) rename tests/{neg-macros => pos-macros}/macros-in-same-project-2/Bar.scala (79%) rename tests/{neg-macros => pos-macros}/macros-in-same-project-2/Foo.scala (100%) rename tests/{neg-macros => pos-macros}/macros-in-same-project-3/Bar.scala (100%) rename tests/{neg-macros => pos-macros}/macros-in-same-project-3/Baz.scala (100%) rename tests/{neg-macros => pos-macros}/macros-in-same-project-3/Foo.scala (100%) diff --git a/tests/neg-macros/macros-in-same-project-1.check b/tests/neg-macros/macros-in-same-project-1.check deleted file mode 100644 index f257aa16f0d2..000000000000 --- a/tests/neg-macros/macros-in-same-project-1.check +++ /dev/null @@ -1,8 +0,0 @@ --- Error: tests/neg-macros/macros-in-same-project-1/Bar.scala:2:13 ----------------------------------------------------- -2 | Foo.myMacro() // error - | ^^^^^^^^^^^^^ - |Failed to expand macro. This macro depends on an implementation that is defined in the same project and not yet compiled. - |In particular method myMacro depends on method aMacroImplementation. - | - |Moving method aMacroImplementation to a different project would fix this. - | This location is in code that was inlined at Bar.scala:2 diff --git a/tests/neg-macros/macros-in-same-project-1/Bar.scala b/tests/neg-macros/macros-in-same-project-1/Bar.scala deleted file mode 100644 index 2924e2b2411a..000000000000 --- a/tests/neg-macros/macros-in-same-project-1/Bar.scala +++ /dev/null @@ -1,3 +0,0 @@ -object Bar { - Foo.myMacro() // error -} diff --git a/tests/neg-macros/macros-in-same-project-2.check b/tests/neg-macros/macros-in-same-project-2.check deleted file mode 100644 index 0ee5ec3f3a98..000000000000 --- a/tests/neg-macros/macros-in-same-project-2.check +++ /dev/null @@ -1,8 +0,0 @@ --- Error: tests/neg-macros/macros-in-same-project-2/Bar.scala:5:9 ------------------------------------------------------ -5 | myMacro() // error - | ^^^^^^^^^ - |Failed to expand macro. This macro depends on an implementation that is defined in the same project and not yet compiled. - |In particular method myMacro depends on method aMacroImplementation, object Foo. - | - |Moving method aMacroImplementation, object Foo to a different project would fix this. - | This location is in code that was inlined at Bar.scala:5 diff --git a/tests/neg-macros/macros-in-same-project-3.check b/tests/neg-macros/macros-in-same-project-3.check deleted file mode 100644 index e2865e55578d..000000000000 --- a/tests/neg-macros/macros-in-same-project-3.check +++ /dev/null @@ -1,8 +0,0 @@ --- Error: tests/neg-macros/macros-in-same-project-3/Baz.scala:2:13 ----------------------------------------------------- -2 | Bar.myMacro() // error - | ^^^^^^^^^^^^^ - |Failed to expand macro. This macro depends on an implementation that is defined in the same project and not yet compiled. - |In particular method myMacro depends on method aMacroImplementation. - | - |Moving method aMacroImplementation to a different project would fix this. - | This location is in code that was inlined at Baz.scala:2 diff --git a/tests/pos-macros/macros-in-same-project-1/Bar.scala b/tests/pos-macros/macros-in-same-project-1/Bar.scala new file mode 100644 index 000000000000..c53df5221218 --- /dev/null +++ b/tests/pos-macros/macros-in-same-project-1/Bar.scala @@ -0,0 +1,3 @@ +object Bar { + Foo.myMacro() +} diff --git a/tests/neg-macros/macros-in-same-project-1/Foo.scala b/tests/pos-macros/macros-in-same-project-1/Foo.scala similarity index 100% rename from tests/neg-macros/macros-in-same-project-1/Foo.scala rename to tests/pos-macros/macros-in-same-project-1/Foo.scala diff --git a/tests/neg-macros/macros-in-same-project-2/Bar.scala b/tests/pos-macros/macros-in-same-project-2/Bar.scala similarity index 79% rename from tests/neg-macros/macros-in-same-project-2/Bar.scala rename to tests/pos-macros/macros-in-same-project-2/Bar.scala index ebd6fb25895f..3f477541a1d3 100644 --- a/tests/neg-macros/macros-in-same-project-2/Bar.scala +++ b/tests/pos-macros/macros-in-same-project-2/Bar.scala @@ -2,6 +2,6 @@ object Bar { inline def myMacro(): Unit = ${ Foo.aMacroImplementation } - myMacro() // error + myMacro() } diff --git a/tests/neg-macros/macros-in-same-project-2/Foo.scala b/tests/pos-macros/macros-in-same-project-2/Foo.scala similarity index 100% rename from tests/neg-macros/macros-in-same-project-2/Foo.scala rename to tests/pos-macros/macros-in-same-project-2/Foo.scala diff --git a/tests/neg-macros/macros-in-same-project-3/Bar.scala b/tests/pos-macros/macros-in-same-project-3/Bar.scala similarity index 100% rename from tests/neg-macros/macros-in-same-project-3/Bar.scala rename to tests/pos-macros/macros-in-same-project-3/Bar.scala diff --git a/tests/neg-macros/macros-in-same-project-3/Baz.scala b/tests/pos-macros/macros-in-same-project-3/Baz.scala similarity index 100% rename from tests/neg-macros/macros-in-same-project-3/Baz.scala rename to tests/pos-macros/macros-in-same-project-3/Baz.scala diff --git a/tests/neg-macros/macros-in-same-project-3/Foo.scala b/tests/pos-macros/macros-in-same-project-3/Foo.scala similarity index 100% rename from tests/neg-macros/macros-in-same-project-3/Foo.scala rename to tests/pos-macros/macros-in-same-project-3/Foo.scala From 463aa7b9d157b5e34aaab9607acaf97125e27098 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Sat, 28 Sep 2019 10:15:56 +0200 Subject: [PATCH 11/31] Fix error message --- compiler/src/dotty/tools/dotc/typer/FrontEnd.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/src/dotty/tools/dotc/typer/FrontEnd.scala b/compiler/src/dotty/tools/dotc/typer/FrontEnd.scala index d533cf01ecc4..2e553dba5be3 100644 --- a/compiler/src/dotty/tools/dotc/typer/FrontEnd.scala +++ b/compiler/src/dotty/tools/dotc/typer/FrontEnd.scala @@ -127,7 +127,7 @@ class FrontEnd extends Phase { | |To fix this, place macros in one set of files and their callers in another. | - |Compiling with -XprintSuspension gives more information.""") + |Compiling with -Xprint-suspension gives more information.""") newUnits } From cd0a9ce21a9892e10e2a4e517b06058372002e82 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Sat, 28 Sep 2019 21:24:14 +0200 Subject: [PATCH 12/31] Fix reflect-inline test --- tests/run-macros/reflect-inline/assert_1.scala | 16 ++++++++++++++++ tests/run-macros/reflect-inline/test_2.scala | 10 ++++++++++ 2 files changed, 26 insertions(+) create mode 100644 tests/run-macros/reflect-inline/assert_1.scala create mode 100644 tests/run-macros/reflect-inline/test_2.scala diff --git a/tests/run-macros/reflect-inline/assert_1.scala b/tests/run-macros/reflect-inline/assert_1.scala new file mode 100644 index 000000000000..d408236f5fe2 --- /dev/null +++ b/tests/run-macros/reflect-inline/assert_1.scala @@ -0,0 +1,16 @@ +import scala.quoted._ + +object api { + inline def (inline x: String) stripMargin: String = + ${ stripImpl(x) } + + private def stripImpl(x: String)(given qctx: QuoteContext): Expr[String] = + Expr(augmentString(x).stripMargin) + + inline def typeChecks(inline x: String): Boolean = + ${ typeChecksImpl(scala.compiletime.testing.typeChecks(x)) } + + private def typeChecksImpl(b: Boolean)(given qctx: QuoteContext): Expr[Boolean] = { + if (b) Expr(true) else Expr(false) + } +} diff --git a/tests/run-macros/reflect-inline/test_2.scala b/tests/run-macros/reflect-inline/test_2.scala new file mode 100644 index 000000000000..2f36d7b52214 --- /dev/null +++ b/tests/run-macros/reflect-inline/test_2.scala @@ -0,0 +1,10 @@ +import api._ + +object Test { + def main(args: Array[String]): Unit = { + val a: String = "5" + assert(typeChecks("|1 + 1".stripMargin)) + assert(scala.compiletime.testing.typeChecks("|1 + 1".stripMargin)) + assert(("|3 + " + a).stripMargin == "3 + 5") + } +} From 9e387f82305fec5778d1836273b87b0f6c9b24f4 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Sat, 28 Sep 2019 21:31:55 +0200 Subject: [PATCH 13/31] Revert "Don't count vals as macro dependencies" This reverts commit 8cea72067fb5736285a6dbc8a209bddae6ee1e18. --- compiler/src/dotty/tools/dotc/typer/Inliner.scala | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/typer/Inliner.scala b/compiler/src/dotty/tools/dotc/typer/Inliner.scala index 3a414f066a6e..387ac09726bb 100644 --- a/compiler/src/dotty/tools/dotc/typer/Inliner.scala +++ b/compiler/src/dotty/tools/dotc/typer/Inliner.scala @@ -1269,11 +1269,7 @@ class Inliner(call: tpd.Tree, rhsToInline: tpd.Tree)(implicit ctx: Context) { override def apply(syms: List[Symbol], tree: tpd.Tree)(implicit ctx: Context): List[Symbol] = if (level != -1) foldOver(syms, tree) else tree match { - case tree: RefTree - if level == -1 - && tree.symbol.isDefinedInCurrentRun - && tree.symbol.isRealMethod - && !tree.symbol.isLocal => + case tree: RefTree if level == -1 && tree.symbol.isDefinedInCurrentRun && !tree.symbol.isLocal => foldOver(tree.symbol :: syms, tree) case Quoted(body) => level += 1 From 7ebdcf03bc50b32826bbb58f2d5b1359fd31644f Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Sat, 28 Sep 2019 22:55:27 +0200 Subject: [PATCH 14/31] Add minimization of previous scalatest stripMargin issue --- tests/neg-macros/reflect-inline/assert_1.scala | 10 ++++++++++ tests/neg-macros/reflect-inline/test_2.scala | 6 ++++++ 2 files changed, 16 insertions(+) create mode 100644 tests/neg-macros/reflect-inline/assert_1.scala create mode 100644 tests/neg-macros/reflect-inline/test_2.scala diff --git a/tests/neg-macros/reflect-inline/assert_1.scala b/tests/neg-macros/reflect-inline/assert_1.scala new file mode 100644 index 000000000000..eec9852b53d9 --- /dev/null +++ b/tests/neg-macros/reflect-inline/assert_1.scala @@ -0,0 +1,10 @@ +import scala.quoted._ + +object api { + inline def (inline x: String) stripMargin2: String = + ${ stripImpl(x) } + + private def stripImpl(x: String)(given qctx: QuoteContext): Expr[String] = + Expr(x.stripMargin) + +} diff --git a/tests/neg-macros/reflect-inline/test_2.scala b/tests/neg-macros/reflect-inline/test_2.scala new file mode 100644 index 000000000000..0be48280e8ec --- /dev/null +++ b/tests/neg-macros/reflect-inline/test_2.scala @@ -0,0 +1,6 @@ +import api._ + +class Test { + val a: String = "5" + a.stripMargin2 // error: `a should be a known constant +} From c7b93268ee9fd4c6756d652479919da6fe5246bf Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Mon, 30 Sep 2019 10:23:30 +0200 Subject: [PATCH 15/31] Suspend macros when class not found during macro expansion --- .../dotty/tools/dotc/transform/Splicer.scala | 33 ++++++++++++------- .../src/dotty/tools/dotc/typer/Inliner.scala | 4 +-- .../neg-macros/macros-in-same-project-4.check | 14 ++++---- .../macros-in-same-project-4/Bar.scala | 3 +- .../macros-in-same-project-5/Bar.scala | 10 ++++++ .../macros-in-same-project-5/Foo.scala | 8 +++++ tests/neg/macro-cycle1.scala | 4 +-- 7 files changed, 51 insertions(+), 25 deletions(-) create mode 100644 tests/neg-macros/macros-in-same-project-5/Bar.scala create mode 100644 tests/neg-macros/macros-in-same-project-5/Foo.scala diff --git a/compiler/src/dotty/tools/dotc/transform/Splicer.scala b/compiler/src/dotty/tools/dotc/transform/Splicer.scala index bd988422541f..9d8d283caa9c 100644 --- a/compiler/src/dotty/tools/dotc/transform/Splicer.scala +++ b/compiler/src/dotty/tools/dotc/transform/Splicer.scala @@ -47,6 +47,8 @@ object Splicer { interpretedExpr.fold(tree)(macroClosure => PickledQuotes.quotedExprToTree(macroClosure(QuoteContext()))) } catch { + case ex: CompilationUnit.SuspendException => + throw ex case ex: StopInterpretation => ctx.error(ex.msg, ex.pos) EmptyTree @@ -348,19 +350,26 @@ object Splicer { sw.write("\n") throw new StopInterpretation(sw.toString, pos) case ex: InvocationTargetException => - val sw = new StringWriter() - sw.write("Exception occurred while executing macro expansion.\n") - val targetException = ex.getTargetException - if (!ctx.settings.Ydebug.value) { - val end = targetException.getStackTrace.lastIndexWhere { x => - x.getClassName == method.getDeclaringClass.getCanonicalName && x.getMethodName == method.getName - } - val shortStackTrace = targetException.getStackTrace.take(end + 1) - targetException.setStackTrace(shortStackTrace) + ex.getTargetException match { + case targetException: NoClassDefFoundError => // FIXME check that the class is beeining compiled now + val className = targetException.getMessage + if (ctx.settings.XprintSuspension.value) + ctx.echo(i"suspension triggered by NoClassDefFoundError($className)", pos) // TODO improve message + ctx.compilationUnit.suspend() // this throws a SuspendException + case targetException => + val sw = new StringWriter() + sw.write("Exception occurred while executing macro expansion.\n") + if (!ctx.settings.Ydebug.value) { + val end = targetException.getStackTrace.lastIndexWhere { x => + x.getClassName == method.getDeclaringClass.getCanonicalName && x.getMethodName == method.getName + } + val shortStackTrace = targetException.getStackTrace.take(end + 1) + targetException.setStackTrace(shortStackTrace) + } + targetException.printStackTrace(new PrintWriter(sw)) + sw.write("\n") + throw new StopInterpretation(sw.toString, pos) } - targetException.printStackTrace(new PrintWriter(sw)) - sw.write("\n") - throw new StopInterpretation(sw.toString, pos) } /** List of classes of the parameters of the signature of `sym` */ diff --git a/compiler/src/dotty/tools/dotc/typer/Inliner.scala b/compiler/src/dotty/tools/dotc/typer/Inliner.scala index 387ac09726bb..e65f1fd5a151 100644 --- a/compiler/src/dotty/tools/dotc/typer/Inliner.scala +++ b/compiler/src/dotty/tools/dotc/typer/Inliner.scala @@ -1242,9 +1242,9 @@ class Inliner(call: tpd.Tree, rhsToInline: tpd.Tree)(implicit ctx: Context) { if dependencies.nonEmpty then for sym <- dependencies do if ctx.compilationUnit.source.file == sym.associatedFile then - ctx.error(em"Cannot call macro $sym defined in the same source file", body.sourcePos) + ctx.error(em"Cannot call macro $sym defined in the same source file", call.sourcePos) if (ctx.settings.XprintSuspension.value) - ctx.echo(i"suspension triggered by macro call to ${sym.showLocated} in ${sym.associatedFile}", body.sourcePos) + ctx.echo(i"suspension triggered by macro call to ${sym.showLocated} in ${sym.associatedFile}", call.sourcePos) ctx.compilationUnit.suspend() // this throws a SuspendException val evaluatedSplice = Splicer.splice(body, inlinedFrom.sourcePos, MacroClassLoader.fromContext)(ctx1) diff --git a/tests/neg-macros/macros-in-same-project-4.check b/tests/neg-macros/macros-in-same-project-4.check index 5cdc12b31cb9..79c78fd0fb7f 100644 --- a/tests/neg-macros/macros-in-same-project-4.check +++ b/tests/neg-macros/macros-in-same-project-4.check @@ -1,8 +1,6 @@ --- Error: tests/neg-macros/macros-in-same-project-4/Bar.scala:5:13 ----------------------------------------------------- -5 | Foo.myMacro() // error - | ^^^^^^^^^^^^^ - |Failed to expand macro. This macro depends on an implementation that is defined in the same project and not yet compiled. - |In particular method myMacro depends on method aMacroImplementation. - | - |Moving method aMacroImplementation to a different project would fix this. - | This location is in code that was inlined at Bar.scala:5 +Cyclic macro dependencies in tests/neg-macros/macros-in-same-project-4/Bar.scala. +Compilation stopped since no further progress can be made. + +To fix this, place macros in one set of files and their callers in another. + +Compiling with -Xprint-suspension gives more information. diff --git a/tests/neg-macros/macros-in-same-project-4/Bar.scala b/tests/neg-macros/macros-in-same-project-4/Bar.scala index de9ec400b49b..da86189bb201 100644 --- a/tests/neg-macros/macros-in-same-project-4/Bar.scala +++ b/tests/neg-macros/macros-in-same-project-4/Bar.scala @@ -1,8 +1,9 @@ +// nopos-error import scala.quoted._ object Bar { - Foo.myMacro() // error + Foo.myMacro() def hello()(given QuoteContext): Expr[Unit] = '{ println("Hello") } } diff --git a/tests/neg-macros/macros-in-same-project-5/Bar.scala b/tests/neg-macros/macros-in-same-project-5/Bar.scala new file mode 100644 index 000000000000..b3b6e792a984 --- /dev/null +++ b/tests/neg-macros/macros-in-same-project-5/Bar.scala @@ -0,0 +1,10 @@ +import scala.quoted._ + +object Bar { + + Foo.myMacro() // error + + def aMacroImplementation(given QuoteContext): Expr[Unit] = Bar.hello() + + def hello()(given QuoteContext): Expr[Unit] = '{ println("Hello") } +} diff --git a/tests/neg-macros/macros-in-same-project-5/Foo.scala b/tests/neg-macros/macros-in-same-project-5/Foo.scala new file mode 100644 index 000000000000..b50e017cc1c5 --- /dev/null +++ b/tests/neg-macros/macros-in-same-project-5/Foo.scala @@ -0,0 +1,8 @@ +import scala.quoted._ +import Bar.aMacroImplementation + +object Foo { + + inline def myMacro(): Unit = ${ aMacroImplementation } + +} diff --git a/tests/neg/macro-cycle1.scala b/tests/neg/macro-cycle1.scala index 9fca9ef3de12..7004812bec46 100644 --- a/tests/neg/macro-cycle1.scala +++ b/tests/neg/macro-cycle1.scala @@ -2,7 +2,7 @@ import scala.quoted.{Expr, QuoteContext} object Test { def fooImpl(given QuoteContext): Expr[Unit] = '{println("hi")} - inline def foo: Unit = ${fooImpl} // error + inline def foo: Unit = ${fooImpl} - foo + foo // error } From 6d5ef8a84e1f716e3bcaffa283fc48d85eff98aa Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Mon, 30 Sep 2019 11:53:12 +0200 Subject: [PATCH 16/31] Improve error message --- compiler/src/dotty/tools/dotc/typer/FrontEnd.scala | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/typer/FrontEnd.scala b/compiler/src/dotty/tools/dotc/typer/FrontEnd.scala index 2e553dba5be3..563b6d7c3d0a 100644 --- a/compiler/src/dotty/tools/dotc/typer/FrontEnd.scala +++ b/compiler/src/dotty/tools/dotc/typer/FrontEnd.scala @@ -122,12 +122,13 @@ class FrontEnd extends Phase { | | ${suspendedUnits.toList}%, % |""" + val enableXprintSuspensionHint = + if (ctx.settings.XprintSuspension.value) "" + else "\n\nCompiling with -Xprint-suspension gives more information." ctx.error(em"""Cyclic macro dependencies $where |Compilation stopped since no further progress can be made. | - |To fix this, place macros in one set of files and their callers in another. - | - |Compiling with -Xprint-suspension gives more information.""") + |To fix this, place macros in one set of files and their callers in another.$enableXprintSuspensionHint""") newUnits } From 1babee2bb3499add1bb218d17a9d062af16dd3e4 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Mon, 30 Sep 2019 16:08:11 +0200 Subject: [PATCH 17/31] Don't suspend if errors were reported. --- compiler/src/dotty/tools/dotc/typer/Inliner.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/src/dotty/tools/dotc/typer/Inliner.scala b/compiler/src/dotty/tools/dotc/typer/Inliner.scala index e65f1fd5a151..0f10d98d8c68 100644 --- a/compiler/src/dotty/tools/dotc/typer/Inliner.scala +++ b/compiler/src/dotty/tools/dotc/typer/Inliner.scala @@ -1239,7 +1239,7 @@ class Inliner(call: tpd.Tree, rhsToInline: tpd.Tree)(implicit ctx: Context) { val ctx1 = tastyreflect.MacroExpansion.context(inlinedFrom) val dependencies = macroDependencies(body) - if dependencies.nonEmpty then + if dependencies.nonEmpty && !ctx.reporter.errorsReported then for sym <- dependencies do if ctx.compilationUnit.source.file == sym.associatedFile then ctx.error(em"Cannot call macro $sym defined in the same source file", call.sourcePos) From ff9c07c58cddf16163fbf97097aac04ecb30cd08 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Mon, 30 Sep 2019 17:30:42 +0200 Subject: [PATCH 18/31] Identify classes defined in current run --- .../src/dotty/tools/dotc/transform/Splicer.scala | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/Splicer.scala b/compiler/src/dotty/tools/dotc/transform/Splicer.scala index 9d8d283caa9c..e0006dd723f4 100644 --- a/compiler/src/dotty/tools/dotc/transform/Splicer.scala +++ b/compiler/src/dotty/tools/dotc/transform/Splicer.scala @@ -351,10 +351,9 @@ object Splicer { throw new StopInterpretation(sw.toString, pos) case ex: InvocationTargetException => ex.getTargetException match { - case targetException: NoClassDefFoundError => // FIXME check that the class is beeining compiled now - val className = targetException.getMessage + case ClassDefinedInCurrentRun(sym) => if (ctx.settings.XprintSuspension.value) - ctx.echo(i"suspension triggered by NoClassDefFoundError($className)", pos) // TODO improve message + ctx.echo(i"suspension triggered by a dependency on $sym", pos) ctx.compilationUnit.suspend() // this throws a SuspendException case targetException => val sw = new StringWriter() @@ -372,6 +371,14 @@ object Splicer { } } + private object ClassDefinedInCurrentRun { + def unapply(targetException: NoClassDefFoundError)(given ctx: Context): Option[Symbol] = { + val className = targetException.getMessage + val sym = ctx.base.staticRef(className.toTypeName).symbol + if (sym.isDefinedInCurrentRun) Some(sym) else None + } + } + /** List of classes of the parameters of the signature of `sym` */ private def paramsSig(sym: Symbol): List[Class[?]] = { def paramClass(param: Type): Class[?] = { From 3a8c56f77f0b543e89144bdd377a45f05c0b3ca5 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Mon, 30 Sep 2019 17:41:53 +0200 Subject: [PATCH 19/31] Remove outdated comment --- compiler/src/dotty/tools/dotc/transform/Splicer.scala | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/Splicer.scala b/compiler/src/dotty/tools/dotc/transform/Splicer.scala index e0006dd723f4..0022c92b174e 100644 --- a/compiler/src/dotty/tools/dotc/transform/Splicer.scala +++ b/compiler/src/dotty/tools/dotc/transform/Splicer.scala @@ -324,7 +324,7 @@ object Splicer { try classLoader.loadClass(name) catch { case _: ClassNotFoundException => - val msg = s"Could not find class $name in classpath$extraMsg" + val msg = s"Could not find class $name in classpath" throw new StopInterpretation(msg, pos) } @@ -332,12 +332,10 @@ object Splicer { try clazz.getMethod(name.toString, paramClasses: _*) catch { case _: NoSuchMethodException => - val msg = em"Could not find method ${clazz.getCanonicalName}.$name with parameters ($paramClasses%, %)$extraMsg" + val msg = em"Could not find method ${clazz.getCanonicalName}.$name with parameters ($paramClasses%, %)" throw new StopInterpretation(msg, pos) } - private def extraMsg = ". The most common reason for that is that you apply macros in the compilation run that defines them" - private def stopIfRuntimeException[T](thunk: => T, method: Method): T = try thunk catch { @@ -351,7 +349,7 @@ object Splicer { throw new StopInterpretation(sw.toString, pos) case ex: InvocationTargetException => ex.getTargetException match { - case ClassDefinedInCurrentRun(sym) => + case MissingClassDefinedInCurrentRun(sym) => if (ctx.settings.XprintSuspension.value) ctx.echo(i"suspension triggered by a dependency on $sym", pos) ctx.compilationUnit.suspend() // this throws a SuspendException @@ -371,7 +369,7 @@ object Splicer { } } - private object ClassDefinedInCurrentRun { + private object MissingClassDefinedInCurrentRun { def unapply(targetException: NoClassDefFoundError)(given ctx: Context): Option[Symbol] = { val className = targetException.getMessage val sym = ctx.base.staticRef(className.toTypeName).symbol From 56f3b2c81f719ed949cfc273c6e02f349b6d00a4 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Mon, 30 Sep 2019 17:52:59 +0200 Subject: [PATCH 20/31] Handle suspended units in InteractiveDriver --- .../src/dotty/tools/dotc/interactive/InteractiveDriver.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/src/dotty/tools/dotc/interactive/InteractiveDriver.scala b/compiler/src/dotty/tools/dotc/interactive/InteractiveDriver.scala index 89472bf126b3..17a0907184c2 100644 --- a/compiler/src/dotty/tools/dotc/interactive/InteractiveDriver.scala +++ b/compiler/src/dotty/tools/dotc/interactive/InteractiveDriver.scala @@ -155,7 +155,7 @@ class InteractiveDriver(val settings: List[String]) extends Driver { run.compileSources(List(source)) run.printSummary() - val unit = ctx.run.units.head + val unit = if ctx.run.units.nonEmpty then ctx.run.units.head else ctx.run.suspendedUnits.head val t = unit.tpdTree cleanup(t) myOpenedTrees(uri) = topLevelTrees(t, source) From e80aefd1d26cf7949f2cab87c66f25a1b7eee6ff Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Wed, 2 Oct 2019 10:48:32 +0200 Subject: [PATCH 21/31] Test for legitimate NoClassDefFoundError in Splicer --- compiler/src/dotty/tools/dotc/transform/Splicer.scala | 7 +++++-- tests/neg-macros/macro-class-not-found-1.check | 8 ++++++++ tests/neg-macros/macro-class-not-found-1/Bar.scala | 5 +++++ tests/neg-macros/macro-class-not-found-1/Foo.scala | 10 ++++++++++ tests/neg-macros/macro-class-not-found-2.check | 8 ++++++++ tests/neg-macros/macro-class-not-found-2/Bar.scala | 5 +++++ tests/neg-macros/macro-class-not-found-2/Foo.scala | 10 ++++++++++ 7 files changed, 51 insertions(+), 2 deletions(-) create mode 100644 tests/neg-macros/macro-class-not-found-1.check create mode 100644 tests/neg-macros/macro-class-not-found-1/Bar.scala create mode 100644 tests/neg-macros/macro-class-not-found-1/Foo.scala create mode 100644 tests/neg-macros/macro-class-not-found-2.check create mode 100644 tests/neg-macros/macro-class-not-found-2/Bar.scala create mode 100644 tests/neg-macros/macro-class-not-found-2/Foo.scala diff --git a/compiler/src/dotty/tools/dotc/transform/Splicer.scala b/compiler/src/dotty/tools/dotc/transform/Splicer.scala index 0022c92b174e..6086ad9e7756 100644 --- a/compiler/src/dotty/tools/dotc/transform/Splicer.scala +++ b/compiler/src/dotty/tools/dotc/transform/Splicer.scala @@ -372,8 +372,11 @@ object Splicer { private object MissingClassDefinedInCurrentRun { def unapply(targetException: NoClassDefFoundError)(given ctx: Context): Option[Symbol] = { val className = targetException.getMessage - val sym = ctx.base.staticRef(className.toTypeName).symbol - if (sym.isDefinedInCurrentRun) Some(sym) else None + if (className eq null) None + else { + val sym = ctx.base.staticRef(className.toTypeName).symbol + if (sym.isDefinedInCurrentRun) Some(sym) else None + } } } diff --git a/tests/neg-macros/macro-class-not-found-1.check b/tests/neg-macros/macro-class-not-found-1.check new file mode 100644 index 000000000000..4117c7cb0b45 --- /dev/null +++ b/tests/neg-macros/macro-class-not-found-1.check @@ -0,0 +1,8 @@ +-- Error: tests/neg-macros/macro-class-not-found-1/Bar.scala:4:13 ------------------------------------------------------ +4 | Foo.myMacro() // error + | ^^^^^^^^^^^^^ + | Exception occurred while executing macro expansion. + | java.lang.NoClassDefFoundError + | at Foo$.aMacroImplementation(Foo.scala:8) + | + | This location is in code that was inlined at Bar.scala:4 diff --git a/tests/neg-macros/macro-class-not-found-1/Bar.scala b/tests/neg-macros/macro-class-not-found-1/Bar.scala new file mode 100644 index 000000000000..df9d90619500 --- /dev/null +++ b/tests/neg-macros/macro-class-not-found-1/Bar.scala @@ -0,0 +1,5 @@ +import scala.quoted._ + +object Bar { + Foo.myMacro() // error +} diff --git a/tests/neg-macros/macro-class-not-found-1/Foo.scala b/tests/neg-macros/macro-class-not-found-1/Foo.scala new file mode 100644 index 000000000000..f722502c5ecf --- /dev/null +++ b/tests/neg-macros/macro-class-not-found-1/Foo.scala @@ -0,0 +1,10 @@ +import scala.quoted._ + +object Foo { + + inline def myMacro(): Unit = ${ aMacroImplementation } + + def aMacroImplementation(given QuoteContext): Expr[Unit] = + throw new NoClassDefFoundError() + +} diff --git a/tests/neg-macros/macro-class-not-found-2.check b/tests/neg-macros/macro-class-not-found-2.check new file mode 100644 index 000000000000..fbeac29d33c1 --- /dev/null +++ b/tests/neg-macros/macro-class-not-found-2.check @@ -0,0 +1,8 @@ +-- Error: tests/neg-macros/macro-class-not-found-2/Bar.scala:4:13 ------------------------------------------------------ +4 | Foo.myMacro() // error + | ^^^^^^^^^^^^^ + | Exception occurred while executing macro expansion. + | java.lang.NoClassDefFoundError: this.is.not.a.Class + | at Foo$.aMacroImplementation(Foo.scala:8) + | + | This location is in code that was inlined at Bar.scala:4 diff --git a/tests/neg-macros/macro-class-not-found-2/Bar.scala b/tests/neg-macros/macro-class-not-found-2/Bar.scala new file mode 100644 index 000000000000..df9d90619500 --- /dev/null +++ b/tests/neg-macros/macro-class-not-found-2/Bar.scala @@ -0,0 +1,5 @@ +import scala.quoted._ + +object Bar { + Foo.myMacro() // error +} diff --git a/tests/neg-macros/macro-class-not-found-2/Foo.scala b/tests/neg-macros/macro-class-not-found-2/Foo.scala new file mode 100644 index 000000000000..f6b68a2770cd --- /dev/null +++ b/tests/neg-macros/macro-class-not-found-2/Foo.scala @@ -0,0 +1,10 @@ +import scala.quoted._ + +object Foo { + + inline def myMacro(): Unit = ${ aMacroImplementation } + + def aMacroImplementation(given QuoteContext): Expr[Unit] = + throw new NoClassDefFoundError("this.is.not.a.Class") + +} From 14bf2c62102ca6b550e7de7a37a847e0b3148052 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Wed, 2 Oct 2019 11:03:23 +0200 Subject: [PATCH 22/31] Add regression tests --- tests/neg-macros/macros-in-same-project-1.scala | 12 ++++++++++++ tests/neg-macros/macros-in-same-project-2.scala | 13 +++++++++++++ tests/neg-macros/macros-in-same-project-6.check | 5 +++++ tests/neg-macros/macros-in-same-project-6/Bar.scala | 5 +++++ tests/neg-macros/macros-in-same-project-6/Foo.scala | 12 ++++++++++++ tests/pos-macros/macros-in-same-project-4/Bar.scala | 11 +++++++++++ tests/pos-macros/macros-in-same-project-4/Baz.scala | 8 ++++++++ 7 files changed, 66 insertions(+) create mode 100644 tests/neg-macros/macros-in-same-project-1.scala create mode 100644 tests/neg-macros/macros-in-same-project-2.scala create mode 100644 tests/neg-macros/macros-in-same-project-6.check create mode 100644 tests/neg-macros/macros-in-same-project-6/Bar.scala create mode 100644 tests/neg-macros/macros-in-same-project-6/Foo.scala create mode 100644 tests/pos-macros/macros-in-same-project-4/Bar.scala create mode 100644 tests/pos-macros/macros-in-same-project-4/Baz.scala diff --git a/tests/neg-macros/macros-in-same-project-1.scala b/tests/neg-macros/macros-in-same-project-1.scala new file mode 100644 index 000000000000..2ccbd6d1ce2c --- /dev/null +++ b/tests/neg-macros/macros-in-same-project-1.scala @@ -0,0 +1,12 @@ + +import scala.quoted._ + +object Bar { + + myMacro() // error + + inline def myMacro(): Unit = ${ aMacroImplementation } + + def aMacroImplementation(given QuoteContext): Expr[Unit] = '{} + +} diff --git a/tests/neg-macros/macros-in-same-project-2.scala b/tests/neg-macros/macros-in-same-project-2.scala new file mode 100644 index 000000000000..0e4053dc1aa9 --- /dev/null +++ b/tests/neg-macros/macros-in-same-project-2.scala @@ -0,0 +1,13 @@ + +import scala.quoted._ + +object Bar { + + myMacro() + + inline def myMacro(): Unit = myMacro2() // error + inline def myMacro2(): Unit = ${ aMacroImplementation } + + def aMacroImplementation(given QuoteContext): Expr[Unit] = '{} + +} diff --git a/tests/neg-macros/macros-in-same-project-6.check b/tests/neg-macros/macros-in-same-project-6.check new file mode 100644 index 000000000000..989cae136283 --- /dev/null +++ b/tests/neg-macros/macros-in-same-project-6.check @@ -0,0 +1,5 @@ +-- Error: tests/neg-macros/macros-in-same-project-6/Bar.scala:4:13 ----------------------------------------------------- +4 | Foo.myMacro() // error + | ^^^^^^^^^^^^^ + | some error + | This location is in code that was inlined at Bar.scala:4 diff --git a/tests/neg-macros/macros-in-same-project-6/Bar.scala b/tests/neg-macros/macros-in-same-project-6/Bar.scala new file mode 100644 index 000000000000..df9d90619500 --- /dev/null +++ b/tests/neg-macros/macros-in-same-project-6/Bar.scala @@ -0,0 +1,5 @@ +import scala.quoted._ + +object Bar { + Foo.myMacro() // error +} diff --git a/tests/neg-macros/macros-in-same-project-6/Foo.scala b/tests/neg-macros/macros-in-same-project-6/Foo.scala new file mode 100644 index 000000000000..7822c7fd234e --- /dev/null +++ b/tests/neg-macros/macros-in-same-project-6/Foo.scala @@ -0,0 +1,12 @@ +import scala.quoted._ + +object Foo { + + inline def myMacro(): Unit = ${ aMacroImplementation } + + def aMacroImplementation(given qctx: QuoteContext): Expr[Unit] = { + import qctx.tasty._ + error("some error", rootPosition) + throw new NoClassDefFoundError("Bar$") + } +} diff --git a/tests/pos-macros/macros-in-same-project-4/Bar.scala b/tests/pos-macros/macros-in-same-project-4/Bar.scala new file mode 100644 index 000000000000..31bd6c04b392 --- /dev/null +++ b/tests/pos-macros/macros-in-same-project-4/Bar.scala @@ -0,0 +1,11 @@ +import scala.quoted._ + +object Bar { + + inline def eqMacro(x: Foo, y: Foo): Boolean = ${ eqMacroExpr('x, 'y) } + def eqMacroExpr(x: Expr[Foo], y: Expr[Foo])(given QuoteContext): Expr[Boolean] = '{ $x == $y } + + inline def plusMacro(x: Foo, y: Foo): Foo = ${ eqPlusExpr('x, 'y) } + def eqPlusExpr(x: Expr[Foo], y: Expr[Foo])(given QuoteContext): Expr[Foo] = '{ new Foo($x.value + $y.value) } + +} diff --git a/tests/pos-macros/macros-in-same-project-4/Baz.scala b/tests/pos-macros/macros-in-same-project-4/Baz.scala new file mode 100644 index 000000000000..b82446e22d34 --- /dev/null +++ b/tests/pos-macros/macros-in-same-project-4/Baz.scala @@ -0,0 +1,8 @@ + + +object Foo { + def eq(x: Foo, y: Foo): Boolean = Bar.eqMacro(x, y) + def plus(x: Foo, y: Foo): Foo = Bar.plusMacro(x, y) +} + +class Foo(val value: Int) From e1c8fdf8b5afeeb5d9b1dd5a504044c3adb689c8 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Wed, 2 Oct 2019 13:29:46 +0200 Subject: [PATCH 23/31] Add output as macro classpath for suspended compilation --- compiler/src/dotty/tools/dotc/core/MacroClassLoader.scala | 3 ++- project/scripts/cmdTests | 4 ++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/compiler/src/dotty/tools/dotc/core/MacroClassLoader.scala b/compiler/src/dotty/tools/dotc/core/MacroClassLoader.scala index cbddcbaa6f05..86f4f7a7c7a6 100644 --- a/compiler/src/dotty/tools/dotc/core/MacroClassLoader.scala +++ b/compiler/src/dotty/tools/dotc/core/MacroClassLoader.scala @@ -21,6 +21,7 @@ object MacroClassLoader { private def makeMacroClassLoader(implicit ctx: Context): ClassLoader = trace("new macro class loader") { val urls = ctx.settings.classpath.value.split(java.io.File.pathSeparatorChar).map(cp => java.nio.file.Paths.get(cp).toUri.toURL) - new java.net.URLClassLoader(urls, getClass.getClassLoader) + val out = ctx.settings.outputDir.value.jpath.toUri.toURL // to find classes in case of suspended compilation + new java.net.URLClassLoader(urls :+ out, getClass.getClassLoader) } } diff --git a/project/scripts/cmdTests b/project/scripts/cmdTests index 79aa4a14ceeb..542dcf3feecb 100755 --- a/project/scripts/cmdTests +++ b/project/scripts/cmdTests @@ -37,6 +37,10 @@ clear_out "$OUT" "$SBT" ";dotc -d $OUT/out.jar $SOURCE; dotc -decompile -classpath $OUT/out.jar -color:never $MAIN" > "$tmp" grep -qe "def main(args: scala.Array\[scala.Predef.String\]): scala.Unit =" "$tmp" +echo "testing sbt dotc with suspension" +clear_out "$OUT" +"$SBT" "dotc -d $OUT tests/pos-macros/macros-in-same-project-1/Bar.scala tests/pos-macros/macros-in-same-project-1/Foo.scala" > "$tmp" + # check that missing source file does not crash message rendering echo "testing that missing source file does not crash message rendering" clear_out "$OUT" From 9134d3ff4b5ba3276a608e6bb72483c05f431e38 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Wed, 2 Oct 2019 17:33:22 +0200 Subject: [PATCH 24/31] Add error when suspension has jar output To avoid a compiler crash. --- compiler/src/dotty/tools/backend/jvm/GenBCode.scala | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/compiler/src/dotty/tools/backend/jvm/GenBCode.scala b/compiler/src/dotty/tools/backend/jvm/GenBCode.scala index 3bca063c858a..b291a6b0ec44 100644 --- a/compiler/src/dotty/tools/backend/jvm/GenBCode.scala +++ b/compiler/src/dotty/tools/backend/jvm/GenBCode.scala @@ -55,6 +55,10 @@ class GenBCode extends Phase { try super.runOn(units) finally myOutput match { case jar: JarArchive => + if (ctx.run.suspendedUnits.nonEmpty) + // If we close the jar the next run will not be able to write on the jar. + // But if we do not close it we cannot use it as part of the macro classpath of the suspended files. + ctx.error("Can not suspend and output to a jar at the same time. See suspension with -Xprint-suspension.") jar.close() case _ => } From a4bdb4f4c162878f7878c46b07ff4c9c1ec7e039 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Thu, 3 Oct 2019 11:10:45 +0200 Subject: [PATCH 25/31] Add supspension to macro docs --- docs/docs/reference/metaprogramming/macros.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/docs/docs/reference/metaprogramming/macros.md b/docs/docs/reference/metaprogramming/macros.md index 0ad19aa737fa..147067fbcbe9 100644 --- a/docs/docs/reference/metaprogramming/macros.md +++ b/docs/docs/reference/metaprogramming/macros.md @@ -598,6 +598,19 @@ def defaultOfImpl(str: String): Expr[Any] = str match { // in a separate file val a: Int = defaultOf("int") val b: String = defaultOf("string") + ``` +### Defining a macro and using it in a single project +It is possible to define macros and use them in the same project as the implementation of +the macros does not depend on code in the file where it used. It might still depend +on types and quoted code that refers to the use-site file. + +To provide this functionality Dotty provides a transparent compilation mode were files that +expand try to expand a macro but fail because the macro has not been compiled yet are suspended. +If there are any suspended files when the compilation ends, the compiler will automatically restart +compilation of the suspended files using the output of the previous (partial) compilation as macro classpath. +In case all files are suspended due to cyclic dependencies the compilation will fail with an error. + + [More details](./macros-spec.md) From ca57ff95fb1e21866919167ab9dbf503d3550099 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Fri, 4 Oct 2019 13:41:36 +0200 Subject: [PATCH 26/31] Test incremental compilation with suspension --- .../macro-expansion-dependencies-1/Main.scala | 3 +++ .../changes/Macro.scala | 11 +++++++++++ .../changes/MacroCompileError.scala | 13 +++++++++++++ .../changes/MacroRuntimeError.scala | 11 +++++++++++ .../project/DottyInjectedPlugin.scala | 11 +++++++++++ .../project/plugins.sbt | 1 + .../macro-expansion-dependencies-1/test | 13 +++++++++++++ .../macro-expansion-dependencies-2/Macro.scala | 11 +++++++++++ .../macro-expansion-dependencies-2/Main.scala | 3 +++ .../changes/MacroRuntime.scala | 9 +++++++++ .../changes/MacroRuntimeCompileError.scala | 11 +++++++++++ .../changes/MacroRuntimeRuntimeError.scala | 9 +++++++++ .../project/DottyInjectedPlugin.scala | 11 +++++++++++ .../project/plugins.sbt | 1 + .../macro-expansion-dependencies-2/test | 13 +++++++++++++ 15 files changed, 131 insertions(+) create mode 100644 sbt-dotty/sbt-test/source-dependencies/macro-expansion-dependencies-1/Main.scala create mode 100644 sbt-dotty/sbt-test/source-dependencies/macro-expansion-dependencies-1/changes/Macro.scala create mode 100644 sbt-dotty/sbt-test/source-dependencies/macro-expansion-dependencies-1/changes/MacroCompileError.scala create mode 100644 sbt-dotty/sbt-test/source-dependencies/macro-expansion-dependencies-1/changes/MacroRuntimeError.scala create mode 100644 sbt-dotty/sbt-test/source-dependencies/macro-expansion-dependencies-1/project/DottyInjectedPlugin.scala create mode 100644 sbt-dotty/sbt-test/source-dependencies/macro-expansion-dependencies-1/project/plugins.sbt create mode 100644 sbt-dotty/sbt-test/source-dependencies/macro-expansion-dependencies-1/test create mode 100644 sbt-dotty/sbt-test/source-dependencies/macro-expansion-dependencies-2/Macro.scala create mode 100644 sbt-dotty/sbt-test/source-dependencies/macro-expansion-dependencies-2/Main.scala create mode 100644 sbt-dotty/sbt-test/source-dependencies/macro-expansion-dependencies-2/changes/MacroRuntime.scala create mode 100644 sbt-dotty/sbt-test/source-dependencies/macro-expansion-dependencies-2/changes/MacroRuntimeCompileError.scala create mode 100644 sbt-dotty/sbt-test/source-dependencies/macro-expansion-dependencies-2/changes/MacroRuntimeRuntimeError.scala create mode 100644 sbt-dotty/sbt-test/source-dependencies/macro-expansion-dependencies-2/project/DottyInjectedPlugin.scala create mode 100644 sbt-dotty/sbt-test/source-dependencies/macro-expansion-dependencies-2/project/plugins.sbt create mode 100644 sbt-dotty/sbt-test/source-dependencies/macro-expansion-dependencies-2/test diff --git a/sbt-dotty/sbt-test/source-dependencies/macro-expansion-dependencies-1/Main.scala b/sbt-dotty/sbt-test/source-dependencies/macro-expansion-dependencies-1/Main.scala new file mode 100644 index 000000000000..4d904bf6177f --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/macro-expansion-dependencies-1/Main.scala @@ -0,0 +1,3 @@ +object Main extends App { + Macro.f() +} diff --git a/sbt-dotty/sbt-test/source-dependencies/macro-expansion-dependencies-1/changes/Macro.scala b/sbt-dotty/sbt-test/source-dependencies/macro-expansion-dependencies-1/changes/Macro.scala new file mode 100644 index 000000000000..1540b3faabee --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/macro-expansion-dependencies-1/changes/Macro.scala @@ -0,0 +1,11 @@ +import scala.quoted._ + +object Macro { + + inline def f(): Unit = ${ macroImplementation } + + def macroImplementation(given QuoteContext): Expr[Unit] = { + '{ println("Implementation in Macro") } + } + +} diff --git a/sbt-dotty/sbt-test/source-dependencies/macro-expansion-dependencies-1/changes/MacroCompileError.scala b/sbt-dotty/sbt-test/source-dependencies/macro-expansion-dependencies-1/changes/MacroCompileError.scala new file mode 100644 index 000000000000..cd91962130da --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/macro-expansion-dependencies-1/changes/MacroCompileError.scala @@ -0,0 +1,13 @@ +import scala.quoted._ + +object Macro { + + inline def f(): Unit = ${ macroImplementation } + + def macroImplementation(given qctx: QuoteContext): Expr[Unit] = { + import qctx.tasty._ + error("some error", rootPosition) + '{ println("Implementation in MacroCompileError") } + } + +} diff --git a/sbt-dotty/sbt-test/source-dependencies/macro-expansion-dependencies-1/changes/MacroRuntimeError.scala b/sbt-dotty/sbt-test/source-dependencies/macro-expansion-dependencies-1/changes/MacroRuntimeError.scala new file mode 100644 index 000000000000..23a4e6665154 --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/macro-expansion-dependencies-1/changes/MacroRuntimeError.scala @@ -0,0 +1,11 @@ +import scala.quoted._ + +object Macro { + + inline def f(): Unit = ${ macroImplementation } + + def macroImplementation(given qctx: QuoteContext): Expr[Unit] = { + '{ ??? } + } + +} diff --git a/sbt-dotty/sbt-test/source-dependencies/macro-expansion-dependencies-1/project/DottyInjectedPlugin.scala b/sbt-dotty/sbt-test/source-dependencies/macro-expansion-dependencies-1/project/DottyInjectedPlugin.scala new file mode 100644 index 000000000000..fb946c4b8c61 --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/macro-expansion-dependencies-1/project/DottyInjectedPlugin.scala @@ -0,0 +1,11 @@ +import sbt._ +import Keys._ + +object DottyInjectedPlugin extends AutoPlugin { + override def requires = plugins.JvmPlugin + override def trigger = allRequirements + + override val projectSettings = Seq( + scalaVersion := sys.props("plugin.scalaVersion") + ) +} diff --git a/sbt-dotty/sbt-test/source-dependencies/macro-expansion-dependencies-1/project/plugins.sbt b/sbt-dotty/sbt-test/source-dependencies/macro-expansion-dependencies-1/project/plugins.sbt new file mode 100644 index 000000000000..c17caab2d98c --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/macro-expansion-dependencies-1/project/plugins.sbt @@ -0,0 +1 @@ +addSbtPlugin("ch.epfl.lamp" % "sbt-dotty" % sys.props("plugin.version")) diff --git a/sbt-dotty/sbt-test/source-dependencies/macro-expansion-dependencies-1/test b/sbt-dotty/sbt-test/source-dependencies/macro-expansion-dependencies-1/test new file mode 100644 index 000000000000..45a1c3f4fb8f --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/macro-expansion-dependencies-1/test @@ -0,0 +1,13 @@ +$ copy-file changes/Macro.scala Macro.scala +> run + +# use an implemntation of the macro that emits a compile time error +$ copy-file changes/MacroCompileError.scala Macro.scala +-> compile + +$ copy-file changes/Macro.scala Macro.scala +> clean +> compile + +$ copy-file changes/MacroRuntimeError.scala Macro.scala +-> run diff --git a/sbt-dotty/sbt-test/source-dependencies/macro-expansion-dependencies-2/Macro.scala b/sbt-dotty/sbt-test/source-dependencies/macro-expansion-dependencies-2/Macro.scala new file mode 100644 index 000000000000..138a629f8761 --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/macro-expansion-dependencies-2/Macro.scala @@ -0,0 +1,11 @@ +import scala.quoted._ + +object Macro { + + inline def f(): Unit = ${ macroImplementation } + + def macroImplementation(given QuoteContext): Expr[Unit] = + MacroRuntime.impl() + + +} diff --git a/sbt-dotty/sbt-test/source-dependencies/macro-expansion-dependencies-2/Main.scala b/sbt-dotty/sbt-test/source-dependencies/macro-expansion-dependencies-2/Main.scala new file mode 100644 index 000000000000..4d904bf6177f --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/macro-expansion-dependencies-2/Main.scala @@ -0,0 +1,3 @@ +object Main extends App { + Macro.f() +} diff --git a/sbt-dotty/sbt-test/source-dependencies/macro-expansion-dependencies-2/changes/MacroRuntime.scala b/sbt-dotty/sbt-test/source-dependencies/macro-expansion-dependencies-2/changes/MacroRuntime.scala new file mode 100644 index 000000000000..011ac9885f86 --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/macro-expansion-dependencies-2/changes/MacroRuntime.scala @@ -0,0 +1,9 @@ +import scala.quoted._ + +object MacroRuntime { + + def impl()(given QuoteContext): Expr[Unit] = { + '{ println("Implementation in Macro") } + } + +} diff --git a/sbt-dotty/sbt-test/source-dependencies/macro-expansion-dependencies-2/changes/MacroRuntimeCompileError.scala b/sbt-dotty/sbt-test/source-dependencies/macro-expansion-dependencies-2/changes/MacroRuntimeCompileError.scala new file mode 100644 index 000000000000..d06584e0023e --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/macro-expansion-dependencies-2/changes/MacroRuntimeCompileError.scala @@ -0,0 +1,11 @@ +import scala.quoted._ + +object MacroRuntime { + + def impl()(given qctx: QuoteContext): Expr[Unit] = { + import qctx.tasty._ + error("some error", rootPosition) + '{ println("Implementation in MacroCompileError") } + } + +} diff --git a/sbt-dotty/sbt-test/source-dependencies/macro-expansion-dependencies-2/changes/MacroRuntimeRuntimeError.scala b/sbt-dotty/sbt-test/source-dependencies/macro-expansion-dependencies-2/changes/MacroRuntimeRuntimeError.scala new file mode 100644 index 000000000000..dcda4e276c3f --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/macro-expansion-dependencies-2/changes/MacroRuntimeRuntimeError.scala @@ -0,0 +1,9 @@ +import scala.quoted._ + +object MacroRuntime { + + def impl()(given qctx: QuoteContext): Expr[Unit] = { + '{ ??? } + } + +} diff --git a/sbt-dotty/sbt-test/source-dependencies/macro-expansion-dependencies-2/project/DottyInjectedPlugin.scala b/sbt-dotty/sbt-test/source-dependencies/macro-expansion-dependencies-2/project/DottyInjectedPlugin.scala new file mode 100644 index 000000000000..fb946c4b8c61 --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/macro-expansion-dependencies-2/project/DottyInjectedPlugin.scala @@ -0,0 +1,11 @@ +import sbt._ +import Keys._ + +object DottyInjectedPlugin extends AutoPlugin { + override def requires = plugins.JvmPlugin + override def trigger = allRequirements + + override val projectSettings = Seq( + scalaVersion := sys.props("plugin.scalaVersion") + ) +} diff --git a/sbt-dotty/sbt-test/source-dependencies/macro-expansion-dependencies-2/project/plugins.sbt b/sbt-dotty/sbt-test/source-dependencies/macro-expansion-dependencies-2/project/plugins.sbt new file mode 100644 index 000000000000..c17caab2d98c --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/macro-expansion-dependencies-2/project/plugins.sbt @@ -0,0 +1 @@ +addSbtPlugin("ch.epfl.lamp" % "sbt-dotty" % sys.props("plugin.version")) diff --git a/sbt-dotty/sbt-test/source-dependencies/macro-expansion-dependencies-2/test b/sbt-dotty/sbt-test/source-dependencies/macro-expansion-dependencies-2/test new file mode 100644 index 000000000000..bf2c8effa3bd --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/macro-expansion-dependencies-2/test @@ -0,0 +1,13 @@ +$ copy-file changes/MacroRuntime.scala MacroRuntime.scala +> run + +# use an implemntation of the macro that emits a compile time error +$ copy-file changes/MacroRuntimeCompileError.scala MacroRuntime.scala +-> compile + +$ copy-file changes/MacroRuntime.scala MacroRuntime.scala +> clean +> compile + +$ copy-file changes/MacroRuntimeRuntimeError.scala MacroRuntime.scala +-> run From 4c353c6d69e3fc69317e3475b134bb3495cd189f Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Mon, 7 Oct 2019 08:52:14 +0200 Subject: [PATCH 27/31] Typos and specify run vs compile time --- docs/docs/reference/metaprogramming/macros.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/docs/reference/metaprogramming/macros.md b/docs/docs/reference/metaprogramming/macros.md index 147067fbcbe9..32c8e47bbd65 100644 --- a/docs/docs/reference/metaprogramming/macros.md +++ b/docs/docs/reference/metaprogramming/macros.md @@ -602,12 +602,12 @@ val b: String = defaultOf("string") ``` ### Defining a macro and using it in a single project -It is possible to define macros and use them in the same project as the implementation of -the macros does not depend on code in the file where it used. It might still depend -on types and quoted code that refers to the use-site file. +It is possible to define macros and use them in the same project as long as the implementation +of the macros does not have run-time dependencies on code in the file where it is used. +It might still have compile-time dependencies on types and quoted code that refers to the use-site file. -To provide this functionality Dotty provides a transparent compilation mode were files that -expand try to expand a macro but fail because the macro has not been compiled yet are suspended. +To provide this functionality Dotty provides a transparent compilation mode where files that +try to expand a macro but fail because the macro has not been compiled yet are suspended. If there are any suspended files when the compilation ends, the compiler will automatically restart compilation of the suspended files using the output of the previous (partial) compilation as macro classpath. In case all files are suspended due to cyclic dependencies the compilation will fail with an error. From 0c33e640f09523d22447949030197342c59e3015 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Mon, 7 Oct 2019 09:09:31 +0200 Subject: [PATCH 28/31] Add test on incremental and suspended compilation --- .../macro-expansion-dependencies-3/Macros.scala | 11 +++++++++++ .../macro-expansion-dependencies-3/Test.scala | 9 +++++++++ .../macro-expansion-dependencies-3/TestB.scala | 6 ++++++ .../changes/TestB.scala | 6 ++++++ .../project/DottyInjectedPlugin.scala | 11 +++++++++++ .../project/plugins.sbt | 1 + .../macro-expansion-dependencies-3/test | 6 ++++++ 7 files changed, 50 insertions(+) create mode 100644 sbt-dotty/sbt-test/source-dependencies/macro-expansion-dependencies-3/Macros.scala create mode 100644 sbt-dotty/sbt-test/source-dependencies/macro-expansion-dependencies-3/Test.scala create mode 100644 sbt-dotty/sbt-test/source-dependencies/macro-expansion-dependencies-3/TestB.scala create mode 100644 sbt-dotty/sbt-test/source-dependencies/macro-expansion-dependencies-3/changes/TestB.scala create mode 100644 sbt-dotty/sbt-test/source-dependencies/macro-expansion-dependencies-3/project/DottyInjectedPlugin.scala create mode 100644 sbt-dotty/sbt-test/source-dependencies/macro-expansion-dependencies-3/project/plugins.sbt create mode 100644 sbt-dotty/sbt-test/source-dependencies/macro-expansion-dependencies-3/test diff --git a/sbt-dotty/sbt-test/source-dependencies/macro-expansion-dependencies-3/Macros.scala b/sbt-dotty/sbt-test/source-dependencies/macro-expansion-dependencies-3/Macros.scala new file mode 100644 index 000000000000..e40323fff85a --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/macro-expansion-dependencies-3/Macros.scala @@ -0,0 +1,11 @@ +import scala.quoted._ + +object Macros { + + inline def foo: A = ${ fooImpl } + + def fooImpl(given qctx: QuoteContext): Expr[A] = { + new B().f + '{ new A; } + } +} diff --git a/sbt-dotty/sbt-test/source-dependencies/macro-expansion-dependencies-3/Test.scala b/sbt-dotty/sbt-test/source-dependencies/macro-expansion-dependencies-3/Test.scala new file mode 100644 index 000000000000..35482f9df666 --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/macro-expansion-dependencies-3/Test.scala @@ -0,0 +1,9 @@ +class A + +object Test { + import Macros._ + + def main(args: Array[String]): Unit = { + foo + } +} diff --git a/sbt-dotty/sbt-test/source-dependencies/macro-expansion-dependencies-3/TestB.scala b/sbt-dotty/sbt-test/source-dependencies/macro-expansion-dependencies-3/TestB.scala new file mode 100644 index 000000000000..04fd155fc7a2 --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/macro-expansion-dependencies-3/TestB.scala @@ -0,0 +1,6 @@ +class B { + import Macros._ + + def f: Int = 3 + // def g: A = foo // comment out `g` first, then enable `g` +} diff --git a/sbt-dotty/sbt-test/source-dependencies/macro-expansion-dependencies-3/changes/TestB.scala b/sbt-dotty/sbt-test/source-dependencies/macro-expansion-dependencies-3/changes/TestB.scala new file mode 100644 index 000000000000..28dd5461cde8 --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/macro-expansion-dependencies-3/changes/TestB.scala @@ -0,0 +1,6 @@ +class B { + import Macros._ + + def f: Int = 3 + def g: A = foo // comment out `g` first, then enable `g` +} diff --git a/sbt-dotty/sbt-test/source-dependencies/macro-expansion-dependencies-3/project/DottyInjectedPlugin.scala b/sbt-dotty/sbt-test/source-dependencies/macro-expansion-dependencies-3/project/DottyInjectedPlugin.scala new file mode 100644 index 000000000000..fb946c4b8c61 --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/macro-expansion-dependencies-3/project/DottyInjectedPlugin.scala @@ -0,0 +1,11 @@ +import sbt._ +import Keys._ + +object DottyInjectedPlugin extends AutoPlugin { + override def requires = plugins.JvmPlugin + override def trigger = allRequirements + + override val projectSettings = Seq( + scalaVersion := sys.props("plugin.scalaVersion") + ) +} diff --git a/sbt-dotty/sbt-test/source-dependencies/macro-expansion-dependencies-3/project/plugins.sbt b/sbt-dotty/sbt-test/source-dependencies/macro-expansion-dependencies-3/project/plugins.sbt new file mode 100644 index 000000000000..c17caab2d98c --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/macro-expansion-dependencies-3/project/plugins.sbt @@ -0,0 +1 @@ +addSbtPlugin("ch.epfl.lamp" % "sbt-dotty" % sys.props("plugin.version")) diff --git a/sbt-dotty/sbt-test/source-dependencies/macro-expansion-dependencies-3/test b/sbt-dotty/sbt-test/source-dependencies/macro-expansion-dependencies-3/test new file mode 100644 index 000000000000..b743359dcdc7 --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/macro-expansion-dependencies-3/test @@ -0,0 +1,6 @@ +> compile + +# make sure that Macros is recompiled due to it's dependencie on B.f +# this will end in a failure to compile due to cyclic macros +$ copy-file changes/TestB.scala TestB.scala +-> compile From 5d863fb00746617051dd5f9cafc78fbc78af25ab Mon Sep 17 00:00:00 2001 From: Liu Fengyun Date: Sat, 19 Oct 2019 12:03:32 +0200 Subject: [PATCH 29/31] Fix check file From 845768000c9acf4b785e7f92b68f140ca3e42469 Mon Sep 17 00:00:00 2001 From: Liu Fengyun Date: Sat, 19 Oct 2019 22:42:10 +0200 Subject: [PATCH 30/31] Fix trailing space in error message --- compiler/src/dotty/tools/dotc/reporting/MessageRendering.scala | 2 +- tests/neg-macros/i6976.check | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/reporting/MessageRendering.scala b/compiler/src/dotty/tools/dotc/reporting/MessageRendering.scala index 251b396ba224..e4e01de27e4d 100644 --- a/compiler/src/dotty/tools/dotc/reporting/MessageRendering.scala +++ b/compiler/src/dotty/tools/dotc/reporting/MessageRendering.scala @@ -104,7 +104,7 @@ trait MessageRendering { } msg.linesIterator - .map { line => " " * (offset - 1) + "|" + padding + line} + .map { line => " " * (offset - 1) + "|" + (if line.isEmpty then "" else padding + line) } .mkString(EOL) } diff --git a/tests/neg-macros/i6976.check b/tests/neg-macros/i6976.check index 64cf0ee8eef0..6cd7aa494345 100644 --- a/tests/neg-macros/i6976.check +++ b/tests/neg-macros/i6976.check @@ -5,5 +5,5 @@ | Exception occurred while executing macro expansion. | scala.MatchError: Inlined(EmptyTree,List(),Literal(Constant(2))) (of class dotty.tools.dotc.ast.Trees$Inlined) | at playground.macros$.mcrImpl(Macro_1.scala:12) - | + | | This location is in code that was inlined at Test_2.scala:5 From 4f59892f25ca264a367f3fefbeea48dc1bb1546f Mon Sep 17 00:00:00 2001 From: Liu Fengyun Date: Sat, 19 Oct 2019 23:27:55 +0200 Subject: [PATCH 31/31] Fix CI: remove trailing spaces in check files --- tests/neg/classOf.check | 2 +- tests/neg/i5433.check | 6 +++--- tests/neg/subtyping.check | 8 ++++---- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/tests/neg/classOf.check b/tests/neg/classOf.check index ef20b642d88c..45ae749a0940 100644 --- a/tests/neg/classOf.check +++ b/tests/neg/classOf.check @@ -6,7 +6,7 @@ 7 | def f2[T <: String] = classOf[T] // error | ^ | T is not a class type - | + | | where: T is a type in method f2 with bounds <: String -- Error: tests/neg/classOf.scala:9:18 --------------------------------------------------------------------------------- 9 | val y = classOf[C { type I = String }] // error diff --git a/tests/neg/i5433.check b/tests/neg/i5433.check index 47fee9734bb6..7f3986ab5553 100644 --- a/tests/neg/i5433.check +++ b/tests/neg/i5433.check @@ -3,7 +3,7 @@ | ^ | class Fail cannot be defined due to a conflict between its parents when | implementing a super-accessor for foo in trait C: - | + | | 1. One of its parent (C) contains a call super.foo in its body, | and when a super-call in a trait is written without an explicit parent | listed in brackets, it is implemented by a generated super-accessor in @@ -19,9 +19,9 @@ | Y (the type of foo in trait C). | Hence, the super-accessor that needs to be generated in Fail | is illegal. - | + | | Here are two possible ways to resolve this: - | + | | 1. Change the linearization order of Fail such that | C comes before B. | 2. Alternatively, replace super.foo in the body of trait C by a diff --git a/tests/neg/subtyping.check b/tests/neg/subtyping.check index 7cf78fc4b7d4..39a02066b3d0 100644 --- a/tests/neg/subtyping.check +++ b/tests/neg/subtyping.check @@ -3,16 +3,16 @@ | ^ | Cannot prove that B#X <:< A#X.. | I found: - | + | | <:<.refl[Nothing] - | + | | But method refl in object <:< does not match type B#X <:< A#X. -- Error: tests/neg/subtyping.scala:12:27 ------------------------------------------------------------------------------ 12 | implicitly[a.T <:< a.U] // error: no implicit argument | ^ | Cannot prove that a.T <:< a.U.. | I found: - | + | | <:<.refl[Nothing] - | + | | But method refl in object <:< does not match type a.T <:< a.U.