@@ -195,7 +195,7 @@ class Definitions {
195195 RootClass , nme.EMPTY_PACKAGE , (emptypkg, emptycls) => ctx.base.rootLoader(emptypkg)).entered
196196 @ tu lazy val EmptyPackageClass : ClassSymbol = EmptyPackageVal .moduleClass.asClass
197197
198- /** A package in which we can place all methods that are interpreted specially by the compiler */
198+ /** A package in which we can place all methods and types that are interpreted specially by the compiler */
199199 @ tu lazy val OpsPackageVal : TermSymbol = newCompletePackageSymbol(RootClass , nme.OPS_PACKAGE ).entered
200200 @ tu lazy val OpsPackageClass : ClassSymbol = OpsPackageVal .moduleClass.asClass
201201
@@ -310,6 +310,103 @@ class Definitions {
310310 }
311311 def ObjectType : TypeRef = ObjectClass .typeRef
312312
313+ /** A type alias of Object used to represent any reference to Object in a Java
314+ * signature, the secret sauce is that subtype checking treats it specially:
315+ *
316+ * tp <:< FromJavaObject
317+ *
318+ * is equivalent to:
319+ *
320+ * tp <:< Any
321+ *
322+ * This is useful to avoid usability problems when interacting with Java
323+ * code where Object is the top type. This is safe because this type will
324+ * only appear in signatures of Java definitions in positions where `Object`
325+ * might appear, let's enumerate all possible cases this gives us:
326+ *
327+ * 1. At the top level:
328+ *
329+ * // A.java
330+ * void meth1(Object arg) {}
331+ * <T> void meth2(T arg) {} // T implicitly extends Object
332+ *
333+ * // B.scala
334+ * meth1(1) // OK
335+ * meth2(1) // OK
336+ *
337+ * This is safe even though Int is not a subtype of Object, because Erasure
338+ * will detect the mismatch and box the value type.
339+ *
340+ * 2. In a class type parameter:
341+ *
342+ * // A.java
343+ * void meth3(scala.List<Object> arg) {}
344+ * <T> void meth4(scala.List<T> arg) {}
345+ *
346+ * // B.scala
347+ * meth3(List[Int](1)) // OK
348+ * meth4(List[Int](1)) // OK
349+ *
350+ * At erasure, type parameters are removed and value types are boxed.
351+ *
352+ * 3. As the type parameter of an array:
353+ *
354+ * // A.java
355+ * void meth5(Object[] arg) {}
356+ * <T> void meth6(T[] arg) {}
357+ *
358+ * // B.scala
359+ * meth5(Array[Int](1)) // error: Array[Int] is not a subtype of Array[Object]
360+ * meth6(Array[Int](1)) // error: Array[Int] is not a subtype of Array[T & Object]
361+ *
362+ *
363+ * This is a bit more subtle: at erasure, Arrays keep their type parameter,
364+ * and primitive Arrays are not subtypes of reference Arrays on the JVM,
365+ * so we can't pass an Array of Int where a reference Array is expected.
366+ * Array is invariant in Scala, so `meth5` is safe even if we use `FromJavaObject`,
367+ * but generic Arrays are treated specially: we always add `& Object` (and here
368+ * we mean the normal java.lang.Object type) to these types when they come from
369+ * Java signatures (see `translateJavaArrayElementType`), this ensure that `meth6`
370+ * is safe to use.
371+ *
372+ * 4. As the repeated argument of a varargs method:
373+ *
374+ * // A.java
375+ * void meth7(Object... args) {}
376+ * <T> void meth8(T... args) {}
377+ *
378+ * // B.scala
379+ * meth7(1) // OK
380+ * meth8(1) // OK
381+ * val ai = Array[Int](1)
382+ * meth7(ai: _*) // OK (will copy the array)
383+ * meth8(ai: _*) // OK (will copy the array)
384+ *
385+ * Java repeated arguments are erased to arrays, so it would be safe to treat
386+ * them in the same way: add an `& Object` to the parameter type to disallow
387+ * passing primitives, but that would be very inconvenient as it is common to
388+ * want to pass a primitive to an Object repeated argument (e.g.
389+ * `String.format("foo: %d", 1)`). So instead we type them _without_ adding the
390+ * `& Object` and let `ElimRepeated` take care of doing any necessary adaptation
391+ * (note that adapting a primitive array to a reference array requires
392+ * copying the whole array, so this transformation only preserves semantics
393+ * if the callee does not try to mutate the varargs array which is a reasonable
394+ * assumption to make).
395+ *
396+ *
397+ * This mechanism is similar to `ObjectTpeJavaRef` in Scala 2, except that we
398+ * create a new symbol with its own name, this is needed because this type
399+ * can show up in inferred types and therefore needs to be preserved when
400+ * pickling so that unpickled trees pass `-Ycheck`.
401+ *
402+ * Note that by default we pretty-print `FromJavaObject` as `Object` or simply omit it
403+ * if it's the sole upper-bound of a type parameter, use `-Yprint-debug` to explicitly
404+ * display it.
405+ */
406+ @ tu lazy val FromJavaObjectSymbol : TypeSymbol =
407+ newPermanentSymbol(OpsPackageClass , tpnme.FromJavaObject , JavaDefined , TypeAlias (ObjectType )).entered
408+ def FromJavaObjectType : TypeRef = FromJavaObjectSymbol .typeRef
409+
313410 @ tu lazy val AnyRefAlias : TypeSymbol = enterAliasType(tpnme.AnyRef , ObjectType )
314411 def AnyRefType : TypeRef = AnyRefAlias .typeRef
315412
0 commit comments