Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions compiler/src/dotty/tools/dotc/core/quoted/PickledQuotes.scala
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,9 @@ object PickledQuotes {
val x1 = SyntheticValDef(NameKinds.UniqueName.fresh("x".toTermName), x)
def x1Ref() = ref(x1.symbol)
def rec(f: Tree): Tree = f match {
case Inlined(call, bindings, expansion) =>
// this case must go before closureDef to avoid dropping the inline node
cpy.Inlined(f)(call, bindings, rec(expansion))
case closureDef(ddef) =>
val paramSym = ddef.vparamss.head.head.symbol
new TreeTypeMap(
Expand All @@ -140,8 +143,6 @@ object PickledQuotes {
).transform(ddef.rhs)
case Block(stats, expr) =>
seq(stats, rec(expr))
case Inlined(call, bindings, expansion) =>
Inlined(call, bindings, rec(expansion))
case _ =>
f.select(nme.apply).appliedTo(x1Ref())
}
Expand Down
16 changes: 4 additions & 12 deletions compiler/src/dotty/tools/dotc/transform/PostTyper.scala
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ package transform
import dotty.tools.dotc.ast.{Trees, tpd, untpd}
import scala.collection.mutable
import core._
import typer.{Checking, VarianceChecker}
import dotty.tools.dotc.typer.Checking
import dotty.tools.dotc.typer.Inliner
import dotty.tools.dotc.typer.VarianceChecker
import Types._, Contexts._, Names._, Flags._, DenotTransformers._, Phases._
import SymDenotations._, StdNames._, Annotations._, Trees._, Scopes._
import Decorators._
Expand Down Expand Up @@ -237,17 +239,7 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisPhase
super.transform(tree1)
}
case Inlined(call, bindings, expansion) if !call.isEmpty =>
// Leave only a call trace consisting of
// - a reference to the top-level class from which the call was inlined,
// - the call's position
// in the call field of an Inlined node.
// The trace has enough info to completely reconstruct positions.
// The minimization is done for two reasons:
// 1. To save space (calls might contain large inline arguments, which would otherwise
// be duplicated
// 2. To enable correct pickling (calls can share symbols with the inlined code, which
// would trigger an assertion when pickling).
val callTrace = Ident(call.symbol.topLevelClass.typeRef).withPos(call.pos)
val callTrace = Inliner.inlineCallTrace(call.symbol, call.pos)
cpy.Inlined(tree)(callTrace, transformSub(bindings), transform(expansion)(inlineContext(call)))
case tree: Template =>
withNoCheckNews(tree.parents.flatMap(newPart)) {
Expand Down
4 changes: 2 additions & 2 deletions compiler/src/dotty/tools/dotc/transform/Splicer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,8 @@ object Splicer {
val interpreter = new Interpreter(pos, classLoader)
try {
// Some parts of the macro are evaluated during the unpickling performed in quotedExprToTree
val interpreted = interpreter.interpret[scala.quoted.Expr[Any]](tree)
interpreted.fold(tree)(x => PickledQuotes.quotedExprToTree(x))
val interpretedExpr = interpreter.interpret[scala.quoted.Expr[Any]](tree)
interpretedExpr.fold(tree)(x => PickledQuotes.quotedExprToTree(x))
}
catch {
case ex: scala.quoted.QuoteError =>
Expand Down
19 changes: 17 additions & 2 deletions compiler/src/dotty/tools/dotc/transform/Staging.scala
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import typer.Implicits.SearchFailureType
import scala.collection.mutable
import dotty.tools.dotc.core.StdNames._
import dotty.tools.dotc.core.quoted._
import dotty.tools.dotc.typer.Inliner
import dotty.tools.dotc.util.SourcePosition


Expand Down Expand Up @@ -381,7 +382,12 @@ class Staging extends MacroTransformWithImplicits {
capturers(body.symbol)(body)
case _=>
val (body1, splices) = nested(isQuote = true).split(body)
if (level == 0 && !ctx.inInlineMethod) pickledQuote(body1, splices, body.tpe, isType).withPos(quote.pos)
if (level == 0 && !ctx.inInlineMethod) {
val body2 =
if (body1.isType) body1
else Inlined(Inliner.inlineCallTrace(ctx.owner, quote.pos), Nil, body1)
pickledQuote(body2, splices, body.tpe, isType).withPos(quote.pos)
}
else {
// In top-level splice in an inline def. Keep the tree as it is, it will be transformed at inline site.
body
Expand Down Expand Up @@ -432,7 +438,11 @@ class Staging extends MacroTransformWithImplicits {
else if (level == 1) {
val (body1, quotes) = nested(isQuote = false).split(splice.qualifier)
val tpe = outer.embedded.getHoleType(splice)
makeHole(body1, quotes, tpe).withPos(splice.pos)
val hole = makeHole(body1, quotes, tpe).withPos(splice.pos)
// We do not place add the inline marker for trees that where lifted as they come from the same file as their
// enclosing quote. Any intemediate splice will add it's own Inlined node and cancel it before splicig the lifted tree.
if (splice.isType || outer.embedded.isLiftedSymbol(splice.qualifier.symbol)) hole
else Inlined(EmptyTree, Nil, hole)
}
else if (enclosingInlineds.nonEmpty) { // level 0 in an inlined call
val spliceCtx = ctx.outer // drop the last `inlineContext`
Expand Down Expand Up @@ -664,7 +674,12 @@ object Staging {
map.get(splice.qualifier.symbol).map(_.tpe.widen).getOrElse(splice.tpe)
}

def isLiftedSymbol(sym: Symbol)(implicit ctx: Context): Boolean = map.contains(sym)

/** Get the list of embedded trees */
def getTrees: List[tpd.Tree] = trees.toList

override def toString: String = s"Embedded($trees, $map)"

}
}
9 changes: 9 additions & 0 deletions compiler/src/dotty/tools/dotc/typer/Inliner.scala
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,15 @@ object Inliner {
(new Reposition).transformInline(inlined)
}
}

/** Leave only a call trace consisting of
* - a reference to the top-level class from which the call was inlined,
* - the call's position
* in the call field of an Inlined node.
* The trace has enough info to completely reconstruct positions.
*/
def inlineCallTrace(callSym: Symbol, pos: Position)(implicit ctx: Context): Tree =
Ident(callSym.topLevelClass.typeRef).withPos(pos)
}

/** Produces an inlined version of `call` via its `inlined` method.
Expand Down
2 changes: 2 additions & 0 deletions tests/run-with-compiler/quote-fun-app-1.check
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
42
43
17 changes: 17 additions & 0 deletions tests/run-with-compiler/quote-fun-app-1.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import scala.quoted._

import scala.quoted.Toolbox.Default._

object Test {

def main(args: Array[String]): Unit = {
val f = f1.run
println(f(42))
println(f(43))
}

def f1: Expr[Int => Int] = '{ n => ~f2('(n)) }
def f2: Expr[Int => Int] = '{ n => ~f3('(n)) }
def f3: Expr[Int => Int] = '{ n => ~f4('(n)) }
def f4: Expr[Int => Int] = '{ n => n }
}
2 changes: 0 additions & 2 deletions tests/run-with-compiler/quote-unrolled-foreach.check
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,6 @@
var i: scala.Int = 0
while (i.<(size)) {
val element: scala.Int = arr.apply(i)

((i: scala.Int) => java.lang.System.out.println(i)).apply(element)
i = i.+(1)
}
Expand Down Expand Up @@ -86,7 +85,6 @@
var i: scala.Int = 0
while (i.<(size)) {
val element: scala.Int = arr1.apply(i)

((x: scala.Int) => scala.Predef.println(x)).apply(element)
i = i.+(1)
}
Expand Down
1 change: 1 addition & 0 deletions tests/run-with-compiler/quote-var.check
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
xyz
38 changes: 38 additions & 0 deletions tests/run-with-compiler/quote-var.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import scala.quoted._

object Test {

sealed trait Var {
def get: Expr[String]
def update(x: Expr[String]): Expr[Unit]
}

object Var {
def apply(init: Expr[String])(body: Var => Expr[String]): Expr[String] = '{
var x = ~init
~body(
new Var {
def get: Expr[String] = '(x)
def update(e: Expr[String]): Expr[Unit] = '{ x = ~e }
}
)
}
}


def test1(): Expr[String] = Var('("abc")) { x =>
'{
~x.update('("xyz"))
~x.get
}
}

def main(args: Array[String]): Unit = {
implicit val toolbox: scala.quoted.Toolbox = scala.quoted.Toolbox.make

println(test1().run)
}
}



8 changes: 8 additions & 0 deletions tests/run/i4947e.check
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
assertImpl: Test$.main(Test_2.scala:7)
true
assertImpl: Test$.main(Test_2.scala:8)
false
assertImpl: Test$.main(Test_2.scala:9)
hi: Test$.main(Test_2.scala:10)
hi again: Test$.main(Test_2.scala:11)
false
11 changes: 11 additions & 0 deletions tests/run/i4947e/Macro_1.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import scala.quoted._

object Macros {
def printStack(tag: String): Unit = {
println(tag + ": "+ new Exception().getStackTrace().apply(1))
}
def assertImpl(expr: Expr[Boolean]) = '{
printStack("assertImpl")
println(~expr)
}
}
15 changes: 15 additions & 0 deletions tests/run/i4947e/Test_2.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
object Test {

inline def assert2(expr: => Boolean): Unit = ~Macros.assertImpl('(expr))

def main(args: Array[String]): Unit = {
val x = 1
assert2(x != 0)
assert2(x == 0)
assert2 {
Macros.printStack("hi")
Macros.printStack("hi again")
x == 0
}
}
}
8 changes: 8 additions & 0 deletions tests/run/i4947f.check
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
assertImpl: Test$.main(Test_2.scala:7)
true
assertImpl: Test$.main(Test_2.scala:8)
false
assertImpl: Test$.main(Test_2.scala:9)
hi: Test$.main(Test_2.scala:10)
hi again: Test$.main(Test_2.scala:11)
false
14 changes: 14 additions & 0 deletions tests/run/i4947f/Macro_1.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import scala.quoted._

object Macros {
def printStack(tag: String): Unit = {
println(tag + ": "+ new Exception().getStackTrace().apply(1))
}
def assertImpl(expr: Expr[Boolean]) = '{
printStack("assertImpl")
println(~expr)
}

inline def assert2(expr: => Boolean): Unit = ~Macros.assertImpl('(expr))

}
15 changes: 15 additions & 0 deletions tests/run/i4947f/Test_2.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
object Test {

import Macros._

def main(args: Array[String]): Unit = {
val x = 1
assert2(x != 0)
assert2(x == 0)
assert2 {
Macros.printStack("hi")
Macros.printStack("hi again")
x == 0
}
}
}