scalabinary-compatibilitypackage-privatemigration-manager

Moving a package-private class—should I consider that binary incompatible?


Because of an issue with package name aux under Windows, I am moving a helper class within the package hierarchy of my library from

de.sciss.scalainterpreter.aux

to

de.sciss.scalainterpreter

The class is private to the library, i.e. private[scalainterpreter] object Helper.

Now using the Typesafe Migration-Manager, obviously it reports that the change is not compatible:

Found 2 binary incompatibiities
===============================
 * class de.sciss.scalainterpreter.aux.Helper does not have a correspondent
   in new version
 * object de.sciss.scalainterpreter.aux.Helper does not have a correspondent
   in new version

But I suspect that if client code does not call into either object, the interfaces are still compatible, and thus I can use a minor version increase to indicate the change, and allow those two versions to be used interchangeably.

Correct?


Solution

  • It's easy to see how inlining can break client code, since the inlined code essentially bleeds into the client interface. This example really asks for a linkage error; we can experiment and do things like javap | grep Helper, but at some level you have to let scalac do its job.

    package lib {
    
      object Lib {
        //import util.Helper
        @inline def result = Helper.help
      }
    
      //package util {
    
      private [lib] object Helper {
        @inline def help = "Does this help?"
      }
    //}
    }
    

    Sample innocently bystanding client:

    package client
    
    object Test {
      import lib.Lib
      def main(args: Array[String]) {
        println(Lib.result)
      }
    }
    

    Changing package of package-private class:

    $ scala -cp "classes;target" client.Test
    Does this help?
    
    apm@halyard ~/tmp/taking-it-private
    $ vi lib.scala
    
    apm@halyard ~/tmp/taking-it-private
    $ rm -rf classes/*
    
    apm@halyard ~/tmp/taking-it-private
    $ smalac -d classes -optimise lib.scala 
    
    apm@halyard ~/tmp/taking-it-private
    $ smala -cp "classes;target" client.Test
    java.lang.ClassNotFoundException: lib.util.Helper$
    

    Javap shows why. [Namely, the call is inlined but it still wants to init the module.]

    I haven't followed the discussions, but for example there are links at: https://github.com/scala/scala/pull/1133 and other discussions on the ML about what expectations about binary compatibility are valid. https://groups.google.com/forum/?fromgroups=#!topic/scala-internals/sJ-xnWL_8PE