Skip to content

Alias givens don't forward the reference for simple expressions outside the current object #23107

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
kyri-petrou opened this issue May 6, 2025 · 1 comment
Labels
area:implicits related to implicits itype:bug

Comments

@kyri-petrou
Copy link

Compiler version

3.3.5 / 3.6.4 / 3.7.0-RC4

Minimized code

class Foo[A]

object Foo {
  val fooString = Foo[String]()
}

object Bar {
  private val fooInt = Foo[Int]()

  given Foo[Int]    = fooInt
  given Foo[String] = Foo.fooString
  given Foo[Any]    = null
}

Output

When the following code is compiled with -Xprint:erasure, the following is printed:

package <empty> {
  @SourceFile(
    "src/main/scala/main.scala") class
     Foo() extends Object() {}
  final lazy module val Foo: Foo = new Foo()
  @SourceFile(
    "src/main/scala/main.scala")
    final module class Foo() extends Object() {
    private def writeReplace(): Object =
      new scala.runtime.ModuleSerializationProxy(classOf[Foo])
    def fooString(): Foo = new Foo()
  }
  final lazy module val Bar: Bar = new Bar()
  @SourceFile(
    "src/main/scala/main.scala")
    final module class Bar() extends Object() {
    private def writeReplace(): Object =
      new scala.runtime.ModuleSerializationProxy(classOf[Bar])
    private val fooInt: Foo = new Foo()
    
    // ---- See here ----
    final given def given_Foo_Int(): Foo = Bar.fooInt
    final lazy given def given_Foo_String(): Foo = Foo.fooString()
    final lazy given def given_Foo_Any(): Foo = null
  }
}

Note that I also checked the decompiled Java code and it's consistent with the outputs post-erasure.

Expectation

According to the documentation, the givens in the example above are all simple expressions and should forward the reference instead of caching it (i.e., lazy). I reported a similar bug quite a long time ago (see #16191) but it seems this was only fixed / applied for cases where the val (i.e., simple expression) is in the current object whereas vals stored in other objects are not forwarded.

From my understanding (which might be wrong), vals in objects are stored as private static final fields with an accessor method, which could explain why this happens in the case of Foo[String], although this behaviour also occurs in cases where the val is annotated with @static. It also occurs in cases where the RHS is a statically known value that doesn't require initialization (e.g., null).

There might be reasons why this behaviour is the expected / correct one but I thought to bring this to your attention and see whether you think this might indeed be a bug or not.

@kyri-petrou kyri-petrou added itype:bug stat:needs triage Every issue needs to have an "area" and "itype" label labels May 6, 2025
@som-snytt
Copy link
Contributor

https://docs.scala-lang.org/scala3/reference/contextual/givens.html says

If the given is a mere alias to some immutable value, the given is implemented as a simple forwarder, without incurring the cost of a field to hold a cached value.

but https://docs.scala-lang.org/scala3/reference/contextual/relationship-implicits.html#given-instances

If an alias has neither type nor context parameters, it is treated as a lazy val, unless the right-hand side is a simple reference, in which case we can use a forwarder to that reference without caching it.

The source of truth makes a narrower promise:

/** This phase optimizes alias givens represented as lazy vals to be uncached
 *  if that does not change runtime behavior. A definition does not need to be
 *  cached if its right hand side has a stable type and is of one of them forms
 *
 *    this
 *    this.y
 *    y
 */

@Gedochao Gedochao added area:implicits related to implicits and removed stat:needs triage Every issue needs to have an "area" and "itype" label labels May 6, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area:implicits related to implicits itype:bug
Projects
None yet
Development

No branches or pull requests

3 participants