Compiler crash in simple macro manipulating a match statement - scala

def control(x: String): Option[String] = macro controlImpl
def controlImpl(c: Context)(x: c.Expr[String]): c.Expr[Option[String]] = {
import c.universe._
val result = x.tree match {
case Match(expr, cases) =>
val matchz = Match(q"""List("hello")""", cases)
q"Some(List(5)).map{a => $matchz}"
case a => a
}
c.Expr[Option[String]](result)
}
This macro crashes during macro expansion with the following error:
java.lang.IllegalArgumentException: Could not find proxy for val o7: Some in List(value o7, method apply, <$anon: Function1>, value f, method apply, <$anon: Function1>, method apply, <$anon: Function0>, value <local ApplicativeTest>, object ApplicativeTest, package scalaz, package <root>) (currentOwner= value one )
This is the macro application point:
val f = IdiomBracket.control {
a.get match {
case List(one) => one
case _ => ""
}
}
What is odd is that if you substitute q"Some(List(5)).map{a => $matchz}" with q"Some($matchz)" then the compiler crash goes away.

"Simple macro" is an oxymoron.
The crash is in lambda lift, which indicates a bad symbol owner.
You can inspect the tree you produce with -Ybrowse:typer.
Consider what you'd like to produce:
class X {
//def g = List("hello") match { case List(x) => x case _ => "" }
def f = Some(List(5)).map{a => List("hello") match { case List(x) => x case _ => "" } }
}
The var in the case is owned by the anonfun you want to create:
Symbol: [has] value x
Symbol owner: value $anonfun
Your actual result:
Symbol: [has] value x
Symbol owner: value <local Controlled>
where Controlled is my sample enclosing object for your code.
You might have to rebuild the cases, as opposed to just cases map (_.untypecheck).
https://github.com/scala/scala/blob/v2.11.5/src/reflect/scala/reflect/api/Trees.scala#L1100

Related

Surprising PartialFunction literal

