summaryrefslogtreecommitdiff
path: root/test/files/run/t5903b.flags
diff options
context:
space:
mode:
authorEugene Burmako <xeno.by@gmail.com>2013-08-17 09:29:28 +0200
committerEugene Burmako <xeno.by@gmail.com>2013-08-18 07:21:16 +0200
commit84a335916556cb0fe939d1c51f27d80d9cf980dc (patch)
tree6205d34ee60c6aaaaeece2bf3dafe2a4978a31c1 /test/files/run/t5903b.flags
parent1cd7a9e840158dab17a3aafc0ce849605706a561 (diff)
downloadscala-84a335916556cb0fe939d1c51f27d80d9cf980dc.tar.gz
scala-84a335916556cb0fe939d1c51f27d80d9cf980dc.tar.bz2
scala-84a335916556cb0fe939d1c51f27d80d9cf980dc.zip
SI-5903 extractor macros
Establishes a pattern that can be used to implement extractor macros that give the programmer control over signatures of unapplications at compile-time. === The pattern === In a nutshell, given an unapply method (for simplicity, in this example the scrutinee is of a concrete type, but it's also possible to have the extractor be polymorphic, as demonstrated in the tests): ``` def unapply(x: SomeType) = ??? ``` One can write a macro that generates extraction signatures for unapply on per-call basis, using the target of the calls (c.prefix) and the type of the scrutinee (that comes with x), and then communicate these signatures to the typechecker. For example, here's how one can define a macro that simply passes the scrutinee back to the pattern match (for information on how to express signatures that involve multiple extractees, visit https://github.com/scala/scala/pull/2848). ``` def unapply(x: SomeType) = macro impl def impl(c: Context)(x: c.Tree) = { q""" new { class Match(x: SomeType) { def isEmpty = false def get = x } def unapply(x: SomeType) = new Match(x) }.unapply($x) """ } ``` In addition to the matcher, which implements domain-specific matching logic, there's quite a bit of boilerplate here, but every part of it looks necessary to arrange a non-frustrating dialogue with the typer. Maybe something better can be done in this department, but I can't see how, without introducing modifications to the typechecker. Even though the pattern uses structural types, somehow no reflective calls are being generated (as verified by -Xlog-reflective-calls and then by manual examination of the produced code). That's a mystery to me, but that's also good news, since that means that extractor macros aren't going to induce performance penalties. Almost. Unfortunately, I couldn't turn matchers into value classes because one can't declare value classes local. Nevertheless, I'm leaving a canary in place (neg/t5903e) that will let us know once this restriction is lifted. === Use cases === In particular, the pattern can be used to implement shapeshifting pattern matchers for string interpolators without resorting to dirty tricks. For example, quasiquote unapplications can be unhardcoded now: ``` def doTypedApply(tree: Tree, fun0: Tree, args: List[Tree], ...) = { ... fun.tpe match { case ExtractorType(unapply) if mode.inPatternMode => // this hardcode in Typers.scala is no longer necessary if (unapply == QuasiquoteClass_api_unapply) macroExpandUnapply(...) else doTypedUnapply(tree, fun0, fun, args, mode, pt) } } ``` Rough implementation strategy here would involve writing an extractor macro that destructures c.prefix, analyzes parts of StringContext and then generates an appropriate matcher as outlined above. === Implementation details === No modifications to core logic of typer or patmat are necessary, as we're just piggybacking on https://github.com/scala/scala/pull/2848. The only minor change I introduced is a guard against misbehaving extractor macros that don't conform to the pattern (e.g. expand into blocks or whatever else). Without the guard we'd crash with an NPE, with the guard we get a sane compilation error.
Diffstat (limited to 'test/files/run/t5903b.flags')
-rw-r--r--test/files/run/t5903b.flags1
1 files changed, 1 insertions, 0 deletions
diff --git a/test/files/run/t5903b.flags b/test/files/run/t5903b.flags
new file mode 100644
index 0000000000..02ecab49e7
--- /dev/null
+++ b/test/files/run/t5903b.flags
@@ -0,0 +1 @@
+-Xlog-reflective-calls \ No newline at end of file