JDK-8237072 : [lworld] Add support for denoting and deriving the reference projection
  • Type: Bug
  • Component: tools
  • Sub-Component: javac
  • Affected Version: repo-valhalla
  • Priority: P4
  • Status: Resolved
  • Resolution: Fixed
  • OS: generic
  • CPU: generic
  • Submitted: 2020-01-14
  • Updated: 2020-05-11
  • Resolved: 2020-05-05
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
Other
repo-valhallaFixed
Related Reports
Blocks :  
Relates :  
Relates :  
Relates :  
Relates :  
Relates :  
Relates :  
Relates :  
Relates :  
Description
From http://cr.openjdk.java.net/~briangoetz/valhalla/sov/02-object-model.html

Reference and value projections
It is often useful to be able to describe the set of references to objects of a given inline class, plus null. Given an inline type V, we would like a reference type R whose value set is given by:


ValSet(R)���=���{null} ��� {ref v���:���v���������ValSet(V)}

We call such a type R a reference projection of V. (A reference type is its own reference projection.)

A reference projection plays the role that a wrapper class plays in the current world. But, we don���t want every inline class to have a hand-written, ad-hoc wrapper; we would like to be able to mechanically derive a reference projection from an inline class, and have a uniform way to refer to it ��� that way we don���t have to maintain a mental dictionary mapping inline classes to their reference projections. For any type T, T.ref denotes the reference projection of T.

For inline classes, we automatically create both the reference and value projections. For an inline class V, V.ref is a type that describes the reference projection for V (the set of references to instances of V, plus null), V.val refers to the value projection for V (the set of instances of V), and (absent special pleading, see below) V is an alias for V.val. (The reference projection is a sealed abstract class that permits only V.val as a subtype.) So for an inline class V that extends abstract class C, we effectively get:

sealed abstract class V.ref
    extends C
    permits V.val { }

inline class V.val extends V.ref { }
and V becomes an alias for V.val. (This sort of aliasing is not new; String is an alias for java.lang.String.) We automatically get an inline widening conversion from V.val to V.ref (and hence from V to V.ref). Additionally, we define an inline narrowing conversion from V.ref to V.val (and hence from V.ref to V) which applies the unref operator, throwing NullPointerException on null.

With this pair of conversions, inline classes have the same relationship with their reference projections as primitives historically did with their wrapper types. Existing rules that are defined in terms of boxing conversions (autoboxing, typing of conditionals, overload selection) can be trivially extended to incorporate inline widening and narrowing conversions too. The result is that existing language rules and user intuition about these conversions carries forward unchanged in the new world ��� but without the runtime costs of boxing, because inline widening does not mandate the creation of a new object with accidental identity as boxing currently does.

A reference type R already meets all the requirements to be its own reference projection, so for a reference type R, we make R.ref an alias for R itself. Now we���ve ensured that every type T has a reference projection that is denoted T.ref.

// ------------------------------------------------------------------------

This ticket is to add support for denoting, deriving and representing the reference projection type. 
Comments
Changeset: a3a846d7 Author: Srikanth Adayapalam <sadayapalam@openjdk.org> Date: 2020-05-05 10:26:28 +0000 URL: https://git.openjdk.java.net/valhalla/commit/a3a846d7
05-05-2020

It is known that we need to refine the notion of bounds conformance; this was discussed at the Dublin offsite. Intuitively, we want to say a type T is in bound B if T.ref would be in bound B; since for reference types R, R == R.ref, this is consistent with the current rule. Similarly, inference has a rule that says "if you infer a primitive, and you need a ref, then box." That rule would need to be broadened to incorporate inline widening. However, these are mostly handwavy approximations; Dan can provide more detail.
01-05-2020

