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 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 _ => } diff --git a/compiler/src/dotty/tools/dotc/CompilationUnit.scala b/compiler/src/dotty/tools/dotc/CompilationUnit.scala index bce33813fa70..ea0faa19ce6d 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) { @@ -31,10 +32,22 @@ 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 + if (ctx.settings.XprintSuspension.value) + ctx.echo(i"suspended: $this") + 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..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} @@ -30,23 +31,33 @@ 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 suspendedUnits = run.suspendedUnits.toList + if (ctx.settings.XprintSuspension.value) + ctx.echo(i"compiling suspended $suspendedUnits%, %") + val run1 = compiler.newRun + for unit <- suspendedUnits do unit.suspended = false + run1.compileUnits(suspendedUnits) + 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/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/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/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) 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/compiler/src/dotty/tools/dotc/transform/Splicer.scala b/compiler/src/dotty/tools/dotc/transform/Splicer.scala index bd988422541f..6086ad9e7756 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 @@ -322,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) } @@ -330,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 { @@ -348,21 +348,38 @@ 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 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 + 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) } + private object MissingClassDefinedInCurrentRun { + def unapply(targetException: NoClassDefFoundError)(given ctx: Context): Option[Symbol] = { + val className = targetException.getMessage + if (className eq null) None + else { + 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[?] = { diff --git a/compiler/src/dotty/tools/dotc/typer/FrontEnd.scala b/compiler/src/dotty/tools/dotc/typer/FrontEnd.scala index f466a6956e62..563b6d7c3d0a 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,23 @@ 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}%, % + |""" + 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.$enableXprintSuspensionHint""") + 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..0f10d98d8c68 100644 --- a/compiler/src/dotty/tools/dotc/typer/Inliner.scala +++ b/compiler/src/dotty/tools/dotc/typer/Inliner.scala @@ -1237,48 +1237,40 @@ 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 && !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) + if (ctx.settings.XprintSuspension.value) + ctx.echo(i"suspension triggered by macro call to ${sym.showLocated} in ${sym.associatedFile}", call.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. * 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) @@ -1290,6 +1282,6 @@ class Inliner(call: tpd.Tree, rhsToInline: tpd.Tree)(implicit ctx: Context) { case _ => foldOver(syms, tree) } - }.apply(Set.empty, tree) + }.apply(Nil, tree) } diff --git a/compiler/src/dotty/tools/dotc/typer/Namer.scala b/compiler/src/dotty/tools/dotc/typer/Namer.scala index b96cb384517d..76e51e87ae8b 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/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" 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/docs/docs/reference/metaprogramming/macros.md b/docs/docs/reference/metaprogramming/macros.md index 0ad19aa737fa..32c8e47bbd65 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 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 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. + + [More details](./macros-spec.md) 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" 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 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 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 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/macros-in-same-project-1/Bar.scala b/tests/neg-macros/macro-class-not-found-1/Bar.scala similarity index 63% rename from tests/neg-macros/macros-in-same-project-1/Bar.scala rename to tests/neg-macros/macro-class-not-found-1/Bar.scala index 2924e2b2411a..df9d90619500 100644 --- a/tests/neg-macros/macros-in-same-project-1/Bar.scala +++ b/tests/neg-macros/macro-class-not-found-1/Bar.scala @@ -1,3 +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") + +} 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.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.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-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-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/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-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/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 +} 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/macro-cycle1.scala b/tests/neg/macro-cycle1.scala new file mode 100644 index 000000000000..7004812bec46 --- /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 // error +} 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. 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 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) diff --git a/tests/run-macros/macros-in-same-project1/Foo.scala b/tests/run-macros/macros-in-same-project1/Foo.scala new file mode 100644 index 000000000000..524ccde401f8 --- /dev/null +++ b/tests/run-macros/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/macros-in-same-project1/Test.scala b/tests/run-macros/macros-in-same-project1/Test.scala new file mode 100644 index 000000000000..81c22b90899a --- /dev/null +++ b/tests/run-macros/macros-in-same-project1/Test.scala @@ -0,0 +1,3 @@ +object Test extends App { + Foo.myMacro() +} \ No newline at end of file diff --git a/tests/run-macros/reflect-inline/assert_1.scala b/tests/run-macros/reflect-inline/assert_1.scala index 693a185eee32..d408236f5fe2 100644 --- a/tests/run-macros/reflect-inline/assert_1.scala +++ b/tests/run-macros/reflect-inline/assert_1.scala @@ -5,7 +5,7 @@ object api { ${ stripImpl(x) } private def stripImpl(x: String)(given qctx: QuoteContext): Expr[String] = - Expr(x.stripMargin) + Expr(augmentString(x).stripMargin) inline def typeChecks(inline x: String): Boolean = ${ typeChecksImpl(scala.compiletime.testing.typeChecks(x)) } diff --git a/tests/run-macros/reflect-inline/test_2.scala b/tests/run-macros/reflect-inline/test_2.scala index f99fbacbf0d7..2f36d7b52214 100644 --- a/tests/run-macros/reflect-inline/test_2.scala +++ b/tests/run-macros/reflect-inline/test_2.scala @@ -7,4 +7,4 @@ object Test { assert(scala.compiletime.testing.typeChecks("|1 + 1".stripMargin)) assert(("|3 + " + a).stripMargin == "3 + 5") } -} \ No newline at end of file +} 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