Skip to content

Commit

Permalink
Detect macro dependencies within the current run
Browse files Browse the repository at this point in the history
  • Loading branch information
nicolasstucki committed Sep 10, 2019
1 parent 7423d31 commit c030b30
Show file tree
Hide file tree
Showing 14 changed files with 133 additions and 8 deletions.
47 changes: 39 additions & 8 deletions compiler/src/dotty/tools/dotc/typer/Inliner.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1239,17 +1239,48 @@ 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 by the tree and defined in the current run */
private def macroDependencies(tree: Tree)(implicit ctx: Context) = {
new TreeAccumulator[Set[Symbol]] {
override def apply(syms: Set[Symbol], tree: tpd.Tree)(implicit ctx: Context): Set[Symbol] = {
val newSyms = tree match {
case tree: RefTree if tree.symbol.isDefinedInCurrentRun && tree.symbol.owner != ctx.owner =>
syms + tree.symbol
case _ => syms
}
foldOver(newSyms, tree)
}
}.apply(Set.empty, tree)
}
}

8 changes: 8 additions & 0 deletions tests/neg-macros/macros-in-same-project-1.check
Original file line number Diff line number Diff line change
@@ -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
3 changes: 3 additions & 0 deletions tests/neg-macros/macros-in-same-project-1/Bar.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
object Bar {
Foo.myMacro() // error
}
9 changes: 9 additions & 0 deletions tests/neg-macros/macros-in-same-project-1/Foo.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import scala.quoted._

object Foo {

inline def myMacro(): Unit = ${ aMacroImplementation }

def aMacroImplementation given QuoteContext: Expr[Unit] = '{ println("Hello") }

}
8 changes: 8 additions & 0 deletions tests/neg-macros/macros-in-same-project-2.check
Original file line number Diff line number Diff line change
@@ -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
7 changes: 7 additions & 0 deletions tests/neg-macros/macros-in-same-project-2/Bar.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
object Bar {

inline def myMacro(): Unit = ${ Foo.aMacroImplementation }

myMacro() // error

}
7 changes: 7 additions & 0 deletions tests/neg-macros/macros-in-same-project-2/Foo.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import scala.quoted._

object Foo {

def aMacroImplementation given QuoteContext: Expr[Unit] = '{ println("Hello") }

}
8 changes: 8 additions & 0 deletions tests/neg-macros/macros-in-same-project-3.check
Original file line number Diff line number Diff line change
@@ -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
9 changes: 9 additions & 0 deletions tests/neg-macros/macros-in-same-project-3/Bar.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import scala.quoted._

object Bar {

inline def myMacro(): Unit = ${ aMacroImplementation }

def aMacroImplementation given QuoteContext: Expr[Unit] = Foo.hello()

}
3 changes: 3 additions & 0 deletions tests/neg-macros/macros-in-same-project-3/Baz.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
object Baz {
Bar.myMacro() // error
}
7 changes: 7 additions & 0 deletions tests/neg-macros/macros-in-same-project-3/Foo.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import scala.quoted._

object Foo {

def hello() given QuoteContext: Expr[Unit] = '{ println("Hello") }

}
8 changes: 8 additions & 0 deletions tests/neg-macros/macros-in-same-project-4.check
Original file line number Diff line number Diff line change
@@ -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
8 changes: 8 additions & 0 deletions tests/neg-macros/macros-in-same-project-4/Bar.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import scala.quoted._

object Bar {

Foo.myMacro() // error

def hello() given QuoteContext: Expr[Unit] = '{ println("Hello") }
}
9 changes: 9 additions & 0 deletions tests/neg-macros/macros-in-same-project-4/Foo.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import scala.quoted._

object Foo {

inline def myMacro(): Unit = ${ aMacroImplementation }

def aMacroImplementation given QuoteContext: Expr[Unit] = Bar.hello()

}

0 comments on commit c030b30

Please sign in to comment.