From karen.kinnear at oracle.com Mon Nov 5 16:17:06 2018 From: karen.kinnear at oracle.com (Karen Kinnear) Date: Mon, 5 Nov 2018 11:17:06 -0500 Subject: Cancel Valhalla EG meeting Weds Nov 7, 2018 Message-ID: Apologies for the late notice. We have two sets of conflicts - a language offsite in Dublin and a Burlington site visit from upper management. We will meet on Weds Nov 21. thanks, Karen From karen.kinnear at oracle.com Mon Nov 19 17:19:49 2018 From: karen.kinnear at oracle.com (Karen Kinnear) Date: Mon, 19 Nov 2018 12:19:49 -0500 Subject: Valhalla EG reminder for November 21, 2018 Message-ID: Notes from October 10 - corrections welcome - sorry for the delay Attendees: Tobi, Dan H, Remi, Brian, Simms, John, Frederic, Dan S, Karen Background documents for next phase of Value Types: Entering the next phase of Project Valhalla - Brian - http://mail.openjdk.java.net/pipermail/valhalla-spec-experts/2018-October/000760.html Values and erased generics - Brian - http://mail.openjdk.java.net/pipermail/valhalla-spec-experts/2018-October/000762.html Q-Types in L-World 10 - John - http://cr.openjdk.java.net/~jrose/values/q-types.html John: described Q-Type as a restricted value set on an L-Type primary mirror is unconstrained and works for backward compatibility and is nullable advanced users can find special mirror with new API if they want to unbox, they can ask for the unboxed mirror Discussion of user model of boxing/unboxing vs. vm model which uses subtyping or possibly conversion Arrays: proposal is to NOT support array covariance for initial experiments [LPoint; <: [LObject; BUT [QPoint; is NOT a subtype of any other array (it is a subtype of LObject;) Note: explicit copying is required between flattened/non-flattened arrays Generics: Proposal is to support erased generics over L-Types and reserve reified generics for Q-Types, allowing phasing in of reified generics (LW100, i.e. a long to go still) after making value types available. Also - users opt-in to erased generics by boxing to e.g. Point.box. Goal is to not constrain LW100 specialized generics too much by erased generics plans. Longer-term vision is to have generic class - something like Arrayish, which should work for value type or primitive or Object arrays. John: depends on how we handle array covariance as well as kind variants of array types Remi: goal of evolving [int as a [value type? Could we support primitives as value types before we support specialized generics? John: interesting suggestion Brian: we would need to be clear what int.Box would mean - currently proposing multiple ?boxes? - current box: Integer, ?lox? - lightweight box for a value type, - might need a new box for primitives to value type Remi: identity river: Karen: value types and their boxes have no identity, therefore there is no meaning to locking Brian: want a way to support user model of locking even without identity Karen: concern about granularity - false sharing on one side - false positives on the other Remi: j.l.Integer has no identity - but some are cached - can not lock reliably Brian: people do use it. Need to figure out more nuanced way to do this Nullable VT: Brian: challenge is many/most value types have no meaning for a default value of 0. concern: if a receiver is a default, uninitialized value - want NPE, we do not want the ability to invoke a method or access a field. We do not want to expose uninitialized default values to users option 1: javac handle this option 2: vm - add ?nullable? value types, T.default treated as a null key question: when do we ?normalize? T.default to be a null concern: ?diversity of nulls? - if we have to check null and T.default at many bytecodes Also goal: user model of allowing a = null; and a==null; Exploring possible models Need to check possible role of verifier Dan H: concern - do we lose the benefits of guaranteeing Q-type is non-null? Brian: change Q-type to mean flattenable, rather than null-free believes we can still flatten and scalarize may need additional field since all bit patterns may already be used if we have a checkcast Q->L we can include a null check here to change T.default to a true null Karen: asked if we could explore a checkcast Q->L rather than a subtype check, provides a place for conversion John: not necessarily the place conversion happens, need to explore ?normalization? points Karen: there are some value types for which all 0?s is valid so the default instance is valid, these are not ?nullable?, you can?t normalize to null ?nullable? value types - willing to pay for an additional field Brian: ensure no invalid receiver for invocation/getfield keep same bytecodes for generic code Dan H: concern - potential source of java puzzlers Brian: concern about vulnerabilities - if V.default is not valid - if you skip a constructor like serialization does ? Dan H: if need null - maybe use L-Type? Or not a candidate for VT? Brian: We want flattening optimizations Remi: Do we still need L-Type? Brian: nullable Q-types are more expensive than null-free values get benefits of flattening/density Dan H: C# model? Brian: Not exactly - VT in C# are mutable - initialization races Remi: C# calls default constructor Dan H: with [VT in C# if you load an element, do you get something constructed? Remi: yes - call constructor for each cell Brian: timing holes can be observed Dan: need null checks on scalarized nulls Karen: concern about additional costs for checking new ?null-marker? field Brian: worth the costs for those who want the flattenability for arrays Frederic: outside JIT - rest of vm will pay huge cost if there is a specific check for each type - lose cache efficiency benefits faster if vm tag or all ?null-marker? fields at the same place rather than having a user-specified field Brian: users may be able to re-use an existing field in some cases Karen: can we explore not observing non-constructed value type? That solves the uninitialized leak issue. Explore default constructor model? Frederic: if value class implements nullable, could provide default factory Remi: could have two kinds of value types, those with a user-defined default and those without Karen: maybe no-arg factory good enough - on need for interface or flag Remi: do we always load the class of a value type before creating a default? Frederic: yes - we always load and init before creating an instance or array thanks, Karen From john.r.rose at oracle.com Wed Nov 21 21:27:38 2018 From: john.r.rose at oracle.com (John Rose) Date: Wed, 21 Nov 2018 13:27:38 -0800 Subject: Valhalla EG reminder for November 21, 2018 In-Reply-To: References: Message-ID: Here is a write-up giving an option for nullable value types. This is what we talked about this morning. I had hoped to produce it before the meeting?better late than never. http://cr.openjdk.java.net/~jrose/values/nullable-values.html # Nullable Value Types in L-World #### Or, "Read my lips: No new nulls" #### Or, "Bottom-lander: There can be only one" #### John Rose, Brian Goetz, Valhalla Working Group ## Basic Premises **Null is the default reference:** In Java, all Java reference types have a common default value, the null reference. This reference appears in uninitialized array elements and field values of that type. The null reference is distinct from any reference that is produced by a `new` expression and/or a constructor call. **A problem with legacy value classes:** In order for today's value-based classes to migrate to reference types proper value types, they must retain the property that their default value is the null reference, since the null reference may appear in user code that uses such classes. **Nullable value classes:** This implies that _some_ value types require the ability to represent a true null reference, as one of the many points in their set of possible values. (This _does not_ imply that any other value of such a type must _also_ be a reference: All other values of the type can and should be proper values.) **Nullability is rare:** Most value types, such as complex numbers or vectors, must _not_ represent the null reference. In particular, arithmetic value types of size `N` bits may need to assign all `2**N` code points to regular non-null values. For example, a type that emulates `byte` could not give up one of its 256 encodings to encode `null`, and adding an extra hidden bit _to all value types_ would have very large costs. In addition, most value types "work like an int", and require a default value which can accept method calls without throwing `NullPointerExceptions`. **Not the default:** Nullability must be explicitly selected by the designer of a value type; it is expected to be a rarely used feature, because it is likley to incur extra costs, in space and time, for encoding and decoding the null reference to and from the flattened form of a value class. **Tweaking the key slogan:** In summary, value types "code like a class and work like an int". But there are a few value types that _also_ want to "work like an `Integer`", in that their default value is the null reference, rather than an appropriate pattern of zero bits. ## User Model **New keyword:** A value class can be declared with a pseudo-modifier `__Nullable`, (TBD, may be just `nullable`) which must be accompanied by the `value` pseudo-modifier. Such a value class is called a _nullable value class_. Other value classes are called _regular value classes_. **Two variable kinds:** Variables come in two kinds, _heap_ and _stack_. A heap variable is a class field (static or non-static) or an array element. A stack variable is a method parameter or local variable. (Local variables include specially declared names such as a `catch` variable.) Heap variables exhibit type-specific default values. Stack variables do not require a default value convention, because they are subject to definite assignment rules, which require explicitly assigned values. **Default values:** For a regular value class `RV`, the expression `RV.default` evaluates to a non-null value of `RV` all of whose fields are their respective default values (typically zero, `false`, or `null`). The uninitialized value of a heap variable of type `RV` or `RV.val` is `RV.defalt`. The uninitialized value of a heap variable of type `RV.box` is `null`, and `RV.box.default` evaluates to `null`. **Nullable defaults to null:** For a nullable value class `NV`, the expression `NV.default` evaluates to `null` (the null reference). The uninitalized value of a heap variable of type `NV`, `NV.val`, or `NV.box` is `null`. **Regular values never null:** Regular value classes can never represent `null` values in their normal unboxed form. (In this, they "work like an int".) Casting a null to a regular value class will throw a `NullPointerException`, just like casting a null `Integer` to `int`. Reflectively storing an untyped null reference to a heap variable of a regular value type will also throw a `NullPointerException`. Loading a heap variable of a regular, non-boxed value class will never produce a null. **Constructors are null-checked:** In order to keep a clear distinction between the null default value and constructed values, value class constructors have a null check on exit. This means that if constructor code accidentally assigns zero or default values to all the instance fields, the constructor will throw `NullPointerException` rather than return the null instance value. No such check is done for regular value classes, since null values are impossible for them. **All values are flattenable:** In heap variables, instances of both regular and nullable value classes behave as if they were flattened, and are in fact routinely flattened. This is likely to affect the performance and footprint of programs which use such variables. In stack variables, flattening may or may not happen, depending on how the interpreter or the JIT is directing execution. **All values are boxable:** All value classes support a "boxed" view which interoperates with `null` and with erased generics. The expression `V.box.default` evaluates to `null` for all value types, both regular and nullable. Heap variables of type `V.box` are _never_ flattened, but as a consolation prize they _can_ receive nulls even for regular `V`. For generics, note that `List` is always legal, but `List` is currently illegal and reserved for future use, when specialized generics are available. Note that the unadorned value type name `V` usually denotes the same type as `V.val`, but we reserve the right to have some occurrences of `V` for certain types to denote `V.box` instead. (This is TBD; perhaps it is part of the migration package for nullable classes.) ### Observations and Fine Print **Null stores as vull:** When storing a `null` to a heap variable of a nullable value class, the JVM will reset all (non-static) fields of that variable to their default values. On the heap, a logically null flat value is called a _flattened value null_, or "vull" for short. **Vull loads as null:** When loading a value from a heap variable of a nullable value class, the JVM will detect "vull" and convert it to a proper null reference. **Vull is a ghost:** Thus, for a nullable value class `NV`, it is impossible to create or observe on stack a non-null instance of `NV` for which all fields of the instance are default. This means that "vulls" are confined to the heap. The JVM enforces this as a low-level invariant, by dynamically transcoding between on-heap "vulls" and on-stack nulls. **Pivot fields:** As a "pro move", a nullable value class can declare that one or more of its non-static fields with the `__NullablePivot` keyword (TBD). When detecting "vulls", the JVM consults only such marked fields for their default value, not all fields of the instance. This may makes "vull" detection faster for legacy classes like `LocalDate`. Such a specially marked field (or fields) may be called a _pivot field_, since the task of "vull" detection "pivots" around that field. By default, if no fields are marked as pivot fields, then in effect all of them serve as pivot fields. **Null stops bad calls:** It is arguable that the most legitimate job of `null` is to avoid executing a method call on a receiver which has not yet been specified. After all, objects do not always have reasonable default values, and so Java (and the JVM) assigns a "default default" value of `null` to object variables that are not otherwise initialized. The null value ensures that if buggy code tries to call a method on an uninitialized variable, an exception will be thrown immediately, rather than executing a method body on an unexpected input. (In this view, field gets are the same as method calls. Other uses of nulls, such as a API sentinel values, were created by creative programmers, who given a hammer will always find more nails.) For a value type without a reasonable default value, programmers have a right to a similar sentinel value which prevents method execution on uninitialized variables. But for a value _with_ a reasonable default value, such machinery would be pure annoyance. Only the designer of the value class knows which case is true. **Inner value classes:** Any non-static nested ("inner") value class `C.IV` must also be declared to be nullable. The reason for this is that every properly constructed instance of `C.IV` must specify a non-null outer instance of type `C`. But if `C.IV` were regular and a method were called on the default value `C.IV.default`, then that method then it would not be able to observe a definite non-null value `C.this`. Such a method call would be inescapably broken. Thus, such method calls must be prevented. The existing language achieves this result by throwing `NullPointerException` on when invoking methods on the default value of `C.IObj`, for an object class `I.IObj`. To preserve this behavior, an inner value class `C.IV` must also present a null default value. This restriction does not apply to static nested value classes `C.NV`. **A slogan:** The slogan for this user model of nullable value types is "no new nulls". It refuses to introduce new "work-alikes" for the null reference. There is no new `NullValueException` which pairs with `NullPointerException`. There is no `Nullable` interface. There not an `isNull` method, certainly not a user-definable one. (An `isNull` method could never return a `false` result, could it? It would have to throw `NullPointerException` instead!) There are no directly observable "vull" values to compete for the throne of `null`; "vulls" are only indirectly observable in the flattening of certain heap variables. In short, the heavy cost of nulls (arguably a "billion dollar mistake") is not multiplied by a new set of null-like values. And the historic cost of nulls is not pushed forward to new value types that don't request it, such as arithmetic types. **Another slogan:** Alternatively, the slogan from "Highlander" applies: There is only ever one `null`. Any would-be "vull" value is dissected from the value space and conjoined to `null` just as soon as it tries to enter the stack. ## Implementation **Affected bytecodes:** At the bytecode level, the instructions `getfield`, `putfield`, `getstatic`, `putstatic`, `withfield`, `aaload`, and `aastore` must transcode between "vull" and proper null. The `defaultvalue` bytecode must not produce "vull". **Null containers rejected:** If a `getfield` instruction is asked for an instance field `NV.f` of a value class `NV`, and if the on-stack value is `null`, then `NullPointerException` is thrown. There is no conversion of the containing instance to a "vull". This is true regardless of the type of the field `NV.f`. If the on-stack value is non-null, and `NV.f` is a "vull", then transcoding occur as usual. This means that the sequence `defaultvalue V; getfield V.f:T` is equivalent to `defaultvalue T` only if `V` is a normal value type. (It must also possess a non-static field `f` of type `T`.) If `V` is nullable, then the `getfield` instruction will throw. **Withfield transcodes twice:** The `withfield` instruction must transcode on both input and output. It must convert a null input value to a temporary "vull", one of whose fields is then updated. It must then detect whether the resulting value is a "vull" and convert that (and only that) back to a null. Unlike `getfield` and `putfield`, `withfield` does not reject a `null` container value. For example, it will produce a null result value if asked to store a default value to a field in an instance where all of the other fields are already set to default values. **Unaffected bytecodes:** Instructions which operate only on stacked or local values do not need further modification to detect "vulls", since "vulls" are never on stack. Receiver null checks for `invokevirtual` and its siblings are unchanged; these instructions will never encounter `vull` values. The `acmp`, `checkcast`, and `instanceof` instructions (and the `aastore` store check) already have special semantics for null references which are unchanged. **Transcoding in field instructions:** For the field instructions, transcoding is a reasonable incremental cost to add, since these instructions resolve their field and therefore know the specific field type; thus the cost of adding transcoding between "vull" and `null` is incremental and added only for fields whose types require this extra step. **Transcoding in array instructions:** Array element access instructions first check the layout of the target array element and then use the proper sequence of steps to convert from a flattened array element (if present) to a regular on-stack reference. As part of this sequence of steps, if the element type is a nullable, flattened value type, "vull" must be detected on load and produced on store, corresponding to an on-stack null reference. **Reflection, etc.:** Access to fields and array elements via reflection, method handles, or JNI is defined in terms of the behaviors of bytecodes, as usual. Thus, reflectively loading or storing a value instance must include a transcoding step exactly when transcoding is required by the corresponding bytecode instruction. **Variable declaration:** The bytecode-level descriptor for a flattenable variable of a value type has the form of a _Q-descriptor_, which begins with the letter "Q" instead of the letter "L" normally used with class-based types. Variables which hold a boxed value are introduced with _L-descriptors_ beginning with "L", like any other reference type. In the setting of the JVM type system, L-descriptors and Q-descriptors denote _L-types_ and _Q-types_. Q-types and L-types roughly correspond to user-level `V.val` and `V.box` types, respectively. Again, in the setting of JVM types (only) we say a value of a Q-type or L-type is a _Q-value_ or _L-value_. **Layout includes nullability:** The nullability of a value class `V` is logically a part of `V`'s overall layout, its size and the format of its fields. This is because layout is the information that dictates the JVM's exact steps when loading or storing a flattened value. Since these steps necessarily include a "vull" check when the value class is nullable, layout includes nullability. **Q-values in the heap:** Q-types are introduced in the heap as part of a class declaration, or when an array type derived from the Q-type is mentioned. The JVM consults the layout of a Q-type `Q-V` when it lays out an instance field of type `Q-V`, or prepares a static field of type `Q-V`, or computes the layout of an array whose element type is `Q-V`. In all cases, the class declaration of `V` is loaded if necessary, and the layout of `V` is consulted, to determine the steps needed to load or store the Q-value. **Q-values on the stack:** Q-types are introduced on the stack as part of method, field, or array type descriptors, or as `checkcast` targets. For nullable value types, both Q-types and L-types can carry null values, while for regular value types, Q-types cannot carry null. Thus, a `checkcast` to a "Q-type" will throw `NullPointerException` if presented at runtime with a null reference on the stack, but only if the referenced type is regular (not nullable). **Verifier rules:** The verifier tracks the distinction between Q-types and L-types, and specifically ensures that an on-stack L-value is never consumed by an instruction which expects a Q-value, if the L-type accepts null but the Q-type does not. If the Q-type is nullable, implicit conversions are logically permissible, and the verifier should allow them. Given the nullable and regular value types `NV` and `RV`, it follows that `Q-NV` is a proper subtype of `L-NV`, but `Q-RV` and `L-RV` can be treated as the same type, since they have the same set of on-stack values. **Verifier rules for supers:** If `C` is a super (class or interface) of `NV` or (respectively) `RV`, then `L-C` is a proper supertype of `Q-NV` and `L-NV`, or (respectively) `Q-RV` and `L-RV`. Note that supertypes of value types are always nullable. Thus, there is no need to distinguish between Q-types and L-types when converting to supers; if there is a null it will be welcome in the supertype. **Optimized calling sequences:** The JIT may elect to use `vull` values for non-receiver parameters or return values, as an alternative to buffering via a nullable indirection. Such calling sequences must be made invisible to the end-user by ensuring that "vull" parameters are transcoded (detected and converted to nulls) as needed, and vice versa on return. Such transcoding operations seem to be reorderable and trackable much like `null` detection is at present. Thus, "vull" transcoding is thought to be optimizable as a straightforward extension to today's JITs. **Constructor translation:** A value class constructor starts with the default value of its class, and builds up the value by assigning to its fields. The rules of the Java language (for final fields and value instance fields) ensure that each field is assigned once and only once. The tracking of assignment along all paths uses a pair of conditions called "definite assignment" and "definite unassignment". On every normal exit from a constructor, each field must be definitely assigned and not definitely unassigned. The JVM has no such rules for tracking assignment at bytecode boundaries. Instead, constructors are allowed to write to final fields any number of times. For values, the corresponding rule is that `withfield` is allowed to assign to an uninitialized value any number of times. In order to prevent null values from escaping from a value class constructor, the compiler must precede each return instruction by a null check. (This can be done with a call to `Objects.requireNonNull` or `Object.getClass`.) Optionally (TBD) the JVM could perform this check automatically. **Non-throwing getter:** Optionally (and TBD), the JVM may choose to define `getfield` on a Q-type container to transcode the container to "vull". Most uses of `getfield` would use the regular L-type container, but this variation would give a "hook" for translation strategies that need to operate on the fields of a possibly uninitialized value. This could happen, for example, inside a constructor. The class component of the `CONSTANT_Fieldref` of such an instruction would be Q-descriptor, rather than the name of a class. Note that, in the JVM, unadorned class names usually denote L-types, not Q-types, so directing a `getfield` instruction to a Q-type container is an unusual step. From karen.kinnear at oracle.com Thu Nov 29 15:12:37 2018 From: karen.kinnear at oracle.com (Karen Kinnear) Date: Thu, 29 Nov 2018 10:12:37 -0500 Subject: Valhalla EG notes November 21, 2018 Message-ID: <32DA6CC2-6BDB-4E4A-ADBF-12CCCF56A68E@oracle.com> Attendees: Remi, Tobi, Dan H, Frederic, John, Karen http://mail.openjdk.java.net/pipermail/valhalla-spec-experts/2018-November/000784.html Thanks to John for the above link to a proposal for dealing with nullable value types I. Nestmates follow-on status of Lookup.defineClass - RFE JDK-8171335 - links to JDK-8205939 We will need to have discussions about restrictions on referring to nonfindable classes at some point in the future before we can target this to a release. II. status of JEP 334 - JVM Constants API links to JDK-8203252 Note: Condy/BSM follow-on: There is a related rfe: JDK-8211334 "ConstantDesc types should be Constable" which depends on JDK-8210685 - which is the implementation side of ?expression Bootstrap methods?. We will need to have discussions about the expression bootstrap methods, JVMS changes, impact on JVMTI etc. at some point in the future before we can target this to a release. III. Value Types: 1. Remi: would like primitives as Value Types ASAP, Karen translated to a request to deliver before generic specialization :-) 2. Substitutability Remi: lambda examples use ==. None use == followed by .equals, in fact none use .equals. Karen: other searches have found 10-50% use == followed by .equals Remi: Hard to find cases with static Object/Interface and dynamic Lambda today Karen: Challenges: component-wise substitutability - primitives - usual check (floating point extra care) - pojos - non-Object/Interface - reference comparison - value type - static or dynamic (Object/Interface) - requires recursive check Concerns: performance - due to lots of fields or depth or both could get StackOverflowError from unbounded links e.g. for a valid use case - tree-node Is it appropriate for acmp to perform a substitutability check? It was intended as a short circuit pretest to a longer test. LW1: acmp always returns false if either is dynamically a value type John: Should we consider expanding acmp to for example check 1 level deep? Dan H: 1 level deep is worse than none -> surprise factor John: acmp: 0 level, 1 level - performance and SOE surprises John: Additional concerns: Cycles: if acmp -> substitutability -> acmp - no way to break the cycle note: no infinite loops: ?well-founded? - use 1 instance to create another - but still potential exponential size/complexity Frederic: Can make infinite loops today - if you have an Object/Interface field Remi: What if you do not perform substitutability checks for non-flattened value types? Frederic: Can NOT base this on flattened or not (ed. note - I suspect Remi meant non-flattenable, you also can not base this on flattenable or not - random potential same/different reference to a value ) Remi: What if e.g. Object/Interface with dynamic value type and not perform substitutability check John: push surprise elsewhere (ed. note - again - random potential same/different reference to a value type) possible to improve one case - that of potential infinite nullability More exploration needed 3. Nullability Editor?s note: LW2: QPoint; null-free reference to Point. LPoint; nullable box for Point. Discussion of nullability post-LW2 ? John: Problem: Value-based class migration to value types which are null friendly - rare Proposal: opt-in new definition of ?nullable? value type vs. ?regular? value type which is null-free and requires boxing to allow nulls T.default is null heap: use T.default as ?vull? - or value null stack: convert to null withfield needs to consume and deliver nulls ?no new nulls? Dan H: getfield needs to convert? John: yes and aaload/aastore/*field Remi: can user decide discriminator? John: can define a pivot field for the null test alternative: specialized test for null - isPresent, isAbsent etc. Karen: performance cost for user specific field John: low incremental cost places that need to know the information need to know layout and nullability at the same time, check pivot field or all fields for default value -> null note: if not flattened, could store null Helps recursion problem for acmp Proposal http://mail.openjdk.java.net/pipermail/valhalla-spec-experts/2018-November/000784.html 4. Locking Karen: Discussed with Brian (ed note: who discussed with Doug Lea) 1. propose disallowing locking if statically known to be a value type 2. issue narrowed to Object/Interface with VT At this point, if in existing code a user is locking an Object or Interface and dynamically finds a value type - they did not know what object they were actually locking, so already at risk. Could consider - just say ?yes? or allow deadlocking Remi: Loom does not like sync Agree there are already deadlock problems here Corrections welcome, thanks, Karen From brian.goetz at oracle.com Fri Nov 30 16:22:55 2018 From: brian.goetz at oracle.com (Brian Goetz) Date: Fri, 30 Nov 2018 11:22:55 -0500 Subject: Valhalla EG notes November 21, 2018 In-Reply-To: <32DA6CC2-6BDB-4E4A-ADBF-12CCCF56A68E@oracle.com> References: <32DA6CC2-6BDB-4E4A-ADBF-12CCCF56A68E@oracle.com> Message-ID: <607935f8-d66c-382a-58d8-76e527caaac5@oracle.com> > 2. Substitutability > Remi: lambda examples use ==. None use == followed by .equals, in fact > none use .equals. > Karen: other searches have found 10-50% use == followed by .equals > Remi: Hard to find cases with static Object/Interface and dynamic > Lambda today Code that knows what it is dealing with generally uses either `==` or `.equals()` but not both. Generic code (including dynamically typed code, like pre-generic collections) generally use `==` followed by `.equals()`, but sometimes just uses `.equals()` (which is OK).? Just using `==` is, of course, dangerous, unless you know that you are dealing with constrained instances (enums, interned strings, etc.) The historical route by which we got here is: ?- In the interpreted days, `==` was a fast filter for `.equals()`, since when crawling a list, most things are not equal to what you're looking for; ?- Because `==` was historically fast, it is harmless as a pre-check. Now, method invocation got faster, and `==` is going to get slower, so obviously the equilibrium will change. > Karen: Challenges: > ? ?component-wise substitutability > ? ? ? - primitives - usual check (floating point extra care) > ? ? ? - pojos - non-Object/Interface - reference comparison > ? ? ? - value type - static or dynamic (Object/Interface) - requires > recursive check > > Concerns: > ? ? ? performance - due to lots of fields or depth or both > ? ? ? could get StackOverflowError from unbounded links > ? ? ? ? ?e.g. for a valid use case - tree-node > > Is it appropriate for acmp to perform a substitutability check? It was > intended as a short circuit pretest to a longer test. I am still preparing my armaments for this assault :) > 3. Nullability > Editor?s note: LW2: QPoint; null-free reference to Point. LPoint; > nullable box for Point. I would prefer to reframe this as "opting into protection against uninitialized values".