Kotlinc source’s file DescriptorUtils.kt demonstarted non-deterministic
insertion of checkExpressionValueIsNotNull for value returned by
CallableDescriptor::getOriginal(). It was difficult to reproduce
this behavior on ф smaller example, but I added a test which was
failing once in 5-10 times while I was testing manually.
I believe this bug is close to KT-23704.
This PR addresses non-determinism to a degree when I can run 120
Compilations with './gradlew dist' and get same classes in all
jars in 'dist'.
NOTE that thew fact that insertion of checkExpressionValueIsNotNull may
depend on order of the types seems suspicios. This CL only addresses
non-determinism part, but I believe it’s worth looking into this more
from semantics point of view.
Lateinit local vars are guaranteed to be non-null after store.
So we mark such stores as storing non-null value
(could be useful for some other constructs, too),
and optimize null checks accordingly.
- Turn some const conditions into non-const conditions
- Make sure inlined const values are used where required
(otherwise they are eliminated by POP backward propagation)
KT-16194 Code with unnecessary safe call contains redundant boxing/unboxing for primitive values
KT-12839 Two null checks are generated when manually null checking platform type
Recognize some additional cases of trivial null checks and trivial instance-of checks.
A variable is "checked for null", if it is:
- a function parameter checked with 'INVOKESTATIC kotlin/jvm/internal/Intrinsics.checkParameterIsNotNull'
- checked for nullability with 'IFNULL/IFNONNULL'
- checked for nullability with 'INSTANCEOF'
(if objectref is instance-of T, then objectref is non-null)
Before analyzing nullability, introduce synthetic assumptions for execution branches
where a variable is guaranteed to be null or not null. For example, the following bytecode:
ALOAD 1 // Ljava/lang/String;
IFNULL L
<non-null branch>
L:
<null branch>
is transformed to
ALOAD 1
IFNULL L1
NEW java/lang/String
ASTORE 1 // tells analyzer that variable 1 is non-null
<non-null branch>
L:
<null branch>
L1:
ACONST_NULL
ASTORE 1 // tells analyzer that variable 1 is null
GOTO L
After the analysis is performed on a preprocessed method,
remember the results for "interesting" instructions
and revert the preprocessing transformations.
After that, perform bytecode transformations as usual.
Do not transform INSTANCEOF to-be-reified, because reification at call site
can introduce null checks. E.g.,
inline fun <reified T> isNullable() = null is T
...
assert(isNullable<String?>())