[IR] Generify lowerings
#KT-1179
This commit is contained in:
committed by
teamcity
parent
22b2554368
commit
282ab398c6
@@ -201,6 +201,11 @@ open class ProtoCompareGenerated(
|
||||
if (!checkEquals(oldTypeTable.getType(old.inlineClassUnderlyingTypeId), newTypeTable.getType(new.inlineClassUnderlyingTypeId))) return false
|
||||
}
|
||||
|
||||
if (old.hasMultiFieldValueClassRepresentation() != new.hasMultiFieldValueClassRepresentation()) return false
|
||||
if (old.hasMultiFieldValueClassRepresentation()) {
|
||||
if (!checkEquals(old.multiFieldValueClassRepresentation, new.multiFieldValueClassRepresentation)) return false
|
||||
}
|
||||
|
||||
if (!checkEqualsClassVersionRequirement(old, new)) return false
|
||||
|
||||
if (old.hasVersionRequirementTable() != new.hasVersionRequirementTable()) return false
|
||||
@@ -290,6 +295,7 @@ open class ProtoCompareGenerated(
|
||||
INLINE_CLASS_UNDERLYING_PROPERTY_NAME,
|
||||
INLINE_CLASS_UNDERLYING_TYPE,
|
||||
INLINE_CLASS_UNDERLYING_TYPE_ID,
|
||||
MULTI_FIELD_VALUE_CLASS_REPRESENTATION,
|
||||
VERSION_REQUIREMENT_LIST,
|
||||
VERSION_REQUIREMENT_TABLE,
|
||||
JVM_EXT_CLASS_MODULE_NAME,
|
||||
@@ -357,6 +363,11 @@ open class ProtoCompareGenerated(
|
||||
if (!checkEquals(oldTypeTable.getType(old.inlineClassUnderlyingTypeId), newTypeTable.getType(new.inlineClassUnderlyingTypeId))) result.add(ProtoBufClassKind.INLINE_CLASS_UNDERLYING_TYPE_ID)
|
||||
}
|
||||
|
||||
if (old.hasMultiFieldValueClassRepresentation() != new.hasMultiFieldValueClassRepresentation()) result.add(ProtoBufClassKind.MULTI_FIELD_VALUE_CLASS_REPRESENTATION)
|
||||
if (old.hasMultiFieldValueClassRepresentation()) {
|
||||
if (!checkEquals(old.multiFieldValueClassRepresentation, new.multiFieldValueClassRepresentation)) result.add(ProtoBufClassKind.MULTI_FIELD_VALUE_CLASS_REPRESENTATION)
|
||||
}
|
||||
|
||||
if (!checkEqualsClassVersionRequirement(old, new)) result.add(ProtoBufClassKind.VERSION_REQUIREMENT_LIST)
|
||||
|
||||
if (old.hasVersionRequirementTable() != new.hasVersionRequirementTable()) result.add(ProtoBufClassKind.VERSION_REQUIREMENT_TABLE)
|
||||
@@ -1015,6 +1026,12 @@ open class ProtoCompareGenerated(
|
||||
return true
|
||||
}
|
||||
|
||||
open fun checkEquals(old: ProtoBuf.Class.MultiFieldValueClassRepresentation, new: ProtoBuf.Class.MultiFieldValueClassRepresentation): Boolean {
|
||||
if (!checkEqualsClassMultiFieldValueClassRepresentationProperty(old, new)) return false
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
open fun checkEquals(old: ProtoBuf.Annotation, new: ProtoBuf.Annotation): Boolean {
|
||||
if (!checkClassIdEquals(old.id, new.id)) return false
|
||||
|
||||
@@ -1239,6 +1256,22 @@ open class ProtoCompareGenerated(
|
||||
return true
|
||||
}
|
||||
|
||||
open fun checkEquals(old: ProtoBuf.Class.MultiFieldValueClassRepresentation.MultiFieldValueClassProperty, new: ProtoBuf.Class.MultiFieldValueClassRepresentation.MultiFieldValueClassProperty): Boolean {
|
||||
if (old.name != new.name) return false
|
||||
|
||||
if (old.hasType() != new.hasType()) return false
|
||||
if (old.hasType()) {
|
||||
if (!checkEquals(old.type, new.type)) return false
|
||||
}
|
||||
|
||||
if (old.hasTypeId() != new.hasTypeId()) return false
|
||||
if (old.hasTypeId()) {
|
||||
if (old.typeId != new.typeId) return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
open fun checkEquals(old: ProtoBuf.Annotation.Argument, new: ProtoBuf.Annotation.Argument): Boolean {
|
||||
if (!checkStringEquals(old.nameId, new.nameId)) return false
|
||||
|
||||
@@ -1655,6 +1688,16 @@ open class ProtoCompareGenerated(
|
||||
return true
|
||||
}
|
||||
|
||||
open fun checkEqualsClassMultiFieldValueClassRepresentationProperty(old: ProtoBuf.Class.MultiFieldValueClassRepresentation, new: ProtoBuf.Class.MultiFieldValueClassRepresentation): Boolean {
|
||||
if (old.propertyCount != new.propertyCount) return false
|
||||
|
||||
for(i in 0..old.propertyCount - 1) {
|
||||
if (!checkEquals(old.getProperty(i), new.getProperty(i))) return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
open fun checkEqualsAnnotationArgument(old: ProtoBuf.Annotation, new: ProtoBuf.Annotation): Boolean {
|
||||
if (old.argumentCount != new.argumentCount) return false
|
||||
|
||||
@@ -1864,6 +1907,10 @@ fun ProtoBuf.Class.hashCode(stringIndexes: (Int) -> Int, fqNameIndexes: (Int) ->
|
||||
hashCode = 31 * hashCode + typeById(inlineClassUnderlyingTypeId).hashCode(stringIndexes, fqNameIndexes, typeById)
|
||||
}
|
||||
|
||||
if (hasMultiFieldValueClassRepresentation()) {
|
||||
hashCode = 31 * hashCode + multiFieldValueClassRepresentation.hashCode(stringIndexes, fqNameIndexes, typeById)
|
||||
}
|
||||
|
||||
for(i in 0..versionRequirementCount - 1) {
|
||||
hashCode = 31 * hashCode + getVersionRequirement(i)
|
||||
}
|
||||
@@ -2365,6 +2412,16 @@ fun ProtoBuf.EnumEntry.hashCode(stringIndexes: (Int) -> Int, fqNameIndexes: (Int
|
||||
return hashCode
|
||||
}
|
||||
|
||||
fun ProtoBuf.Class.MultiFieldValueClassRepresentation.hashCode(stringIndexes: (Int) -> Int, fqNameIndexes: (Int) -> Int, typeById: (Int) -> ProtoBuf.Type): Int {
|
||||
var hashCode = 1
|
||||
|
||||
for(i in 0..propertyCount - 1) {
|
||||
hashCode = 31 * hashCode + getProperty(i).hashCode(stringIndexes, fqNameIndexes, typeById)
|
||||
}
|
||||
|
||||
return hashCode
|
||||
}
|
||||
|
||||
fun ProtoBuf.Annotation.hashCode(stringIndexes: (Int) -> Int, fqNameIndexes: (Int) -> Int, typeById: (Int) -> ProtoBuf.Type): Int {
|
||||
var hashCode = 1
|
||||
|
||||
@@ -2565,6 +2622,22 @@ fun ProtoBuf.Type.Argument.hashCode(stringIndexes: (Int) -> Int, fqNameIndexes:
|
||||
return hashCode
|
||||
}
|
||||
|
||||
fun ProtoBuf.Class.MultiFieldValueClassRepresentation.MultiFieldValueClassProperty.hashCode(stringIndexes: (Int) -> Int, fqNameIndexes: (Int) -> Int, typeById: (Int) -> ProtoBuf.Type): Int {
|
||||
var hashCode = 1
|
||||
|
||||
hashCode = 31 * hashCode + name
|
||||
|
||||
if (hasType()) {
|
||||
hashCode = 31 * hashCode + type.hashCode(stringIndexes, fqNameIndexes, typeById)
|
||||
}
|
||||
|
||||
if (hasTypeId()) {
|
||||
hashCode = 31 * hashCode + typeId
|
||||
}
|
||||
|
||||
return hashCode
|
||||
}
|
||||
|
||||
fun ProtoBuf.Annotation.Argument.hashCode(stringIndexes: (Int) -> Int, fqNameIndexes: (Int) -> Int, typeById: (Int) -> ProtoBuf.Type): Int {
|
||||
var hashCode = 1
|
||||
|
||||
|
||||
@@ -29,7 +29,6 @@ import org.jetbrains.kotlin.protobuf.MessageLite
|
||||
import org.jetbrains.kotlin.serialization.deserialization.ProtoEnumFlags
|
||||
import org.jetbrains.kotlin.serialization.deserialization.descriptorVisibility
|
||||
import org.jetbrains.kotlin.serialization.deserialization.getClassId
|
||||
import java.util.*
|
||||
|
||||
data class Difference(
|
||||
val isClassAffected: Boolean = false,
|
||||
@@ -292,7 +291,8 @@ class DifferenceCalculatorForClass(
|
||||
}
|
||||
ProtoBufClassKind.INLINE_CLASS_UNDERLYING_PROPERTY_NAME,
|
||||
ProtoBufClassKind.INLINE_CLASS_UNDERLYING_TYPE,
|
||||
ProtoBufClassKind.INLINE_CLASS_UNDERLYING_TYPE_ID -> {
|
||||
ProtoBufClassKind.INLINE_CLASS_UNDERLYING_TYPE_ID,
|
||||
ProtoBufClassKind.MULTI_FIELD_VALUE_CLASS_REPRESENTATION -> {
|
||||
isClassAffected = true
|
||||
}
|
||||
ProtoBufClassKind.CONTEXT_RECEIVER_TYPE_LIST,
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -9,10 +9,7 @@ import com.intellij.psi.PsiCompiledElement
|
||||
import org.jetbrains.kotlin.KtFakeSourceElementKind
|
||||
import org.jetbrains.kotlin.KtNodeTypes
|
||||
import org.jetbrains.kotlin.KtSourceElement
|
||||
import org.jetbrains.kotlin.descriptors.ClassKind
|
||||
import org.jetbrains.kotlin.descriptors.InlineClassRepresentation
|
||||
import org.jetbrains.kotlin.descriptors.Modality
|
||||
import org.jetbrains.kotlin.descriptors.Visibilities
|
||||
import org.jetbrains.kotlin.descriptors.*
|
||||
import org.jetbrains.kotlin.fir.*
|
||||
import org.jetbrains.kotlin.fir.backend.generators.FakeOverrideGenerator
|
||||
import org.jetbrains.kotlin.fir.builder.buildPackageDirective
|
||||
@@ -71,6 +68,7 @@ import org.jetbrains.kotlin.resolve.calls.util.isSingleUnderscore
|
||||
import org.jetbrains.kotlin.types.ConstantValueKind
|
||||
import org.jetbrains.kotlin.types.Variance
|
||||
import org.jetbrains.kotlin.util.OperatorNameConventions
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
|
||||
|
||||
internal fun <T : IrElement> FirElement.convertWithOffsets(
|
||||
f: (startOffset: Int, endOffset: Int) -> T
|
||||
@@ -196,7 +194,15 @@ fun FirReference.toSymbolForCall(
|
||||
): IrSymbol? {
|
||||
return when (this) {
|
||||
is FirResolvedNamedReference ->
|
||||
resolvedSymbol.toSymbolForCall(dispatchReceiver, session, classifierStorage, declarationStorage, preferGetter, explicitReceiver, isDelegate)
|
||||
resolvedSymbol.toSymbolForCall(
|
||||
dispatchReceiver,
|
||||
session,
|
||||
classifierStorage,
|
||||
declarationStorage,
|
||||
preferGetter,
|
||||
explicitReceiver,
|
||||
isDelegate
|
||||
)
|
||||
is FirErrorNamedReference ->
|
||||
candidateSymbol?.toSymbolForCall(
|
||||
dispatchReceiver,
|
||||
@@ -648,6 +654,16 @@ fun Fir2IrComponents.computeInlineClassRepresentation(klass: FirRegularClass): I
|
||||
)
|
||||
}
|
||||
|
||||
// TODO: implement multiFieldValueClassRepresentation in FirRegularClass instead.
|
||||
fun Fir2IrComponents.computeMultiFieldValueClassRepresentation(klass: FirRegularClass): MultiFieldValueClassRepresentation<IrSimpleType>? {
|
||||
val parameters = klass.getMultiFieldValueClassUnderlyingParameters(session) ?: return null
|
||||
return MultiFieldValueClassRepresentation(parameters.map {
|
||||
val type = it.returnTypeRef.toIrType(typeConverter).safeAs<IrSimpleType>()
|
||||
?: error("Value class underlying type is not a simple type: ${klass.render()}")
|
||||
it.name to type
|
||||
})
|
||||
}
|
||||
|
||||
fun FirRegularClass.getIrSymbolsForSealedSubclasses(components: Fir2IrComponents): List<IrClassSymbol> {
|
||||
val session = components.session
|
||||
val symbolProvider = session.symbolProvider
|
||||
|
||||
@@ -139,6 +139,11 @@ class Fir2IrLazyClass(
|
||||
set(_) {
|
||||
error("Mutating Fir2Ir lazy elements is not possible")
|
||||
}
|
||||
override var multiFieldValueClassRepresentation: MultiFieldValueClassRepresentation<IrSimpleType>?
|
||||
get() = computeMultiFieldValueClassRepresentation(fir)
|
||||
set(_) {
|
||||
error("Mutating Fir2Ir lazy elements is not possible")
|
||||
}
|
||||
|
||||
private val fakeOverridesByName = mutableMapOf<Name, Collection<IrDeclaration>>()
|
||||
|
||||
|
||||
+6
@@ -48207,6 +48207,12 @@ public class FirBlackBoxCodegenTestGenerated extends AbstractFirBlackBoxCodegenT
|
||||
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/valueClasses"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM_IR, true);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("classFlattening.kt")
|
||||
public void testClassFlattening() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/valueClasses/classFlattening.kt", TransformersFunctions.getReplaceOptionalJvmInlineAnnotationWithReal());
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("equality.kt")
|
||||
public void testEquality() throws Exception {
|
||||
|
||||
@@ -38,3 +38,6 @@ internal fun ConeKotlinType.unsubstitutedUnderlyingTypeForInlineClass(session: F
|
||||
// TODO: implement inlineClassRepresentation in FirRegularClass instead.
|
||||
fun FirRegularClass.getInlineClassUnderlyingParameter(session: FirSession): FirValueParameter? =
|
||||
if (isInline) primaryConstructorIfAny(session)?.fir?.valueParameters?.singleOrNull() else null
|
||||
|
||||
fun FirRegularClass.getMultiFieldValueClassUnderlyingParameters(session: FirSession): List<FirValueParameter>? =
|
||||
if (isInline) primaryConstructorIfAny(session)?.fir?.valueParameters?.takeIf { it.size > 1 } else null
|
||||
|
||||
+1
@@ -95,6 +95,7 @@ class SyntheticClassOrObjectDescriptor(
|
||||
override fun getUnsubstitutedMemberScope(kotlinTypeRefiner: KotlinTypeRefiner) = unsubstitutedMemberScope
|
||||
override fun getSealedSubclasses() = emptyList<ClassDescriptor>()
|
||||
override fun getInlineClassRepresentation(): InlineClassRepresentation<SimpleType>? = null
|
||||
override fun getMultiFieldValueClassRepresentation(): MultiFieldValueClassRepresentation<SimpleType>? = null
|
||||
|
||||
init {
|
||||
assert(modality != Modality.SEALED) { "Implement getSealedSubclasses() for this class: ${this::class.java}" }
|
||||
|
||||
+24
@@ -7,6 +7,7 @@ package org.jetbrains.kotlin.resolve.lazy.descriptors;
|
||||
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.PsiNameIdentifierOwner;
|
||||
import kotlin.Pair;
|
||||
import kotlin.annotations.jvm.ReadOnly;
|
||||
import kotlin.collections.CollectionsKt;
|
||||
import kotlin.jvm.functions.Function1;
|
||||
@@ -48,6 +49,7 @@ import org.jetbrains.kotlin.storage.NullableLazyValue;
|
||||
import org.jetbrains.kotlin.storage.StorageManager;
|
||||
import org.jetbrains.kotlin.types.*;
|
||||
import org.jetbrains.kotlin.types.checker.KotlinTypeRefiner;
|
||||
import org.jetbrains.kotlin.types.checker.SimpleClassicTypeSystemContext;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
@@ -646,6 +648,28 @@ public class LazyClassDescriptor extends ClassDescriptorBase implements ClassDes
|
||||
);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public MultiFieldValueClassRepresentation<SimpleType> getMultiFieldValueClassRepresentation() {
|
||||
if (!InlineClassesUtilsKt.isValueClass(this) || InlineClassesUtilsKt.isInlineClass(this)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
ClassConstructorDescriptor constructor = getUnsubstitutedPrimaryConstructor();
|
||||
// Don't crash on invalid code. It is IC, not MFVC.
|
||||
if (constructor == null) {
|
||||
return null;
|
||||
}
|
||||
List<ValueParameterDescriptor> parameters = constructor.getValueParameters();
|
||||
if (parameters.size() <= 1) {
|
||||
return null;
|
||||
}
|
||||
List<Pair<Name, SimpleType>> properties = parameters.stream()
|
||||
.map(parameter -> new Pair<>(parameter.getName(), (SimpleType) parameter.getType()))
|
||||
.collect(Collectors.toList());
|
||||
return new MultiFieldValueClassRepresentation<>(properties);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
// not using DescriptorRenderer to preserve laziness
|
||||
|
||||
+38
-147
@@ -5,8 +5,6 @@
|
||||
|
||||
package org.jetbrains.kotlin.backend.jvm.lower
|
||||
|
||||
import org.jetbrains.kotlin.backend.common.FileLoweringPass
|
||||
import org.jetbrains.kotlin.backend.common.IrElementTransformerVoidWithContext
|
||||
import org.jetbrains.kotlin.backend.common.ir.copyParameterDeclarationsFrom
|
||||
import org.jetbrains.kotlin.backend.common.ir.passTypeArgumentsFrom
|
||||
import org.jetbrains.kotlin.backend.common.lower.createIrBuilder
|
||||
@@ -28,15 +26,16 @@ import org.jetbrains.kotlin.ir.builders.declarations.buildFun
|
||||
import org.jetbrains.kotlin.ir.declarations.*
|
||||
import org.jetbrains.kotlin.ir.expressions.*
|
||||
import org.jetbrains.kotlin.ir.expressions.impl.*
|
||||
import org.jetbrains.kotlin.ir.symbols.IrValueSymbol
|
||||
import org.jetbrains.kotlin.ir.transformStatement
|
||||
import org.jetbrains.kotlin.ir.types.*
|
||||
import org.jetbrains.kotlin.ir.types.IrType
|
||||
import org.jetbrains.kotlin.ir.types.classOrNull
|
||||
import org.jetbrains.kotlin.ir.types.isNullable
|
||||
import org.jetbrains.kotlin.ir.types.makeNotNull
|
||||
import org.jetbrains.kotlin.ir.util.*
|
||||
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
|
||||
import org.jetbrains.kotlin.load.java.JvmAbi
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.resolve.JVM_INLINE_ANNOTATION_FQ_NAME
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
|
||||
|
||||
val jvmInlineClassPhase = makeIrFilePhase(
|
||||
::JvmInlineClassLowering,
|
||||
@@ -57,96 +56,24 @@ val jvmInlineClassPhase = makeIrFilePhase(
|
||||
* We do not unfold inline class types here. Instead, the type mapper will lower inline class
|
||||
* types to the types of their underlying field.
|
||||
*/
|
||||
private class JvmInlineClassLowering(private val context: JvmBackendContext) : FileLoweringPass, IrElementTransformerVoidWithContext() {
|
||||
private val valueMap = mutableMapOf<IrValueSymbol, IrValueDeclaration>()
|
||||
private class JvmInlineClassLowering(context: JvmBackendContext) : JvmValueClassAbstractLowering(context) {
|
||||
override val replacements: MemoizedValueClassAbstractReplacements
|
||||
get() = context.inlineClassReplacements
|
||||
|
||||
private fun addBindingsFor(original: IrFunction, replacement: IrFunction) {
|
||||
for ((param, newParam) in original.explicitParameters.zip(replacement.explicitParameters)) {
|
||||
valueMap[param.symbol] = newParam
|
||||
}
|
||||
}
|
||||
override fun IrClass.isSpecificLoweringLogicApplicable(): Boolean = isSingleFieldValueClass
|
||||
|
||||
override fun lower(irFile: IrFile) {
|
||||
irFile.transformChildrenVoid()
|
||||
}
|
||||
override fun IrFunction.isSpecificFieldGetter(): Boolean = isInlineClassFieldGetter
|
||||
|
||||
override fun visitClassNew(declaration: IrClass): IrStatement {
|
||||
// The arguments to the primary constructor are in scope in the initializers of IrFields.
|
||||
declaration.primaryConstructor?.let {
|
||||
context.inlineClassReplacements.getReplacementFunction(it)?.let { replacement -> addBindingsFor(it, replacement) }
|
||||
}
|
||||
|
||||
declaration.transformDeclarationsFlat { memberDeclaration ->
|
||||
if (memberDeclaration is IrFunction) {
|
||||
withinScope(memberDeclaration) {
|
||||
transformFunctionFlat(memberDeclaration)
|
||||
}
|
||||
} else {
|
||||
memberDeclaration.accept(this, null)
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
if (declaration.isSingleFieldValueClass) {
|
||||
val irConstructor = declaration.primaryConstructor!!
|
||||
// The field getter is used by reflection and cannot be removed here unless it is internal.
|
||||
declaration.declarations.removeIf {
|
||||
it == irConstructor || (it is IrFunction && it.isInlineClassFieldGetter && !it.visibility.isPublicAPI)
|
||||
}
|
||||
buildPrimaryInlineClassConstructor(declaration, irConstructor)
|
||||
buildBoxFunction(declaration)
|
||||
buildUnboxFunction(declaration)
|
||||
buildSpecializedEqualsMethod(declaration)
|
||||
addJvmInlineAnnotation(declaration)
|
||||
}
|
||||
|
||||
return declaration
|
||||
}
|
||||
|
||||
private fun addJvmInlineAnnotation(declaration: IrClass) {
|
||||
if (declaration.hasAnnotation(JVM_INLINE_ANNOTATION_FQ_NAME)) return
|
||||
override fun addJvmInlineAnnotation(valueClass: IrClass) {
|
||||
if (valueClass.hasAnnotation(JVM_INLINE_ANNOTATION_FQ_NAME)) return
|
||||
val constructor = context.ir.symbols.jvmInlineAnnotation.constructors.first()
|
||||
declaration.annotations = declaration.annotations + IrConstructorCallImpl.fromSymbolOwner(
|
||||
valueClass.annotations = valueClass.annotations + IrConstructorCallImpl.fromSymbolOwner(
|
||||
constructor.owner.returnType,
|
||||
constructor
|
||||
)
|
||||
}
|
||||
|
||||
private fun transformFunctionFlat(function: IrFunction): List<IrDeclaration>? {
|
||||
if (function is IrConstructor && function.isPrimary && function.constructedClass.isSingleFieldValueClass)
|
||||
return null
|
||||
|
||||
val replacement = context.inlineClassReplacements.getReplacementFunction(function)
|
||||
if (replacement == null) {
|
||||
function.transformChildrenVoid()
|
||||
return null
|
||||
}
|
||||
|
||||
if (function is IrSimpleFunction && function.overriddenSymbols.any { it.owner.parentAsClass.isFun }) {
|
||||
// If fun interface methods are already mangled, do not mangle them twice.
|
||||
val suffix = function.hashSuffix()
|
||||
if (suffix != null && function.name.asString().endsWith(suffix)) {
|
||||
function.transformChildrenVoid()
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
addBindingsFor(function, replacement)
|
||||
return when (function) {
|
||||
is IrSimpleFunction -> transformSimpleFunctionFlat(function, replacement)
|
||||
is IrConstructor -> transformConstructorFlat(function, replacement)
|
||||
else -> throw IllegalStateException()
|
||||
}
|
||||
}
|
||||
|
||||
private fun IrFunction.hashSuffix(): String? =
|
||||
InlineClassAbi.hashSuffix(
|
||||
this,
|
||||
context.state.functionsWithInlineClassReturnTypesMangled,
|
||||
context.state.useOldManglingSchemeForFunctionsWithInlineClassesInSignatures
|
||||
)
|
||||
|
||||
private fun transformSimpleFunctionFlat(function: IrSimpleFunction, replacement: IrSimpleFunction): List<IrDeclaration> {
|
||||
override fun transformSimpleFunctionFlat(function: IrSimpleFunction, replacement: IrSimpleFunction): List<IrDeclaration> {
|
||||
replacement.valueParameters.forEach {
|
||||
it.transformChildrenVoid()
|
||||
it.defaultValue?.patchDeclarationParents(replacement)
|
||||
@@ -182,11 +109,11 @@ private class JvmInlineClassLowering(private val context: JvmBackendContext) : F
|
||||
bridgeFunction.overriddenSymbols = replacement.overriddenSymbols
|
||||
|
||||
// Replace the function body with a wrapper
|
||||
if (!bridgeFunction.isFakeOverride || !bridgeFunction.parentAsClass.isSingleFieldValueClass) {
|
||||
createBridgeBody(bridgeFunction, replacement)
|
||||
} else {
|
||||
if (bridgeFunction.isFakeOverride && bridgeFunction.parentAsClass.isSingleFieldValueClass) {
|
||||
// Fake overrides redirect from the replacement to the original function, which is in turn replaced during interfacePhase.
|
||||
createBridgeBody(replacement, bridgeFunction)
|
||||
} else {
|
||||
createBridgeBody(bridgeFunction, replacement)
|
||||
}
|
||||
|
||||
return listOf(replacement, bridgeFunction)
|
||||
@@ -227,7 +154,8 @@ private class JvmInlineClassLowering(private val context: JvmBackendContext) : F
|
||||
|
||||
// Secondary constructors for boxed types get translated to static functions returning
|
||||
// unboxed arguments. We remove the original constructor.
|
||||
private fun transformConstructorFlat(constructor: IrConstructor, replacement: IrSimpleFunction): List<IrDeclaration> {
|
||||
// Primary constructors' case is handled at the start of transformFunctionFlat
|
||||
override fun transformConstructorFlat(constructor: IrConstructor, replacement: IrSimpleFunction): List<IrDeclaration> {
|
||||
replacement.valueParameters.forEach { it.transformChildrenVoid() }
|
||||
replacement.body = context.createIrBuilder(replacement.symbol, replacement.startOffset, replacement.endOffset).irBlockBody(
|
||||
replacement
|
||||
@@ -469,40 +397,6 @@ private class JvmInlineClassLowering(private val context: JvmBackendContext) : F
|
||||
return super.visitGetField(expression)
|
||||
}
|
||||
|
||||
override fun visitReturn(expression: IrReturn): IrExpression {
|
||||
expression.returnTargetSymbol.owner.safeAs<IrFunction>()?.let { target ->
|
||||
val suffix = target.hashSuffix()
|
||||
if (suffix != null && target.name.asString().endsWith(suffix))
|
||||
return super.visitReturn(expression)
|
||||
|
||||
context.inlineClassReplacements.getReplacementFunction(target)?.let {
|
||||
return context.createIrBuilder(it.symbol, expression.startOffset, expression.endOffset).irReturn(
|
||||
expression.value.transform(this, null)
|
||||
)
|
||||
}
|
||||
}
|
||||
return super.visitReturn(expression)
|
||||
}
|
||||
|
||||
private fun visitStatementContainer(container: IrStatementContainer) {
|
||||
container.statements.transformFlat { statement ->
|
||||
if (statement is IrFunction)
|
||||
transformFunctionFlat(statement)
|
||||
else
|
||||
listOf(statement.transformStatement(this))
|
||||
}
|
||||
}
|
||||
|
||||
override fun visitContainerExpression(expression: IrContainerExpression): IrExpression {
|
||||
visitStatementContainer(expression)
|
||||
return expression
|
||||
}
|
||||
|
||||
override fun visitBlockBody(body: IrBlockBody): IrBody {
|
||||
visitStatementContainer(body)
|
||||
return body
|
||||
}
|
||||
|
||||
override fun visitGetValue(expression: IrGetValue): IrExpression {
|
||||
valueMap[expression.symbol]?.let {
|
||||
return IrGetValueImpl(
|
||||
@@ -525,16 +419,9 @@ private class JvmInlineClassLowering(private val context: JvmBackendContext) : F
|
||||
return super.visitSetValue(expression)
|
||||
}
|
||||
|
||||
// Anonymous initializers in inline classes are processed when building the primary constructor.
|
||||
override fun visitAnonymousInitializerNew(declaration: IrAnonymousInitializer): IrStatement {
|
||||
if (declaration.parent.safeAs<IrClass>()?.isSingleFieldValueClass == true)
|
||||
return declaration
|
||||
return super.visitAnonymousInitializerNew(declaration)
|
||||
}
|
||||
|
||||
private fun buildPrimaryInlineClassConstructor(irClass: IrClass, irConstructor: IrConstructor) {
|
||||
override fun buildPrimaryValueClassConstructor(valueClass: IrClass, irConstructor: IrConstructor) {
|
||||
// Add the default primary constructor
|
||||
irClass.addConstructor {
|
||||
valueClass.addConstructor {
|
||||
updateFrom(irConstructor)
|
||||
visibility = DescriptorVisibilities.PRIVATE
|
||||
origin = JvmLoweredDeclarationOrigin.SYNTHETIC_INLINE_CLASS_MEMBER
|
||||
@@ -547,8 +434,8 @@ private class JvmInlineClassLowering(private val context: JvmBackendContext) : F
|
||||
body = context.createIrBuilder(this.symbol).irBlockBody(this) {
|
||||
+irDelegatingConstructorCall(context.irBuiltIns.anyClass.owner.constructors.single())
|
||||
+irSetField(
|
||||
irGet(irClass.thisReceiver!!),
|
||||
getInlineClassBackingField(irClass),
|
||||
irGet(valueClass.thisReceiver!!),
|
||||
getInlineClassBackingField(valueClass),
|
||||
irGet(this@apply.valueParameters[0])
|
||||
)
|
||||
}
|
||||
@@ -558,13 +445,13 @@ private class JvmInlineClassLowering(private val context: JvmBackendContext) : F
|
||||
// null-checks, default arguments, and anonymous initializers.
|
||||
val function = context.inlineClassReplacements.getReplacementFunction(irConstructor)!!
|
||||
|
||||
val initBlocks = irClass.declarations.filterIsInstance<IrAnonymousInitializer>()
|
||||
val initBlocks = valueClass.declarations.filterIsInstance<IrAnonymousInitializer>()
|
||||
|
||||
function.valueParameters.forEach { it.transformChildrenVoid() }
|
||||
function.body = context.createIrBuilder(function.symbol).irBlockBody {
|
||||
val argument = function.valueParameters[0]
|
||||
val thisValue = irTemporary(coerceInlineClasses(irGet(argument), argument.type, function.returnType, skipCast = true))
|
||||
valueMap[irClass.thisReceiver!!.symbol] = thisValue
|
||||
valueMap[valueClass.thisReceiver!!.symbol] = thisValue
|
||||
for (initBlock in initBlocks) {
|
||||
for (stmt in initBlock.body.statements) {
|
||||
+stmt.transformStatement(this@JvmInlineClassLowering).patchDeclarationParents(function)
|
||||
@@ -573,21 +460,25 @@ private class JvmInlineClassLowering(private val context: JvmBackendContext) : F
|
||||
+irReturn(irGet(thisValue))
|
||||
}
|
||||
|
||||
irClass.declarations.removeAll(initBlocks)
|
||||
irClass.declarations += function
|
||||
valueClass.declarations.removeAll(initBlocks)
|
||||
valueClass.declarations += function
|
||||
}
|
||||
|
||||
private fun buildBoxFunction(irClass: IrClass) {
|
||||
val function = context.inlineClassReplacements.getBoxFunction(irClass)
|
||||
override fun buildBoxFunction(valueClass: IrClass) {
|
||||
val function = context.inlineClassReplacements.getBoxFunction(valueClass)
|
||||
with(context.createIrBuilder(function.symbol)) {
|
||||
function.body = irExprBody(
|
||||
irCall(irClass.primaryConstructor!!.symbol).apply {
|
||||
irCall(valueClass.primaryConstructor!!.symbol).apply {
|
||||
passTypeArgumentsFrom(function)
|
||||
putValueArgument(0, irGet(function.valueParameters[0]))
|
||||
}
|
||||
)
|
||||
}
|
||||
irClass.declarations += function
|
||||
valueClass.declarations += function
|
||||
}
|
||||
|
||||
override fun buildUnboxFunctions(valueClass: IrClass) {
|
||||
buildUnboxFunction(valueClass)
|
||||
}
|
||||
|
||||
private fun buildUnboxFunction(irClass: IrClass) {
|
||||
@@ -602,13 +493,13 @@ private class JvmInlineClassLowering(private val context: JvmBackendContext) : F
|
||||
irClass.declarations += function
|
||||
}
|
||||
|
||||
private fun buildSpecializedEqualsMethod(irClass: IrClass) {
|
||||
val function = context.inlineClassReplacements.getSpecializedEqualsMethod(irClass, context.irBuiltIns)
|
||||
override fun buildSpecializedEqualsMethod(valueClass: IrClass) {
|
||||
val function = context.inlineClassReplacements.getSpecializedEqualsMethod(valueClass, context.irBuiltIns)
|
||||
val left = function.valueParameters[0]
|
||||
val right = function.valueParameters[1]
|
||||
val type = left.type.unboxInlineClass()
|
||||
|
||||
function.body = context.createIrBuilder(irClass.symbol).run {
|
||||
function.body = context.createIrBuilder(valueClass.symbol).run {
|
||||
irExprBody(
|
||||
irEquals(
|
||||
coerceInlineClasses(irGet(left), left.type, type),
|
||||
@@ -617,6 +508,6 @@ private class JvmInlineClassLowering(private val context: JvmBackendContext) : F
|
||||
)
|
||||
}
|
||||
|
||||
irClass.declarations += function
|
||||
valueClass.declarations += function
|
||||
}
|
||||
}
|
||||
|
||||
+78
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
* Copyright 2010-2022 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.backend.jvm.lower
|
||||
|
||||
import org.jetbrains.kotlin.backend.jvm.JvmBackendContext
|
||||
import org.jetbrains.kotlin.backend.jvm.MemoizedValueClassAbstractReplacements
|
||||
import org.jetbrains.kotlin.backend.jvm.isMultiFieldValueClassFieldGetter
|
||||
import org.jetbrains.kotlin.ir.declarations.*
|
||||
import org.jetbrains.kotlin.ir.expressions.*
|
||||
|
||||
private class JvmInlineMultiFieldValueClassLowering(context: JvmBackendContext) : JvmValueClassAbstractLowering(context) {
|
||||
override val replacements: MemoizedValueClassAbstractReplacements
|
||||
get() = context.multiFieldValueClassReplacements
|
||||
|
||||
override fun IrClass.isSpecificLoweringLogicApplicable(): Boolean = isMultiFieldValueClass
|
||||
|
||||
override fun IrFunction.isSpecificFieldGetter(): Boolean = isMultiFieldValueClassFieldGetter
|
||||
|
||||
override fun transformSimpleFunctionFlat(function: IrSimpleFunction, replacement: IrSimpleFunction): List<IrDeclaration> {
|
||||
TODO()
|
||||
}
|
||||
|
||||
override fun buildPrimaryValueClassConstructor(valueClass: IrClass, irConstructor: IrConstructor) {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
override fun buildBoxFunction(valueClass: IrClass) {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
override fun buildUnboxFunctions(valueClass: IrClass) {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
override fun buildSpecializedEqualsMethod(valueClass: IrClass) {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
override fun addJvmInlineAnnotation(valueClass: IrClass) = Unit
|
||||
|
||||
@Suppress("UNUSED_PARAMETER")
|
||||
override fun transformConstructorFlat(constructor: IrConstructor, replacement: IrSimpleFunction): List<IrDeclaration> {
|
||||
TODO()
|
||||
}
|
||||
|
||||
override fun visitFunctionReference(expression: IrFunctionReference): IrExpression {
|
||||
// todo implement
|
||||
return super.visitFunctionReference(expression)
|
||||
}
|
||||
|
||||
override fun visitFunctionAccess(expression: IrFunctionAccessExpression): IrExpression {
|
||||
// todo implement
|
||||
return super.visitFunctionAccess(expression)
|
||||
}
|
||||
|
||||
override fun visitCall(expression: IrCall): IrExpression {
|
||||
// todo implement
|
||||
return super.visitCall(expression)
|
||||
}
|
||||
|
||||
override fun visitGetField(expression: IrGetField): IrExpression {
|
||||
// todo implement
|
||||
return super.visitGetField(expression)
|
||||
}
|
||||
|
||||
override fun visitGetValue(expression: IrGetValue): IrExpression {
|
||||
// todo implement
|
||||
return super.visitGetValue(expression)
|
||||
}
|
||||
|
||||
override fun visitSetValue(expression: IrSetValue): IrExpression {
|
||||
// todo implement
|
||||
return super.visitSetValue(expression)
|
||||
}
|
||||
}
|
||||
+173
@@ -0,0 +1,173 @@
|
||||
/*
|
||||
* Copyright 2010-2022 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.backend.jvm.lower
|
||||
|
||||
import org.jetbrains.kotlin.backend.common.FileLoweringPass
|
||||
import org.jetbrains.kotlin.backend.common.IrElementTransformerVoidWithContext
|
||||
import org.jetbrains.kotlin.backend.common.lower.createIrBuilder
|
||||
import org.jetbrains.kotlin.backend.jvm.InlineClassAbi
|
||||
import org.jetbrains.kotlin.backend.jvm.JvmBackendContext
|
||||
import org.jetbrains.kotlin.backend.jvm.MemoizedValueClassAbstractReplacements
|
||||
import org.jetbrains.kotlin.ir.IrStatement
|
||||
import org.jetbrains.kotlin.ir.builders.irReturn
|
||||
import org.jetbrains.kotlin.ir.declarations.*
|
||||
import org.jetbrains.kotlin.ir.expressions.*
|
||||
import org.jetbrains.kotlin.ir.symbols.IrValueSymbol
|
||||
import org.jetbrains.kotlin.ir.transformStatement
|
||||
import org.jetbrains.kotlin.ir.util.*
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
|
||||
|
||||
internal abstract class JvmValueClassAbstractLowering(val context: JvmBackendContext) : FileLoweringPass,
|
||||
IrElementTransformerVoidWithContext() {
|
||||
abstract val replacements: MemoizedValueClassAbstractReplacements
|
||||
|
||||
protected val valueMap = mutableMapOf<IrValueSymbol, IrValueDeclaration>()
|
||||
|
||||
private fun addBindingsFor(original: IrFunction, replacement: IrFunction) {
|
||||
for ((param, newParam) in original.explicitParameters.zip(replacement.explicitParameters)) {
|
||||
valueMap[param.symbol] = newParam
|
||||
}
|
||||
}
|
||||
|
||||
final override fun lower(irFile: IrFile) {
|
||||
irFile.transformChildrenVoid()
|
||||
}
|
||||
|
||||
abstract fun IrClass.isSpecificLoweringLogicApplicable(): Boolean
|
||||
|
||||
abstract fun IrFunction.isSpecificFieldGetter(): Boolean
|
||||
|
||||
final override fun visitClassNew(declaration: IrClass): IrStatement {
|
||||
// The arguments to the primary constructor are in scope in the initializers of IrFields.
|
||||
declaration.primaryConstructor?.let {
|
||||
replacements.getReplacementFunction(it)?.let { replacement -> addBindingsFor(it, replacement) }
|
||||
}
|
||||
|
||||
declaration.transformDeclarationsFlat { memberDeclaration ->
|
||||
if (memberDeclaration is IrFunction) {
|
||||
withinScope(memberDeclaration) {
|
||||
transformFunctionFlat(memberDeclaration)
|
||||
}
|
||||
} else {
|
||||
memberDeclaration.accept(this, null)
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
if (declaration.isSpecificLoweringLogicApplicable()) {
|
||||
val irConstructor = declaration.primaryConstructor!!
|
||||
// The field getter is used by reflection and cannot be removed here unless it is internal.
|
||||
declaration.declarations.removeIf {
|
||||
it == irConstructor || (it is IrFunction && it.isSpecificFieldGetter() && !it.visibility.isPublicAPI)
|
||||
}
|
||||
buildPrimaryValueClassConstructor(declaration, irConstructor)
|
||||
buildBoxFunction(declaration)
|
||||
buildUnboxFunctions(declaration)
|
||||
buildSpecializedEqualsMethod(declaration)
|
||||
addJvmInlineAnnotation(declaration)
|
||||
}
|
||||
|
||||
return declaration
|
||||
}
|
||||
|
||||
protected fun transformFunctionFlat(function: IrFunction): List<IrDeclaration>? {
|
||||
if (function is IrConstructor && function.isPrimary && function.constructedClass.isSpecificLoweringLogicApplicable()) {
|
||||
return null
|
||||
}
|
||||
|
||||
val replacement = replacements.getReplacementFunction(function)
|
||||
if (replacement == null) {
|
||||
function.transformChildrenVoid()
|
||||
return null
|
||||
}
|
||||
|
||||
if (function is IrSimpleFunction && function.overriddenSymbols.any { it.owner.parentAsClass.isFun }) {
|
||||
// If fun interface methods are already mangled, do not mangle them twice.
|
||||
val suffix = function.hashSuffix()
|
||||
if (suffix != null && function.name.asString().endsWith(suffix)) {
|
||||
function.transformChildrenVoid()
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
addBindingsFor(function, replacement)
|
||||
return when (function) {
|
||||
is IrSimpleFunction -> transformSimpleFunctionFlat(function, replacement)
|
||||
is IrConstructor -> transformConstructorFlat(function, replacement)
|
||||
else -> throw IllegalStateException()
|
||||
}
|
||||
}
|
||||
|
||||
private fun IrFunction.hashSuffix(): String? = InlineClassAbi.hashSuffix(
|
||||
this,
|
||||
context.state.functionsWithInlineClassReturnTypesMangled,
|
||||
context.state.useOldManglingSchemeForFunctionsWithInlineClassesInSignatures
|
||||
)
|
||||
|
||||
protected abstract fun transformConstructorFlat(constructor: IrConstructor, replacement: IrSimpleFunction): List<IrDeclaration>
|
||||
|
||||
protected abstract fun transformSimpleFunctionFlat(function: IrSimpleFunction, replacement: IrSimpleFunction): List<IrDeclaration>
|
||||
|
||||
protected abstract fun buildPrimaryValueClassConstructor(valueClass: IrClass, irConstructor: IrConstructor)
|
||||
|
||||
protected abstract fun buildBoxFunction(valueClass: IrClass)
|
||||
|
||||
protected abstract fun buildUnboxFunctions(valueClass: IrClass)
|
||||
|
||||
protected abstract fun buildSpecializedEqualsMethod(valueClass: IrClass) // todo hashCode
|
||||
|
||||
protected abstract fun addJvmInlineAnnotation(valueClass: IrClass)
|
||||
|
||||
// abstract override fun visitFunctionReference(expression: IrFunctionReference): IrExpression
|
||||
|
||||
// abstract override fun visitFunctionAccess(expression: IrFunctionAccessExpression): IrExpression
|
||||
|
||||
// abstract override fun visitCall(expression: IrCall): IrExpression
|
||||
|
||||
// abstract override fun visitGetField(expression: IrGetField): IrExpression
|
||||
|
||||
final override fun visitReturn(expression: IrReturn): IrExpression {
|
||||
expression.returnTargetSymbol.owner.safeAs<IrFunction>()?.let { target ->
|
||||
val suffix = target.hashSuffix()
|
||||
if (suffix != null && target.name.asString().endsWith(suffix))
|
||||
return super.visitReturn(expression)
|
||||
|
||||
replacements.getReplacementFunction(target)?.let {
|
||||
return context.createIrBuilder(it.symbol, expression.startOffset, expression.endOffset).irReturn(
|
||||
expression.value.transform(this, null)
|
||||
)
|
||||
}
|
||||
}
|
||||
return super.visitReturn(expression)
|
||||
}
|
||||
|
||||
private fun visitStatementContainer(container: IrStatementContainer) {
|
||||
container.statements.transformFlat { statement ->
|
||||
if (statement is IrFunction)
|
||||
transformFunctionFlat(statement)
|
||||
else
|
||||
listOf(statement.transformStatement(this))
|
||||
}
|
||||
}
|
||||
|
||||
final override fun visitContainerExpression(expression: IrContainerExpression): IrExpression {
|
||||
visitStatementContainer(expression)
|
||||
return expression
|
||||
}
|
||||
|
||||
final override fun visitBlockBody(body: IrBlockBody): IrBody {
|
||||
visitStatementContainer(body)
|
||||
return body
|
||||
}
|
||||
|
||||
// Anonymous initializers in inline classes are processed when building the primary constructor.
|
||||
final override fun visitAnonymousInitializerNew(declaration: IrAnonymousInitializer): IrStatement =
|
||||
if (declaration.parent.safeAs<IrClass>()?.isSpecificLoweringLogicApplicable() == true)
|
||||
declaration
|
||||
else
|
||||
super.visitAnonymousInitializerNew(declaration)
|
||||
|
||||
}
|
||||
@@ -147,3 +147,11 @@ val IrClass.inlineClassFieldName: Name
|
||||
val IrFunction.isInlineClassFieldGetter: Boolean
|
||||
get() = (parent as? IrClass)?.isSingleFieldValueClass == true && this is IrSimpleFunction && extensionReceiverParameter == null &&
|
||||
correspondingPropertySymbol?.let { it.owner.getter == this && it.owner.name == parentAsClass.inlineClassFieldName } == true
|
||||
|
||||
val IrFunction.isMultiFieldValueClassFieldGetter: Boolean
|
||||
get() = (parent as? IrClass)?.isMultiFieldValueClass == true && this is IrSimpleFunction && extensionReceiverParameter == null &&
|
||||
correspondingPropertySymbol?.let {
|
||||
val multiFieldValueClassRepresentation = parentAsClass.multiFieldValueClassRepresentation
|
||||
?: error("Multi-field value class must have multiFieldValueClassRepresentation: ${parentAsClass.render()}")
|
||||
it.owner.getter == this && multiFieldValueClassRepresentation.containsPropertyWithName(it.owner.name)
|
||||
} == true
|
||||
|
||||
@@ -129,6 +129,9 @@ class JvmBackendContext(
|
||||
|
||||
val inlineClassReplacements = MemoizedInlineClassReplacements(state.functionsWithInlineClassReturnTypesMangled, irFactory, this)
|
||||
|
||||
val multiFieldValueClassReplacements =
|
||||
MemoizedMultiFieldValueClassReplacements(state.functionsWithInlineClassReturnTypesMangled, irFactory, this)
|
||||
|
||||
val continuationClassesVarsCountByType: MutableMap<IrAttributeContainer, Map<Type, Int>> = hashMapOf()
|
||||
|
||||
val inlineMethodGenerationLock = Any()
|
||||
|
||||
+4
-4
@@ -34,9 +34,9 @@ import java.util.concurrent.ConcurrentHashMap
|
||||
*/
|
||||
class MemoizedInlineClassReplacements(
|
||||
private val mangleReturnTypes: Boolean,
|
||||
private val irFactory: IrFactory,
|
||||
private val context: JvmBackendContext
|
||||
) {
|
||||
irFactory: IrFactory,
|
||||
context: JvmBackendContext
|
||||
) : MemoizedValueClassAbstractReplacements(irFactory, context) {
|
||||
private val storageManager = LockBasedStorageManager("inline-class-replacements")
|
||||
private val propertyMap = ConcurrentHashMap<IrPropertySymbol, IrProperty>()
|
||||
|
||||
@@ -46,7 +46,7 @@ class MemoizedInlineClassReplacements(
|
||||
/**
|
||||
* Get a replacement for a function or a constructor.
|
||||
*/
|
||||
val getReplacementFunction: (IrFunction) -> IrSimpleFunction? =
|
||||
override val getReplacementFunction: (IrFunction) -> IrSimpleFunction? =
|
||||
storageManager.createMemoizedFunctionWithNullableValues {
|
||||
when {
|
||||
// Don't mangle anonymous or synthetic functions, except for generated SAM wrapper methods
|
||||
|
||||
+302
@@ -0,0 +1,302 @@
|
||||
/*
|
||||
* Copyright 2010-2021 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.backend.jvm
|
||||
|
||||
import org.jetbrains.kotlin.backend.common.ir.copyTo
|
||||
import org.jetbrains.kotlin.backend.common.ir.copyTypeParameters
|
||||
import org.jetbrains.kotlin.backend.jvm.ir.*
|
||||
import org.jetbrains.kotlin.descriptors.DescriptorVisibilities
|
||||
import org.jetbrains.kotlin.descriptors.Modality
|
||||
import org.jetbrains.kotlin.ir.builders.declarations.buildFun
|
||||
import org.jetbrains.kotlin.ir.builders.declarations.buildProperty
|
||||
import org.jetbrains.kotlin.ir.declarations.*
|
||||
import org.jetbrains.kotlin.ir.symbols.IrPropertySymbol
|
||||
import org.jetbrains.kotlin.ir.types.isInt
|
||||
import org.jetbrains.kotlin.ir.util.*
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.storage.LockBasedStorageManager
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
|
||||
/**
|
||||
* Keeps track of replacement functions and multi-field value class box/unbox functions.
|
||||
*/
|
||||
class MemoizedMultiFieldValueClassReplacements(
|
||||
private val mangleReturnTypes: Boolean, // todo always false
|
||||
irFactory: IrFactory,
|
||||
context: JvmBackendContext
|
||||
) : MemoizedValueClassAbstractReplacements(irFactory, context) { // There is only sample logic yet
|
||||
private val storageManager = LockBasedStorageManager("multi-field-value-class-replacements")
|
||||
private val propertyMap = ConcurrentHashMap<IrPropertySymbol, IrProperty>()
|
||||
|
||||
val originalFunctionForStaticReplacement: MutableMap<IrFunction, IrFunction> = ConcurrentHashMap()
|
||||
internal val originalFunctionForMethodReplacement: MutableMap<IrFunction, IrFunction> = ConcurrentHashMap()
|
||||
|
||||
/**
|
||||
* Get a replacement for a function or a constructor.
|
||||
*/
|
||||
override val getReplacementFunction: (IrFunction) -> IrSimpleFunction? = storageManager.createMemoizedFunctionWithNullableValues {
|
||||
when {
|
||||
// Don't mangle anonymous or synthetic functions, except for generated SAM wrapper methods
|
||||
(it.isLocal && it is IrSimpleFunction && it.overriddenSymbols.isEmpty()) ||
|
||||
(it.origin == IrDeclarationOrigin.DELEGATED_PROPERTY_ACCESSOR && it.visibility == DescriptorVisibilities.LOCAL) ||
|
||||
it.isStaticInlineClassReplacement ||
|
||||
it.origin.isSynthetic && it.origin != IrDeclarationOrigin.SYNTHETIC_GENERATED_SAM_IMPLEMENTATION ->
|
||||
null
|
||||
|
||||
it.isMultiFieldValueClassFieldGetter ->
|
||||
if (it.hasMangledReturnType)
|
||||
createMethodReplacement(it)
|
||||
else
|
||||
null
|
||||
|
||||
// Mangle all functions in the body of an inline class
|
||||
it.parent.safeAs<IrClass>()?.isMultiFieldValueClass == true ->
|
||||
when {
|
||||
it.isRemoveAtSpecialBuiltinStub() ->
|
||||
null
|
||||
it.isInlineClassMemberFakeOverriddenFromJvmDefaultInterfaceMethod() ||
|
||||
it.origin == IrDeclarationOrigin.IR_BUILTINS_STUB ->
|
||||
createMethodReplacement(it)
|
||||
else ->
|
||||
createStaticReplacement(it)
|
||||
}
|
||||
|
||||
// Otherwise, mangle functions with mangled parameters, ignoring constructors
|
||||
it is IrSimpleFunction && !it.isFromJava() && (it.hasMangledParameters || mangleReturnTypes && it.hasMangledReturnType) ->
|
||||
createMethodReplacement(it)
|
||||
|
||||
else ->
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
private fun IrFunction.isRemoveAtSpecialBuiltinStub() =
|
||||
origin == IrDeclarationOrigin.IR_BUILTINS_STUB &&
|
||||
name.asString() == "remove" &&
|
||||
valueParameters.size == 1 &&
|
||||
valueParameters[0].type.isInt()
|
||||
|
||||
private fun IrFunction.isInlineClassMemberFakeOverriddenFromJvmDefaultInterfaceMethod(): Boolean {
|
||||
if (this !is IrSimpleFunction) return false
|
||||
if (!this.isFakeOverride) return false
|
||||
val parentClass = parentClassOrNull ?: return false
|
||||
if (!parentClass.isMultiFieldValueClass) return false
|
||||
|
||||
val overridden = resolveFakeOverride() ?: return false
|
||||
if (!overridden.parentAsClass.isJvmInterface) return false
|
||||
if (overridden.modality == Modality.ABSTRACT) return false
|
||||
|
||||
// We have a non-abstract interface member.
|
||||
// It is a JVM default interface method if one of the following conditions are true:
|
||||
// - it is a Java method,
|
||||
// - it is a Kotlin function compiled to JVM default interface method.
|
||||
return overridden.isFromJava() || overridden.isCompiledToJvmDefault(context.state.jvmDefaultMode)
|
||||
}
|
||||
|
||||
// /**
|
||||
// * Get the box function for an inline class. Concretely, this is a synthetic
|
||||
// * static function named "box-impl" which takes an unboxed value and returns
|
||||
// * a boxed value.
|
||||
// */
|
||||
// val getBoxFunction: (IrClass) -> IrSimpleFunction =
|
||||
// storageManager.createMemoizedFunction { irClass ->
|
||||
// require(irClass.isSingleFieldValueClass)
|
||||
// irFactory.buildFun {
|
||||
// name = Name.identifier(KotlinTypeMapper.BOX_JVM_METHOD_NAME)
|
||||
// origin = JvmLoweredDeclarationOrigin.SYNTHETIC_INLINE_CLASS_MEMBER
|
||||
// returnType = irClass.defaultType
|
||||
// }.apply {
|
||||
// parent = irClass
|
||||
// copyTypeParametersFrom(irClass)
|
||||
// addValueParameter {
|
||||
// name = InlineClassDescriptorResolver.BOXING_VALUE_PARAMETER_NAME
|
||||
// type = irClass.inlineClassRepresentation!!.underlyingType
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * Get the unbox function for an inline class. Concretely, this is a synthetic
|
||||
// * member function named "unbox-impl" which returns an unboxed result.
|
||||
// */
|
||||
// val getUnboxFunction: (IrClass) -> IrSimpleFunction =
|
||||
// storageManager.createMemoizedFunction { irClass ->
|
||||
// require(irClass.isSingleFieldValueClass)
|
||||
// irFactory.buildFun {
|
||||
// name = Name.identifier(KotlinTypeMapper.UNBOX_JVM_METHOD_NAME)
|
||||
// origin = JvmLoweredDeclarationOrigin.SYNTHETIC_INLINE_CLASS_MEMBER
|
||||
// returnType = irClass.inlineClassRepresentation!!.underlyingType
|
||||
// }.apply {
|
||||
// parent = irClass
|
||||
// createDispatchReceiverParameter()
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// private val specializedEqualsCache = storageManager.createCacheWithNotNullValues<IrClass, IrSimpleFunction>()
|
||||
// fun getSpecializedEqualsMethod(irClass: IrClass, irBuiltIns: IrBuiltIns): IrSimpleFunction {
|
||||
// require(irClass.isSingleFieldValueClass)
|
||||
// return specializedEqualsCache.computeIfAbsent(irClass) {
|
||||
// irFactory.buildFun {
|
||||
// name = InlineClassDescriptorResolver.SPECIALIZED_EQUALS_NAME
|
||||
// // TODO: Revisit this once we allow user defined equals methods in inline classes.
|
||||
// origin = JvmLoweredDeclarationOrigin.INLINE_CLASS_GENERATED_IMPL_METHOD
|
||||
// returnType = irBuiltIns.booleanType
|
||||
// }.apply {
|
||||
// parent = irClass
|
||||
// // We ignore type arguments here, since there is no good way to go from type arguments to types in the IR anyway.
|
||||
// val typeArgument =
|
||||
// IrSimpleTypeImpl(null, irClass.symbol, false, List(irClass.typeParameters.size) { IrStarProjectionImpl }, listOf())
|
||||
// addValueParameter {
|
||||
// name = InlineClassDescriptorResolver.SPECIALIZED_EQUALS_FIRST_PARAMETER_NAME
|
||||
// type = typeArgument
|
||||
// }
|
||||
// addValueParameter {
|
||||
// name = InlineClassDescriptorResolver.SPECIALIZED_EQUALS_SECOND_PARAMETER_NAME
|
||||
// type = typeArgument
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
private fun createMethodReplacement(function: IrFunction): IrSimpleFunction =
|
||||
buildReplacement(function, function.origin) {
|
||||
originalFunctionForMethodReplacement[this] = function
|
||||
dispatchReceiverParameter = function.dispatchReceiverParameter?.copyTo(this, index = -1)
|
||||
extensionReceiverParameter = function.extensionReceiverParameter?.copyTo(
|
||||
// The function's name will be mangled, so preserve the old receiver name.
|
||||
this, index = -1, name = Name.identifier(function.extensionReceiverName(context.state))
|
||||
)
|
||||
contextReceiverParametersCount = function.contextReceiverParametersCount
|
||||
valueParameters = function.valueParameters.mapIndexed { index, parameter ->
|
||||
parameter.copyTo(this, index = index, defaultValue = null).also {
|
||||
// Assuming that constructors and non-override functions are always replaced with the unboxed
|
||||
// equivalent, deep-copying the value here is unnecessary. See `JvmInlineClassLowering`.
|
||||
it.defaultValue = parameter.defaultValue?.patchDeclarationParents(this)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun createStaticReplacement(function: IrFunction): IrSimpleFunction =
|
||||
buildReplacement(function, JvmLoweredDeclarationOrigin.STATIC_INLINE_CLASS_REPLACEMENT, noFakeOverride = true) {
|
||||
originalFunctionForStaticReplacement[this] = function
|
||||
|
||||
val newValueParameters = mutableListOf<IrValueParameter>()
|
||||
if (function.dispatchReceiverParameter != null) {
|
||||
// FAKE_OVERRIDEs have broken dispatch receivers
|
||||
newValueParameters += function.parentAsClass.thisReceiver!!.copyTo(
|
||||
this, index = newValueParameters.size, name = Name.identifier("arg${newValueParameters.size}"),
|
||||
type = function.parentAsClass.defaultType, origin = IrDeclarationOrigin.MOVED_DISPATCH_RECEIVER
|
||||
)
|
||||
}
|
||||
if (function.contextReceiverParametersCount != 0) {
|
||||
function.valueParameters.take(function.contextReceiverParametersCount).forEachIndexed { i, contextReceiver ->
|
||||
newValueParameters += contextReceiver.copyTo(
|
||||
this, index = newValueParameters.size, name = Name.identifier("contextReceiver$i"),
|
||||
origin = IrDeclarationOrigin.MOVED_CONTEXT_RECEIVER
|
||||
)
|
||||
}
|
||||
}
|
||||
function.extensionReceiverParameter?.let {
|
||||
newValueParameters += it.copyTo(
|
||||
this, index = newValueParameters.size, name = Name.identifier(function.extensionReceiverName(context.state)),
|
||||
origin = IrDeclarationOrigin.MOVED_EXTENSION_RECEIVER
|
||||
)
|
||||
}
|
||||
for (parameter in function.valueParameters.drop(function.contextReceiverParametersCount)) {
|
||||
newValueParameters += parameter.copyTo(this, index = newValueParameters.size, defaultValue = null).also {
|
||||
// See comment next to a similar line above.
|
||||
it.defaultValue = parameter.defaultValue?.patchDeclarationParents(this)
|
||||
}
|
||||
}
|
||||
valueParameters = newValueParameters
|
||||
}
|
||||
|
||||
private fun buildReplacement(
|
||||
function: IrFunction,
|
||||
replacementOrigin: IrDeclarationOrigin,
|
||||
noFakeOverride: Boolean = false,
|
||||
body: IrFunction.() -> Unit
|
||||
): IrSimpleFunction {
|
||||
val useOldManglingScheme = context.state.useOldManglingSchemeForFunctionsWithInlineClassesInSignatures
|
||||
val replacement = buildReplacementInner(function, replacementOrigin, noFakeOverride, useOldManglingScheme, body)
|
||||
// When using the new mangling scheme we might run into dependencies using the old scheme
|
||||
// for which we will fall back to the old mangling scheme as well.
|
||||
if (
|
||||
!useOldManglingScheme &&
|
||||
replacement.name.asString().contains("-") &&
|
||||
function.parentClassId?.let { classFileContainsMethod(it, replacement, context) } == false
|
||||
) {
|
||||
return buildReplacementInner(function, replacementOrigin, noFakeOverride, true, body)
|
||||
}
|
||||
return replacement
|
||||
}
|
||||
|
||||
private fun buildReplacementInner(
|
||||
function: IrFunction,
|
||||
replacementOrigin: IrDeclarationOrigin,
|
||||
noFakeOverride: Boolean,
|
||||
useOldManglingScheme: Boolean,
|
||||
body: IrFunction.() -> Unit,
|
||||
): IrSimpleFunction = irFactory.buildFun {
|
||||
updateFrom(function)
|
||||
if (function is IrConstructor) {
|
||||
// The [updateFrom] call will set the modality to FINAL for constructors, while the JVM backend would use OPEN here.
|
||||
modality = Modality.OPEN
|
||||
}
|
||||
origin = when {
|
||||
function.origin == IrDeclarationOrigin.GENERATED_SINGLE_FIELD_VALUE_CLASS_MEMBER ->
|
||||
JvmLoweredDeclarationOrigin.INLINE_CLASS_GENERATED_IMPL_METHOD
|
||||
function is IrConstructor && function.constructedClass.isSingleFieldValueClass ->
|
||||
JvmLoweredDeclarationOrigin.STATIC_INLINE_CLASS_CONSTRUCTOR
|
||||
else ->
|
||||
replacementOrigin
|
||||
}
|
||||
if (noFakeOverride) {
|
||||
isFakeOverride = false
|
||||
}
|
||||
name = InlineClassAbi.mangledNameFor(function, mangleReturnTypes, useOldManglingScheme)
|
||||
returnType = function.returnType
|
||||
}.apply {
|
||||
parent = function.parent
|
||||
annotations = function.annotations
|
||||
copyTypeParameters(function.allTypeParameters)
|
||||
if (function.metadata != null) {
|
||||
metadata = function.metadata
|
||||
function.metadata = null
|
||||
}
|
||||
copyAttributes(function as? IrAttributeContainer)
|
||||
|
||||
if (function is IrSimpleFunction) {
|
||||
val propertySymbol = function.correspondingPropertySymbol
|
||||
if (propertySymbol != null) {
|
||||
val property = propertyMap.getOrPut(propertySymbol) {
|
||||
irFactory.buildProperty {
|
||||
name = propertySymbol.owner.name
|
||||
updateFrom(propertySymbol.owner)
|
||||
}.apply {
|
||||
parent = propertySymbol.owner.parent
|
||||
copyAttributes(propertySymbol.owner)
|
||||
annotations = propertySymbol.owner.annotations
|
||||
backingField = propertySymbol.owner.backingField
|
||||
}
|
||||
}
|
||||
correspondingPropertySymbol = property.symbol
|
||||
when (function) {
|
||||
propertySymbol.owner.getter -> property.getter = this
|
||||
propertySymbol.owner.setter -> property.setter = this
|
||||
else -> error("Orphaned property getter/setter: ${function.render()}")
|
||||
}
|
||||
}
|
||||
|
||||
overriddenSymbols = function.overriddenSymbols.map {
|
||||
getReplacementFunction(it.owner)?.symbol ?: it
|
||||
}
|
||||
}
|
||||
|
||||
body()
|
||||
}
|
||||
}
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
* Copyright 2010-2022 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.backend.jvm
|
||||
|
||||
import org.jetbrains.kotlin.ir.declarations.IrFactory
|
||||
import org.jetbrains.kotlin.ir.declarations.IrFunction
|
||||
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
|
||||
|
||||
abstract class MemoizedValueClassAbstractReplacements(protected val irFactory: IrFactory, protected val context: JvmBackendContext) {
|
||||
/**
|
||||
* Get a replacement for a function or a constructor.
|
||||
*/
|
||||
abstract val getReplacementFunction: (IrFunction) -> IrSimpleFunction?
|
||||
}
|
||||
@@ -65,6 +65,8 @@ open class IrClassImpl(
|
||||
|
||||
override var inlineClassRepresentation: InlineClassRepresentation<IrSimpleType>? = null
|
||||
|
||||
override var multiFieldValueClassRepresentation: MultiFieldValueClassRepresentation<IrSimpleType>? = null
|
||||
|
||||
override var metadata: MetadataSource? = null
|
||||
|
||||
override var attributeOwnerId: IrAttributeContainer = this
|
||||
|
||||
@@ -40,6 +40,8 @@ abstract class IrClass :
|
||||
|
||||
abstract var inlineClassRepresentation: InlineClassRepresentation<IrSimpleType>?
|
||||
|
||||
abstract var multiFieldValueClassRepresentation: MultiFieldValueClassRepresentation<IrSimpleType>?
|
||||
|
||||
abstract var sealedSubclasses: List<IrClassSymbol>
|
||||
|
||||
override fun <R, D> accept(visitor: IrElementVisitor<R, D>, data: D): R =
|
||||
|
||||
@@ -17,6 +17,7 @@ import org.jetbrains.kotlin.metadata.ProtoBuf
|
||||
import org.jetbrains.kotlin.metadata.deserialization.NameResolver
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedClassDescriptor
|
||||
import org.jetbrains.kotlin.types.SimpleType
|
||||
|
||||
class IrLazyClass(
|
||||
override val startOffset: Int,
|
||||
@@ -101,9 +102,14 @@ class IrLazyClass(
|
||||
}
|
||||
|
||||
override var inlineClassRepresentation: InlineClassRepresentation<IrSimpleType>? by lazyVar(stubGenerator.lock) {
|
||||
descriptor.inlineClassRepresentation?.mapUnderlyingType {
|
||||
it.toIrType() as? IrSimpleType ?: error("Inline class underlying type is not a simple type: ${render()}")
|
||||
}
|
||||
descriptor.inlineClassRepresentation?.mapUnderlyingType(::simplyTypeToIrOrThrow)
|
||||
}
|
||||
|
||||
private fun simplyTypeToIrOrThrow(it: SimpleType) =
|
||||
it.toIrType() as? IrSimpleType ?: error("Inline class underlying type is not a simple type: ${render()}")
|
||||
|
||||
override var multiFieldValueClassRepresentation: MultiFieldValueClassRepresentation<IrSimpleType>? by lazyVar(stubGenerator.lock) {
|
||||
descriptor.multiFieldValueClassRepresentation?.mapUnderlyingType(::simplyTypeToIrOrThrow)
|
||||
}
|
||||
|
||||
override var attributeOwnerId: IrAttributeContainer = this
|
||||
|
||||
@@ -629,6 +629,9 @@ open class IrBasedClassDescriptor(owner: IrClass) : ClassDescriptor, IrBasedDecl
|
||||
override fun getInlineClassRepresentation(): InlineClassRepresentation<SimpleType>? =
|
||||
owner.inlineClassRepresentation?.mapUnderlyingType { it.toIrBasedKotlinType() as SimpleType }
|
||||
|
||||
override fun getMultiFieldValueClassRepresentation(): MultiFieldValueClassRepresentation<SimpleType>? =
|
||||
owner.multiFieldValueClassRepresentation?.mapUnderlyingType { it.toIrBasedKotlinType() as SimpleType }
|
||||
|
||||
override fun getOriginal() = this
|
||||
|
||||
override fun isExpect() = owner.isExpect
|
||||
@@ -759,6 +762,8 @@ open class IrBasedEnumEntryDescriptor(owner: IrEnumEntry) : ClassDescriptor, IrB
|
||||
|
||||
override fun getInlineClassRepresentation(): InlineClassRepresentation<SimpleType>? = TODO("not implemented")
|
||||
|
||||
override fun getMultiFieldValueClassRepresentation(): MultiFieldValueClassRepresentation<SimpleType>? = TODO("not implemented")
|
||||
|
||||
override fun getOriginal() = this
|
||||
|
||||
override fun isExpect() = false
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
// WITH_STDLIB
|
||||
// TARGET_BACKEND: JVM_IR
|
||||
// WORKS_WHEN_VALUE_CLASS
|
||||
// LANGUAGE: +ValueClasses
|
||||
|
||||
@JvmInline
|
||||
value class IC(val x: UInt)
|
||||
|
||||
fun ic(x: IC) = x.x
|
||||
|
||||
@JvmInline
|
||||
value class SimpleMFVC(val x: UInt, val y: IC, val z: String)
|
||||
|
||||
fun smfvc(ic: IC, x: SimpleMFVC, ic1: UInt) = ic(ic) + x.x + ic(x.y) + ic1
|
||||
|
||||
@JvmInline
|
||||
value class GreaterMFVC(val x: SimpleMFVC, val y: IC, val z: SimpleMFVC)
|
||||
|
||||
fun gmfvc(ic: IC, x: GreaterMFVC, ic1: UInt) = smfvc(ic, x.x, 0U) + ic(x.y) + smfvc(IC(0U), x.z, ic1)
|
||||
|
||||
fun box() = "todo"
|
||||
+6
@@ -48207,6 +48207,12 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
|
||||
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/valueClasses"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM_IR, true);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("classFlattening.kt")
|
||||
public void testClassFlattening() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/valueClasses/classFlattening.kt", TransformersFunctions.getReplaceOptionalJvmInlineAnnotationWithReal());
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("equality.kt")
|
||||
public void testEquality() throws Exception {
|
||||
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright 2010-2022 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.descriptors
|
||||
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.types.model.SimpleTypeMarker
|
||||
|
||||
class MultiFieldValueClassRepresentation<Type : SimpleTypeMarker>(
|
||||
val underlyingPropertyNamesToTypes: List<Pair<Name, Type>>
|
||||
) {
|
||||
init {
|
||||
require(underlyingPropertyNamesToTypes.size > 1) { "MultiFieldValueClassRepresentation has at least 2 properties" }
|
||||
}
|
||||
|
||||
private val map = underlyingPropertyNamesToTypes.toMap().also {
|
||||
require(it.size == underlyingPropertyNamesToTypes.size) { "Some properties have the same names" }
|
||||
}
|
||||
|
||||
fun containsPropertyWithName(name: Name): Boolean = name in map
|
||||
fun propertyTypeByName(name: Name): Type? = map[name]
|
||||
|
||||
inline fun <Other : SimpleTypeMarker> mapUnderlyingType(transform: (Type) -> Other): MultiFieldValueClassRepresentation<Other> =
|
||||
MultiFieldValueClassRepresentation(underlyingPropertyNamesToTypes.map { (key, value) -> key to transform(value) })
|
||||
}
|
||||
+2
-2
@@ -9,7 +9,6 @@ import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||
import org.jetbrains.kotlin.builtins.StandardNames
|
||||
import org.jetbrains.kotlin.builtins.jvm.createMappedTypeParametersSubstitution
|
||||
import org.jetbrains.kotlin.descriptors.*
|
||||
import org.jetbrains.kotlin.descriptors.annotations.Annotations
|
||||
import org.jetbrains.kotlin.descriptors.impl.ClassDescriptorBase
|
||||
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
|
||||
import org.jetbrains.kotlin.load.java.FakePureImplementationsProvider
|
||||
@@ -40,7 +39,6 @@ import org.jetbrains.kotlin.types.*
|
||||
import org.jetbrains.kotlin.types.checker.KotlinTypeRefiner
|
||||
import org.jetbrains.kotlin.utils.addIfNotNull
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
|
||||
import java.util.*
|
||||
|
||||
class LazyJavaClassDescriptor(
|
||||
val outerContext: LazyJavaResolverContext,
|
||||
@@ -200,6 +198,8 @@ class LazyJavaClassDescriptor(
|
||||
|
||||
override fun getInlineClassRepresentation(): InlineClassRepresentation<SimpleType>? = null
|
||||
|
||||
override fun getMultiFieldValueClassRepresentation(): MultiFieldValueClassRepresentation<SimpleType>? = null
|
||||
|
||||
override fun toString() = "Lazy Java class ${this.fqNameUnsafe}"
|
||||
|
||||
private inner class LazyJavaClassTypeConstructor : AbstractClassTypeConstructor(c.storageManager) {
|
||||
|
||||
+1
@@ -88,6 +88,7 @@ class FunctionClassDescriptor(
|
||||
override fun getSource(): SourceElement = SourceElement.NO_SOURCE
|
||||
override fun getSealedSubclasses() = emptyList<ClassDescriptor>()
|
||||
override fun getInlineClassRepresentation(): InlineClassRepresentation<SimpleType>? = null
|
||||
override fun getMultiFieldValueClassRepresentation(): MultiFieldValueClassRepresentation<SimpleType>? = null
|
||||
|
||||
override fun getDeclaredTypeParameters() = parameters
|
||||
|
||||
|
||||
@@ -104,6 +104,9 @@ public interface ClassDescriptor extends ClassifierDescriptorWithTypeParameters,
|
||||
@Nullable
|
||||
InlineClassRepresentation<SimpleType> getInlineClassRepresentation();
|
||||
|
||||
@Nullable
|
||||
MultiFieldValueClassRepresentation<SimpleType> getMultiFieldValueClassRepresentation();
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
ClassDescriptor getOriginal();
|
||||
|
||||
@@ -85,6 +85,7 @@ class NotFoundClasses(private val storageManager: StorageManager, private val mo
|
||||
override fun getCompanionObjectDescriptor(): ClassDescriptor? = null
|
||||
override fun getSealedSubclasses(): Collection<ClassDescriptor> = emptyList()
|
||||
override fun getInlineClassRepresentation(): InlineClassRepresentation<SimpleType>? = null
|
||||
override fun getMultiFieldValueClassRepresentation(): MultiFieldValueClassRepresentation<SimpleType>? = null
|
||||
|
||||
override fun toString() = "class $name (not found)"
|
||||
}
|
||||
|
||||
@@ -181,4 +181,10 @@ public class ClassDescriptorImpl extends ClassDescriptorBase {
|
||||
public InlineClassRepresentation<SimpleType> getInlineClassRepresentation() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public MultiFieldValueClassRepresentation<SimpleType> getMultiFieldValueClassRepresentation() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
+6
@@ -199,6 +199,12 @@ public class EnumEntrySyntheticClassDescriptor extends ClassDescriptorBase {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public MultiFieldValueClassRepresentation<SimpleType> getMultiFieldValueClassRepresentation() {
|
||||
return null;
|
||||
}
|
||||
|
||||
private class EnumEntryScope extends MemberScopeImpl {
|
||||
private final MemoizedFunctionToNotNull<Name, Collection<? extends SimpleFunctionDescriptor>> functions;
|
||||
private final MemoizedFunctionToNotNull<Name, Collection<? extends PropertyDescriptor>> properties;
|
||||
|
||||
+19
-1
@@ -5,6 +5,7 @@
|
||||
|
||||
package org.jetbrains.kotlin.descriptors.impl;
|
||||
|
||||
import kotlin.Pair;
|
||||
import kotlin.collections.CollectionsKt;
|
||||
import kotlin.jvm.functions.Function1;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@@ -328,10 +329,27 @@ public class LazySubstitutingClassDescriptor extends ModuleAwareClassDescriptor
|
||||
//noinspection ConstantConditions
|
||||
return representation == null ? null : new InlineClassRepresentation<SimpleType>(
|
||||
representation.getUnderlyingPropertyName(),
|
||||
substituteSimpleType(getInlineClassRepresentation().getUnderlyingType())
|
||||
substituteSimpleType(representation.getUnderlyingType())
|
||||
);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public MultiFieldValueClassRepresentation<SimpleType> getMultiFieldValueClassRepresentation() {
|
||||
MultiFieldValueClassRepresentation<SimpleType> representation = original.getMultiFieldValueClassRepresentation();
|
||||
if (representation == null) {
|
||||
return null;
|
||||
}
|
||||
List<Pair<Name, SimpleType>> underlyingList = new ArrayList<Pair<Name, SimpleType>>(representation.getUnderlyingPropertyNamesToTypes());
|
||||
for (int i = 0; i < underlyingList.size(); i++) {
|
||||
Pair<Name, SimpleType> pair = underlyingList.get(i);
|
||||
Name name = pair.getFirst();
|
||||
SimpleType simpleType = pair.getSecond();
|
||||
underlyingList.set(i, new Pair<Name, SimpleType>(name, substituteSimpleType(simpleType)));
|
||||
}
|
||||
return new MultiFieldValueClassRepresentation<SimpleType>(underlyingList);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public SimpleType getDefaultFunctionTypeForSamInterface() {
|
||||
|
||||
@@ -195,6 +195,12 @@ public class MutableClassDescriptor extends ClassDescriptorBase {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public MultiFieldValueClassRepresentation<SimpleType> getMultiFieldValueClassRepresentation() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return DeclarationDescriptorImpl.toString(this);
|
||||
|
||||
+37
-10
@@ -53,7 +53,12 @@ class DeserializedClassDescriptor(
|
||||
private val typeConstructor = DeserializedClassTypeConstructor()
|
||||
|
||||
private val memberScopeHolder =
|
||||
ScopesHolderForClass.create(this, c.storageManager, c.components.kotlinTypeChecker.kotlinTypeRefiner, this::DeserializedClassMemberScope)
|
||||
ScopesHolderForClass.create(
|
||||
this,
|
||||
c.storageManager,
|
||||
c.components.kotlinTypeChecker.kotlinTypeRefiner,
|
||||
this::DeserializedClassMemberScope
|
||||
)
|
||||
|
||||
private val memberScope get() = memberScopeHolder.getScope(c.components.kotlinTypeChecker.kotlinTypeRefiner)
|
||||
private val enumEntries = if (kind == ClassKind.ENUM_CLASS) EnumEntryClassDescriptors() else null
|
||||
@@ -64,6 +69,8 @@ class DeserializedClassDescriptor(
|
||||
private val companionObjectDescriptor = c.storageManager.createNullableLazyValue { computeCompanionObjectDescriptor() }
|
||||
private val sealedSubclasses = c.storageManager.createLazyValue { computeSubclassesForSealedClass() }
|
||||
private val inlineClassRepresentation = c.storageManager.createNullableLazyValue { computeInlineClassRepresentation() }
|
||||
private val multiFieldValueClassRepresentation =
|
||||
c.storageManager.createNullableLazyValue { computeMultiFieldValueClassRepresentation() }
|
||||
|
||||
internal val thisAsProtoContainer: ProtoContainer.Class = ProtoContainer.Class(
|
||||
classProto, c.nameResolver, c.typeTable, sourceElement,
|
||||
@@ -176,9 +183,11 @@ class DeserializedClassDescriptor(
|
||||
override fun getSealedSubclasses() = sealedSubclasses()
|
||||
|
||||
override fun getInlineClassRepresentation(): InlineClassRepresentation<SimpleType>? = inlineClassRepresentation()
|
||||
override fun getMultiFieldValueClassRepresentation(): MultiFieldValueClassRepresentation<SimpleType>? =
|
||||
multiFieldValueClassRepresentation()
|
||||
|
||||
private fun computeInlineClassRepresentation(): InlineClassRepresentation<SimpleType>? {
|
||||
if (!isInlineOrValueClass()) return null
|
||||
if (!isInlineClass()) return null
|
||||
|
||||
val propertyName = when {
|
||||
classProto.hasInlineClassUnderlyingPropertyName() ->
|
||||
@@ -193,17 +202,35 @@ class DeserializedClassDescriptor(
|
||||
}
|
||||
|
||||
val type = classProto.inlineClassUnderlyingType(c.typeTable)?.let(c.typeDeserializer::simpleType)
|
||||
?: run {
|
||||
val underlyingProperty =
|
||||
memberScope.getContributedVariables(propertyName, NoLookupLocation.FROM_DESERIALIZATION)
|
||||
.singleOrNull { it.extensionReceiverParameter == null }
|
||||
?: error("Inline class has no underlying property: $this")
|
||||
underlyingProperty.type as SimpleType
|
||||
}
|
||||
?: getPropertyTypeFromContributedVariables(propertyName)
|
||||
|
||||
return InlineClassRepresentation(propertyName, type)
|
||||
}
|
||||
|
||||
private fun getPropertyTypeFromContributedVariables(propertyName: Name): SimpleType {
|
||||
val underlyingProperty = memberScope.getContributedVariables(propertyName, NoLookupLocation.FROM_DESERIALIZATION)
|
||||
.singleOrNull { it.extensionReceiverParameter == null } ?: error("Value class has no underlying property: $this")
|
||||
return underlyingProperty.type as SimpleType
|
||||
}
|
||||
|
||||
private fun computeMultiFieldValueClassRepresentation(): MultiFieldValueClassRepresentation<SimpleType>? {
|
||||
if (!isValueClass() || isInlineClass()) return null
|
||||
val representation = classProto.multiFieldValueClassRepresentation
|
||||
?: error("No multiFieldValueClassRepresentation for multi-field value class: $this")
|
||||
val propertyList = representation.propertyList.map { valueClassProperty ->
|
||||
val name: Name = c.nameResolver.getName(valueClassProperty.name)
|
||||
val protoType: ProtoBuf.Type = when {
|
||||
valueClassProperty.hasType() -> valueClassProperty.type
|
||||
valueClassProperty.hasTypeId() -> c.typeTable[valueClassProperty.typeId]
|
||||
else -> error("Unlike inline classes before 1.5.0, multi-field value classes always have intermediate representation")
|
||||
}
|
||||
name to c.typeDeserializer.simpleType(protoType)
|
||||
}
|
||||
require(propertyList.size > 1)
|
||||
require(propertyList.distinctBy { (name, _) -> name }.size == propertyList.size)
|
||||
return MultiFieldValueClassRepresentation(propertyList)
|
||||
}
|
||||
|
||||
override fun toString() =
|
||||
"deserialized ${if (isExpect) "expect " else ""}class $name" // not using descriptor renderer to preserve laziness
|
||||
|
||||
@@ -256,7 +283,7 @@ class DeserializedClassDescriptor(
|
||||
|
||||
private inner class DeserializedClassMemberScope(private val kotlinTypeRefiner: KotlinTypeRefiner) : DeserializedMemberScope(
|
||||
c, classProto.functionList, classProto.propertyList, classProto.typeAliasList,
|
||||
classProto.nestedClassNameList.map(c.nameResolver::getName).let { { it } } // workaround KT-13454
|
||||
classProto.nestedClassNameList.map(c.nameResolver::getName).let { list -> { list } } // workaround KT-13454
|
||||
) {
|
||||
private val classDescriptor: DeserializedClassDescriptor get() = this@DeserializedClassDescriptor
|
||||
|
||||
|
||||
+1
-1
@@ -26,7 +26,7 @@ class JvmMetadataVersion(versionArray: IntArray, val isStrictSemantics: Boolean)
|
||||
|
||||
companion object {
|
||||
@JvmField
|
||||
val INSTANCE = JvmMetadataVersion(1, 7, 0)
|
||||
val INSTANCE = JvmMetadataVersion(1, 7, 1)
|
||||
|
||||
@JvmField
|
||||
val INVALID_VERSION = JvmMetadataVersion()
|
||||
|
||||
@@ -242,6 +242,19 @@ message Class {
|
||||
optional Type inline_class_underlying_type = 18;
|
||||
optional int32 inline_class_underlying_type_id = 19 [(type_id_in_table) = true];
|
||||
|
||||
message MultiFieldValueClassRepresentation {
|
||||
message MultiFieldValueClassProperty {
|
||||
required int32 name = 1;
|
||||
oneof type_representation {
|
||||
Type type = 2;
|
||||
int32 type_id = 3;
|
||||
}
|
||||
}
|
||||
repeated MultiFieldValueClassProperty property = 1;
|
||||
}
|
||||
|
||||
optional MultiFieldValueClassRepresentation multi_field_value_class_representation = 22;
|
||||
|
||||
optional TypeTable type_table = 30;
|
||||
|
||||
// Index into the VersionRequirementTable
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user