Skip to content
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

Fix handling of Java varargs #8977

Closed
michelou opened this issue May 13, 2020 · 1 comment
Closed

Fix handling of Java varargs #8977

michelou opened this issue May 13, 2020 · 1 comment

Comments

@michelou
Copy link
Collaborator

Issue to be solved by pull request #8718 (smarter)

Minimized code / Output

Dotty REPL (tested with version 0.24 and newer)

Let's try 3 different ways to print out formatted strings:

$ c:\opt\dotty-0.24.0-RC1\bin\dotr
Starting dotty REPL...
scala> val pi = 3.1415926
val pi: Double = 3.1415926

scala> printf("pi = %6.4f\n", pi)
pi = 3.1416

scala> println(f"pi = $pi%6.4f")
pi = 3.1416

scala> System.out.printf("pi = %6.4f\n", pi)
1 |System.out.printf("pi = %6.4f\n", pi)
  |^^^^^^^^^^^^^^^^^
  |None of the overloaded alternatives of method printf in class PrintStream with types
  | (x$0: java.util.Locale, x$1: String, x$2: Object*): java.io.PrintStream
  | (x$0: String, x$1: Object*): java.io.PrintStream
  |match arguments (("pi = %6.4f\n" : String), (pi : Double))

Scala REPL (tested with version 2.13.2)

$ c:\opt\scala-2.13.2\bin\scala
Welcome to Scala 2.13.2 (OpenJDK 64-Bit Server VM, Java 1.8.0_252).
Type in expressions for evaluation. Or try :help.

scala> val pi = 3.1415926
val pi: Double = 3.1415926

scala> System.out.printf("pi = %6.4f\n", pi)
pi = 3.1416
val res0: java.io.PrintStream = java.io.PrintStream@689faf79

Expectation

Same output for Dotty and Scala 2 with System.out.printf.

Workaround in Dotty : add.asInstanceOf[Object] to printf argument(s).

$ c:\opt\dotty-0.24.0-RC1\bin\dotr
Starting dotty REPL...
scala> val pi = 3.1415926
val pi: Double = 3.1415926

scala> System.out.printf("pi = %6.4f\n", pi.asInstanceOf[Object])
pi = 3.1416
val res1: java.io.PrintStream = java.io.PrintStream@2e6b379c
smarter added a commit to dotty-staging/dotty that referenced this issue Aug 19, 2020
In Java unlike Scala, `Object` is the top type, this leads to various
usability problems when attempting to call or override a Java method
from Scala. So far, we relied mainly on one mechanism to improve the
situation: in the ClassfileParser, some references to `Object` in
signatures were replaced by `Any` (cf `objToAny`). But this had several
shortcomings:
- To compensate for this substitution,
  `TypeComparer#matchingMethodParams` had to special case Any in Java
  methods and treat it like Object
- There were various situation were this substitution was not applied,
  notably when using varargs (`Object... args`) or when jointly
  compiling .java sources since this is handled by JavaParser and not
  ClassfileParser.

This commit replaces all of this by a more systematic solution: all
references to `Object` in Java definitions (both in classfiles and .java
source files) are replaced by a special type `FromJavaObject` which is a
type alias of `Object` with one extra subtyping rule:

   tp <:< FromJavaObject

is equivalent to:

   tp <:< Any

See the documentation of `FromJavaObjectSymbol` for details on
why this makes sense.

This solution is very much inspired by
scala/scala#7966 (which was refined in
scala/scala#8049 and
scala/scala#8638) with two main differences:
- We use a type with its own symbol and name distinct from
  `java.lang.Object`, because this type can show up in inferred types
  and therefore needs to be preserved when pickling so that unpickled
  trees pass `-Ycheck`.
- Unlike in Scala 2, we cannot get rid of `JavaMethodType` because when
  calling `Type#signature` we need to know whether we're in a Java
  method or not, because signatures are based on erased types and erasure
  differs between Scala and Java (we cannot ignore this and always
  base signatures on the Scala erasure, because signatures are used
  to distinguish between overloads and overrides so they must agree
  with the actual JVM bytecode signature).

Fixes scala#7467, scala#7963, scala#8588, scala#8977.
smarter added a commit to dotty-staging/dotty that referenced this issue Aug 21, 2020
In Java unlike Scala, `Object` is the top type, this leads to various
usability problems when attempting to call or override a Java method
from Scala. So far, we relied mainly on one mechanism to improve the
situation: in the ClassfileParser, some references to `Object` in
signatures were replaced by `Any` (cf `objToAny`). But this had several
shortcomings:
- To compensate for this substitution,
  `TypeComparer#matchingMethodParams` had to special case Any in Java
  methods and treat it like Object
- There were various situation were this substitution was not applied,
  notably when using varargs (`Object... args`) or when jointly
  compiling .java sources since this is handled by JavaParser and not
  ClassfileParser.

This commit replaces all of this by a more systematic solution: all
references to `Object` in Java definitions (both in classfiles and .java
source files) are replaced by a special type `FromJavaObject` which is a
type alias of `Object` with one extra subtyping rule:

   tp <:< FromJavaObject

is equivalent to:

   tp <:< Any

See the documentation of `FromJavaObjectSymbol` for details on
why this makes sense.

This solution is very much inspired by
scala/scala#7966 (which was refined in
scala/scala#8049 and
scala/scala#8638) with two main differences:
- We use a type with its own symbol and name distinct from
  `java.lang.Object`, because this type can show up in inferred types
  and therefore needs to be preserved when pickling so that unpickled
  trees pass `-Ycheck`.
- Unlike in Scala 2, we cannot get rid of `JavaMethodType` because when
  calling `Type#signature` we need to know whether we're in a Java
  method or not, because signatures are based on erased types and erasure
  differs between Scala and Java (we cannot ignore this and always
  base signatures on the Scala erasure, because signatures are used
  to distinguish between overloads and overrides so they must agree
  with the actual JVM bytecode signature).

Fixes scala#7467, scala#7963, scala#8588, scala#8977.
@smarter
Copy link
Member

smarter commented Aug 24, 2020

Fixed in #9601.

@smarter smarter closed this as completed Aug 24, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants