K2: support implicit integer to unsigned conversions...

with dedicated opt-in language feature and special
annotation or module capability.
Not intended for a general use, solves specific K/N
scenario with interop libs.
#KT-55902 fixed
This commit is contained in:
Ilya Chernikov
2023-02-04 13:21:58 +01:00
committed by Space Team
parent 3e2f8b834c
commit be2a85be71
30 changed files with 581 additions and 29 deletions
@@ -18,7 +18,8 @@ class BinaryModuleData(
fun createDependencyModuleData(
name: Name,
platform: TargetPlatform,
analyzerServices: PlatformDependentAnalyzerServices
analyzerServices: PlatformDependentAnalyzerServices,
capabilities: FirModuleCapabilities = FirModuleCapabilities.Empty
): FirModuleData {
return FirModuleDataImpl(
name,
@@ -27,6 +28,7 @@ class BinaryModuleData(
friendDependencies = emptyList(),
platform,
analyzerServices,
capabilities
)
}
@@ -29,17 +29,24 @@ class DependencyListForCliModule(
private val allFriendsDependencies = mutableListOf<FirModuleData>()
private val allDependsOnDependencies = mutableListOf<FirModuleData>()
private val filtersMap: Map<FirModuleData, MutableSet<Path>> =
private val filtersMap =
listOf(
binaryModuleData.dependsOn,
binaryModuleData.friends,
binaryModuleData.regular
).associateWith { mutableSetOf() }
).associateWithTo(mutableMapOf<FirModuleData, MutableSet<Path>>()) { mutableSetOf() }
fun dependency(vararg path: Path) {
filtersMap.getValue(binaryModuleData.regular) += path
}
fun dependency(moduleData: FirModuleData, vararg path: Path) {
filtersMap.getOrPut(moduleData) {
allRegularDependencies.add(moduleData)
mutableSetOf()
} += path
}
fun dependency(vararg path: String) {
path.mapTo(filtersMap.getValue(binaryModuleData.regular)) { Paths.get(it) }
}
@@ -49,6 +56,18 @@ class DependencyListForCliModule(
paths.mapTo(filtersMap.getValue(binaryModuleData.regular)) { Paths.get(it) }
}
@JvmName("dependenciesString")
fun dependencies(moduleData: FirModuleData, paths: Collection<String>) {
paths.mapTo(
filtersMap.getOrPut(moduleData) {
allRegularDependencies.add(moduleData)
mutableSetOf()
}
) {
Paths.get(it)
}
}
@JvmName("friendDependenciesString")
fun friendDependencies(paths: Collection<String>) {
paths.mapTo(filtersMap.getValue(binaryModuleData.friends)) { Paths.get(it) }
@@ -6,6 +6,8 @@
package org.jetbrains.kotlin.fir.backend.generators
import org.jetbrains.kotlin.KtFakeSourceElementKind
import org.jetbrains.kotlin.builtins.StandardNames
import org.jetbrains.kotlin.config.LanguageFeature
import org.jetbrains.kotlin.fir.backend.*
import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.declarations.utils.isCompanion
@@ -14,16 +16,14 @@ import org.jetbrains.kotlin.fir.expressions.*
import org.jetbrains.kotlin.fir.expressions.builder.buildAnnotationCall
import org.jetbrains.kotlin.fir.expressions.impl.FirNoReceiverExpression
import org.jetbrains.kotlin.fir.getContainingClassLookupTag
import org.jetbrains.kotlin.fir.languageVersionSettings
import org.jetbrains.kotlin.fir.references.*
import org.jetbrains.kotlin.fir.references.builder.buildResolvedNamedReference
import org.jetbrains.kotlin.fir.render
import org.jetbrains.kotlin.fir.resolve.*
import org.jetbrains.kotlin.fir.resolve.calls.FirSyntheticFunctionSymbol
import org.jetbrains.kotlin.fir.resolve.defaultType
import org.jetbrains.kotlin.fir.resolve.fullyExpandedType
import org.jetbrains.kotlin.fir.resolve.substitution.ConeSubstitutor
import org.jetbrains.kotlin.fir.resolve.substitution.ConeSubstitutorByMap
import org.jetbrains.kotlin.fir.resolve.toFirRegularClassSymbol
import org.jetbrains.kotlin.fir.resolve.toSymbol
import org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.approximateDeclarationType
import org.jetbrains.kotlin.fir.scopes.unsubstitutedScope
import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol
@@ -935,7 +935,9 @@ class CallAndReferenceGenerator(
irArgument = irArgument.applySamConversionIfNeeded(argument, parameter, substitutor)
}
}
return irArgument.applyAssigningArrayElementsToVarargInNamedForm(argument, parameter)
return irArgument
.applyAssigningArrayElementsToVarargInNamedForm(argument, parameter)
.applyImplicitIntegerCoercionIfNeeded(argument, parameter)
}
private fun IrExpression.applyAssigningArrayElementsToVarargInNamedForm(
@@ -962,6 +964,66 @@ class CallAndReferenceGenerator(
return this
}
private fun IrExpression.applyImplicitIntegerCoercionIfNeeded(
argument: FirExpression,
parameter: FirValueParameter?
): IrExpression {
if (!session.languageVersionSettings.supportsFeature(LanguageFeature.ImplicitSignedToUnsignedIntegerConversion)) return this
if (parameter == null || !parameter.isMarkedWithImplicitIntegerCoercion) return this
fun IrExpression.applyToElement(argument: FirExpression, conversionFunction: IrSimpleFunctionSymbol): IrExpression =
if (argument is FirConstExpression<*> ||
argument.calleeReference?.toResolvedCallableSymbol()?.let {
it.resolvedStatus.isConst && it.isMarkedWithImplicitIntegerCoercion
} == true
) {
IrCallImpl(
startOffset, endOffset,
conversionFunction.owner.returnType,
conversionFunction,
typeArgumentsCount = 0,
valueArgumentsCount = 0
).apply {
extensionReceiver = this@applyToElement
}
} else this@applyToElement
if (parameter.isMarkedWithImplicitIntegerCoercion) {
if (this is IrVarargImpl && argument is FirVarargArgumentsExpression) {
val targetTypeFqName = varargElementType.classFqName ?: return this
val conversionFunctions = irBuiltIns.getNonBuiltInFunctionsByExtensionReceiver(
Name.identifier("to" + targetTypeFqName.shortName().asString()),
StandardNames.BUILT_INS_PACKAGE_NAME.asString()
)
if (conversionFunctions.isNotEmpty()) {
elements.forEachIndexed { i, irVarargElement ->
val targetFun = argument.arguments[i].typeRef.toIrType().classifierOrNull?.let { conversionFunctions[it] }
if (targetFun != null && irVarargElement is IrExpression) {
elements[i] =
irVarargElement.applyToElement(argument.arguments[i], targetFun)
}
}
}
return this
} else {
val targetIrType = parameter.returnTypeRef.toIrType()
val targetTypeFqName = targetIrType.classFqName ?: return this
val conversionFunctions = irBuiltIns.getNonBuiltInFunctionsByExtensionReceiver(
Name.identifier("to" + targetTypeFqName.shortName().asString()),
StandardNames.BUILT_INS_PACKAGE_NAME.asString()
)
val sourceTypeClassifier = argument.typeRef.toIrType().classifierOrNull ?: return this
val conversionFunction = conversionFunctions[sourceTypeClassifier] ?: return this
return this.applyToElement(argument, conversionFunction)
}
}
return this
}
internal fun IrExpression.applyTypeArguments(access: FirQualifiedAccessExpression): IrExpression {
if (this !is IrMemberAccessExpression<*>) return this
val argumentsCount = access.typeArguments.size
@@ -51555,6 +51555,12 @@ public class FirLightTreeBlackBoxCodegenTestGenerated extends AbstractFirLightTr
runTest("compiler/testData/codegen/box/unsignedTypes/nullableUnsignedEqualsLiteral.kt");
}
@Test
@TestMetadata("signedToUnsignedConversions.kt")
public void testSignedToUnsignedConversions() throws Exception {
runTest("compiler/testData/codegen/box/unsignedTypes/signedToUnsignedConversions.kt");
}
@Test
@TestMetadata("unsignedArraySize.kt")
public void testUnsignedArraySize() throws Exception {
@@ -51555,6 +51555,12 @@ public class FirPsiBlackBoxCodegenTestGenerated extends AbstractFirPsiBlackBoxCo
runTest("compiler/testData/codegen/box/unsignedTypes/nullableUnsignedEqualsLiteral.kt");
}
@Test
@TestMetadata("signedToUnsignedConversions.kt")
public void testSignedToUnsignedConversions() throws Exception {
runTest("compiler/testData/codegen/box/unsignedTypes/signedToUnsignedConversions.kt");
}
@Test
@TestMetadata("unsignedArraySize.kt")
public void testUnsignedArraySize() throws Exception {
@@ -0,0 +1,32 @@
/*
* Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors.
* 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.fir.resolve
import org.jetbrains.kotlin.builtins.StandardNames
import org.jetbrains.kotlin.fir.FirModuleCapability
import org.jetbrains.kotlin.fir.declarations.FirCallableDeclaration
import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol
import org.jetbrains.kotlin.fir.symbols.resolvedAnnotationClassIds
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.Name
import kotlin.reflect.KClass
object ImplicitIntegerCoercionModuleCapability : FirModuleCapability() {
override val key: KClass<out FirModuleCapability> = ImplicitIntegerCoercionModuleCapability::class
}
private val implicitIntegerCoercionAnnotationClassId =
ClassId(StandardNames.KOTLIN_INTERNAL_FQ_NAME, Name.identifier("ImplicitIntegerCoercion"))
val FirCallableSymbol<*>.isMarkedWithImplicitIntegerCoercion
get() =
fir.moduleData.capabilities.contains(ImplicitIntegerCoercionModuleCapability) ||
resolvedAnnotationClassIds.any { it == implicitIntegerCoercionAnnotationClassId }
val FirCallableDeclaration.isMarkedWithImplicitIntegerCoercion
get() =
moduleData.capabilities.contains(ImplicitIntegerCoercionModuleCapability) ||
resolvedAnnotationClassIds(symbol).any { it == implicitIntegerCoercionAnnotationClassId }
@@ -6,16 +6,16 @@
package org.jetbrains.kotlin.fir.resolve.calls
import org.jetbrains.kotlin.builtins.functions.isBasicFunctionOrKFunction
import org.jetbrains.kotlin.config.LanguageFeature
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.declarations.FirCallableDeclaration
import org.jetbrains.kotlin.fir.declarations.FirValueParameter
import org.jetbrains.kotlin.fir.expressions.*
import org.jetbrains.kotlin.fir.languageVersionSettings
import org.jetbrains.kotlin.fir.lookupTracker
import org.jetbrains.kotlin.fir.references.FirResolvedNamedReference
import org.jetbrains.kotlin.fir.resolve.ScopeSession
import org.jetbrains.kotlin.fir.resolve.createFunctionType
import org.jetbrains.kotlin.fir.expressions.unwrapSmartcastExpression
import org.jetbrains.kotlin.fir.resolve.fullyExpandedType
import org.jetbrains.kotlin.fir.references.toResolvedCallableSymbol
import org.jetbrains.kotlin.fir.resolve.*
import org.jetbrains.kotlin.fir.resolve.inference.LambdaWithTypeVariableAsExpectedTypeAtom
import org.jetbrains.kotlin.fir.resolve.inference.model.ConeArgumentConstraintPosition
import org.jetbrains.kotlin.fir.resolve.inference.model.ConeReceiverConstraintPosition
@@ -487,7 +487,9 @@ private fun Candidate.prepareExpectedType(
)
}
}
} ?: basicExpectedType
}
?: getExpectedTypeWithImplicintIntegerCoercion(session, argument, parameter, basicExpectedType)
?: basicExpectedType
return this.substitutor.substituteOrSelf(expectedType)
}
@@ -510,6 +512,29 @@ private fun Candidate.getExpectedTypeWithSAMConversion(
}
}
private fun getExpectedTypeWithImplicintIntegerCoercion(
session: FirSession,
argument: FirExpression,
parameter: FirValueParameter,
candidateExpectedType: ConeKotlinType
): ConeKotlinType? {
if (!session.languageVersionSettings.supportsFeature(LanguageFeature.ImplicitSignedToUnsignedIntegerConversion)) return null
if (!parameter.isMarkedWithImplicitIntegerCoercion) return null
val argumentType =
if (argument.isIntegerLiteralOrOperatorCall()) argument.resultType.coneType
else {
argument.calleeReference?.toResolvedCallableSymbol()?.takeIf {
it.rawStatus.isConst && it.isMarkedWithImplicitIntegerCoercion
}?.resolvedReturnType
}
// TODO: consider adding a check that argument could be converted to the parameter type (maybe difficult for platform types)
return argumentType?.withNullability(candidateExpectedType.nullability, session.typeContext)
}
fun FirExpression.isFunctional(
session: FirSession,
scopeSession: ScopeSession,
@@ -0,0 +1,43 @@
/*
* Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors.
* 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.fir
import org.jetbrains.kotlin.fir.util.ConeTypeRegistry
import org.jetbrains.kotlin.util.AttributeArrayOwner
import org.jetbrains.kotlin.util.TypeRegistry
import kotlin.reflect.KClass
abstract class FirModuleCapability {
abstract val key: KClass<out FirModuleCapability>
}
class FirModuleCapabilities private constructor(
capabilities: List<FirModuleCapability>
) : AttributeArrayOwner<FirModuleCapability, FirModuleCapability>() {
companion object : ConeTypeRegistry<FirModuleCapability, FirModuleCapability>() {
val Empty: FirModuleCapabilities = FirModuleCapabilities(emptyList())
fun create(attributes: List<FirModuleCapability>): FirModuleCapabilities {
return if (attributes.isEmpty()) {
Empty
} else {
FirModuleCapabilities(attributes)
}
}
}
init {
for (capability in capabilities) {
registerComponent(capability.key, capability)
}
}
override val typeRegistry: TypeRegistry<FirModuleCapability, FirModuleCapability>
get() = Companion
}
@@ -50,6 +50,9 @@ abstract class FirModuleData : FirSessionComponent {
// refactor them to make API clearer
abstract val analyzerServices: PlatformDependentAnalyzerServices
open val capabilities: FirModuleCapabilities
get() = FirModuleCapabilities.Empty
private var _session: FirSession? = null
val session: FirSession
get() = _session
@@ -73,7 +76,8 @@ class FirModuleDataImpl(
override val dependsOnDependencies: List<FirModuleData>,
override val friendDependencies: List<FirModuleData>,
override val platform: TargetPlatform,
override val analyzerServices: PlatformDependentAnalyzerServices
override val analyzerServices: PlatformDependentAnalyzerServices,
override val capabilities: FirModuleCapabilities = FirModuleCapabilities.Empty
) : FirModuleData()
val FirSession.nullableModuleData: FirModuleData? by FirSession.nullableSessionComponentAccessor()
@@ -31,6 +31,9 @@ val FirTypeRef.coneType: ConeKotlinType
get() = coneTypeSafe()
?: error("Expected FirResolvedTypeRef with ConeKotlinType but was ${this::class.simpleName} ${render()}")
val FirTypeRef.coneTypeOrNull: ConeKotlinType?
get() = coneTypeSafe()
val FirTypeRef.isAny: Boolean get() = isBuiltinType(StandardClassIds.Any, false)
val FirTypeRef.isNullableAny: Boolean get() = isBuiltinType(StandardClassIds.Any, true)
val FirTypeRef.isNothing: Boolean get() = isBuiltinType(StandardClassIds.Nothing, false)