Skip to content

java signature requirements lead to unsoundness in non-covariant positions #4388

Closed as not planned
@scabug

Description

@scabug

The following program compiles with no warnings, then throws a CCE when run.

import scala.math.Ordering;
import scala.math.Ordering$;

public class J {
  public static void main(String[] args) {
    // Here's where we are really punished...
    // Again I see the generation of a Character method
    // as the only way out.
    Ordering<Object> charOrd = Ordering.Char$.MODULE$;
    
    charOrd.compare(new Object(), new Object());
  }
}
% scala29 J
java.lang.ClassCastException: java.lang.Object cannot be cast to java.lang.Character
	at scala.runtime.BoxesRunTime.unboxToChar(Unknown Source)
	at scala.math.Ordering$$Char$$.compare(Ordering.scala:181)
	at J.main(J.java:10)

In 2.8.1 it fails to compile in the desirable way, because Ordering.Char is an Ordering.
(Note that 2.8.1 requires a slightly different code because of changes to the InnerClasses attribute)

// 2.8 only
import scala.math.Ordering;
import scala.math.Ordering$;
import scala.math.Ordering$Char$;

public class J {  
  public static void main(String[] args) {
    // Here's where we are really punished...
    // Again I see the generation of a Character method
    // as the only way out.
    Ordering<Object> charOrd = Ordering$Char$.MODULE$;
    
    charOrd.compare(new Object(), new Object());
  }
}
J.java:8: incompatible types
found   : scala.math.Ordering$$Char$$
required: scala.math.Ordering<java.lang.Object>
    Ordering<Object> charOrd = Ordering$$Char$$.MODULE$$;
                                             ^
1 error

But that was changed in an attempt to fix #4214, as explained in the commit message for r24363:

Another lap around the track with generic signatures.
At the root of the issue reported in SI-4214 is our old friend
(fondly remembered from the days of primitive equality)
boxed/primitive unification.

  // scala
  trait T[A] {
    def f(): A
  }
  // the generic signature spec doesn't allow for parameterizing
  // on primitive types, so this cannot remain Char.  However
  // translating it to Character, as was done, also has issues.
  class C extends T[Char] {
    def f(): Char = 'a'
  }

  // Note that neither of the signatures for f, the implementation
  // or the bridge method, matches the type parameter.
  Generic interfaces in class:
    T<java.lang.Character>
  Generic signatures:
    public char C.f()
    public java.lang.Object C.f()

After this commit, primitive type parameters are translated into
Object instead of the boxed type.  It was martin's idea, so no review.
Closes SI-4214.

Again I see no way out which does not involve generating more bridge methods which put things in boxes and take things out of other boxes.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions