scala / scala3

The Scala 3 compiler, also known as Dotty.
https://dotty.epfl.ch
Apache License 2.0
5.87k stars 1.06k forks source link

Static forwarder for local method is not consistently generated #18248

Open raboof opened 1 year ago

raboof commented 1 year ago

Compiler version

3.3.2-RC1-bin-20230718-da16f43-NIGHTLY

Minimized code

I have not minimized this yet, I will improve the issue later, but encountered it when building https://github.com/apache/incubator-pekko/blob/v1.0.1-RC1/actor-typed/src/main/scala/org/apache/pekko/actor/typed/internal/ExtensionsImpl.scala#L32-L138 with sbt:

private[pekko] trait ExtensionsImpl extends Extensions { self: ActorSystem[_] with InternalRecipientRef[_] =>

  def loadExtensions(): Unit = {

    def loadExtensions(key: String, throwOnLoadFail: Boolean): Unit = {
        (...)
    }
    (...)

    loadExtensions("pekko.actor.typed.library-extensions", throwOnLoadFail = true)
    loadExtensions("pekko.actor.typed.extensions", throwOnLoadFail = false)
  }

  (...)
}

Output

The resulting ExtensionsImpl.class sometimes has a public static void loadExtensions$1$(org.apache.pekko.actor.typed.internal.ExtensionsImpl, java.lang.String, boolean);, and sometimes doesn't.

This method cannot be called from 'outside' so this forwarder likely isn't needed, but it would be good to either consistently generate it or consistently not generate it.

Expectation

Produce exactly the same class for the same source each time.

raboof commented 1 year ago

Unfortunately I haven't managed to create a minimal reproducer, however I can fairly reliably reproduce the problem building Pekko.

I'm noticing something strange in BCodeSkelBuilder.scala: it seems just when needsStaticImplMethod is being determined, concurrently dd.symbol.lastDenot changes from one that is 'public' with name loadExtensions to one that is private with name loadExtensions$1. Does that ring a bell to anyone?

(also: it's confusing that there are two methods named 'loadExtensions' here. the problem is with the 'inner' method and reproduces when the names are different as well)

raboof commented 5 months ago

Interestingly, it turns out the fact that both methods were named loadExtensions was irrelevant here: after changing the code to:

private[pekko] trait ExtensionsImpl extends Extensions { self: ActorSystem[_] with InternalRecipientRef[_] =>

  def loadExtensions(): Unit = {

    def loadExtensionsFor(key: String, throwOnLoadFail: Boolean): Unit = {
        (...)
    }
    (...)

    loadExtensionsFor("pekko.actor.typed.library-extensions", throwOnLoadFail = true)
    loadExtensionsFor("pekko.actor.typed.extensions", throwOnLoadFail = false)
  }

  (...)
}

we encountered the problem again in our 1.1.0-M1 release