question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Difference in compiler behavior 3.1.2 vs 3.1.3 - implicit resolution / type mismatch

See original GitHub issue

Compiler version

3.1.2 / 3.1.3

Minimized code

This one’s in production code and difficult to provide a minimized example for, so apologies ahead of time…

This issue arises in the context of implicit resolution to find (I’m presuming) the applicable ToEntityMarshaller for the JsArray type.

For the implicit marshaller support I copied into my baseline the PlayJsonSupport trait from here: https://github.com/hseeberger/akka-http-json/blob/master/akka-http-play-json/src/main/scala/de/heikoseeberger/akkahttpplayjson/PlayJsonSupport.scala since it’s only one file and the library is not yet available in general for Scala 3.

// In an Akka-Http route using the following library versions: 
//  val playJsonVersion          = "2.10.0-RC6"
//  val akkaHttpVersion          = "10.2.9"
//  val akkaVersion              = "2.6.19"
// All Akka dependencies are cross-compiled w/ `for3Use2_13`

// ...
def generateJsArray: JsArray = { /* ... */ }
//...

// some path routing logic in the Akka-Http server DSL then: 
get {
  complete[JsArray](StatusCodes.OK, generateJsArray)
}

UPDATE: Here’s a repo with a self-contained example. Change the scalaVersion in build.sbt to reproduce the issue: https://github.com/ALPSMAC/sample-scala3-compiler-bug/tree/main

Output

Under Scala 3.1.2 this compiles as expected. Under Scala 3.1.3 I get a Type Mismatch Error:

[error] -- [E057] Type Mismatch Error: /home/apolack/work/projects/(elided)/src/main/scala/(elided)/ProductionRESTAPI.scala:268:23 
[error] 268 |                      )
[error]     |                       ^
[error]     |Type argument Any does not conform to upper bound play.api.libs.json.JsValue
[error]     |---------------------------------------------------------------------------
[error]     | Explanation (enabled by `-explain`)
[error]     |- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
[error]     | I tried to show that
[error]     |   Any
[error]     | conforms to
[error]     |   play.api.libs.json.JsValue
[error]     | but the comparison trace ended with `false`:
[error]     |
[error]     |   ==> Any  <:  play.api.libs.json.JsValue
[error]     |     ==> Any  <:  play.api.libs.json.JsValue
[error]     |       ==> Any  <:  play.api.libs.json.JsValue
[error]     |       <== Any  <:  play.api.libs.json.JsValue = false
[error]     |     <== Any  <:  play.api.libs.json.JsValue = false
[error]     |   <== Any  <:  play.api.libs.json.JsValue = false
[error]     |
[error]     | The tests were made under the empty constraint
[error]      ---------------------------------------------------------------------------
[error] Explanation
[error] ===========
[error] I tried to show that
[error]   Any
[error] conforms to
[error]   play.api.libs.json.JsValue
[error] but the comparison trace ended with `false`:
[error] 
[error]   ==> Any  <:  play.api.libs.json.JsValue
[error]     ==> Any  <:  play.api.libs.json.JsValue
[error]       ==> Any  <:  play.api.libs.json.JsValue
[error]       <== Any  <:  play.api.libs.json.JsValue = false
[error]     <== Any  <:  play.api.libs.json.JsValue = false
[error]   <== Any  <:  play.api.libs.json.JsValue = false
[error] 
[error] The tests were made under the empty constraint
[error] one error found

Thinking perhaps passing the JsArray as a type argument explicitly to complete would help I tried that… but either way (with or without the explicit type argument) I still get the same compiler error under 3.1.3, but not under 3.1.2.

Expectation

The code should compile under Scala 3.1.3… or if not, at least consistently not compile under both 3.1.2 and 3.1.3. I wouldn’t expect a minor compiler revision to break backwards compatibility in this way.

Thanks

Thanks for all of the great work on Scala 3. I’m looking forward to the ecosystem further evolving and stabilizing.

Issue Analytics

  • State:open
  • Created a year ago
  • Comments:8 (3 by maintainers)

github_iconTop GitHub Comments

