Skip to content

Commit 148f57e

Browse files
committed
Add Term.toExpr and TypeTree.toType
1 parent 6876240 commit 148f57e

File tree

11 files changed

+138
-20
lines changed

11 files changed

+138
-20
lines changed

compiler/src/dotty/tools/dotc/core/quoted/PickledQuotes.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ object PickledQuotes {
6464
case value: Class[_] => ref(defn.Predef_classOf).appliedToType(classToType(value))
6565
case value => Literal(Constant(value))
6666
}
67-
case expr: TreeExpr[Tree] @unchecked => expr.tree
67+
case expr: TreeExpr[Tree, Context] @unchecked => expr.tree
6868
case expr: FunctionAppliedTo[_, _] =>
6969
functionAppliedTo(quotedExprToTree(expr.f), quotedExprToTree(expr.x))
7070
}

compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1150,7 +1150,7 @@ class TreeUnpickler(reader: TastyReader,
11501150
val args = until(end)(readTerm())
11511151
val splice = splices(idx)
11521152
def wrap(arg: Tree) =
1153-
if (arg.isTerm) new TreeExpr(arg, PickledQuotes.pickleExpr(arg))
1153+
if (arg.isTerm) new TreeExpr(arg, ctx)
11541154
else new TreeType(arg)
11551155
val reifiedArgs = args.map(wrap)
11561156
if (isType) {

compiler/src/dotty/tools/dotc/quoted/Toolbox.scala

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
package dotty.tools.dotc.quoted
22

33
import dotty.tools.dotc.ast.tpd
4+
import dotty.tools.dotc.core.Contexts.Context
45
import dotty.tools.dotc.core.Constants._
6+
import dotty.tools.dotc.core.quoted.PickledQuotes
57
import dotty.tools.dotc.printing.RefinedPrinter
68

79
import scala.quoted.Expr
@@ -24,8 +26,9 @@ object Toolbox {
2426
def run(expr: Expr[T]): T = expr match {
2527
case expr: LiftedExpr[T] =>
2628
expr.value
27-
case expr: TreeExpr[Tree] @unchecked =>
28-
new QuoteDriver().run(expr.pickled, runSettings)
29+
case expr: TreeExpr[Tree, Context] @unchecked =>
30+
val pickled = PickledQuotes.pickleExpr(expr.tree)(expr.ctx).asInstanceOf[Expr[T]]
31+
new QuoteDriver().run(pickled, runSettings)
2932
case _ =>
3033
new QuoteDriver().run(expr, runSettings)
3134
}

compiler/src/dotty/tools/dotc/tasty/TastyImpl.scala

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ import dotty.tools.dotc.core.StdNames.nme
77
import dotty.tools.dotc.core.Symbols.Symbol
88
import dotty.tools.dotc.core.Decorators._
99
import dotty.tools.dotc.core.quoted.PickledQuotes
10+
import dotty.tools.dotc.reporting.Reporter
11+
import dotty.tools.dotc.reporting.diagnostic.MessageContainer
1012
import dotty.tools.dotc.util.SourcePosition
1113

1214
import scala.quoted
@@ -218,8 +220,39 @@ object TastyImpl extends scala.tasty.Tasty {
218220
type Term = tpd.Tree
219221

220222
def TermDeco(t: Term): AbstractTerm = new AbstractTerm {
223+
221224
def pos(implicit ctx: Context): Position = t.pos
225+
222226
def tpe(implicit ctx: Context): Types.Type = t.tpe
227+
228+
def toExpr[T: quoted.Type](implicit ctx: Context): quoted.Expr[T] = {
229+
typecheck(ctx)
230+
new quoted.Exprs.TreeExpr(t, ctx).asInstanceOf[quoted.Expr[T]]
231+
}
232+
233+
private def typecheck[T: quoted.Type](ctx: Contexts.Context) = {
234+
implicit val ctx0: Contexts.FreshContext = ctx.fresh
235+
ctx0.setTyperState(ctx0.typerState.fresh())
236+
ctx0.typerState.setReporter(new Reporter {
237+
def doReport(m: MessageContainer)(implicit ctx: Contexts.Context): Unit = ()
238+
})
239+
val tp = QuotedTypeDeco(implicitly[quoted.Type[T]]).toTasty
240+
ctx0.typer.typed(t, tp.tpe)
241+
if (ctx0.reporter.hasErrors) {
242+
val stack = new Exception().getStackTrace
243+
def filter(elem: StackTraceElement) =
244+
elem.getClassName.startsWith("dotty.tools.dotc.tasty.TastyImpl") ||
245+
!elem.getClassName.startsWith("dotty.tools.dotc")
246+
throw new quoted.QuoteError(
247+
s"""Error during macro expansion while typing term
248+
|term: ${t.show}
249+
|with expected type: ${tp.tpe.show}
250+
|
251+
| ${stack.takeWhile(filter).mkString("\n ")}
252+
""".stripMargin
253+
)
254+
}
255+
}
223256
}
224257

225258
def termClassTag: ClassTag[Term] = implicitly[ClassTag[Term]]
@@ -460,6 +493,7 @@ object TastyImpl extends scala.tasty.Tasty {
460493
def TypeTreeDeco(x: TypeTree): AbstractTypeTree = new AbstractTypeTree {
461494
def pos(implicit ctx: Context): Position = x.pos
462495
def tpe(implicit ctx: Context): Types.Type = x.tpe.stripTypeVar
496+
def toType(implicit ctx: Context): quoted.Type[_] = new quoted.Types.TreeType(x)
463497
}
464498

465499
def typeTreeClassTag: ClassTag[TypeTree] = implicitly[ClassTag[TypeTree]]

compiler/src/dotty/tools/dotc/transform/Splicer.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ object Splicer {
6767
assert(!tp.hasAnnotation(defn.InlineParamAnnot))
6868
// Replace argument by its binding
6969
val arg1 = bindMap.getOrElse(arg, arg)
70-
new scala.quoted.Exprs.TreeExpr(arg1, PickledQuotes.pickleExpr(arg1))
70+
new scala.quoted.Exprs.TreeExpr(arg1, ctx)
7171
}
7272
args1 ::: liftArgs(tp.resType, args.tail)
7373
case tp: PolyType =>

library/src/scala/quoted/Expr.scala

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,8 +50,7 @@ object Exprs {
5050
*
5151
* May contain references to code defined outside this Expr instance.
5252
*/
53-
final class TreeExpr[Tree](val tree: Tree, pickle: => Expr[_]) extends quoted.Expr[Any] {
54-
def pickled[T]: Expr[T] = pickle.asInstanceOf[Expr[T]]
53+
final class TreeExpr[Tree, Context](val tree: Tree, val ctx: Context) extends quoted.Expr[Any] {
5554
override def toString: String = s"Expr(<raw>)"
5655
}
5756

library/src/scala/tasty/Tasty.scala

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,9 @@ abstract class Tasty {
168168

169169
type Term <: Statement with Parent
170170

171-
trait AbstractTerm extends Typed with Positioned
171+
trait AbstractTerm extends Typed with Positioned {
172+
def toExpr[T: quoted.Type](implicit ctx: Context): quoted.Expr[T]
173+
}
172174
implicit def TermDeco(t: Term): AbstractTerm
173175

174176
implicit def termClassTag: ClassTag[Term]
@@ -344,7 +346,9 @@ abstract class Tasty {
344346

345347
type TypeTree <: TypeOrBoundsTree with Parent
346348

347-
trait AbstractTypeTree extends Typed with Positioned
349+
trait AbstractTypeTree extends Typed with Positioned {
350+
def toType(implicit ctx: Context): quoted.Type[_]
351+
}
348352
implicit def TypeTreeDeco(x: TypeTree): AbstractTypeTree
349353

350354
implicit def typeTreeClassTag: ClassTag[TypeTree]
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
import scala.quoted._
2+
3+
import scala.tasty.Universe
4+
import scala.tasty.util.TastyPrinter
5+
6+
object Asserts {
7+
8+
implicit class Ops[T](left: T) {
9+
def ===(right: T): Boolean = left == right
10+
def !==(right: T): Boolean = left != right
11+
}
12+
13+
object Ops
14+
15+
inline def macroAssert(cond: Boolean): Unit =
16+
~impl('(cond))(Universe.compilationUniverse) // FIXME infer Universe.compilationUniverse within top level ~
17+
18+
def impl(cond: Expr[Boolean])(implicit u: Universe): Expr[Unit] = {
19+
import u._
20+
import u.tasty._
21+
22+
val tree = cond.toTasty
23+
24+
def isOps(tpe: TypeOrBounds): Boolean = tpe match {
25+
case Type.SymRef(DefDef("Ops", _, _, _, _), _) => true // TODO check that the parent is Asserts
26+
case _ => false
27+
}
28+
29+
object OpsTree {
30+
def unapply(arg: Term): Option[Term] = arg match {
31+
case Term.Apply(Term.TypeApply(term, _), left :: Nil) if isOps(term.tpe) =>
32+
Some(left)
33+
case _ => None
34+
}
35+
}
36+
37+
tree match {
38+
case Term.Apply(Term.Select(OpsTree(left), op, _), right :: Nil) =>
39+
'(assertTrue(~left.toExpr[Boolean])) // Buggy code. To generate the errors
40+
case _ =>
41+
'(assertTrue(~cond))
42+
}
43+
44+
}
45+
46+
def assertEquals[T](left: T, right: T): Unit = {
47+
if (left != right) {
48+
println(
49+
s"""Error left did not equal right:
50+
| left = $left
51+
| right = $right""".stripMargin)
52+
}
53+
54+
}
55+
56+
def assertNotEquals[T](left: T, right: T): Unit = {
57+
if (left == right) {
58+
println(
59+
s"""Error left was equal to right:
60+
| left = $left
61+
| right = $right""".stripMargin)
62+
}
63+
64+
}
65+
66+
def assertTrue(cond: Boolean): Unit = {
67+
if (!cond)
68+
println("Condition was false")
69+
}
70+
71+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
2+
import Asserts._
3+
4+
object Test {
5+
def main(args: Array[String]): Unit = {
6+
macroAssert(true === "cde")
7+
macroAssert("acb" === "cde") // error
8+
macroAssert(false !== "acb")
9+
macroAssert("acb" !== "acb") // error
10+
}
11+
12+
}

tests/run/tasty-macro-assert.check

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
Condition was false
22
Error left did not equal right:
3-
left = Literal(String(acb))
4-
right = Literal(String(cde))
3+
left = acb
4+
right = cde
55
Error left was equal to right:
6-
left = Literal(String(acb))
7-
right = Literal(String(acb))
6+
left = acb
7+
right = acb

tests/run/tasty-macro-assert/quoted_1.scala

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import scala.quoted._
2-
import dotty.tools.dotc.quoted.Toolbox._
32

43
import scala.tasty.Universe
54
import scala.tasty.util.TastyPrinter
@@ -37,13 +36,9 @@ object Asserts {
3736

3837
tree match {
3938
case Term.Apply(Term.Select(OpsTree(left), op, _), right :: Nil) =>
40-
// FIXME splice the threes directly
41-
val printer = new TastyPrinter(tasty)
42-
val lExpr = printer.stringOfTree(left).toExpr
43-
val rExpr = printer.stringOfTree(right).toExpr
4439
op match {
45-
case "===" => '(assertEquals(~lExpr, ~rExpr))
46-
case "!==" => '(assertNotEquals(~lExpr, ~rExpr))
40+
case "===" => '(assertEquals(~left.toExpr[Any], ~right.toExpr[Any]))
41+
case "!==" => '(assertNotEquals(~left.toExpr[Any], ~right.toExpr[Any]))
4742
}
4843
case _ =>
4944
'(assertTrue(~cond))

0 commit comments

Comments
 (0)