diff --git a/compiler/src/dotty/tools/dotc/typer/Inliner.scala b/compiler/src/dotty/tools/dotc/typer/Inliner.scala index c1e3222892d6..0444b5b6a9f6 100644 --- a/compiler/src/dotty/tools/dotc/typer/Inliner.scala +++ b/compiler/src/dotty/tools/dotc/typer/Inliner.scala @@ -1239,17 +1239,60 @@ 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 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 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) + + 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]] { + private[this] var level = -1 + override def apply(syms: Set[Symbol], tree: tpd.Tree)(implicit ctx: Context): Set[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) + case Quoted(body) => + level += 1 + try apply(syms, body) + finally level -= 1 + case Spliced(body) => + level -= 1 + try apply(syms, body) + finally level += 1 + case _ => + foldOver(syms, tree) + } + } + }.apply(Set.empty, tree) } } diff --git a/tests/neg-macros/macros-in-same-project-1.check b/tests/neg-macros/macros-in-same-project-1.check new file mode 100644 index 000000000000..f257aa16f0d2 --- /dev/null +++ b/tests/neg-macros/macros-in-same-project-1.check @@ -0,0 +1,8 @@ +-- 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 new file mode 100644 index 000000000000..2924e2b2411a --- /dev/null +++ b/tests/neg-macros/macros-in-same-project-1/Bar.scala @@ -0,0 +1,3 @@ +object Bar { + Foo.myMacro() // error +} diff --git a/tests/neg-macros/macros-in-same-project-1/Foo.scala b/tests/neg-macros/macros-in-same-project-1/Foo.scala new file mode 100644 index 000000000000..0bc30057b4fe --- /dev/null +++ b/tests/neg-macros/macros-in-same-project-1/Foo.scala @@ -0,0 +1,9 @@ +import scala.quoted._ + +object Foo { + + inline def myMacro(): Unit = ${ aMacroImplementation } + + def aMacroImplementation given QuoteContext: Expr[Unit] = '{ println("Hello") } + +} diff --git a/tests/neg-macros/macros-in-same-project-2.check b/tests/neg-macros/macros-in-same-project-2.check new file mode 100644 index 000000000000..0ee5ec3f3a98 --- /dev/null +++ b/tests/neg-macros/macros-in-same-project-2.check @@ -0,0 +1,8 @@ +-- 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/Bar.scala b/tests/neg-macros/macros-in-same-project-2/Bar.scala new file mode 100644 index 000000000000..ebd6fb25895f --- /dev/null +++ b/tests/neg-macros/macros-in-same-project-2/Bar.scala @@ -0,0 +1,7 @@ +object Bar { + + inline def myMacro(): Unit = ${ Foo.aMacroImplementation } + + myMacro() // error + +} diff --git a/tests/neg-macros/macros-in-same-project-2/Foo.scala b/tests/neg-macros/macros-in-same-project-2/Foo.scala new file mode 100644 index 000000000000..f4a4a63f72cf --- /dev/null +++ b/tests/neg-macros/macros-in-same-project-2/Foo.scala @@ -0,0 +1,7 @@ +import scala.quoted._ + +object Foo { + + def aMacroImplementation given QuoteContext: Expr[Unit] = '{ println("Hello") } + +} diff --git a/tests/neg-macros/macros-in-same-project-3.check b/tests/neg-macros/macros-in-same-project-3.check new file mode 100644 index 000000000000..e2865e55578d --- /dev/null +++ b/tests/neg-macros/macros-in-same-project-3.check @@ -0,0 +1,8 @@ +-- 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-3/Bar.scala b/tests/neg-macros/macros-in-same-project-3/Bar.scala new file mode 100644 index 000000000000..1cf2a2fa40e4 --- /dev/null +++ b/tests/neg-macros/macros-in-same-project-3/Bar.scala @@ -0,0 +1,9 @@ +import scala.quoted._ + +object Bar { + + inline def myMacro(): Unit = ${ aMacroImplementation } + + def aMacroImplementation given QuoteContext: Expr[Unit] = Foo.hello() + +} diff --git a/tests/neg-macros/macros-in-same-project-3/Baz.scala b/tests/neg-macros/macros-in-same-project-3/Baz.scala new file mode 100644 index 000000000000..fa9ced2b6930 --- /dev/null +++ b/tests/neg-macros/macros-in-same-project-3/Baz.scala @@ -0,0 +1,3 @@ +object Baz { + Bar.myMacro() // error +} diff --git a/tests/neg-macros/macros-in-same-project-3/Foo.scala b/tests/neg-macros/macros-in-same-project-3/Foo.scala new file mode 100644 index 000000000000..ab5e99c0df41 --- /dev/null +++ b/tests/neg-macros/macros-in-same-project-3/Foo.scala @@ -0,0 +1,7 @@ +import scala.quoted._ + +object Foo { + + def hello() given QuoteContext: Expr[Unit] = '{ println("Hello") } + +} diff --git a/tests/neg-macros/macros-in-same-project-4.check b/tests/neg-macros/macros-in-same-project-4.check new file mode 100644 index 000000000000..5cdc12b31cb9 --- /dev/null +++ b/tests/neg-macros/macros-in-same-project-4.check @@ -0,0 +1,8 @@ +-- 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 diff --git a/tests/neg-macros/macros-in-same-project-4/Bar.scala b/tests/neg-macros/macros-in-same-project-4/Bar.scala new file mode 100644 index 000000000000..5a17499ffea7 --- /dev/null +++ b/tests/neg-macros/macros-in-same-project-4/Bar.scala @@ -0,0 +1,8 @@ +import scala.quoted._ + +object Bar { + + Foo.myMacro() // error + + def hello() given QuoteContext: Expr[Unit] = '{ println("Hello") } +} diff --git a/tests/neg-macros/macros-in-same-project-4/Foo.scala b/tests/neg-macros/macros-in-same-project-4/Foo.scala new file mode 100644 index 000000000000..b880850afdcb --- /dev/null +++ b/tests/neg-macros/macros-in-same-project-4/Foo.scala @@ -0,0 +1,9 @@ +import scala.quoted._ + +object Foo { + + inline def myMacro(): Unit = ${ aMacroImplementation } + + def aMacroImplementation given QuoteContext: Expr[Unit] = Bar.hello() + +}