Dotty: Quoted matching on imported method causes tuple class-cast exception

Created on 19 Apr 2020  路  4Comments  路  Source: lampepfl/dotty

Minimized code

Let's say I have a trait with a method extended by a companion:

object Companion extends Trait
trait Trait {
  def fun(first: String): String = "anything"
}

Then I try to match this code in a macro:

tree.unseal.underlyingArgument.seal match {
    case vv @ '{ ($s: Trait).fun($arg) } => arg
}

When invoking the macro with the Companion as an import:

import Companion._
mac(fun("blah"))

The following compiler error occurs:

Exception occurred while executing macro expansion.
java.lang.ClassCastException: scala.Tuple1 cannot be cast to scala.Tuple2
    at companionbug.Macro$.macImpl(Macro.scala:16)

Expectation

The code should compile and the match should work.

Code Sample

Full code sample can be found here:
https://github.com/deusaquilus/companion_bug

metaprogramming bug high

All 4 comments

I would like to add that this issue is very relevant to Quill because for things like query schemas, the following pattern is typically used:

import QuotationContext._
quote {
  querySchema[Table]("tableName", /*... */)
}

In this case, querySchema is actually QuotationContext.querySchema. Every single method in the Quotation DSL uses this pattern.

It is interesting to note that this problem only occurs when importing from a companion. Instantiating the trait directly and then importing the methods, it will work:

val inst = new Trait {}
import inst._
mac(fun("blah")) // Matching works!

Also, there seems to be a curious workaround. If you write the companion object into a val and then import from the val, this problem does not occur:

val comp = Companion
import comp._
mac(fun("blah"))

This is very odd but will at least let me make progress for now. Definitely won't suffice for Quill users though.

@nicolasstucki @liufengyun FYI, this is the only blocking issue I have at the moment, I've got a decent workaround for everything else.

I was curious to see what happens if you use Companion.type directly. So this:

object Companion {
  def fun(first: String): String = "anything"
}

Matching against this:

case vv @ '{ ($s: Companion.type).fun($arg) } => 

using this:

import Companion._
mac(fun("blah"))

This also causes the same:

java.lang.ClassCastException: scala.Tuple1 cannot be cast to scala.Tuple2

We were ignoring the thee prefix when the scrutinee did not mention it explicitly in the tree. I will add a regression test for that one.

Was this page helpful?
0 / 5 - 0 ratings