Skip to content

Commit

Permalink
Merge pull request #7192 from dotty-staging/refine-macro-from-same-pr…
Browse files Browse the repository at this point in the history
…oject-detection

Detect macro dependencies within the current run
  • Loading branch information
nicolasstucki authored Sep 11, 2019
2 parents 0f6ea2a + 0e3c29d commit c6367d8
Show file tree
Hide file tree
Showing 14 changed files with 145 additions and 8 deletions.
59 changes: 51 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,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)
}
}

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 c6367d8

Please sign in to comment.