diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/AsmUtil.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/AsmUtil.java index 42b4e4ff79d..537d52aca79 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/AsmUtil.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/AsmUtil.java @@ -37,6 +37,7 @@ import org.jetbrains.kotlin.load.java.JavaVisibilities; import org.jetbrains.kotlin.load.java.JvmAbi; import org.jetbrains.kotlin.load.java.JvmAnnotationNames; import org.jetbrains.kotlin.load.java.descriptors.JavaCallableMemberDescriptor; +import org.jetbrains.kotlin.jvm.RuntimeAssertionInfo; import org.jetbrains.kotlin.name.FqName; import org.jetbrains.kotlin.resolve.DescriptorUtils; import org.jetbrains.kotlin.resolve.annotations.AnnotationsPackage; @@ -45,7 +46,6 @@ import org.jetbrains.kotlin.resolve.jvm.JvmClassName; import org.jetbrains.kotlin.resolve.jvm.JvmPackage; import org.jetbrains.kotlin.resolve.jvm.JvmPrimitiveType; import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin; -import org.jetbrains.kotlin.types.Approximation; import org.jetbrains.kotlin.types.JetType; import org.jetbrains.kotlin.types.TypesPackage; import org.jetbrains.org.objectweb.asm.*; @@ -637,10 +637,10 @@ public class AsmUtil { public static StackValue genNotNullAssertions( @NotNull GenerationState state, @NotNull final StackValue stackValue, - @Nullable final Approximation.Info approximationInfo + @Nullable final RuntimeAssertionInfo runtimeAssertionInfo ) { if (!state.isCallAssertionsEnabled()) return stackValue; - if (approximationInfo == null || !TypesPackage.assertNotNull(approximationInfo)) return stackValue; + if (runtimeAssertionInfo == null || !runtimeAssertionInfo.getNeedNotNullAssertion()) return stackValue; return new StackValue(stackValue.type) { @@ -649,7 +649,7 @@ public class AsmUtil { stackValue.put(type, v); if (type.getSort() == Type.OBJECT || type.getSort() == Type.ARRAY) { v.dup(); - v.visitLdcInsn(approximationInfo.getMessage()); + v.visitLdcInsn(runtimeAssertionInfo.getMessage()); v.invokestatic("kotlin/jvm/internal/Intrinsics", "checkExpressionValueIsNotNull", "(Ljava/lang/Object;Ljava/lang/String;)V", false); } diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java index f9466586266..462dfbf20d7 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java @@ -49,10 +49,12 @@ import org.jetbrains.kotlin.descriptors.*; import org.jetbrains.kotlin.descriptors.impl.ScriptCodeDescriptor; import org.jetbrains.kotlin.diagnostics.DiagnosticUtils; import org.jetbrains.kotlin.diagnostics.Errors; +import org.jetbrains.kotlin.jvm.bindingContextSlices.BindingContextSlicesPackage; import org.jetbrains.kotlin.lexer.JetTokens; import org.jetbrains.kotlin.load.java.JvmAbi; import org.jetbrains.kotlin.load.java.descriptors.JavaClassDescriptor; import org.jetbrains.kotlin.load.java.descriptors.SamConstructorDescriptor; +import org.jetbrains.kotlin.jvm.RuntimeAssertionInfo; import org.jetbrains.kotlin.name.Name; import org.jetbrains.kotlin.psi.*; import org.jetbrains.kotlin.renderer.DescriptorRenderer; @@ -75,7 +77,6 @@ import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodParameterKind; import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodParameterSignature; import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature; import org.jetbrains.kotlin.resolve.scopes.receivers.*; -import org.jetbrains.kotlin.types.Approximation; import org.jetbrains.kotlin.types.JetType; import org.jetbrains.kotlin.types.TypeProjection; import org.jetbrains.kotlin.types.TypeUtils; @@ -280,12 +281,12 @@ public class ExpressionCodegen extends JetVisitor implem StackValue stackValue = selector.accept(visitor, receiver); - Approximation.Info approximationInfo = null; + RuntimeAssertionInfo runtimeAssertionInfo = null; if (selector instanceof JetExpression) { - approximationInfo = bindingContext.get(BindingContext.EXPRESSION_RESULT_APPROXIMATION, (JetExpression) selector); + runtimeAssertionInfo = bindingContext.get(BindingContextSlicesPackage.getRUNTIME_ASSERTION_INFO(), (JetExpression) selector); } - return genNotNullAssertions(state, stackValue, approximationInfo); + return genNotNullAssertions(state, stackValue, runtimeAssertionInfo); } catch (ProcessCanceledException e) { throw e; diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/FunctionCodegen.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/FunctionCodegen.java index cd7016c3f68..64bfd7e0ba8 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/FunctionCodegen.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/FunctionCodegen.java @@ -35,6 +35,7 @@ import org.jetbrains.kotlin.codegen.state.GenerationState; import org.jetbrains.kotlin.codegen.state.JetTypeMapper; import org.jetbrains.kotlin.descriptors.*; import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor; +import org.jetbrains.kotlin.jvm.RuntimeAssertionInfo; import org.jetbrains.kotlin.load.kotlin.nativeDeclarations.NativeDeclarationsPackage; import org.jetbrains.kotlin.name.FqName; import org.jetbrains.kotlin.psi.JetNamedFunction; @@ -51,8 +52,6 @@ import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin; import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodParameterKind; import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodParameterSignature; import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature; -import org.jetbrains.kotlin.types.Approximation; -import org.jetbrains.kotlin.types.TypesPackage; import org.jetbrains.org.objectweb.asm.AnnotationVisitor; import org.jetbrains.org.objectweb.asm.Label; import org.jetbrains.org.objectweb.asm.MethodVisitor; @@ -834,10 +833,10 @@ public class FunctionCodegen { StackValue stackValue = AsmUtil.genNotNullAssertions( state, StackValue.onStack(delegateToMethod.getReturnType()), - TypesPackage.getApproximationTo( - delegatedTo.getReturnType(), + RuntimeAssertionInfo.create( delegateFunction.getReturnType(), - new Approximation.DataFlowExtras.OnlyMessage(delegatedTo.getName() + "(...)") + delegatedTo.getReturnType(), + new RuntimeAssertionInfo.DataFlowExtras.OnlyMessage(delegatedTo.getName() + "(...)") ) ); diff --git a/compiler/frontend.java/src/org/jetbrains/kotlin/jvm/RuntimeAssertions.kt b/compiler/frontend.java/src/org/jetbrains/kotlin/jvm/RuntimeAssertions.kt new file mode 100644 index 00000000000..c42d9a6bf48 --- /dev/null +++ b/compiler/frontend.java/src/org/jetbrains/kotlin/jvm/RuntimeAssertions.kt @@ -0,0 +1,116 @@ +/* + * Copyright 2010-2015 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. + */ + +package org.jetbrains.kotlin.jvm + +import com.intellij.openapi.util.text.StringUtil +import org.jetbrains.kotlin.descriptors.ReceiverParameterDescriptor +import org.jetbrains.kotlin.load.java.JvmAnnotationNames +import org.jetbrains.kotlin.jvm.bindingContextSlices.RUNTIME_ASSERTION_INFO +import org.jetbrains.kotlin.psi.JetExpression +import org.jetbrains.kotlin.resolve.BindingContext +import org.jetbrains.kotlin.resolve.calls.checkers.AdditionalTypeChecker +import org.jetbrains.kotlin.resolve.calls.context.CallResolutionContext +import org.jetbrains.kotlin.resolve.calls.context.ResolutionContext +import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowValueFactory +import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue +import org.jetbrains.kotlin.types.JetType +import org.jetbrains.kotlin.types.TypeUtils +import org.jetbrains.kotlin.types.checker.JetTypeChecker +import org.jetbrains.kotlin.types.upperIfFlexible +import kotlin.platform.platformStatic + +public class RuntimeAssertionInfo(public val needNotNullAssertion: Boolean, public val message: String) { + public interface DataFlowExtras { + class OnlyMessage(message: String) : DataFlowExtras { + override val canBeNull: Boolean get() = true + override val possibleTypes: Set get() = setOf() + override val presentableText: String = message + } + + val canBeNull: Boolean + val possibleTypes: Set + val presentableText: String + } + + companion object { + platformStatic public fun create( + expectedType: JetType, + expressionType: JetType, + dataFlowExtras: DataFlowExtras + ): RuntimeAssertionInfo? { + fun assertNotNull(): Boolean { + if (expectedType.isError() || expressionType.isError()) return false + + // T : Any, T! = T..T? + // Let T$ will be copy of T! with enhanced nullability. + // Cases when nullability assertion needed: T! -> T, T$ -> T + + // Expected type either T?, T! or T$ + if (TypeUtils.isNullableType(expectedType) || expectedType.hasEnhancedNullability()) return false + + // Expression type is not nullable and not enhanced (neither T?, T! or T$) + val isExpressionTypeNullable = TypeUtils.isNullableType(expressionType) + if (!isExpressionTypeNullable && !expressionType.hasEnhancedNullability()) return false + + // Smart-cast T! or T? + if (!dataFlowExtras.canBeNull && isExpressionTypeNullable) return false + + return true + } + + return if (assertNotNull()) + RuntimeAssertionInfo(needNotNullAssertion = true, message = dataFlowExtras.presentableText) + else + null + } + + private fun JetType.hasEnhancedNullability() + = getAnnotations().findAnnotation(JvmAnnotationNames.ENHANCED_NULLABILITY_ANNOTATION) != null + } +} + +public object RuntimeAssertionsTypeChecker : AdditionalTypeChecker { + override fun checkType(expression: JetExpression, expressionType: JetType, c: ResolutionContext<*>) { + if (TypeUtils.noExpectedType(c.expectedType)) return + + val assertionInfo = RuntimeAssertionInfo.create( + c.expectedType, + expressionType, + object : RuntimeAssertionInfo.DataFlowExtras { + override val canBeNull: Boolean + get() = c.dataFlowInfo.getNullability(dataFlowValue).canBeNull() + override val possibleTypes: Set + get() = c.dataFlowInfo.getPossibleTypes(dataFlowValue) + override val presentableText: String + get() = StringUtil.trimMiddle(expression.getText(), 50) + + private val dataFlowValue = DataFlowValueFactory.createDataFlowValue(expression, expressionType, c) + } + ) + + if (assertionInfo != null) { + c.trace.record(RUNTIME_ASSERTION_INFO, expression, assertionInfo) + } + } + + override fun checkReceiver( + receiverParameter: ReceiverParameterDescriptor, + receiverArgument: ReceiverValue, + safeAccess: Boolean, + c: CallResolutionContext<*> + ) { } +} diff --git a/compiler/frontend.java/src/org/jetbrains/kotlin/jvm/bindingContextSlices.kt b/compiler/frontend.java/src/org/jetbrains/kotlin/jvm/bindingContextSlices.kt new file mode 100644 index 00000000000..00d32c8a0fa --- /dev/null +++ b/compiler/frontend.java/src/org/jetbrains/kotlin/jvm/bindingContextSlices.kt @@ -0,0 +1,26 @@ +/* + * Copyright 2010-2015 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. + */ + +package org.jetbrains.kotlin.jvm.bindingContextSlices + +import org.jetbrains.kotlin.jvm.RuntimeAssertionInfo +import org.jetbrains.kotlin.psi.JetExpression +import org.jetbrains.kotlin.util.slicedMap.BasicWritableSlice +import org.jetbrains.kotlin.util.slicedMap.RewritePolicy +import org.jetbrains.kotlin.util.slicedMap.WritableSlice +import org.jetbrains.kotlin.utils.DO_NOTHING + +public val RUNTIME_ASSERTION_INFO: WritableSlice = BasicWritableSlice(RewritePolicy.DO_NOTHING) diff --git a/compiler/frontend.java/src/org/jetbrains/kotlin/load/kotlin/KotlinJvmCheckerProvider.kt b/compiler/frontend.java/src/org/jetbrains/kotlin/load/kotlin/KotlinJvmCheckerProvider.kt index a8b0f8f4625..4b61deb32b2 100644 --- a/compiler/frontend.java/src/org/jetbrains/kotlin/load/kotlin/KotlinJvmCheckerProvider.kt +++ b/compiler/frontend.java/src/org/jetbrains/kotlin/load/kotlin/KotlinJvmCheckerProvider.kt @@ -20,6 +20,7 @@ import org.jetbrains.kotlin.cfg.WhenChecker import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.diagnostics.DiagnosticSink import org.jetbrains.kotlin.diagnostics.Errors +import org.jetbrains.kotlin.jvm.RuntimeAssertionsTypeChecker import org.jetbrains.kotlin.lexer.JetTokens import org.jetbrains.kotlin.load.java.lazy.types.isMarkedNotNull import org.jetbrains.kotlin.load.java.lazy.types.isMarkedNullable @@ -63,7 +64,7 @@ public class KotlinJvmCheckerProvider(private val module: ModuleDescriptor) : Ad JavaAnnotationMethodCallChecker(), TraitDefaultMethodCallChecker(), ReflectionAPICallChecker(module)), - additionalTypeCheckers = listOf(JavaNullabilityWarningsChecker()), + additionalTypeCheckers = listOf(JavaNullabilityWarningsChecker(), RuntimeAssertionsTypeChecker), additionalSymbolUsageValidators = listOf() ) diff --git a/compiler/testData/codegen/notNullAssertions/AssertionChecker.kt b/compiler/testData/codegen/notNullAssertions/AssertionChecker.kt index 17523ca17c2..d3643218d1d 100644 --- a/compiler/testData/codegen/notNullAssertions/AssertionChecker.kt +++ b/compiler/testData/codegen/notNullAssertions/AssertionChecker.kt @@ -45,10 +45,10 @@ fun checkAssertions(illegalStateExpected: Boolean) { check("plus") { A() + A() } // field - // check("NULL") { A().NULL } + check("NULL") { A().NULL } // static field - // check("STATIC_NULL") { A.STATIC_NULL } + check("STATIC_NULL") { A.STATIC_NULL } // postfix expression // TODO: diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/GenerateNotNullAssertionsTest.java b/compiler/tests/org/jetbrains/kotlin/codegen/GenerateNotNullAssertionsTest.java index 9f698b8c185..d30c89bca3f 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/GenerateNotNullAssertionsTest.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/GenerateNotNullAssertionsTest.java @@ -126,6 +126,7 @@ public class GenerateNotNullAssertionsTest extends CodegenTestCase { loadFile("notNullAssertions/arrayListGet.kt"); String text = generateToText(); + assertTrue(text.contains("checkExpressionValueIsNotNull")); assertTrue(text.contains("checkParameterIsNotNull")); } @@ -136,7 +137,7 @@ public class GenerateNotNullAssertionsTest extends CodegenTestCase { loadFile("notNullAssertions/javaMultipleSubstitutions.kt"); String text = generateToText(); - assertEquals(0, StringUtil.getOccurrenceCount(text, "checkExpressionValueIsNotNull")); + assertEquals(3, StringUtil.getOccurrenceCount(text, "checkExpressionValueIsNotNull")); assertEquals(3, StringUtil.getOccurrenceCount(text, "checkParameterIsNotNull")); } diff --git a/core/descriptor.loader.java/src/org/jetbrains/kotlin/load/java/JvmAnnotationNames.java b/core/descriptor.loader.java/src/org/jetbrains/kotlin/load/java/JvmAnnotationNames.java index a6c887c1762..c3ff64321d4 100644 --- a/core/descriptor.loader.java/src/org/jetbrains/kotlin/load/java/JvmAnnotationNames.java +++ b/core/descriptor.loader.java/src/org/jetbrains/kotlin/load/java/JvmAnnotationNames.java @@ -46,6 +46,10 @@ public final class JvmAnnotationNames { public static final FqName PURELY_IMPLEMENTS_ANNOTATION = new FqName("kotlin.jvm.PurelyImplements"); + // Just for internal use: there is no such real classes in bytecode + public static final FqName ENHANCED_NULLABILITY_ANNOTATION = new FqName("kotlin.jvm.internal.EnhancedNullability"); + public static final FqName ENHANCED_MUTABILITY_ANNOTATION = new FqName("kotlin.jvm.internal.EnhancedMutability"); + public static class KotlinClass { public static final JvmClassName CLASS_NAME = JvmClassName.byInternalName("kotlin/jvm/internal/KotlinClass"); public static final ClassId KIND_CLASS_ID = diff --git a/core/descriptor.loader.java/src/org/jetbrains/kotlin/load/java/typeEnhacement/typeEnhancement.kt b/core/descriptor.loader.java/src/org/jetbrains/kotlin/load/java/typeEnhacement/typeEnhancement.kt index 332b72e9a05..b1d2eb30f36 100644 --- a/core/descriptor.loader.java/src/org/jetbrains/kotlin/load/java/typeEnhacement/typeEnhancement.kt +++ b/core/descriptor.loader.java/src/org/jetbrains/kotlin/load/java/typeEnhacement/typeEnhancement.kt @@ -18,12 +18,19 @@ package org.jetbrains.kotlin.load.java.typeEnhacement import org.jetbrains.kotlin.descriptors.ClassDescriptor import org.jetbrains.kotlin.descriptors.ClassifierDescriptor +import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor +import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor +import org.jetbrains.kotlin.descriptors.annotations.Annotations +import org.jetbrains.kotlin.descriptors.annotations.CompositeAnnotations +import org.jetbrains.kotlin.load.java.JvmAnnotationNames import org.jetbrains.kotlin.load.java.typeEnhacement.JavaTypeQualifiers import org.jetbrains.kotlin.load.java.typeEnhacement.MutabilityQualifier.MUTABLE import org.jetbrains.kotlin.load.java.typeEnhacement.MutabilityQualifier.READ_ONLY import org.jetbrains.kotlin.load.java.typeEnhacement.NullabilityQualifier.NOT_NULL import org.jetbrains.kotlin.load.java.typeEnhacement.NullabilityQualifier.NULLABLE +import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.platform.JavaToKotlinClassMap +import org.jetbrains.kotlin.resolve.constants.CompileTimeConstant import org.jetbrains.kotlin.types.* // The index in the lambda is the position of the type component: @@ -68,7 +75,7 @@ private fun JetType.enhanceInflexible(qualifiers: (Int) -> JavaTypeQualifiers, i ?: return Result(this, 1) val effectiveQualifiers = qualifiers(index) - val enhancedClassifier = originalClass.enhanceMutability(effectiveQualifiers, position) + val (enhancedClassifier, enhancedMutabilityAnnotations) = originalClass.enhanceMutability(effectiveQualifiers, position) var globalArgIndex = index + 1 val enhancedArguments = getArguments().mapIndexed { @@ -87,10 +94,17 @@ private fun JetType.enhanceInflexible(qualifiers: (Int) -> JavaTypeQualifiers, i } } - val enhancedType = JetTypeImpl( + val (enhancedNullability, enhancedNullabilityAnnotations) = this.getEnhancedNullability(effectiveQualifiers, position) + val newAnnotations = listOf( getAnnotations(), + enhancedMutabilityAnnotations, + enhancedNullabilityAnnotations + ).filterNotNull().compositeAnnotationsOrSingle() + + val enhancedType = JetTypeImpl( + newAnnotations, enhancedClassifier.getTypeConstructor(), - this.getEnhancedNullability(effectiveQualifiers, position), + enhancedNullability, enhancedArguments, if (enhancedClassifier is ClassDescriptor) enhancedClassifier.getMemberScope(enhancedArguments) @@ -99,36 +113,72 @@ private fun JetType.enhanceInflexible(qualifiers: (Int) -> JavaTypeQualifiers, i return Result(enhancedType, globalArgIndex - index) } +private fun List.compositeAnnotationsOrSingle() = when (size()) { + 0 -> error("At least one Annotations object expected") + 1 -> single() + else -> CompositeAnnotations(this) +} + private fun TypeComponentPosition.shouldEnhance() = this != TypeComponentPosition.INFLEXIBLE -private fun ClassifierDescriptor.enhanceMutability(qualifiers: JavaTypeQualifiers, position: TypeComponentPosition): ClassifierDescriptor { - if (!position.shouldEnhance()) return this - if (this !is ClassDescriptor) return this // mutability is not applicable for type parameters +private data class EnhancementResult(val result: T, val enhancementAnnotations: Annotations?) +private fun T.noChange() = EnhancementResult(this, null) +private fun T.enhancedNullability() = EnhancementResult(this, ENHANCED_NULLABILITY_ANNOTATIONS) +private fun T.enhancedMutability() = EnhancementResult(this, ENHANCED_MUTABILITY_ANNOTATIONS) + +private fun ClassifierDescriptor.enhanceMutability(qualifiers: JavaTypeQualifiers, position: TypeComponentPosition): EnhancementResult { + if (!position.shouldEnhance()) return this.noChange() + if (this !is ClassDescriptor) return this.noChange() // mutability is not applicable for type parameters val mapping = JavaToKotlinClassMap.INSTANCE when (qualifiers.mutability) { READ_ONLY -> { if (position == TypeComponentPosition.FLEXIBLE_LOWER && mapping.isMutable(this)) { - return mapping.convertMutableToReadOnly(this) + return mapping.convertMutableToReadOnly(this).enhancedMutability() } } MUTABLE -> { if (position == TypeComponentPosition.FLEXIBLE_UPPER && mapping.isReadOnly(this) ) { - return mapping.convertReadOnlyToMutable(this) + return mapping.convertReadOnlyToMutable(this).enhancedMutability() } } } - return this + return this.noChange() } -private fun JetType.getEnhancedNullability(qualifiers: JavaTypeQualifiers, position: TypeComponentPosition): Boolean { - if (!position.shouldEnhance()) return this.isMarkedNullable() +private fun JetType.getEnhancedNullability(qualifiers: JavaTypeQualifiers, position: TypeComponentPosition): EnhancementResult { + if (!position.shouldEnhance()) return this.isMarkedNullable().noChange() return when (qualifiers.nullability) { - NULLABLE -> true - NOT_NULL -> false - else -> this.isMarkedNullable() + NULLABLE -> true.enhancedNullability() + NOT_NULL -> false.enhancedNullability() + else -> this.isMarkedNullable().noChange() } } + +private val ENHANCED_NULLABILITY_ANNOTATIONS = EnhancedTypeAnnotations(JvmAnnotationNames.ENHANCED_NULLABILITY_ANNOTATION) +private val ENHANCED_MUTABILITY_ANNOTATIONS = EnhancedTypeAnnotations(JvmAnnotationNames.ENHANCED_MUTABILITY_ANNOTATION) + +private class EnhancedTypeAnnotations(private val fqNameToMatch: FqName) : Annotations { + override fun isEmpty() = false + + override fun findAnnotation(fqName: FqName) = when (fqName) { + fqNameToMatch -> EnhancedTypeAnnotationDescriptor + else -> null + } + + override fun findExternalAnnotation(fqName: FqName) = null + + // Note, that this class may break Annotations contract (!isEmpty && iterator.isEmpty()) + // It's a hack that we need unless we have stable "user data" in JetType + override fun iterator(): Iterator = emptyList().iterator() +} + +private object EnhancedTypeAnnotationDescriptor : AnnotationDescriptor { + private fun throwError() = error("No methods should be called on this descriptor. Only it's presence matters") + override fun getType() = throwError() + override fun getAllValueArguments() = throwError() + override fun toString() = "[EnhancedType]" +} diff --git a/idea/testData/debugger/tinyApp/outs/stepIntoSpecificKotlinClasses.out b/idea/testData/debugger/tinyApp/outs/stepIntoSpecificKotlinClasses.out index 94468ad37ce..bdd1cc9bd58 100644 --- a/idea/testData/debugger/tinyApp/outs/stepIntoSpecificKotlinClasses.out +++ b/idea/testData/debugger/tinyApp/outs/stepIntoSpecificKotlinClasses.out @@ -4,9 +4,9 @@ Connected to the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socke stepIntoSpecificKotlinClasses.kt:8 MyJavaClass.java:12 stepIntoSpecificKotlinClasses.kt:8 +Intrinsics.!EXT! +stepIntoSpecificKotlinClasses.kt:8 stepIntoSpecificKotlinClasses.kt:9 -stepIntoSpecificKotlinClasses.kt:10 -Thread.!EXT! Disconnected from the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket' Process finished with exit code 0