Avoid non-null assertions for inline classes based on nullable types
Note that there are more places where assertions for inline classes should refined: - lateinit vars - values that come from Java - type casts (interfaces to inline class type)
This commit is contained in:
@@ -31,6 +31,7 @@ import org.jetbrains.kotlin.name.FqName;
|
||||
import org.jetbrains.kotlin.protobuf.MessageLite;
|
||||
import org.jetbrains.kotlin.renderer.DescriptorRenderer;
|
||||
import org.jetbrains.kotlin.resolve.DescriptorUtils;
|
||||
import org.jetbrains.kotlin.resolve.InlineClassesUtilsKt;
|
||||
import org.jetbrains.kotlin.resolve.annotations.AnnotationUtilKt;
|
||||
import org.jetbrains.kotlin.resolve.inline.InlineUtil;
|
||||
import org.jetbrains.kotlin.resolve.jvm.JvmClassName;
|
||||
@@ -683,7 +684,7 @@ public class AsmUtil {
|
||||
@NotNull String name
|
||||
) {
|
||||
KotlinType type = parameter.getType();
|
||||
if (isNullableType(type)) return;
|
||||
if (isNullableType(type) || InlineClassesUtilsKt.isNullableUnderlyingType(type)) return;
|
||||
|
||||
int index = frameMap.getIndex(parameter);
|
||||
Type asmType = typeMapper.mapType(type);
|
||||
|
||||
@@ -1,17 +1,6 @@
|
||||
/*
|
||||
* Copyright 2010-2016 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
* Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
|
||||
* that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.resolve.jvm
|
||||
@@ -30,6 +19,7 @@ import org.jetbrains.kotlin.resolve.calls.checkers.CallChecker
|
||||
import org.jetbrains.kotlin.resolve.calls.checkers.CallCheckerContext
|
||||
import org.jetbrains.kotlin.resolve.calls.context.ResolutionContext
|
||||
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
|
||||
import org.jetbrains.kotlin.resolve.isNullableUnderlyingType
|
||||
import org.jetbrains.kotlin.resolve.scopes.receivers.ExpressionReceiver
|
||||
import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue
|
||||
import org.jetbrains.kotlin.types.*
|
||||
@@ -62,7 +52,12 @@ class RuntimeAssertionInfo(val needNotNullAssertion: Boolean, val message: Strin
|
||||
// Cases when nullability assertion needed: T! -> T, T$ -> T
|
||||
|
||||
// Expected type either T?, T! or T$
|
||||
if (TypeUtils.isNullableType(expectedType) || expectedType.hasEnhancedNullability()) return false
|
||||
if (TypeUtils.isNullableType(expectedType) ||
|
||||
expectedType.hasEnhancedNullability() ||
|
||||
expectedType.isNullableUnderlyingType()
|
||||
) {
|
||||
return false
|
||||
}
|
||||
|
||||
// Expression type is not nullable and not enhanced (neither T?, T! or T$)
|
||||
val isExpressionTypeNullable = TypeUtils.isNullableType(expressionType)
|
||||
|
||||
Vendored
+38
@@ -0,0 +1,38 @@
|
||||
// !LANGUAGE: +InlineClasses
|
||||
|
||||
inline class Result<T>(val a: Any?)
|
||||
|
||||
fun resultOfIntToResultOfInt(r: Result<Int>): Result<Int> {
|
||||
return r
|
||||
}
|
||||
|
||||
fun <T> idResult(r: Result<T>): Result<T> = r
|
||||
|
||||
fun Result<Int>.extension(): Result<Int> = this
|
||||
|
||||
fun box(): String {
|
||||
val r = Result<Int>(null)
|
||||
|
||||
resultOfIntToResultOfInt(r)
|
||||
resultOfIntToResultOfInt(Result<Int>(null))
|
||||
|
||||
val nonNull1 = resultOfIntToResultOfInt(r)
|
||||
val nonNull2 = resultOfIntToResultOfInt(Result<Int>(null))
|
||||
|
||||
resultOfIntToResultOfInt(nonNull1)
|
||||
|
||||
if (nonNull1.a != null) return "fail"
|
||||
if (nonNull2.a != null) return "fail"
|
||||
|
||||
if (resultOfIntToResultOfInt(r).a != null) return "fail"
|
||||
|
||||
idResult(Result<String>(null))
|
||||
|
||||
val id = idResult(r)
|
||||
if (id.a != null) return "fail"
|
||||
|
||||
r.extension()
|
||||
Result<Int>(null).extension()
|
||||
|
||||
return "OK"
|
||||
}
|
||||
Vendored
+14
@@ -0,0 +1,14 @@
|
||||
// !LANGUAGE: +InlineClasses
|
||||
|
||||
inline class AsNonNullPrimitive(val i: Int)
|
||||
inline class AsNonNullReference(val s: String) // 2 assertions (constructor, box method)
|
||||
|
||||
fun nonNullPrimitive(a: AsNonNullPrimitive) {}
|
||||
|
||||
fun nonNullReference(b: AsNonNullReference) {} // assertion
|
||||
fun AsNonNullReference.nonNullReferenceExtension(b1: AsNonNullReference) {} // 2 assertions
|
||||
|
||||
fun asNullablePrimitive(c: AsNonNullPrimitive?) {}
|
||||
fun asNullableReference(c: AsNonNullReference?) {}
|
||||
|
||||
// 5 checkParameterIsNotNull
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
// !LANGUAGE: +InlineClasses
|
||||
|
||||
inline class AsAny(val a: Any?)
|
||||
|
||||
fun asNotNullAny(a: AsAny) {}
|
||||
fun AsAny.asNotNullAnyExtension(b: AsAny): AsAny = this
|
||||
|
||||
// 0 checkParameterIsNotNull
|
||||
Generated
+6
@@ -10653,6 +10653,12 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("noAssertionsOnInlineClassBasedOnNullableType.kt")
|
||||
public void testNoAssertionsOnInlineClassBasedOnNullableType() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/inlineClasses/noAssertionsOnInlineClassBasedOnNullableType.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("passInlineClassAsVararg.kt")
|
||||
public void testPassInlineClassAsVararg() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/inlineClasses/passInlineClassAsVararg.kt");
|
||||
|
||||
+6
@@ -10653,6 +10653,12 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("noAssertionsOnInlineClassBasedOnNullableType.kt")
|
||||
public void testNoAssertionsOnInlineClassBasedOnNullableType() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/inlineClasses/noAssertionsOnInlineClassBasedOnNullableType.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("passInlineClassAsVararg.kt")
|
||||
public void testPassInlineClassAsVararg() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/inlineClasses/passInlineClassAsVararg.kt");
|
||||
|
||||
@@ -1947,6 +1947,12 @@ public class BytecodeTextTestGenerated extends AbstractBytecodeTextTest {
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("assertionsForParametersOfInlineClassTypes.kt")
|
||||
public void testAssertionsForParametersOfInlineClassTypes() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/bytecodeText/inlineClasses/assertionsForParametersOfInlineClassTypes.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("boxInlineClassesOnPassingToVarargs.kt")
|
||||
public void testBoxInlineClassesOnPassingToVarargs() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/bytecodeText/inlineClasses/boxInlineClassesOnPassingToVarargs.kt");
|
||||
@@ -2049,6 +2055,12 @@ public class BytecodeTextTestGenerated extends AbstractBytecodeTextTest {
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("noAssertionsForInlineClassesBasedOnNullableTypes.kt")
|
||||
public void testNoAssertionsForInlineClassesBasedOnNullableTypes() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/bytecodeText/inlineClasses/noAssertionsForInlineClassesBasedOnNullableTypes.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("passInlineClassesWithSpreadOperatorToVarargs.kt")
|
||||
public void testPassInlineClassesWithSpreadOperatorToVarargs() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/bytecodeText/inlineClasses/passInlineClassesWithSpreadOperatorToVarargs.kt");
|
||||
|
||||
+6
@@ -10653,6 +10653,12 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("noAssertionsOnInlineClassBasedOnNullableType.kt")
|
||||
public void testNoAssertionsOnInlineClassBasedOnNullableType() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/inlineClasses/noAssertionsOnInlineClassBasedOnNullableType.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("passInlineClassAsVararg.kt")
|
||||
public void testPassInlineClassAsVararg() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/inlineClasses/passInlineClassAsVararg.kt");
|
||||
|
||||
@@ -10,6 +10,7 @@ import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor
|
||||
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
import org.jetbrains.kotlin.types.TypeUtils
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
|
||||
|
||||
fun ClassDescriptor.underlyingRepresentation(): ValueParameterDescriptor? {
|
||||
@@ -30,4 +31,11 @@ fun KotlinType.isInlineClassType(): Boolean = constructor.declarationDescriptor?
|
||||
fun KotlinType.substitutedUnderlyingType(): KotlinType? {
|
||||
val parameter = unsubstitutedUnderlyingParameter() ?: return null
|
||||
return memberScope.getContributedVariables(parameter.name, NoLookupLocation.FOR_ALREADY_TRACKED).singleOrNull()?.type
|
||||
}
|
||||
|
||||
fun KotlinType.isNullableUnderlyingType(): Boolean {
|
||||
if (!isInlineClassType()) return false
|
||||
val underlyingType = unsubstitutedUnderlyingType() ?: return false
|
||||
|
||||
return TypeUtils.isNullableType(underlyingType)
|
||||
}
|
||||
+6
@@ -11715,6 +11715,12 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest {
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("noAssertionsOnInlineClassBasedOnNullableType.kt")
|
||||
public void testNoAssertionsOnInlineClassBasedOnNullableType() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/inlineClasses/noAssertionsOnInlineClassBasedOnNullableType.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("passInlineClassAsVararg.kt")
|
||||
public void testPassInlineClassAsVararg() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/inlineClasses/passInlineClassAsVararg.kt");
|
||||
|
||||
Reference in New Issue
Block a user