JVM_IR: flexible nullability as annotation

We carry information about flexible nullability of IrType as an
annotation, so there is no need to look at its underlying KotlinType.
This commit is contained in:
Georgy Bronnikov
2020-07-23 15:32:09 +03:00
parent a2e9521ad5
commit a27d63f58f
5 changed files with 54 additions and 3 deletions
@@ -5,14 +5,22 @@
package org.jetbrains.kotlin.backend.jvm
import org.jetbrains.kotlin.backend.common.ir.createImplicitParameterDeclarationWithWrappedDescriptor
import org.jetbrains.kotlin.backend.common.ir.createParameterDeclarations
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.codegen.SamType
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.annotations.FilteredAnnotations
import org.jetbrains.kotlin.ir.builders.declarations.addConstructor
import org.jetbrains.kotlin.ir.builders.declarations.buildClass
import org.jetbrains.kotlin.ir.declarations.IrClass
import org.jetbrains.kotlin.ir.declarations.IrConstructor
import org.jetbrains.kotlin.ir.declarations.IrDeclarationOrigin
import org.jetbrains.kotlin.ir.declarations.IrFactory
import org.jetbrains.kotlin.ir.declarations.impl.IrExternalPackageFragmentImpl
import org.jetbrains.kotlin.ir.declarations.impl.IrFactoryImpl
import org.jetbrains.kotlin.ir.symbols.impl.DescriptorlessExternalPackageFragmentSymbol
import org.jetbrains.kotlin.ir.util.constructors
import org.jetbrains.kotlin.load.java.JvmAnnotationNames
import org.jetbrains.kotlin.load.java.descriptors.JavaCallableMemberDescriptor
import org.jetbrains.kotlin.load.java.descriptors.JavaClassDescriptor
@@ -20,6 +28,7 @@ import org.jetbrains.kotlin.load.java.descriptors.getParentJavaStaticClassScope
import org.jetbrains.kotlin.load.java.sam.JavaSingleAbstractMethodUtils
import org.jetbrains.kotlin.load.java.typeEnhancement.hasEnhancedNullability
import org.jetbrains.kotlin.load.kotlin.JvmPackagePartSource
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.psi2ir.generators.GeneratorExtensions
import org.jetbrains.kotlin.resolve.jvm.JvmClassName
import org.jetbrains.kotlin.resolve.jvm.annotations.hasJvmFieldAnnotation
@@ -95,4 +104,25 @@ class JvmGeneratorExtensions(private val generateFacades: Boolean = true) : Gene
override fun getParentClassStaticScope(descriptor: ClassDescriptor): MemberScope? =
descriptor.getParentJavaStaticClassScope()
private val annotationPackage =
IrExternalPackageFragmentImpl(DescriptorlessExternalPackageFragmentSymbol(), KotlinBuiltIns.ANNOTATION_PACKAGE_FQ_NAME)
private val flexibleNullabilityAnnotationClass = IrFactoryImpl.buildClass {
kind = ClassKind.ANNOTATION_CLASS
name = FLEXIBLE_NULLABILITY_ANNOTATION_FQ_NAME.shortName()
}.apply {
createImplicitParameterDeclarationWithWrappedDescriptor()
parent = annotationPackage
addConstructor {
isPrimary = true
}
}
override val flexibleNullabilityAnnotationConstructor: IrConstructor? = flexibleNullabilityAnnotationClass.constructors.single()
companion object {
val FLEXIBLE_NULLABILITY_ANNOTATION_FQ_NAME =
KotlinBuiltIns.ANNOTATION_PACKAGE_FQ_NAME.child(Name.identifier("FlexibleNullability"))
}
}
@@ -20,6 +20,7 @@ import org.jetbrains.annotations.NotNull
import org.jetbrains.annotations.Nullable
import org.jetbrains.kotlin.backend.common.ir.ir2string
import org.jetbrains.kotlin.backend.jvm.JvmBackendContext
import org.jetbrains.kotlin.backend.jvm.JvmGeneratorExtensions
import org.jetbrains.kotlin.backend.jvm.JvmLoweredDeclarationOrigin
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.codegen.AsmUtil
@@ -39,7 +40,6 @@ import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.resolve.checkers.ExpectedActualDeclarationChecker
import org.jetbrains.kotlin.types.TypeSystemCommonBackendContext
import org.jetbrains.kotlin.types.isNullabilityFlexible
import org.jetbrains.kotlin.types.model.KotlinTypeMarker
import org.jetbrains.org.objectweb.asm.AnnotationVisitor
import org.jetbrains.org.objectweb.asm.Type
@@ -158,7 +158,7 @@ abstract class AnnotationCodegen(
}
// A flexible type whose lower bound in not-null and upper bound is nullable, should not be annotated
if (type.toKotlinType().isNullabilityFlexible()) {
if (type.isNullabilityFlexible()) {
return
}
@@ -185,6 +185,9 @@ abstract class AnnotationCodegen(
val retentionPolicy = getRetentionPolicy(annotationClass)
if (retentionPolicy == RetentionPolicy.SOURCE) return null
// FlexibleNullability is an internal annotation, used only inside the compiler
if (annotationClass.fqNameWhenAvailable == JvmGeneratorExtensions.FLEXIBLE_NULLABILITY_ANNOTATION_FQ_NAME) return null
// We do not generate annotations whose classes are optional (annotated with `@OptionalExpectation`) because if an annotation entry
// is resolved to the expected declaration, this means that annotation has no actual class, and thus should not be generated.
// (Otherwise we would've resolved the entry to the actual annotation class.)
@@ -376,6 +379,9 @@ private fun IrClass.getAnnotationRetention(): KotlinRetention? {
return KotlinRetention.valueOf(retentionArgumentValue.name.asString())
}
private fun IrType.isNullabilityFlexible(): Boolean =
hasAnnotation(JvmGeneratorExtensions.FLEXIBLE_NULLABILITY_ANNOTATION_FQ_NAME)
// To be generalized to IrMemberAccessExpression as soon as properties get symbols.
private fun IrConstructorCall.getValueArgument(name: Name): IrExpression? {
val index = symbol.owner.valueParameters.find { it.name == name }?.index ?: return null
@@ -8,6 +8,7 @@ package org.jetbrains.kotlin.ir.util
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
import org.jetbrains.kotlin.ir.declarations.IrClass
import org.jetbrains.kotlin.ir.declarations.IrConstructor
import org.jetbrains.kotlin.ir.declarations.IrDeclarationOrigin
import org.jetbrains.kotlin.ir.declarations.IrFactory
import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedContainerSource
@@ -31,6 +32,9 @@ open class StubGeneratorExtensions {
companion object Instance : EnhancedNullability()
}
open val flexibleNullabilityAnnotationConstructor: IrConstructor?
get() = null
companion object {
@JvmField
val EMPTY = StubGeneratorExtensions()
@@ -14,8 +14,10 @@ import org.jetbrains.kotlin.descriptors.TypeAliasDescriptor
import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor
import org.jetbrains.kotlin.descriptors.annotations.Annotations
import org.jetbrains.kotlin.ir.ObsoleteDescriptorBasedAPI
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
import org.jetbrains.kotlin.ir.declarations.IrTypeParametersContainer
import org.jetbrains.kotlin.ir.expressions.IrConstructorCall
import org.jetbrains.kotlin.ir.expressions.impl.IrConstructorCallImpl
import org.jetbrains.kotlin.ir.symbols.IrTypeParameterSymbol
import org.jetbrains.kotlin.ir.types.IrType
import org.jetbrains.kotlin.ir.types.IrTypeAbbreviation
@@ -127,6 +129,15 @@ class TypeTranslator(
else ->
throw AssertionError("Unexpected type descriptor $ktTypeDescriptor :: ${ktTypeDescriptor::class}")
}
if (flexibleApproximatedType.isNullabilityFlexible())
extensions.flexibleNullabilityAnnotationConstructor?.let { flexibleTypeAnnotationConstructor ->
annotations += IrConstructorCallImpl(
UNDEFINED_OFFSET, UNDEFINED_OFFSET,
flexibleTypeAnnotationConstructor.constructedClassType,
flexibleTypeAnnotationConstructor.symbol,
0, 0, 0
)
}
}.buildTypeProjection()
}
@@ -43,7 +43,7 @@ import static org.jetbrains.kotlin.utils.CollectionsKt.newHashSetWithExpectedSiz
public abstract class KotlinBuiltIns {
public static final Name BUILT_INS_PACKAGE_NAME = Name.identifier("kotlin");
public static final FqName BUILT_INS_PACKAGE_FQ_NAME = FqName.topLevel(BUILT_INS_PACKAGE_NAME);
private static final FqName ANNOTATION_PACKAGE_FQ_NAME = BUILT_INS_PACKAGE_FQ_NAME.child(Name.identifier("annotation"));
public static final FqName ANNOTATION_PACKAGE_FQ_NAME = BUILT_INS_PACKAGE_FQ_NAME.child(Name.identifier("annotation"));
public static final FqName COLLECTIONS_PACKAGE_FQ_NAME = BUILT_INS_PACKAGE_FQ_NAME.child(Name.identifier("collections"));
public static final FqName RANGES_PACKAGE_FQ_NAME = BUILT_INS_PACKAGE_FQ_NAME.child(Name.identifier("ranges"));
public static final FqName TEXT_PACKAGE_FQ_NAME = BUILT_INS_PACKAGE_FQ_NAME.child(Name.identifier("text"));