As of https://github.com/openjdk/valhalla/pull/32, there are various problems in the interplay of inlines types with generics and inference: Removing the experimental support for generics over values results in several constructs that used to compile earlier (albeit only with -XDallowGenericsOverValues) not compiling anymore. These expose some issues that feed into the language design. We need to evolve a short term answer (so that the JDK components tests that are impacted don't get blocked) and a long term one (the real solution) Here is a snippet that captures these problems: import java.util.Arrays; interface I {} public inline class X implements I { int x = 10; void problem_01() { X [] a1 = new X[0]; X [] a2 = Arrays.copyOf(a1, a2.length, X[].class); /* error: incompatible types: inference variable T has incompatible bounds X [] a2 = Arrays.copyOf(a1, a2.length, X[].class); ^ lower bounds: X,Object lower bounds: X$ref where T,U are type-variables: T extends Object declared in method <T,U>copyOf(U[],int,Class<? extends T[]>) U extends Object declared in method <T,U>copyOf(U[],int,Class<? extends T[]>) 1 error */ } void foo(Class<? extends I> p) {} void problem_02() { foo(X.class); /* error: incompatible types: Class<X> cannot be converted to Class<? extends I> foo(X.class); */ } String data() { return null; } // Problem: 3, we infer a stream of X.ref's causing // the method reference to not type check. void unbound_lookup_with_projected_receiver() { X [] a = new X[0]; Arrays.stream(a).map(X::data).filter(p -> p != null).forEach(System.out::println); /* error: incompatible types: invalid method reference Arrays.stream(a).map(X::data).filter(p -> p != null).forEach(System.out::println); ^ method data in class X cannot be applied to given types required: no arguments found: X$ref reason: actual and formal argument lists differ in length */ } void problem_04() { /* test/hotspot/jtreg/runtime/valhalla/valuetypes/UnsafeTest.java:125: warning: [removal] putObject(Object,long,Object) in Unsafe has been deprecated and marked for removal U.putObject(v, off_o, List.of("Value1", "Value2", "Value3")); ^ /home/srikanth/valhalla/test/valhalla/test/hotspot/jtreg/runtime/valhalla/valuetypes/UnsafeTest.java:127: error: method valueHeaderSize in class Unsafe cannot be applied to given types; U.putInt(v, off_v + off_i - U.valueHeaderSize(Value2.class), 999); ^ required: Class<V> found: Class<Value2> reason: inference variable V has incompatible bounds equality constraints: Value2 lower bounds: Object where V is a type-variable: V extends Object declared in method <V>valueHeaderSize(Class<V>) */ } void problem_05() { /* test/hotspot/jtreg/compiler/valhalla/valuetypes/TestIntrinsics.java:119: error: incomparable types: Class<CAP#1> and Class<MyValue1$ref> boolean check2 = MyValue1.class.asIndirectType().getSuperclass() == MyValue1.ref.class; ^ where CAP#1 is a fresh type-variable: CAP#1 extends Object super: MyValue1 from capture of ? super MyValue1 */ } public static void main(String [] args) { } }
30-04-2020

As of https://github.com/openjdk/valhalla/pull/32, there are many known issues that have been deliberately deferred to a later iteration: - With Brian's consent I am using a simpler translation strategy than what is outlined in the SoV documents. These are good enough for now, but when VBC migration is undertaken, will have to enhanced. - No support for ref-default and val-default classes yet. - No support for class hierarchy sealing. - You can't do new V.ref() (V.ref is abstract) although SoV calls for it. - Handling of .ref and .val is quick and dirty; Need revisit. - The nest mate related attributes are not fully properly emitted as of now - Need to verify that attributes from V are not carried over inadvertantly to V$ref.class - Class hierarchy walking in a world where inline types are islands calls for a type switch. I have done this only in places uncovered by existing tests. We need to go through various utility methods (similar to what is done in Symbol.java and Resolve.java) to make sure these changes are consistently applied. - Diamond inference with value classes crashes now (implementation creates factory methods, projections are missing and need to be created)
30-04-2020

Pull request raised here: https://github.com/openjdk/valhalla/pull/32
30-04-2020

Here specifically are the things this ticket will implement: 0. For an inline class V that extends abstract class C, generate a pair of classes V.ref and V.val such that V.ref is of the form /* sealed */ abstract class V.ref extends C /* permits V.val */ { } // no sealing support yet in Valhalla. and V.val is of the form inline class V.val extends V.ref { } and V becomes an alias for V.val 1. In source code, accept the C.ref and C.val notation for a class C that is an inline class (only). In other scenarios, .val and .ref will continue to be handled by routine lookups (which may result in error), where C.val is the type of the value projection and C.ref is the type of the reference projection. 2. The members declared in C become members of both C.ref and C.val. 3. Allow conversions from inline to reference types via an inline widening conversion, similar to boxing, but with a significant difference: the result of the conversion is not an identity object (as a box would be), but a reference to an inline object. (Calling Object::getClass on the resulting Object will report back the class of the original inline object, not a box type.) 4. Allow an inline narrowing conversion from V.ref to V.val (and hence from V.ref to V) which results in an inline object that is the value projection if the reference undergoing conversion is not null and throwing NullPointerException on null. 5. Verify that With this pair of conversions, inline classes have the same relationship with their reference projections as primitives historically did with their wrapper types: i.e Existing rules that are defined in terms of boxing conversions (autoboxing, typing of conditionals, overload selection) should be trivially extendable to incorporate inline widening and narrowing conversions too. 6. In most cases, such as field descriptors and method descriptors, uses of C.ref is translated as LC$ref;, uses of C.val is translated as QC$val;, and uses of C are translated into C.val 7. *All* accesses to members through C$ref is translated by first casting to C$val, and accessing the accessee through the value projection. (this is not what is described in http://cr.openjdk.java.net/~briangoetz/valhalla/sov/04-translation.html, but is correct for non-ref-default values) 8. When we convert a C$val to a C$ref, no actual bytecode need be emitted for this conversion,. In the other direction, the language compiler will emit a checkcast bytecode when narrowing a C$ref to a C$val What is deferred to later: 1. C.ref and C.val are supposed to be nest mates. 2. C.ref is supposed to be a sealed class. 3. No support yet for ref-default classes. 4. Support for translation strategy described in http://cr.openjdk.java.net/~briangoetz/valhalla/sov/04-translation.html
17-04-2020