I just got puzzled by the fact that this partial function pf doesn't blow up with a MatchError when inner is not InnerA
sealed trait Inner
case class InnerA(name: String) extends Inner
case class InnerB(name: String, value: Int) extends Inner
case class Input(id: String, inner: Inner)
case class Output(id: String, inner: InnerA)
val pf: PartialFunction[Input, Output] = { input =>
input.inner match {
case innerA: InnerA =>
val Input(id, _) = input
Output(id, innerA)
}
}
instead it is simply undefined so this passes
Seq(
Input("1", InnerA("a1")),
Input("2", InnerB("b2", 2)),
Input("3", InnerA("a3"))
).collect(pf) shouldBe Seq(
Output("1", InnerA("a1")),
Output("3", InnerA("a3"))
)
If I add a line I get a compilation warning and trying to pass an InnerB in the collect above throws a MatchError (as I originally expected):
val pf: PartialFunction[Input, Output] = { input =>
println(input)
input.inner match {
case innerA: InnerA =>
val Input(id, _) = input
Output(id, innerA)
}
}
InnerB(b2,2) (of class casa.InnerB)
scala.MatchError: InnerB(b2,2) (of class casa.InnerB)
Why is this? Is this quirk documented somewhere?
(I'm using Scala 2.13.3)
What is happening is that, in the first case, the compiler is "removing" the input match from and using input.inner.
When I run scalac -Xprint:typer Test.scala, the first code turns into:
final override def applyOrElse[A1 <: Input, B1 >: Output](input: A1, default: A1 => B1): B1 = (input.inner: Inner #unchecked) match {
case (innerA # (_: InnerA)) => {
val id: String = (input: A1 #unchecked) match {
case (id: String, inner: Inner): Input((id # _), _) => id
};
Output.apply(id, innerA)
}
case (defaultCase$ # _) => default.apply(input)
};
final def isDefinedAt(input: Input): Boolean = (input.inner: Inner #unchecked) match {
case (innerA # (_: InnerA)) => true
case (defaultCase$ # _) => false
}
Which means that your function will behave like a PartialFunction[Inner, Output], so the compiler knows that it doesn't need to warn you that your match is not exhaustive.
On the other hand, when you see the results for the method with the print instruction, you get:
final override def applyOrElse[A1 <: Input, B1 >: Output](input: A1, default: A1 => B1): B1 = ((input.asInstanceOf[Input]: Input): Input #unchecked) match {
case (defaultCase$ # _) => {
scala.Predef.println("xxx");
input.inner match {
case (innerA # (_: InnerA)) => {
val id: String = (input: A1 #unchecked) match {
case (id: String, inner: Inner): Input((id # _), _) => id
};
Output.apply(id, innerA)
}
}
}
case (defaultCase$ # _) => default.apply(input)
};
final def isDefinedAt(input: Input): Boolean = ((input.asInstanceOf[Input]: Input): Input #unchecked) match {
case (defaultCase$ # _) => true
case (defaultCase$ # _) => false
}
In this case, you're creating a PartialFunction[Input, Output] that is defined for all intervals of Input, and this is fine. But when the compiler checks the inner input.inner match, it warns you that this match - not the first one - is not exhaustive.
this partial function pf doesn't blow up with a MatchError when inner is not InnerA
If you call it directly with such an argument
pf(Input("a", InnerB("b", 0)))
you do get a MatchError; but the whole point of PartialFunction is to provide additional information in isDefinedAt, and collect uses it by not calling pf where isDefinedAt returns false.
Is this quirk documented somewhere?
See paragraph 6.23.1 Translation of specification:
When a PartialFunction is required, an additional member isDefinedAt is synthesized, which simply returns true. However, if the function literal has the shape x => x match { … }, then isDefinedAt is derived from the pattern match in the following way: each case from the match expression evaluates to true, and if there is no default case, a default case is added that evaluates to false.
So for your second version isDefinedAt is always true; the first one doesn't exactly fit x => x match..., but apparently it's supported too. Now the usual way to define a PartialFunction is like Luis Miguel Mejía Suárez's comment says
{ case Input(id, InnerA(name)) => Output(id, InnerA(name)) }
but it simply gets translated into
x => x match { case Input(id, InnerA(name)) => Output(id, InnerA(name)) }

How can I consume a Scala macro/quasiquote for code templates?

I want to generate a bunch of objects at compile time that follow a simple pattern, so I wrote the following macro:
object MyMacro {
def readWrite[T](taName: String, readParse: String => T, label: String, format: T => String): Any = macro readWriteImpl[T]
def readWriteImpl[T: c.WeakTypeTag](c: Context)(taName: c.Expr[String], readParse: c.Expr[String => T], label: c.Expr[String], format: c.Expr[T => String]): c.Expr[Any] = {
import c.universe._
def termName(s: c.Expr[String]): TermName = s.tree match {
case Literal(Constant(s: String)) => TermName(s)
case _ => c.abort(c.enclosingPosition, "Not a string literal")
}
c.Expr[Any](q"""
object ${termName(taName)} extends TypeAdapter.=:=[${implicitly[c.WeakTypeTag[T]].tpe}] {
def read[WIRE](path: Path, reader: Transceiver[WIRE], isMapKey: Boolean = false): ${implicitly[c.WeakTypeTag[T]].tpe} =
reader.readString(path) match {
case null => null.asInstanceOf[${implicitly[c.WeakTypeTag[T]].tpe}]
case s => Try( $readParse(s) ) match {
case Success(d) => d
case Failure(u) => throw new ReadMalformedError(path, "Failed to parse "+${termName(label)}+" from input '"+s+"'", List.empty[String], u)
}
}
def write[WIRE](t: ${implicitly[c.WeakTypeTag[T]].tpe}, writer: Transceiver[WIRE], out: Builder[Any, WIRE]): Unit =
t match {
case null => writer.writeNull(out)
case _ => writer.writeString($format(t), out)
}
}
""")
}
}
I'm not sure I have the return value for readWrite and readWriteImpl correct--the compiler complains mightily about some assertion failure!
I'm also not sure how to actually consume this macro. First I tried (in a separate compilation unit):
object TimeFactories {
MyMacro.readWrite[Duration](
"DurationTypeAdapterFactory",
(s: String) => Duration.parse(s),
"Duration",
(t: Duration) => t.toString)
}
Didn't work. If I tried to reference TimeFactories.DurationTypeAdapterFactory I got an error saying it wasn't found. Next I thought I'd try assigning it to a val...didn't work either:
object Foo {
val duration = MyMacro.readWrite[Duration](
"DurationTypeAdapterFactory",
(s: String) => Duration.parse(s),
"Duration",
(t: Duration) => t.toString).asInstanceOf[TypeAdapterFactory]
}
How can I wire this up so I get generated code compiled like this:
object TimeFactories{
object DurationTypeAdapterFactory extends TypeAdapter.=:=[Duration] {
def read[WIRE](path: Path, reader: Transceiver[WIRE], isMapKey: Boolean = false): Duration =
reader.readString(path) match {
case null => null.asInstanceOf[Duration]
case s => Try( Duration.parse(s) ) match {
case Success(d) => d
case Failure(u) => throw new ReadMalformedError(path, "Failed to parse Duration from input 'Duration'", List.empty[String], u)
}
}
def write[WIRE](t: Duration, writer: Transceiver[WIRE], out: Builder[Any, WIRE]): Unit =
t match {
case null => writer.writeNull(out)
case _ => writer.writeString(t.toString, out)
}
}
// ... More invocations of the readWrite macro with other types for T
}
I don't think, that you can generate new identifiers using macros and than use them publicly.
Instead, try to replace object ${termName(taName)} extends TypeAdapter simply with new TypeAdapter and assign invocation of the macro to a val (as in your second example). You will then reference an anonymous (and generated) class stored in a val. Parameter taName becomes redundant.

def macro inside case statement

I'd like to ask where def macros can be called and when they are expanded? I guess we cant just put an appropriate generated AST anywhere it fits?
For example, I want this:
(2,1) match {
case StandaloneMacros.permutations(1,2) => true ;
case (_,_) => false
}
become this after macro expansion
(2,1) match {
case (1,2) | (2,1) => true ;
case (_,_) => false
}
My macro permutations produces an Alternative of tuples. But when I run the first snippet, I get
macro method permutations is not a case class, nor does it have an unapply/unapplySeq member
I also tried defining a Permutations object with unapply macro method but got another error:
scala.reflect.internal.FatalError: unexpected tree: class scala.reflect.internal.Trees$Alternative
So: Is it possible to achieve at all?
I came up with a solution some time ago, and I thought I'd share it with you.
To achieve above task I've used a Transformer and transformCaseDefs
object Matcher {
def apply[A, B](expr: A)(patterns: PartialFunction[A, B]): B = macro apply_impl[A,B]
def apply_impl[A: c.WeakTypeTag, B: c.WeakTypeTag](c: Context)(expr: c.Expr[A])(patterns: c.Expr[PartialFunction[A, B]]): c.Expr[B] = {
import c.universe._
def allElemsAreLiterals(l: List[Tree]) = l forall {
case Literal(_) | Ident(_) => true
case _ => throw new Exception("this type of pattern is not supported")
}
val transformer = new Transformer {
override def transformCaseDefs(trees: List[CaseDef]) = trees.map {
case cas # CaseDef(pat # Apply(typTree, argList), guard, body) if allElemsAreLiterals(argList) =>
val permutations = argList.permutations.toList.map(t => q"(..$t)").map {
case Apply(_, args) => Apply(typTree, args)
}
val newPattern = Alternative(permutations)
CaseDef(newPattern, guard, body)
case x => x
}
}
c.Expr[B](q"${transformer.transform(patterns.tree)}($expr)")
}
}
It would be shorter, but somehow you need to provide the same TypeTree which was used in original (before transformation) case statement.
This way, it can be used like this
val x = (1,2,3)
Matcher(x) {
case (2,3,1) => true
case _ => false
}
which is then translated to something like
val x = (1,2,3)
x match {
case (1,2,3) | (1,3,2) | (2,1,3) | (2,3,1) | (3,1,2) | (3,2,1) => true
case _ => false
}

Scala macro: rewrite partial function into match

I would like to rewrite partial function into match expression.
//macro and impl
def pfRewrite(pf: PartialFunction[Int, Int]) = macro ResponderImpl.pfRewriteImpl
def pfRewriteImpl(c: Context)(pf: c.Expr[PartialFunction[Int, Int]]) = {
import c.universe._
val cas = pf.tree.collect {
case x: DefDef if x.name.decoded == "applyOrElse" =>
x.collect {
case cq"$x => $y" => cq"""$x => $y"""
}
}.flatten
val sVal = newTermName("someVal")
val rewrite = q"""
$sVal match {
case ..$cas
}
"""
c.Expr(rewrite)
}
In code i got a PartialFunction and take a cases from applyOrElse method, next i create a match expression for "someVal". This value from code:
//test
def test {
val someVal = 10
pfRewrite {
case 1 => 10
case _ => 100
}
}
But i got errors:
[error] found : Int(10)
[error] required: Nothing
[error] case 1 => 10
[error] ^
and etc.
It possible rewrite partial function call into match ?
Typically these kinds of awkward error messages stem from mixing typed and untyped trees (for more details take a look at Scala macros: What is the difference between typed (aka typechecked) an untyped Trees). Therefore, until we've fixed the situation, it's good practice to do resetLocalAttrs on typed trees that you plan to mix with untyped ones.
This case is not an exception. Using c.resetLocalAttrs(pf.tree) instead of just pf.tree reveals the real problem with the macro expansion:
12:57 ~/Projects/Master/sandbox (master)$ scalac Test.scala
Test.scala:5: error: not found: value default
Macros.pfRewrite {
^
If we enable -Xprint:typer, which is one of the ways to see macro expansions (along with -Ymacro-debug-lite and -Ymacro-debug-verbose), then we'll see what's going on:
[[syntax trees at end of typer]] // Test.scala
package <empty> {
object Test extends AnyRef with App {
def <init>(): Test.type = {
Test.super.<init>();
()
};
def test: Unit = {
val someVal: Int = 10;
{
someVal match {
case 1 => 10
case _ => 100
case (defaultCase$ # _) => <default: error>.apply(<x1: error>)
};
()
}
}
}
}
It looks like partial function synthesis also adds a default case that we can't reuse as-is, as it refers to some synthetic variables defined in the synthesized class.

How can I match classes in a Scala "match" statement?

How can I use a "match" statement to identify the value of a class variable? The following is invalid, and I can't find an acceptable variant -- other than if ... else if ... else ...
val c: Class[_] = classOf[Int]
val what = c match { case classOf[Int] => "int!"; case classOf[Float] => "float!" }
The compiler complains: error: not found: type classOf
And of course, I can't use Class[Int] because that type information is erased:
c match { case Class[Int] => "int!"; case Class[Float] => "float!" }
error: type Class of type Class does not take type parameters.
I've also tried variants like Int.class, all to no avail. (And I don't really want to convert to strings: I feel it's important to have the compiler catch renamed/moved classes.)
Am I being dense, or have I stumbled into a Scala blind spot?
The verbose case comparison works:
val what = c match {
case q if q == classOf[Int] => "int!"
case q if q == classOf[Float] => "float!"
}
Of course, being a lower-case identifier, classOf should not work directly in a case statement anyway. However, neither does an escaped
case `classOf`[Int]
work in this case, so you’ll have to go with the if-guard.
You can match on class values if you create a stable identifier (ie. a val) for them,
scala> val c: Class[_] = classOf[Int]
c: Class[_] = int
scala> val ClassOfInt = classOf[Int]
ClassOfInt: java.lang.Class[Int] = int
scala> val ClassOfFloat = classOf[Float]
ClassOfFloat: java.lang.Class[Float] = float
scala> val what = c match {
| case ClassOfInt => "int!"
| case ClassOfFloat => "float!"
| }
what: String = int!
Note that you can't match on type (ie. Class[Int]) because erasure means that the different type instantiations of Class[T] are indistinguishable at runtime ... hence the warning below
scala> val what = c match {
| case _: Class[Int] => "int!"
| case _: Class[Float] => "float!"
| }
warning: there were 2 unchecked warnings; re-run with -unchecked for details
what: java.lang.String = int!
I encountered the same problem and placing the class in a 'stable identifier' wasn't that practical. I found the next best thing was to have tidy 'else if' statements.
Using this method:
private def is[T <: AnyRef : Manifest](implicit cls: Class[_]) =
cls == manifest[T].runtimeClass
I can write:
implicit val arg = cls
if (is[ClassA]) ...
else if (is[ClassB]) ...
...
else throw new IllegalArgumentException("Unknown class: " + cls)
To consider inheritance:
val what = c match {
case q if classOf[Int].isAssignableFrom(q) => "int!"
case q if classOf[Float].isAssignableFrom(q) => "float!"
}