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:
Mikhail Zarechenskiy
2018-02-20 12:46:14 +03:00
parent e3c58eced1
commit f23b5103ec
11 changed files with 115 additions and 15 deletions
@@ -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)
@@ -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"
}
@@ -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
@@ -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
@@ -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");
@@ -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");
@@ -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)
}
@@ -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");