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

ObjectTpeJava is truly unique; bounds; isAnyTpe #8049

Merged
merged 2 commits into from May 14, 2019

Conversation

adriaanm
Copy link
Contributor

@adriaanm adriaanm commented May 10, 2019

Make sure the TypeRef is truly unique, even when embedded
in other types, such as a TypeBounds. Since we're dealing
with structural equality, TypeBounds(lo, hi) == TypeBounds(lo', hi')
if lo == lo' and hi == hi', if hi = ObjectTpe and hi' = ObjectTpe',
unique'ing (hashconsing) such a TypeBounds would indirectly
replace ObjectTpe with ObjectTpeJava or vice versa.

We can't use eq when trying to identify ObjectTpeJava in unique
due to cycles.

isAnyTpe returns true for ObjectTpeJava.

Further fixes by Lukas:

  • Don't show ObjectTpeJava upper bound in TypeBounds.toString
  • Upper bounds of wildcards should be ObjectTpeJava
  • Fix wildcardExtrapolation for BoundedWildcardType with hi ObjectTpeJava

Reduce _ >: lo <: ObjectTpeJava to lo. This eliminates the
BoundedWildcardType in the expected type when type-checking the
lambda

(c: java.util.Collection[String]).removeIf(x => true)

where

boolean removeIf(Predicate<? super E> filter)

Follow up for #7966

Fix scala/bug#11525

@adriaanm adriaanm requested review from retronym and lrytz May 10, 2019 22:16
@scala-jenkins scala-jenkins added this to the 2.13.1 milestone May 10, 2019
@adriaanm adriaanm modified the milestones: 2.13.1, 2.13.0-RC2 May 10, 2019
@adriaanm
Copy link
Contributor Author

adriaanm commented May 10, 2019

Strawman fix. Not sure this is ok for performance, but the test reliably shows that if we don't do something like this, unique will collapse our two ObjectTpe variants.

ObjectTpeJava could still be replaced by ObjectTpe when uniqueing a Type that contains ObjectTpeJava, since e.g. TypeBounds(...ObjectTpeJava...).equalsTypeBounds(...ObjectTpe...) and thus uniqueing the TypeBounds will also replace our precious ObjectTpeJava by ObjectTpe.

@retronym
Copy link
Member

This fix exposed another bug in my attempt at this: Optional<?> in Java code should be translated to Optional[_ <: Object[Java]]. Pushed an attempted fix.

@adriaanm

This comment has been minimized.

@retronym
Copy link
Member

Minimisation of the latest mole:

class Test {
  def foo(c: java.util.Collection[String]): Unit = {
    c.removeIf(x => true)
  }
}

@lrytz lrytz force-pushed the pr7966-followup branch 3 times, most recently from 68b6b62 to 0122f31 Compare May 13, 2019 12:40
if (tp.typeSymbol == AnyClass) TypeBounds.empty
else TypeBounds.lower(tp)
case '*' => TypeBounds.empty
if (tp.typeSymbol == AnyClass) TypeBounds.upper(definitions.ObjectTpeJava)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you add an example for the importance of distinguishing AnyTpe and ObjectTpeJava here?

@adriaanm adriaanm changed the title make ObjectTpeJavaRef truly immune to unique ObjectTpeJava is truly unique; bounds; isAnyTpe May 13, 2019
Make sure the TypeRef is truly unique, even when embedded
in other types, such as a TypeBounds. Since we're dealing
with structural equality, `TypeBounds(lo, hi) == TypeBounds(lo', hi')`
if `lo == lo'` and `hi == hi'`, if `hi = ObjectTpe` and `hi' = ObjectTpe'`,
`unique`'ing (hashconsing) such a `TypeBounds` would indirectly
replace `ObjectTpe` with `ObjectTpeJava` or vice versa.

We can't use `eq` when trying to identify `ObjectTpeJava` in `unique`
due to cycles.

Further fixes by Lukas:
  - Don't show ObjectTpeJava upper bound in TypeBounds.toString
  - Upper bounds of wildcards should be ObjectTpeJava
  - Fix wildcardExtrapolation for BoundedWildcardType with hi ObjectTpeJava

Reduce `_ >: lo <: ObjectTpeJava` to `lo`. This eliminates the
`BoundedWildcardType` in the expected type when type-checking the
lambda

    (c: java.util.Collection[String]).removeIf(x => true)

where

    boolean removeIf(Predicate<? super E> filter)
@retronym
Copy link
Member

/synch

1 similar comment
@retronym
Copy link
Member

/synch

@retronym retronym merged commit 06392a5 into scala:2.13.x May 14, 2019
retronym pushed a commit to retronym/community-builds that referenced this pull request May 14, 2019
smarter added a commit to dotty-staging/dotty that referenced this pull request 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 pull request 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.
@retronym retronym mentioned this pull request Oct 18, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
4 participants