diff --git a/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/IrBuiltInsOverFir.kt b/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/IrBuiltInsOverFir.kt index d2c553baa6d..a22a56e9d99 100644 --- a/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/IrBuiltInsOverFir.kt +++ b/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/IrBuiltInsOverFir.kt @@ -454,15 +454,31 @@ class IrBuiltInsOverFir( } override val extensionToString: IrSimpleFunctionSymbol by lazy { - findFunctions(kotlinPackage, Name.identifier("toString")).first { function -> + findFunctions(kotlinPackage, OperatorNameConventions.TO_STRING).single { function -> function.owner.extensionReceiverParameter?.let { receiver -> receiver.type == anyNType } ?: false } } - override val stringPlus: IrSimpleFunctionSymbol - get() = intClass.functions.single { - it.owner.name == OperatorNameConventions.PLUS && it.owner.valueParameters[0].type == stringType + override val memberToString: IrSimpleFunctionSymbol by lazy { + findBuiltInClassMemberFunctions(anyClass, OperatorNameConventions.TO_STRING).single { function -> + function.owner.valueParameters.isEmpty() } + } + + override val extensionStringPlus: IrSimpleFunctionSymbol by lazy { + findFunctions(kotlinPackage, OperatorNameConventions.PLUS).single { function -> + val isStringExtension = + function.owner.extensionReceiverParameter?.let { receiver -> receiver.type == stringType.withHasQuestionMark(true) } + ?: false + isStringExtension && function.owner.valueParameters.size == 1 && function.owner.valueParameters[0].type == anyNType + } + } + + override val memberStringPlus: IrSimpleFunctionSymbol by lazy { + findBuiltInClassMemberFunctions(stringClass, OperatorNameConventions.PLUS).single { function -> + function.owner.valueParameters.size == 1 && function.owner.valueParameters[0].type == anyNType + } + } private class KotlinPackageFuns( val arrayOf: IrSimpleFunctionSymbol, diff --git a/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/lazy/Fir2IrLazyClass.kt b/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/lazy/Fir2IrLazyClass.kt index 970a14f6d05..cbfe88c6d25 100644 --- a/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/lazy/Fir2IrLazyClass.kt +++ b/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/lazy/Fir2IrLazyClass.kt @@ -27,6 +27,7 @@ import org.jetbrains.kotlin.ir.types.IrSimpleType import org.jetbrains.kotlin.ir.types.IrType import org.jetbrains.kotlin.ir.types.impl.IrSimpleTypeImpl import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.util.OperatorNameConventions class Fir2IrLazyClass( components: Fir2IrComponents, @@ -214,9 +215,9 @@ class Fir2IrLazyClass( private fun FirNamedFunctionSymbol.isAbstractMethodOfAny(): Boolean { val fir = fir if (fir.modality != Modality.ABSTRACT) return false - return when (fir.name.asString()) { - "equals" -> fir.valueParameters.singleOrNull()?.returnTypeRef?.isNullableAny == true - "hashCode", "toString" -> fir.valueParameters.isEmpty() + return when (fir.name) { + OperatorNameConventions.EQUALS -> fir.valueParameters.singleOrNull()?.returnTypeRef?.isNullableAny == true + OperatorNameConventions.HASH_CODE, OperatorNameConventions.TO_STRING -> fir.valueParameters.isEmpty() else -> false } } diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/FunctionsFromAny.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/FunctionsFromAny.kt index ff2c838fb45..ed917bcfb34 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/FunctionsFromAny.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/FunctionsFromAny.kt @@ -16,8 +16,8 @@ import org.jetbrains.kotlin.util.OperatorNameConventions object FunctionsFromAny { val EQUALS_METHOD_NAME = OperatorNameConventions.EQUALS - val HASH_CODE_METHOD_NAME = Name.identifier("hashCode") - val TO_STRING_METHOD_NAME = Name.identifier("toString") + val HASH_CODE_METHOD_NAME = OperatorNameConventions.HASH_CODE + val TO_STRING_METHOD_NAME = OperatorNameConventions.TO_STRING fun addFunctionFromAnyIfNeeded( thisDescriptor: ClassDescriptor, diff --git a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/ir/Ir.kt b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/ir/Ir.kt index 00ab95378a7..8a32e8125b3 100644 --- a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/ir/Ir.kt +++ b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/ir/Ir.kt @@ -18,9 +18,7 @@ import org.jetbrains.kotlin.ir.symbols.IrClassSymbol import org.jetbrains.kotlin.ir.symbols.IrClassifierSymbol import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol -import org.jetbrains.kotlin.ir.types.IrType -import org.jetbrains.kotlin.ir.types.classOrNull -import org.jetbrains.kotlin.ir.types.defaultType +import org.jetbrains.kotlin.ir.types.* import org.jetbrains.kotlin.ir.util.ReferenceSymbolTable import org.jetbrains.kotlin.ir.util.defaultType import org.jetbrains.kotlin.ir.util.fqNameWhenAvailable @@ -187,7 +185,20 @@ open class BuiltinSymbolsBase(val irBuiltIns: IrBuiltIns, private val symbolTabl fun kmutableproperty2(): IrClassSymbol = irBuiltIns.kMutableProperty2Class val extensionToString: IrSimpleFunctionSymbol get() = irBuiltIns.extensionToString - val stringPlus: IrSimpleFunctionSymbol get() = irBuiltIns.stringPlus + val memberToString: IrSimpleFunctionSymbol get() = irBuiltIns.memberToString + val extensionStringPlus: IrSimpleFunctionSymbol get() = irBuiltIns.extensionStringPlus + val memberStringPlus: IrSimpleFunctionSymbol get() = irBuiltIns.memberStringPlus + + fun isStringPlus(functionSymbol: IrFunctionSymbol): Boolean { + val plusSymbol = if (functionSymbol.owner.dispatchReceiverParameter?.type?.isString() == true) + memberStringPlus + else if (functionSymbol.owner.extensionReceiverParameter?.type?.isNullableString() == true) + extensionStringPlus + else + return false + + return functionSymbol == plusSymbol + } } // Some symbols below are used in kotlin-native, so they can't be private diff --git a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/ir/IrUtils.kt b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/ir/IrUtils.kt index dab4b721ba0..1e38dc7577a 100644 --- a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/ir/IrUtils.kt +++ b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/ir/IrUtils.kt @@ -36,6 +36,7 @@ import org.jetbrains.kotlin.ir.util.* import org.jetbrains.kotlin.ir.visitors.IrElementVisitor import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.name.SpecialNames +import org.jetbrains.kotlin.util.OperatorNameConventions import java.io.StringWriter fun ir2string(ir: IrElement?): String = ir?.render() ?: "" @@ -399,9 +400,9 @@ fun isElseBranch(branch: IrBranch) = branch is IrElseBranch || ((branch.conditio fun IrFunction.isMethodOfAny(): Boolean = extensionReceiverParameter == null && dispatchReceiverParameter != null && - when (name.asString()) { - "hashCode", "toString" -> valueParameters.isEmpty() - "equals" -> valueParameters.singleOrNull()?.type?.isNullableAny() == true + when (name) { + OperatorNameConventions.HASH_CODE, OperatorNameConventions.TO_STRING -> valueParameters.isEmpty() + OperatorNameConventions.EQUALS -> valueParameters.singleOrNull()?.type?.isNullableAny() == true else -> false } diff --git a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/FlattenStringConcatenationLowering.kt b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/FlattenStringConcatenationLowering.kt index c5141058ce7..1185ae3b415 100644 --- a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/FlattenStringConcatenationLowering.kt +++ b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/FlattenStringConcatenationLowering.kt @@ -22,6 +22,7 @@ import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid +import org.jetbrains.kotlin.util.OperatorNameConventions val flattenStringConcatenationPhase = makeIrFilePhase( ::FlattenStringConcatenationLowering, @@ -86,14 +87,14 @@ class FlattenStringConcatenationLowering(val context: CommonBackendContext) : Fi && receiver.type.isStringClassType() && function.returnType.isStringClassType() && function.valueParameters.size == 1 - && function.name.asString() == "plus" + && function.name == OperatorNameConventions.PLUS && function.fqNameWhenAvailable?.parent() in PARENT_NAMES } /** @return true if the function is Any.toString or an override of Any.toString */ val IrSimpleFunction.isToString: Boolean get() { - if (name.asString() != "toString" || valueParameters.size != 0 || !returnType.isString()) + if (name != OperatorNameConventions.TO_STRING || valueParameters.isNotEmpty() || !returnType.isString()) return false return (dispatchReceiverParameter != null && extensionReceiverParameter == null @@ -103,7 +104,7 @@ class FlattenStringConcatenationLowering(val context: CommonBackendContext) : Fi /** @return true if the function is Any?.toString */ private val IrSimpleFunction.isNullableToString: Boolean get() { - if (name.asString() != "toString" || valueParameters.size != 0 || !returnType.isString()) + if (name != OperatorNameConventions.TO_STRING || valueParameters.isNotEmpty() || !returnType.isString()) return false return dispatchReceiverParameter == null diff --git a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/StringConcatenationLowering.kt b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/StringConcatenationLowering.kt index 258c4c09670..a240d4f8718 100644 --- a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/StringConcatenationLowering.kt +++ b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/StringConcatenationLowering.kt @@ -55,10 +55,6 @@ class StringConcatenationLowering(context: CommonBackendContext) : FileLoweringP it.valueParameters.isEmpty() } - private val toStringFunction = stringBuilder.functions.single { - it.valueParameters.isEmpty() && it.name == OperatorNameConventions.TO_STRING - } - private val defaultAppendFunction = stringBuilder.functions.single { it.name == nameAppend && it.valueParameters.size == 1 && @@ -90,22 +86,19 @@ class StringConcatenationLowering(context: CommonBackendContext) : FileLoweringP builder.irCall(symbols.extensionToString).apply { extensionReceiver = argument } - else builder.irCall( - irBuiltIns.anyClass.functions - .single { it.owner.name == OperatorNameConventions.TO_STRING }).apply { + else builder.irCall(symbols.memberToString).apply { dispatchReceiver = argument } } arguments.size == 2 && arguments[0].type.isStringClassType() -> if (arguments[0].type.isNullable()) - builder.irCall(symbols.stringPlus).apply { + builder.irCall(symbols.extensionStringPlus).apply { extensionReceiver = arguments[0] putValueArgument(0, arguments[1]) } else - builder.irCall(symbols.string.functions - .single { it.owner.name == OperatorNameConventions.PLUS }).apply { + builder.irCall(symbols.memberStringPlus).apply { dispatchReceiver = arguments[0] putValueArgument(0, arguments[1]) } @@ -119,7 +112,7 @@ class StringConcatenationLowering(context: CommonBackendContext) : FileLoweringP putValueArgument(0, arg) } } - +irCall(toStringFunction).apply { + +irCall(symbols.memberToString).apply { dispatchReceiver = irGet(stringBuilderImpl) } } diff --git a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/IrBuiltIns.kt b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/IrBuiltIns.kt index 8db6bb46eb4..738f4d05923 100644 --- a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/IrBuiltIns.kt +++ b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/IrBuiltIns.kt @@ -16,8 +16,6 @@ import org.jetbrains.kotlin.ir.symbols.IrClassSymbol import org.jetbrains.kotlin.ir.symbols.IrClassifierSymbol import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol import org.jetbrains.kotlin.ir.types.IrType -import org.jetbrains.kotlin.ir.types.classifierOrNull -import org.jetbrains.kotlin.ir.util.SymbolTable import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.Name @@ -142,7 +140,10 @@ abstract class IrBuiltIns { abstract val intXorSymbol: IrSimpleFunctionSymbol abstract val extensionToString: IrSimpleFunctionSymbol - abstract val stringPlus: IrSimpleFunctionSymbol + abstract val memberToString: IrSimpleFunctionSymbol + + abstract val extensionStringPlus: IrSimpleFunctionSymbol + abstract val memberStringPlus: IrSimpleFunctionSymbol abstract val arrayOf: IrSimpleFunctionSymbol abstract val arrayOfNulls: IrSimpleFunctionSymbol diff --git a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/descriptors/IrBuiltInsOverDescriptors.kt b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/descriptors/IrBuiltInsOverDescriptors.kt index b4e66ccd1a4..fc642c78feb 100644 --- a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/descriptors/IrBuiltInsOverDescriptors.kt +++ b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/descriptors/IrBuiltInsOverDescriptors.kt @@ -39,6 +39,7 @@ import org.jetbrains.kotlin.resolve.scopes.MemberScope import org.jetbrains.kotlin.storage.LockBasedStorageManager import org.jetbrains.kotlin.types.* import org.jetbrains.kotlin.types.checker.KotlinTypeChecker +import org.jetbrains.kotlin.util.OperatorNameConventions @ObsoleteDescriptorBasedAPI class IrBuiltInsOverDescriptors( @@ -502,14 +503,22 @@ class IrBuiltInsOverDescriptors( } else null } - override val extensionToString: IrSimpleFunctionSymbol = findFunctions(Name.identifier("toString"), "kotlin").first { + override val extensionToString: IrSimpleFunctionSymbol = findFunctions(OperatorNameConventions.TO_STRING, "kotlin").first { val descriptor = it.descriptor descriptor is SimpleFunctionDescriptor && descriptor.dispatchReceiverParameter == null && descriptor.extensionReceiverParameter != null && - KotlinBuiltIns.isNullableAny(descriptor.extensionReceiverParameter!!.type) && descriptor.valueParameters.size == 0 + KotlinBuiltIns.isNullableAny(descriptor.extensionReceiverParameter!!.type) && descriptor.valueParameters.isEmpty() } - override val stringPlus: IrSimpleFunctionSymbol = findFunctions(Name.identifier("plus"), "kotlin").first { + override val memberToString: IrSimpleFunctionSymbol = findBuiltInClassMemberFunctions( + anyClass, + OperatorNameConventions.TO_STRING + ).single { + val descriptor = it.descriptor + descriptor is SimpleFunctionDescriptor && descriptor.valueParameters.isEmpty() + } + + override val extensionStringPlus: IrSimpleFunctionSymbol = findFunctions(OperatorNameConventions.PLUS, "kotlin").first { val descriptor = it.descriptor descriptor is SimpleFunctionDescriptor && descriptor.dispatchReceiverParameter == null && descriptor.extensionReceiverParameter != null && @@ -518,6 +527,16 @@ class IrBuiltInsOverDescriptors( KotlinBuiltIns.isNullableAny(descriptor.valueParameters.first().type) } + override val memberStringPlus: IrSimpleFunctionSymbol = findBuiltInClassMemberFunctions( + stringClass, + OperatorNameConventions.PLUS + ).single { + val descriptor = it.descriptor + descriptor is SimpleFunctionDescriptor && + descriptor.valueParameters.size == 1 && + KotlinBuiltIns.isNullableAny(descriptor.valueParameters.first().type) + } + override fun functionN(arity: Int): IrClass = functionFactory.functionN(arity) override fun kFunctionN(arity: Int): IrClass = functionFactory.kFunctionN(arity) override fun suspendFunctionN(arity: Int): IrClass = functionFactory.suspendFunctionN(arity) diff --git a/core/compiler.common/src/org/jetbrains/kotlin/util/OperatorNameConventions.kt b/core/compiler.common/src/org/jetbrains/kotlin/util/OperatorNameConventions.kt index ae4495e2187..5c90f78b444 100644 --- a/core/compiler.common/src/org/jetbrains/kotlin/util/OperatorNameConventions.kt +++ b/core/compiler.common/src/org/jetbrains/kotlin/util/OperatorNameConventions.kt @@ -24,6 +24,7 @@ object OperatorNameConventions { @JvmField val PROVIDE_DELEGATE = Name.identifier("provideDelegate") @JvmField val EQUALS = Name.identifier("equals") + @JvmField val HASH_CODE = Name.identifier("hashCode") @JvmField val COMPARE_TO = Name.identifier("compareTo") @JvmField val CONTAINS = Name.identifier("contains") @JvmField val INVOKE = Name.identifier("invoke")