1reaction
ALPSMACcommented, Aug 11, 2022

Pushed a repo that replicates the behavior: https://github.com/ALPSMAC/sample-scala3-compiler-bug/tree/main

If you change the scalaVersion in build.sbt from 3.1.2 to 3.1.3 or 3.2.0-RC3 you can cause the compilation error.

Thanks!

0reactions
ALPSMACcommented, Aug 18, 2022

I had a little bit of time just now to play with this… and I haven’t been able to reproduce it in a simpler scenario yet. I do know what does not produce the difference in behavior. Namely this example, complex at it is, does seem to type check and compile correctly:

object MinimizedExample {

  object Setup {
    sealed abstract class Marshaller[-A, +B] {
      def toB(a: A): B
    }

    type ToMessageMarshaller[T] = Marshaller[T, Message]

    final case class Message(bytes: Array[Byte])

    final case class RequestContext[T](t: T)

    final case class RouteResult(status: Int, message: Message)

    type AsyncAction[T] = RequestContext[T] => scala.concurrent.Future[RouteResult]

    trait JsValue {
      val rawValue: String
      def toBytes = rawValue.getBytes("utf8")
    }
    case class JsArray(value: scala.collection.IndexedSeq[JsValue] = Array[JsValue]()) extends JsValue{
      override val rawValue: String = value.map{_.rawValue}.mkString(",")
    }
    case class JsString(str: String) extends JsValue{
      override val rawValue: String = str
    }

    trait Writes[A]{
      def write(a: A): JsValue
    }

    implicit def marshaller[A](
      implicit writes: Writes[A],
      printer: JsValue => String = { v => v.rawValue }
    ): ToMessageMarshaller[A] = {
      new Marshaller[A, Message]{
        override def toB(a: A): Message = Message(writes.write(a).toBytes)
      }
    }

    final case class Response(a: String, b: String, c: String){
      def toJson = responseWrites.write(this)
    }

    implicit val responseWrites: Writes[Response] = new Writes[Response]{
      override def write(r: Response): JsValue = JsString(r.a + ":" + r.b + ":" + r.c)
    }

    // Identity - not sure where this gets defined originally but presumably it must for things to have been
    // working in 3.1.2...???
    implicit val writesJsValue: Writes[JsValue] = new Writes[JsValue]{
      override def write(v: JsValue): JsValue = v
    }

    import scala.concurrent.ExecutionContext.Implicits.global

    def complete[T](status: Int, t: => T)(implicit m: ToMessageMarshaller[T]): AsyncAction[T] = {
      { (ctx: RequestContext[T]) =>
        scala.concurrent.Future(RouteResult(
          status, m.toB(t)
        ))
      }
    }
  }

  object Use {
    import Setup._

    def responses: JsArray = JsArray((0 until 10).map{ i =>
      Response(i.toString, "j", "k").toJson
    })

    complete(200, responses)
  }

}

When I have some more time later I’ll try to circle back around and see if I can’t narrow the scope more… but I’m posting my findings thus-far on the off chance that knowing what is working helps those more qualified than myself in the guts of the Scala compiler narrow the cause.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Scala 3.0.1-RC1 – further stabilising the compiler - EPFL
This work enhances the ability to cross-compile Scala 2 code that uses the Kind Projector plugin to Scala 3. Improved error reporting. Down...
Read more >
Migration Guide: SQL, Datasets and DataFrame - Apache Spark
In Spark 3.2, Spark supports DayTimeIntervalType and YearMonthIntervalType as inputs and outputs of TRANSFORM clause in Hive SERDE mode, the behavior is ...
Read more >
Ada Part for TR 24772 - open-std
directive to the compiler that is used to select partition-wide or system-wide ... Ada does not permit implicit conversions between different numeric types, ......
Read more >
A Study of Typing-Related Bugs in JVM Compilers
type inference or a mix of object-oriented with functional programming features, ... differences between the compiler's expected and actual behavior.
Read more >
CUDA C Programming Guide
Clarified that values of const-qualified variables with builtin floating-point types cannot be used directly in device code when the Microsoft compiler is ...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found