scala3 icon indicating copy to clipboard operation
scala3 copied to clipboard

Compiler ignores instance of typeclass and tries to derive it instead

Open WojciechMazur opened this issue 2 years ago • 0 comments

Regression found in Open CB #4758 for wangzaixiang/wjson Part of the 3.2.1-RC1 regressions tracker https://github.com/lampepfl/dotty/issues/15949

There seem to be 2 reasons why code now fails to compile:

  1. In Scala 3.2.0 compiler used defined instance of given [T:JsValueMapper]: JsValueMapper[Map[String, T]] in 3.2.1 it tries to derive type class instead using given [T: JsValueMapper, CC[x] <: IterableOps[x, CC, CC[x]]]: Conversion[CC[T], JsArray]. See AST below
  2. While deriving Map[String, Int] TypeTree.of[T].symbol where T=Tuple2[String, Int] returns NoSymbol

Compiler version

3.2.1-RC1

Minimized code

// test.scala
import scala.collection.IterableOps

enum JsValue:
    case JsNumber(value: Double|Long)
    case JsString(value: String)
    case JsArray(elements: Seq[JsValue])
    case JsObject(fields: Map[String, JsValue])
    
object JsValue:
  given [T: JsValueMapper]: Conversion[T, JsValue] = ???
  given [T: JsValueMapper, CC[x] <: IterableOps[x, CC, CC[x]]]: Conversion[CC[T], JsArray] = ???

trait JsValueMapper[T]:
  def toJson(t: T): JsValue

object JsValueMapper:
  inline given[T](using deriving.Mirror.ProductOf[T]): JsValueMapper[T] = ${JsValueMapperMacro.generateImpl[T]}

  given JsValueMapper[Int] = ???
  given JsValueMapper[String] = ???
  given [T:JsValueMapper]: JsValueMapper[Map[String, T]] = ???

extension [T: JsValueMapper](obj: T)
  inline def toJson: JsValue = summon[JsValueMapper[T]].toJson(obj)

@main def Test = 
  val works = Map("a"->1,"b"->2,"c"->3).toJson
  val fails = Map("a"->1,"b"->2,"c"->3): JsValue // error
// macro.scala
import scala.quoted.*
import scala.deriving.*

object JsValueMapperMacro:
  def generateImpl[T: Type](using Quotes): Expr[JsValueMapper[T]] =
    import quotes.reflect.*
    import JsValueMapper.*
    val sym = TypeTree.of[T].symbol // no symbol for Tuple2[String,Int]
    val module = Ref(sym.companionModule)
    ???

Output

[error] java.lang.AssertionError: assertion failed
[error]         at scala.runtime.Scala3RunTime$.assertFailed(Scala3RunTime.scala:11)
[error]         at scala.quoted.runtime.impl.QuotesImpl$reflect$Ref$.apply(QuotesImpl.scala:435)
[error]         at scala.quoted.runtime.impl.QuotesImpl$reflect$Ref$.apply(QuotesImpl.scala:434)
[error]         at JsValueMapperMacro$.generateImpl(macro.scala:9)
[error] 
[error]   val fails = Map("a"->1,"b"->2,"c"->3): JsValue // error
[error]               ^

AST in Scala 3.2.0

val fails: wjson.JsValue = 
   wjson.JsValue.given_Conversion_T_JsValue[Map[String, Int]](
     wjson.JsValueMapper.given_JsValueMapper_Map[Int](
       wjson.JsValueMapper.given_JsValueMapper_Int
     )
   ).apply(
     Map.apply[String, Int](
       [ArrowAssoc[String]("a").->[Int](1),
         ArrowAssoc[String]("b").->[Int](2)
       ,ArrowAssoc[String]("c").->[Int](3) : (String, Int)]*
     )
   ):wjson.JsValue

AST in Scala 3.2.1-RC1

val fails: wjson.JsValue = 
  wjson.JsValue.given_Conversion_CC_JsArray[(String, Int), 
    scala.collection.immutable.Iterable
  ](
    wjson.JsValueMapper.given_JsValueMapper_T[(String, Int)](
      new scala.runtime.TupleMirror(2).$asInstanceOf[
        
          scala.deriving.Mirror.Product{
            MirroredMonoType = (String, Int); 
              MirroredType = (String, Int)
            ; MirroredLabel = ("Tuple2" : String); 
              MirroredElemTypes = (String, Int)
            ; MirroredElemLabels = (("_1" : String), ("_2" : String))
          }
        
      ]
    )
  ).apply(
    Map.apply[String, Int](
      [ArrowAssoc[String]("a").->[Int](1),
        ArrowAssoc[String]("b").->[Int](2)
      ,ArrowAssoc[String]("c").->[Int](3) : (String, Int)]*
    )
  ):wjson.JsValue

Expectation

Should compile

WojciechMazur avatar Sep 08 '22 09:09 WojciechMazur