From f23b5103ecf3fbde05bfbe7e3f396553663d3d97 Mon Sep 17 00:00:00 2001 From: Mikhail Zarechenskiy Date: Tue, 20 Feb 2018 12:46:14 +0300 Subject: [PATCH] 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) --- .../org/jetbrains/kotlin/codegen/AsmUtil.java | 3 +- .../kotlin/resolve/jvm/RuntimeAssertions.kt | 23 +++++------ ...ertionsOnInlineClassBasedOnNullableType.kt | 38 +++++++++++++++++++ ...sertionsForParametersOfInlineClassTypes.kt | 14 +++++++ ...onsForInlineClassesBasedOnNullableTypes.kt | 8 ++++ .../ir/IrBlackBoxCodegenTestGenerated.java | 6 +++ .../codegen/BlackBoxCodegenTestGenerated.java | 6 +++ .../codegen/BytecodeTextTestGenerated.java | 12 ++++++ .../LightAnalysisModeTestGenerated.java | 6 +++ .../kotlin/resolve/inlineClassesUtils.kt | 8 ++++ .../semantics/JsCodegenBoxTestGenerated.java | 6 +++ 11 files changed, 115 insertions(+), 15 deletions(-) create mode 100644 compiler/testData/codegen/box/inlineClasses/noAssertionsOnInlineClassBasedOnNullableType.kt create mode 100644 compiler/testData/codegen/bytecodeText/inlineClasses/assertionsForParametersOfInlineClassTypes.kt create mode 100644 compiler/testData/codegen/bytecodeText/inlineClasses/noAssertionsForInlineClassesBasedOnNullableTypes.kt diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/AsmUtil.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/AsmUtil.java index ed83da551e4..72610ccbfe2 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/AsmUtil.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/AsmUtil.java @@ -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); diff --git a/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/RuntimeAssertions.kt b/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/RuntimeAssertions.kt index 2bfcab3ac0b..f3150c26ae5 100644 --- a/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/RuntimeAssertions.kt +++ b/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/RuntimeAssertions.kt @@ -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) diff --git a/compiler/testData/codegen/box/inlineClasses/noAssertionsOnInlineClassBasedOnNullableType.kt b/compiler/testData/codegen/box/inlineClasses/noAssertionsOnInlineClassBasedOnNullableType.kt new file mode 100644 index 00000000000..f79cdd74141 --- /dev/null +++ b/compiler/testData/codegen/box/inlineClasses/noAssertionsOnInlineClassBasedOnNullableType.kt @@ -0,0 +1,38 @@ +// !LANGUAGE: +InlineClasses + +inline class Result(val a: Any?) + +fun resultOfIntToResultOfInt(r: Result): Result { + return r +} + +fun idResult(r: Result): Result = r + +fun Result.extension(): Result = this + +fun box(): String { + val r = Result(null) + + resultOfIntToResultOfInt(r) + resultOfIntToResultOfInt(Result(null)) + + val nonNull1 = resultOfIntToResultOfInt(r) + val nonNull2 = resultOfIntToResultOfInt(Result(null)) + + resultOfIntToResultOfInt(nonNull1) + + if (nonNull1.a != null) return "fail" + if (nonNull2.a != null) return "fail" + + if (resultOfIntToResultOfInt(r).a != null) return "fail" + + idResult(Result(null)) + + val id = idResult(r) + if (id.a != null) return "fail" + + r.extension() + Result(null).extension() + + return "OK" +} \ No newline at end of file diff --git a/compiler/testData/codegen/bytecodeText/inlineClasses/assertionsForParametersOfInlineClassTypes.kt b/compiler/testData/codegen/bytecodeText/inlineClasses/assertionsForParametersOfInlineClassTypes.kt new file mode 100644 index 00000000000..cbe6995ef99 --- /dev/null +++ b/compiler/testData/codegen/bytecodeText/inlineClasses/assertionsForParametersOfInlineClassTypes.kt @@ -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 \ No newline at end of file diff --git a/compiler/testData/codegen/bytecodeText/inlineClasses/noAssertionsForInlineClassesBasedOnNullableTypes.kt b/compiler/testData/codegen/bytecodeText/inlineClasses/noAssertionsForInlineClassesBasedOnNullableTypes.kt new file mode 100644 index 00000000000..79abce39e38 --- /dev/null +++ b/compiler/testData/codegen/bytecodeText/inlineClasses/noAssertionsForInlineClassesBasedOnNullableTypes.kt @@ -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 \ No newline at end of file diff --git a/compiler/tests-ir-jvm/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java b/compiler/tests-ir-jvm/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java index b75566047fb..15b52b39fa3 100644 --- a/compiler/tests-ir-jvm/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java +++ b/compiler/tests-ir-jvm/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java @@ -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"); diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java index 8d580a49710..67372d0d15e 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java @@ -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"); diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeTextTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeTextTestGenerated.java index 1e9b06f566f..44a055097ef 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeTextTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeTextTestGenerated.java @@ -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"); diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java index ea66373112f..faf29c4e8c1 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java @@ -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"); diff --git a/core/descriptors/src/org/jetbrains/kotlin/resolve/inlineClassesUtils.kt b/core/descriptors/src/org/jetbrains/kotlin/resolve/inlineClassesUtils.kt index 6c37934860e..b87821a23b5 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/resolve/inlineClassesUtils.kt +++ b/core/descriptors/src/org/jetbrains/kotlin/resolve/inlineClassesUtils.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) } \ No newline at end of file diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java index 44407f1453c..73c9fb6974a 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java @@ -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");