Skip to content

Commit fd8f29c

Browse files
authored
Merge pull request #4473 from dotty-staging/tasty-to-quote
Add Term.toExpr in Tasty
2 parents 0e0dab3 + e16964d commit fd8f29c

File tree

8 files changed

+132
-14
lines changed

8 files changed

+132
-14
lines changed

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

Lines changed: 2 additions & 0 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

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

Lines changed: 36 additions & 3 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
@@ -228,9 +230,40 @@ object TastyImpl extends scala.tasty.Tasty {
228230

229231
type Term = tpd.Tree
230232

231-
def TermDeco(t: Term): AbstractTerm = new AbstractTerm {
232-
def pos(implicit ctx: Context): Position = t.pos
233-
def tpe(implicit ctx: Context): Types.Type = t.tpe
233+
def TermDeco(tree: Term): AbstractTerm = new AbstractTerm {
234+
235+
def pos(implicit ctx: Context): Position = tree.pos
236+
237+
def tpe(implicit ctx: Context): Types.Type = tree.tpe
238+
239+
def toExpr[T: quoted.Type](implicit ctx: Context): quoted.Expr[T] = {
240+
typecheck(ctx)
241+
new quoted.Exprs.TastyTreeExpr(tree).asInstanceOf[quoted.Expr[T]]
242+
}
243+
244+
private def typecheck[T: quoted.Type](ctx: Contexts.Context): Unit = {
245+
implicit val ctx0: Contexts.FreshContext = ctx.fresh
246+
ctx0.setTyperState(ctx0.typerState.fresh())
247+
ctx0.typerState.setReporter(new Reporter {
248+
def doReport(m: MessageContainer)(implicit ctx: Contexts.Context): Unit = ()
249+
})
250+
val tp = QuotedTypeDeco(implicitly[quoted.Type[T]]).toTasty
251+
ctx0.typer.typed(tree, tp.tpe)
252+
if (ctx0.reporter.hasErrors) {
253+
val stack = new Exception().getStackTrace
254+
def filter(elem: StackTraceElement) =
255+
elem.getClassName.startsWith("dotty.tools.dotc.tasty.TastyImpl") ||
256+
!elem.getClassName.startsWith("dotty.tools.dotc")
257+
throw new scala.tasty.TastyTypecheckError(
258+
s"""Error during tasty reflection while typing term
259+
|term: ${tree.show}
260+
|with expected type: ${tp.tpe.show}
261+
|
262+
| ${stack.takeWhile(filter).mkString("\n ")}
263+
""".stripMargin
264+
)
265+
}
266+
}
234267
}
235268

236269
def termClassTag: ClassTag[Term] = implicitly[ClassTag[Term]]

library/src/scala/tasty/Tasty.scala

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -180,7 +180,9 @@ abstract class Tasty { tasty =>
180180

181181
type Term <: Statement with Parent
182182

183-
trait AbstractTerm extends Typed with Positioned
183+
trait AbstractTerm extends Typed with Positioned {
184+
def toExpr[T: quoted.Type](implicit ctx: Context): quoted.Expr[T]
185+
}
184186
implicit def TermDeco(t: Term): AbstractTerm
185187

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

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

Lines changed: 2 additions & 6 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

@@ -36,12 +35,9 @@ object Asserts {
3635

3736
tree match {
3837
case Term.Apply(Term.Select(OpsTree(left), op, _), right :: Nil) =>
39-
// FIXME splice the threes directly
40-
val lExpr = left.show.toExpr
41-
val rExpr = right.show.toExpr
4238
op match {
43-
case "===" => '(assertEquals(~lExpr, ~rExpr))
44-
case "!==" => '(assertNotEquals(~lExpr, ~rExpr))
39+
case "===" => '(assertEquals(~left.toExpr[Any], ~right.toExpr[Any]))
40+
case "!==" => '(assertNotEquals(~left.toExpr[Any], ~right.toExpr[Any]))
4541
}
4642
case _ =>
4743
'(assertTrue(~cond))

0 commit comments

Comments
 (0)