Skip to content

Commit 08aa3c0

Browse files
committed
Add Term.toExpr and TypeTree.toType
1 parent 715b037 commit 08aa3c0

File tree

6 files changed

+131
-12
lines changed

6 files changed

+131
-12
lines changed

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

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,14 @@ package dotty.tools.dotc.tasty
22

33
import dotty.tools.dotc.ast.{Trees, tpd, untpd}
44
import dotty.tools.dotc.core
5+
import dotty.tools.dotc.core.Contexts.FreshContext
56
import dotty.tools.dotc.core._
67
import dotty.tools.dotc.core.StdNames.nme
78
import dotty.tools.dotc.core.Symbols.Symbol
89
import dotty.tools.dotc.core.Decorators._
910
import dotty.tools.dotc.core.quoted.PickledQuotes
11+
import dotty.tools.dotc.reporting.Reporter
12+
import dotty.tools.dotc.reporting.diagnostic.MessageContainer
1013
import dotty.tools.dotc.util.SourcePosition
1114

1215
import scala.quoted
@@ -232,6 +235,36 @@ object TastyImpl extends scala.tasty.Tasty {
232235
implicit def TermDeco(t: Term): AbstractTerm = new AbstractTerm {
233236
def pos(implicit ctx: Context): Position = new TastyPosition(t.pos)
234237
def tpe: Types.Type = t.tpe
238+
239+
def toExpr[T: quoted.Type](implicit ctx: Context): quoted.Expr[T] = {
240+
def typecheck(implicit ctx0: FreshContext) = {
241+
ctx0.setTyperState(ctx0.typerState.fresh())
242+
ctx0.typerState.setReporter(new Reporter {
243+
def doReport(m: MessageContainer)(implicit ctx: Contexts.Context): Unit = ()
244+
})
245+
val tp = implicitly[quoted.Type[T]].toTasty
246+
ctx0.typer.typed(t, tp.tpe)
247+
if (ctx0.reporter.hasErrors) {
248+
val stack = new Exception().getStackTrace
249+
def filter(elem: StackTraceElement) =
250+
elem.getClassName.startsWith("dotty.tools.dotc.tasty.TastyImpl") ||
251+
!elem.getClassName.startsWith("dotty.tools.dotc")
252+
// throw new quoted.QuoteError( // TODO use this instead of ctx.error
253+
ctx.error(
254+
s"""Error during macro expansion while typing term
255+
|term: ${t.show}
256+
|with expected type: ${tp.tpe.show}
257+
|
258+
| ${stack.takeWhile(filter).mkString("\n ")}
259+
""".stripMargin,
260+
t.pos
261+
)
262+
}
263+
264+
}
265+
typecheck(ctx.fresh)
266+
new quoted.Exprs.TreeExpr(t).asInstanceOf[quoted.Expr[T]]
267+
}
235268
}
236269

237270
def termClassTag: ClassTag[Term] = implicitly[ClassTag[Term]]
@@ -304,6 +337,7 @@ object TastyImpl extends scala.tasty.Tasty {
304337
}
305338

306339
val Typed: TypedExtractor = new TypedExtractor {
340+
def apply(term: Term, tpt: TypeTree)(implicit ctx: Context): Term = tpd.Typed(term, tpt)
307341
def unapply(x: Term)(implicit ctx: Context): Option[(Term, TypeTree)] = x match {
308342
case x: tpd.Typed @unchecked => Some((x.expr, x.tpt))
309343
case _ => None
@@ -463,6 +497,7 @@ object TastyImpl extends scala.tasty.Tasty {
463497
implicit def TypeTreeDeco(x: TypeTree): AbstractTypeTree = new AbstractTypeTree {
464498
def pos(implicit ctx: Context): Position = new TastyPosition(x.pos)
465499
def tpe: Types.Type = x.tpe
500+
def toType(implicit ctx: Context): quoted.Type[_] = new quoted.Types.TreeType(x)
466501
}
467502

468503
val Synthetic: SyntheticExtractor = new SyntheticExtractor {

library/src/scala/tasty/Tasty.scala

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

181181
type Term <: Statement
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]
@@ -232,6 +234,7 @@ abstract class Tasty {
232234

233235
val Typed: TypedExtractor
234236
abstract class TypedExtractor {
237+
def apply(term: Term, tpt: TypeTree)(implicit ctx: Context): Term
235238
def unapply(x: Term)(implicit ctx: Context): Option[(Term, TypeTree)]
236239
}
237240

@@ -344,7 +347,9 @@ abstract class Tasty {
344347

345348
type TypeTree <: MaybeTypeTree
346349

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

350355
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: MaybeType): Boolean = tpe match {
25+
case 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 Apply(TypeApply(term, _), left :: Nil) if isOps(term.tpe) =>
32+
Some(left)
33+
case _ => None
34+
}
35+
}
36+
37+
tree match {
38+
case Apply(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 & 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
import scala.tasty.util.TastyPrinter
@@ -37,12 +36,9 @@ object Asserts {
3736

3837
tree match {
3938
case Apply(Select(OpsTree(left), op, _), right :: Nil) =>
40-
// FIXME splice the threes directly
41-
val lExpr = TastyPrinter.stringOfTree(tasty)(left).toExpr
42-
val rExpr = TastyPrinter.stringOfTree(tasty)(right).toExpr
4339
op match {
44-
case "===" => '(assertEquals(~lExpr, ~rExpr))
45-
case "!==" => '(assertNotEquals(~lExpr, ~rExpr))
40+
case "===" => '(assertEquals(~left.toExpr[Any], ~right.toExpr[Any]))
41+
case "!==" => '(assertNotEquals(~left.toExpr[Any], ~right.toExpr[Any]))
4642
}
4743
case _ =>
4844
'(assertTrue(~cond))

0 commit comments

Comments
 (0)