IR: consolidate generation of synthetic members for data class

This commit is contained in:
Jinseong Jeon
2020-04-12 23:08:50 -07:00
committed by Mikhail Glukhikh
parent 1500131eb0
commit c370b86141
54 changed files with 1760 additions and 415 deletions
@@ -65,12 +65,6 @@ fun KtSecondaryConstructor.isConstructorDelegatingToSuper(bindingContext: Bindin
}
}
inline fun ClassDescriptor.findFirstFunction(name: String, predicate: (CallableMemberDescriptor) -> Boolean) =
unsubstitutedMemberScope.findFirstFunction(name, predicate)
inline fun MemberScope.findFirstFunction(name: String, predicate: (CallableMemberDescriptor) -> Boolean) =
getContributedFunctions(Name.identifier(name), NoLookupLocation.FROM_BACKEND).first(predicate)
fun MemberScope.findSingleFunction(name: Name): FunctionDescriptor =
getContributedFunctions(name, NoLookupLocation.FROM_BACKEND).single()
@@ -18,9 +18,7 @@ package org.jetbrains.kotlin.psi2ir.generators
import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.backend.common.DataClassMethodGenerator
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
import org.jetbrains.kotlin.ir.builders.*
import org.jetbrains.kotlin.ir.declarations.*
@@ -29,23 +27,14 @@ import org.jetbrains.kotlin.ir.expressions.IrMemberAccessExpression
import org.jetbrains.kotlin.ir.expressions.impl.IrGetValueImpl
import org.jetbrains.kotlin.ir.expressions.mapTypeParameters
import org.jetbrains.kotlin.ir.expressions.mapValueParameters
import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol
import org.jetbrains.kotlin.ir.util.DataClassMembersGenerator
import org.jetbrains.kotlin.ir.util.declareSimpleFunctionWithOverrides
import org.jetbrains.kotlin.ir.util.properties
import org.jetbrains.kotlin.ir.util.referenceFunction
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.psi.KtClassOrObject
import org.jetbrains.kotlin.psi.KtParameter
import org.jetbrains.kotlin.psi2ir.containsNull
import org.jetbrains.kotlin.psi2ir.endOffsetOrUndefined
import org.jetbrains.kotlin.psi2ir.findFirstFunction
import org.jetbrains.kotlin.psi2ir.startOffsetOrUndefined
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils
import org.jetbrains.kotlin.resolve.scopes.MemberScope
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.checker.KotlinTypeChecker
import org.jetbrains.kotlin.types.typeUtil.representativeUpperBound
class DataClassMembersGenerator(
declarationGenerator: DeclarationGenerator
@@ -99,15 +88,6 @@ class DataClassMembersGenerator(
irDispatchReceiverParameter.symbol
)
}
fun irOther(): IrExpression {
val irFirstParameter = irFunction.valueParameters[0]
return IrGetValueImpl(
startOffset, endOffset,
irFirstParameter.type,
irFirstParameter.symbol
)
}
}
private inner class MyDataClassMethodGenerator(
@@ -115,6 +95,20 @@ class DataClassMembersGenerator(
val irClass: IrClass,
val origin: IrDeclarationOrigin
) : DataClassMethodGenerator(ktClassOrObject, declarationGenerator.context.bindingContext) {
private val irDataClassMembersGenerator = object : DataClassMembersGenerator(context, context.symbolTable, irClass, origin) {
override fun declareSimpleFunction(startOffset: Int, endOffset: Int, functionDescriptor: FunctionDescriptor): IrFunction =
declareSimpleFunction(startOffset, endOffset, origin, functionDescriptor)
override fun generateSyntheticFunctionParameterDeclarations(irFunction: IrFunction) {
FunctionGenerator(declarationGenerator).generateSyntheticFunctionParameterDeclarations(irFunction)
}
override fun commitSubstituted(irMemberAccessExpression: IrMemberAccessExpression, descriptor: CallableDescriptor) {
irMemberAccessExpression.commitSubstituted(descriptor)
}
}
private inline fun buildMember(
function: FunctionDescriptor,
psiElement: PsiElement? = null,
@@ -144,12 +138,9 @@ class DataClassMembersGenerator(
private fun getBackingField(parameter: ValueParameterDescriptor): IrField {
val property = getOrFail(BindingContext.VALUE_PARAMETER_AS_PROPERTY, parameter)
return getBackingField(property)
return irDataClassMembersGenerator.getBackingField(property)
}
private fun getBackingField(property: PropertyDescriptor) =
irClass.properties.single { it.descriptor == property }.backingField!!
override fun generateCopyFunction(function: FunctionDescriptor, constructorParameters: List<KtParameter>) {
if (!irClass.isData) return
@@ -176,147 +167,13 @@ class DataClassMembersGenerator(
}
}
override fun generateEqualsMethod(function: FunctionDescriptor, properties: List<PropertyDescriptor>) {
buildMember(function, declaration) {
val irType = classDescriptor.defaultType.toIrType()
override fun generateEqualsMethod(function: FunctionDescriptor, properties: List<PropertyDescriptor>) =
irDataClassMembersGenerator.generateEqualsMethod(function, properties)
if (!irClass.isInline) {
+irIfThenReturnTrue(irEqeqeq(irThis(), irOther()))
}
+irIfThenReturnFalse(irNotIs(irOther(), irType))
val otherWithCast = irTemporary(irAs(irOther(), irType), "other_with_cast")
for (property in properties) {
val field = getBackingField(property)
val arg1 = irGetField(irThis(), field)
val arg2 = irGetField(irGet(irType, otherWithCast.symbol), field)
+irIfThenReturnFalse(irNotEquals(arg1, arg2))
}
+irReturnTrue()
}
}
override fun generateHashCodeMethod(function: FunctionDescriptor, properties: List<PropertyDescriptor>) =
irDataClassMembersGenerator.generateHashCodeMethod(function, properties)
private val intClass = context.builtIns.int
private val intType = context.builtIns.intType
private val intTimes =
intClass.findFirstFunction("times") { KotlinTypeChecker.DEFAULT.equalTypes(it.valueParameters[0].type, intType) }
.let { context.symbolTable.referenceFunction(it) }
private val intPlus =
intClass.findFirstFunction("plus") { KotlinTypeChecker.DEFAULT.equalTypes(it.valueParameters[0].type, intType) }
.let { context.symbolTable.referenceFunction(it) }
private fun MemberScope.findHashCodeFunctionOrNull() =
getContributedFunctions(Name.identifier("hashCode"), NoLookupLocation.FROM_BACKEND)
.find { it.valueParameters.isEmpty() }
private fun getHashCodeFunction(
type: KotlinType,
symbolResolve: (FunctionDescriptor) -> IrSimpleFunctionSymbol
): IrSimpleFunctionSymbol =
when (val typeConstructorDescriptor = type.constructor.declarationDescriptor) {
is ClassDescriptor ->
if (KotlinBuiltIns.isArrayOrPrimitiveArray(typeConstructorDescriptor))
context.irBuiltIns.dataClassArrayMemberHashCodeSymbol
else
symbolResolve(
type.memberScope.findHashCodeFunctionOrNull()
?: context.builtIns.any.unsubstitutedMemberScope.findHashCodeFunctionOrNull()!!
)
is TypeParameterDescriptor ->
getHashCodeFunction(typeConstructorDescriptor.representativeUpperBound, symbolResolve)
else ->
throw AssertionError("Unexpected type: $type")
}
override fun generateHashCodeMethod(function: FunctionDescriptor, properties: List<PropertyDescriptor>) {
buildMember(function, declaration) {
val irIntType = context.irBuiltIns.intType
var result: IrExpression? = null
for (property in properties) {
val hashCodeOfProperty = getHashCodeOfProperty(property)
result = if (result == null) {
hashCodeOfProperty
} else {
val shiftedResult = irCallOp(intTimes, irIntType, result, irInt(31))
irCallOp(intPlus, irIntType, shiftedResult, hashCodeOfProperty)
}
}
+irReturn(result ?: irInt(0))
}
}
private fun MemberFunctionBuilder.getHashCodeOfProperty(property: PropertyDescriptor): IrExpression {
val field = getBackingField(property)
val propertyType = property.type
return when {
propertyType.containsNull() ->
irIfNull(
context.irBuiltIns.intType,
irGetField(irThis(), field),
irInt(0),
getHashCodeOf(
propertyType,
irGetField(irThis(), field)
)
)
else ->
getHashCodeOf(
propertyType,
irGetField(irThis(), field)
)
}
}
private fun MemberFunctionBuilder.getHashCodeOf(kotlinType: KotlinType, irValue: IrExpression): IrExpression {
var substituted: FunctionDescriptor? = null
val hashCodeFunctionSymbol = getHashCodeFunction(kotlinType) {
substituted = it
declarationGenerator.context.symbolTable.referenceSimpleFunction(it.original)
}
return irCall(hashCodeFunctionSymbol, context.irBuiltIns.intType).apply {
if (hashCodeFunctionSymbol.descriptor.dispatchReceiverParameter != null) {
dispatchReceiver = irValue
} else {
putValueArgument(0, irValue)
}
commitSubstituted(substituted ?: hashCodeFunctionSymbol.descriptor)
}
}
override fun generateToStringMethod(function: FunctionDescriptor, properties: List<PropertyDescriptor>) {
buildMember(function, declaration) {
val irConcat = irConcat()
irConcat.addArgument(irString(classDescriptor.name.asString() + "("))
var first = true
for (property in properties) {
if (!first) irConcat.addArgument(irString(", "))
irConcat.addArgument(irString(property.name.asString() + "="))
val irPropertyValue = irGetField(irThis(), getBackingField(property))
val typeConstructorDescriptor = property.type.constructor.declarationDescriptor
val irPropertyStringValue =
if (typeConstructorDescriptor is ClassDescriptor &&
KotlinBuiltIns.isArrayOrPrimitiveArray(typeConstructorDescriptor)
)
irCall(context.irBuiltIns.dataClassArrayMemberToStringSymbol, context.irBuiltIns.stringType).apply {
putValueArgument(0, irPropertyValue)
}
else
irPropertyValue
irConcat.addArgument(irPropertyStringValue)
first = false
}
irConcat.addArgument(irString(")"))
+irReturn(irConcat)
}
}
override fun generateToStringMethod(function: FunctionDescriptor, properties: List<PropertyDescriptor>) =
irDataClassMembersGenerator.generateToStringMethod(function, properties)
}
}
@@ -22,7 +22,7 @@ import org.jetbrains.kotlin.ir.declarations.addMember
import org.jetbrains.kotlin.ir.expressions.IrSyntheticBodyKind
import org.jetbrains.kotlin.ir.expressions.impl.IrSyntheticBodyImpl
import org.jetbrains.kotlin.ir.util.declareSimpleFunctionWithOverrides
import org.jetbrains.kotlin.psi2ir.findFirstFunction
import org.jetbrains.kotlin.ir.util.findFirstFunction
class EnumClassMembersGenerator(declarationGenerator: DeclarationGenerator) : DeclarationGeneratorExtension(declarationGenerator) {
fun generateSpecialMembers(irClass: IrClass) {
@@ -0,0 +1,318 @@
/*
* Copyright 2010-2020 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.ir.util
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.descriptors.CallableDescriptor
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
import org.jetbrains.kotlin.ir.builders.IrBlockBodyBuilder
import org.jetbrains.kotlin.ir.builders.IrGeneratorContext
import org.jetbrains.kotlin.ir.builders.Scope
import org.jetbrains.kotlin.ir.builders.irAs
import org.jetbrains.kotlin.ir.builders.irCall
import org.jetbrains.kotlin.ir.builders.irCallOp
import org.jetbrains.kotlin.ir.builders.irConcat
import org.jetbrains.kotlin.ir.builders.irEqeqeq
import org.jetbrains.kotlin.ir.builders.irGet
import org.jetbrains.kotlin.ir.builders.irGetField
import org.jetbrains.kotlin.ir.builders.irIfNull
import org.jetbrains.kotlin.ir.builders.irIfThenReturnFalse
import org.jetbrains.kotlin.ir.builders.irIfThenReturnTrue
import org.jetbrains.kotlin.ir.builders.irInt
import org.jetbrains.kotlin.ir.builders.irNotEquals
import org.jetbrains.kotlin.ir.builders.irNotIs
import org.jetbrains.kotlin.ir.builders.irReturn
import org.jetbrains.kotlin.ir.builders.irReturnTrue
import org.jetbrains.kotlin.ir.builders.irString
import org.jetbrains.kotlin.ir.builders.irTemporary
import org.jetbrains.kotlin.ir.declarations.IrClass
import org.jetbrains.kotlin.ir.declarations.IrDeclaration
import org.jetbrains.kotlin.ir.declarations.IrDeclarationOrigin
import org.jetbrains.kotlin.ir.declarations.IrField
import org.jetbrains.kotlin.ir.declarations.IrFunction
import org.jetbrains.kotlin.ir.expressions.IrExpression
import org.jetbrains.kotlin.ir.expressions.IrMemberAccessExpression
import org.jetbrains.kotlin.ir.expressions.impl.IrGetValueImpl
import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol
import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.resolve.scopes.MemberScope
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.checker.KotlinTypeChecker
import org.jetbrains.kotlin.types.isNullable
import org.jetbrains.kotlin.types.typeUtil.representativeUpperBound
abstract class DataClassMembersGenerator(
val context: IrGeneratorContext,
val symbolTable: SymbolTable,
val irClass: IrClass,
val origin: IrDeclarationOrigin
) {
inline fun <T : IrDeclaration> T.buildWithScope(builder: (T) -> Unit): T =
also { irDeclaration ->
symbolTable.withScope(irDeclaration.descriptor) {
builder(irDeclaration)
}
}
private inner class MemberFunctionBuilder(
startOffset: Int = UNDEFINED_OFFSET,
endOffset: Int = UNDEFINED_OFFSET,
val irFunction: IrFunction
) : IrBlockBodyBuilder(context, Scope(irFunction.symbol), startOffset, endOffset) {
inline fun addToClass(builder: MemberFunctionBuilder.(IrFunction) -> Unit): IrFunction {
irFunction.buildWithScope {
builder(irFunction)
irFunction.body = doBuild()
}
irClass.declarations.add(irFunction)
return irFunction
}
fun irThis(): IrExpression {
val irDispatchReceiverParameter = irFunction.dispatchReceiverParameter!!
return IrGetValueImpl(
startOffset, endOffset,
irDispatchReceiverParameter.type,
irDispatchReceiverParameter.symbol
)
}
fun irOther(): IrExpression {
val irFirstParameter = irFunction.valueParameters[0]
return IrGetValueImpl(
startOffset, endOffset,
irFirstParameter.type,
irFirstParameter.symbol
)
}
fun generateEqualsMethodBody(properties: List<PropertyDescriptor>) {
val irType = irClass.defaultType
if (!irClass.isInline) {
+irIfThenReturnTrue(irEqeqeq(irThis(), irOther()))
}
+irIfThenReturnFalse(irNotIs(irOther(), irType))
val otherWithCast = irTemporary(irAs(irOther(), irType), "other_with_cast")
for (property in properties) {
val field = getBackingField(property)
val arg1 = irGetField(irThis(), field)
val arg2 = irGetField(irGet(irType, otherWithCast.symbol), field)
+irIfThenReturnFalse(irNotEquals(arg1, arg2))
}
+irReturnTrue()
}
private val intClass = context.builtIns.int
private val intType = context.builtIns.intType
private val intTimesSymbol: IrFunctionSymbol =
intClass.findFirstFunction("times") { KotlinTypeChecker.DEFAULT.equalTypes(it.valueParameters[0].type, intType) }
.let { symbolTable.referenceFunction(it) }
private val intPlusSymbol: IrFunctionSymbol =
intClass.findFirstFunction("plus") { KotlinTypeChecker.DEFAULT.equalTypes(it.valueParameters[0].type, intType) }
.let { symbolTable.referenceFunction(it) }
fun generateHashCodeMethodBody(properties: List<PropertyDescriptor>) {
val irIntType = context.irBuiltIns.intType
var result: IrExpression? = null
for (property in properties) {
val hashCodeOfProperty = getHashCodeOfProperty(property)
result = if (result == null) {
hashCodeOfProperty
} else {
val shiftedResult = irCallOp(intTimesSymbol, irIntType, result, irInt(31))
irCallOp(intPlusSymbol, irIntType, shiftedResult, hashCodeOfProperty)
}
}
+irReturn(result ?: irInt(0))
}
private fun getHashCodeOfProperty(property: PropertyDescriptor): IrExpression {
val field = getBackingField(property)
return when {
property.type.isNullable() ->
irIfNull(
context.irBuiltIns.intType,
irGetField(irThis(), field),
irInt(0),
getHashCodeOf(property, irGetField(irThis(), field))
)
else ->
getHashCodeOf(property, irGetField(irThis(), field))
}
}
private fun getHashCodeOf(property: PropertyDescriptor, irValue: IrExpression): IrExpression {
var substituted: FunctionDescriptor? = null
val hashCodeFunctionSymbol = getHashCodeFunction(property) {
substituted = it
symbolTable.referenceSimpleFunction(it.original)
}
return irCall(hashCodeFunctionSymbol, context.irBuiltIns.intType).apply {
if (hashCodeFunctionSymbol.descriptor.dispatchReceiverParameter != null) {
dispatchReceiver = irValue
} else {
putValueArgument(0, irValue)
}
commitSubstituted(this, substituted ?: hashCodeFunctionSymbol.descriptor)
}
}
fun generateToStringMethodBody(properties: List<PropertyDescriptor>) {
val irConcat = irConcat()
irConcat.addArgument(irString(irClass.descriptor.name.asString() + "("))
var first = true
for (property in properties) {
if (!first) irConcat.addArgument(irString(", "))
irConcat.addArgument(irString(property.name.asString() + "="))
val irPropertyValue = irGetField(irThis(), getBackingField(property))
val typeConstructorDescriptor = property.type.constructor.declarationDescriptor
val irPropertyStringValue =
if (typeConstructorDescriptor is ClassDescriptor &&
KotlinBuiltIns.isArrayOrPrimitiveArray(typeConstructorDescriptor)
)
irCall(context.irBuiltIns.dataClassArrayMemberToStringSymbol, context.irBuiltIns.stringType).apply {
putValueArgument(0, irPropertyValue)
}
else
irPropertyValue
irConcat.addArgument(irPropertyStringValue)
first = false
}
irConcat.addArgument(irString(")"))
+irReturn(irConcat)
}
}
fun getBackingField(property: PropertyDescriptor): IrField =
irClass.properties.single { it.descriptor == property }.backingField!!
abstract fun declareSimpleFunction(startOffset: Int, endOffset: Int, functionDescriptor: FunctionDescriptor): IrFunction
abstract fun generateSyntheticFunctionParameterDeclarations(irFunction: IrFunction)
// Build a member from a descriptor (psi2ir) as well as its body.
private inline fun buildMember(
function: FunctionDescriptor,
startOffset: Int = UNDEFINED_OFFSET,
endOffset: Int = UNDEFINED_OFFSET,
body: MemberFunctionBuilder.(IrFunction) -> Unit
) {
MemberFunctionBuilder(startOffset, endOffset, declareSimpleFunction(startOffset, endOffset, function)).addToClass { irFunction ->
irFunction.buildWithScope {
generateSyntheticFunctionParameterDeclarations(irFunction)
body(irFunction)
}
}
}
// Use a prebuilt member (fir2ir) and build a member body for it.
private inline fun buildMember(
irFunction: IrFunction,
startOffset: Int = UNDEFINED_OFFSET,
endOffset: Int = UNDEFINED_OFFSET,
body: MemberFunctionBuilder.(IrFunction) -> Unit
) {
MemberFunctionBuilder(startOffset, endOffset, irFunction).addToClass { function ->
function.buildWithScope {
generateSyntheticFunctionParameterDeclarations(function)
body(function)
}
}
}
// Entry for psi2ir
fun generateEqualsMethod(function: FunctionDescriptor, properties: List<PropertyDescriptor>) {
buildMember(function, irClass.startOffset, irClass.endOffset) {
generateEqualsMethodBody(properties)
}
}
// Entry for fir2ir
fun generateEqualsMethod(irFunction: IrFunction, properties: List<PropertyDescriptor>) {
buildMember(irFunction, irClass.startOffset, irClass.endOffset) {
generateEqualsMethodBody(properties)
}
}
private fun MemberScope.findHashCodeFunctionOrNull() =
getContributedFunctions(Name.identifier("hashCode"), NoLookupLocation.FROM_BACKEND)
.find { it.valueParameters.isEmpty() }
private fun getHashCodeFunction(type: KotlinType): FunctionDescriptor =
type.memberScope.findHashCodeFunctionOrNull()
?: context.builtIns.any.unsubstitutedMemberScope.findHashCodeFunctionOrNull()!!
private fun getHashCodeFunction(
type: KotlinType,
symbolResolve: (FunctionDescriptor) -> IrSimpleFunctionSymbol
): IrSimpleFunctionSymbol =
when (val typeConstructorDescriptor = type.constructor.declarationDescriptor) {
is ClassDescriptor ->
if (KotlinBuiltIns.isArrayOrPrimitiveArray(typeConstructorDescriptor))
context.irBuiltIns.dataClassArrayMemberHashCodeSymbol
else
symbolResolve(getHashCodeFunction(type))
is TypeParameterDescriptor ->
getHashCodeFunction(typeConstructorDescriptor.representativeUpperBound, symbolResolve)
else ->
throw AssertionError("Unexpected type: $type")
}
private fun getHashCodeFunction(
property: PropertyDescriptor,
symbolResolve: (FunctionDescriptor) -> IrSimpleFunctionSymbol
): IrSimpleFunctionSymbol =
getHashCodeFunction(property.type, symbolResolve)
abstract fun commitSubstituted(irMemberAccessExpression: IrMemberAccessExpression, descriptor: CallableDescriptor)
// Entry for psi2ir
fun generateHashCodeMethod(function: FunctionDescriptor, properties: List<PropertyDescriptor>) {
buildMember(function, irClass.startOffset, irClass.endOffset) {
generateHashCodeMethodBody(properties)
}
}
// Entry for fir2ir
fun generateHashCodeMethod(irFunction: IrFunction, properties: List<PropertyDescriptor>) {
buildMember(irFunction, irClass.startOffset, irClass.endOffset) {
generateHashCodeMethodBody(properties)
}
}
// Entry for psi2ir
fun generateToStringMethod(function: FunctionDescriptor, properties: List<PropertyDescriptor>) {
buildMember(function, irClass.startOffset, irClass.endOffset) {
generateToStringMethodBody(properties)
}
}
// Entry for fir2ir
fun generateToStringMethod(irFunction: IrFunction, properties: List<PropertyDescriptor>) {
buildMember(irFunction, irClass.startOffset, irClass.endOffset) {
generateToStringMethodBody(properties)
}
}
}
@@ -0,0 +1,18 @@
/*
* Copyright 2010-2020 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.ir.util
import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.resolve.scopes.MemberScope
inline fun ClassDescriptor.findFirstFunction(name: String, predicate: (CallableMemberDescriptor) -> Boolean) =
unsubstitutedMemberScope.findFirstFunction(name, predicate)
inline fun MemberScope.findFirstFunction(name: String, predicate: (CallableMemberDescriptor) -> Boolean) =
getContributedFunctions(Name.identifier(name), NoLookupLocation.FROM_BACKEND).first(predicate)