[FIR] Properly detect callable reference type according conversions
This commit is contained in:
@@ -42,6 +42,7 @@ object StandardClassIds {
|
||||
val String = "String".baseId()
|
||||
|
||||
val KProperty = "KProperty".reflectId()
|
||||
val KMutableProperty = "KMutableProperty".reflectId()
|
||||
val KProperty0 = "KProperty0".reflectId()
|
||||
val KMutableProperty0 = "KMutableProperty0".reflectId()
|
||||
val KProperty1 = "KProperty1".reflectId()
|
||||
@@ -50,6 +51,7 @@ object StandardClassIds {
|
||||
val KMutableProperty2 = "KMutableProperty2".reflectId()
|
||||
val KFunction = "KFunction".reflectId()
|
||||
val KClass = "KClass".reflectId()
|
||||
val KCallable = "KCallable".reflectId()
|
||||
|
||||
val Comparable = "Comparable".baseId()
|
||||
val Number = "Number".baseId()
|
||||
|
||||
+402
@@ -0,0 +1,402 @@
|
||||
/*
|
||||
* 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.fir.resolve.calls
|
||||
|
||||
import org.jetbrains.kotlin.fir.FirElement
|
||||
import org.jetbrains.kotlin.fir.FirSession
|
||||
import org.jetbrains.kotlin.fir.FirSourceElement
|
||||
import org.jetbrains.kotlin.fir.declarations.*
|
||||
import org.jetbrains.kotlin.fir.expressions.FirAnnotationCall
|
||||
import org.jetbrains.kotlin.fir.expressions.FirExpression
|
||||
import org.jetbrains.kotlin.fir.expressions.FirNamedArgumentExpression
|
||||
import org.jetbrains.kotlin.fir.expressions.FirResolvedQualifier
|
||||
import org.jetbrains.kotlin.fir.expressions.builder.buildNamedArgumentExpression
|
||||
import org.jetbrains.kotlin.fir.resolve.DoubleColonLHS
|
||||
import org.jetbrains.kotlin.fir.resolve.createFunctionalType
|
||||
import org.jetbrains.kotlin.fir.resolve.diagnostics.ConeUnsupportedCallableReferenceTarget
|
||||
import org.jetbrains.kotlin.fir.resolve.inference.extractInputOutputTypesFromCallableReferenceExpectedType
|
||||
import org.jetbrains.kotlin.fir.resolve.inference.isSuspendFunctionType
|
||||
import org.jetbrains.kotlin.fir.symbols.StandardClassIds
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol
|
||||
import org.jetbrains.kotlin.fir.types.*
|
||||
import org.jetbrains.kotlin.fir.visitors.FirTransformer
|
||||
import org.jetbrains.kotlin.fir.visitors.FirVisitor
|
||||
import org.jetbrains.kotlin.resolve.calls.components.SuspendConversionStrategy
|
||||
import org.jetbrains.kotlin.resolve.calls.inference.ConstraintSystemOperation
|
||||
import org.jetbrains.kotlin.resolve.calls.inference.model.SimpleConstraintSystemConstraintPosition
|
||||
import org.jetbrains.kotlin.resolve.calls.tower.isSuccess
|
||||
import org.jetbrains.kotlin.types.expressions.CoercionStrategy
|
||||
|
||||
|
||||
internal object CheckCallableReferenceExpectedType : CheckerStage() {
|
||||
override suspend fun check(candidate: Candidate, callInfo: CallInfo, sink: CheckerSink, context: ResolutionContext) {
|
||||
val outerCsBuilder = callInfo.outerCSBuilder ?: return
|
||||
val expectedType = callInfo.expectedType
|
||||
if (candidate.symbol !is FirCallableSymbol<*>) return
|
||||
|
||||
val resultingReceiverType = when (callInfo.lhs) {
|
||||
is DoubleColonLHS.Type -> callInfo.lhs.type.takeIf { callInfo.explicitReceiver !is FirResolvedQualifier }
|
||||
else -> null
|
||||
}
|
||||
|
||||
val fir: FirCallableDeclaration<*> = candidate.symbol.fir
|
||||
|
||||
val (rawResultingType, callableReferenceAdaptation) = buildReflectionType(fir, resultingReceiverType, callInfo, context)
|
||||
val resultingType = candidate.substitutor.substituteOrSelf(rawResultingType)
|
||||
|
||||
if (callableReferenceAdaptation.needCompatibilityResolveForCallableReference()) {
|
||||
sink.reportDiagnostic(LowerPriorityToPreserveCompatibilityDiagnostic)
|
||||
}
|
||||
|
||||
candidate.resultingTypeForCallableReference = resultingType
|
||||
candidate.usesSuspendConversion =
|
||||
callableReferenceAdaptation?.suspendConversionStrategy == SuspendConversionStrategy.SUSPEND_CONVERSION
|
||||
candidate.outerConstraintBuilderEffect = fun ConstraintSystemOperation.() {
|
||||
addOtherSystem(candidate.system.asReadOnlyStorage())
|
||||
|
||||
val position = SimpleConstraintSystemConstraintPosition //TODO
|
||||
|
||||
if (expectedType != null) {
|
||||
addSubtypeConstraint(resultingType, expectedType, position)
|
||||
}
|
||||
|
||||
val declarationReceiverType: ConeKotlinType? =
|
||||
(fir as? FirCallableMemberDeclaration<*>)?.receiverTypeRef?.coneType
|
||||
?.let(candidate.substitutor::substituteOrSelf)
|
||||
|
||||
if (resultingReceiverType != null && declarationReceiverType != null) {
|
||||
addSubtypeConstraint(resultingReceiverType, declarationReceiverType, position)
|
||||
}
|
||||
}
|
||||
|
||||
var isApplicable = true
|
||||
|
||||
outerCsBuilder.runTransaction {
|
||||
candidate.outerConstraintBuilderEffect!!(this)
|
||||
|
||||
isApplicable = !hasContradiction
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
if (!isApplicable) {
|
||||
sink.yieldDiagnostic(InapplicableCandidate)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
val resultingReceiverType = when (callInfo.lhs) {
|
||||
is DoubleColonLHS.Type -> callInfo.lhs.type.takeIf { callInfo.explicitReceiver !is FirResolvedQualifier }
|
||||
else -> null
|
||||
}
|
||||
*/
|
||||
private fun buildReflectionType(
|
||||
fir: FirCallableDeclaration<*>,
|
||||
receiverType: ConeKotlinType?,
|
||||
callInfo: CallInfo,
|
||||
context: ResolutionContext
|
||||
): Pair<ConeKotlinType, CallableReferenceAdaptation?> {
|
||||
val returnTypeRef = context.bodyResolveComponents.returnTypeCalculator.tryCalculateReturnType(fir)
|
||||
return when (fir) {
|
||||
is FirFunction -> {
|
||||
val unboundReferenceTarget = if (receiverType != null) 1 else 0
|
||||
val callableReferenceAdaptation =
|
||||
getCallableReferenceAdaptation(context.session, fir, callInfo.expectedType, unboundReferenceTarget)
|
||||
|
||||
val parameters = mutableListOf<ConeKotlinType>()
|
||||
|
||||
val returnType = callableReferenceAdaptation?.let {
|
||||
parameters += it.argumentTypes
|
||||
if (it.coercionStrategy == CoercionStrategy.COERCION_TO_UNIT) {
|
||||
context.session.builtinTypes.unitType.type
|
||||
} else {
|
||||
returnTypeRef.coneType
|
||||
}
|
||||
} ?: returnTypeRef.coneType.also {
|
||||
fir.valueParameters.mapTo(parameters) { it.returnTypeRef.coneType }
|
||||
}
|
||||
|
||||
val isSuspend = (fir as? FirSimpleFunction)?.isSuspend == true ||
|
||||
callableReferenceAdaptation?.suspendConversionStrategy == SuspendConversionStrategy.SUSPEND_CONVERSION
|
||||
return createFunctionalType(
|
||||
parameters,
|
||||
receiverType = receiverType,
|
||||
rawReturnType = returnType,
|
||||
isKFunctionType = true,
|
||||
isSuspend = isSuspend
|
||||
) to callableReferenceAdaptation
|
||||
}
|
||||
is FirVariable -> createKPropertyType(fir, receiverType, returnTypeRef) to null
|
||||
else -> ConeClassErrorType(ConeUnsupportedCallableReferenceTarget(fir)) to null
|
||||
}
|
||||
}
|
||||
|
||||
internal class CallableReferenceAdaptation(
|
||||
val argumentTypes: Array<ConeKotlinType>,
|
||||
val coercionStrategy: CoercionStrategy,
|
||||
val defaults: Int,
|
||||
val mappedArguments: Map<FirValueParameter, ResolvedCallArgument>,
|
||||
val suspendConversionStrategy: SuspendConversionStrategy
|
||||
)
|
||||
|
||||
private fun CallableReferenceAdaptation?.needCompatibilityResolveForCallableReference(): Boolean {
|
||||
// KT-13934: check containing declaration for companion object
|
||||
if (this == null) return false
|
||||
return defaults != 0 ||
|
||||
suspendConversionStrategy != SuspendConversionStrategy.NO_CONVERSION ||
|
||||
coercionStrategy != CoercionStrategy.NO_COERCION ||
|
||||
mappedArguments.values.any { it is ResolvedCallArgument.VarargArgument }
|
||||
}
|
||||
|
||||
private fun getCallableReferenceAdaptation(
|
||||
session: FirSession,
|
||||
function: FirFunction<*>,
|
||||
expectedType: ConeKotlinType?,
|
||||
unboundReceiverCount: Int
|
||||
): CallableReferenceAdaptation? {
|
||||
if (expectedType == null) return null
|
||||
|
||||
// Do not adapt references against KCallable type as it's impossible to map defaults/vararg to absent parameters of KCallable
|
||||
if (expectedType.isKCallableType()) return null
|
||||
|
||||
val (inputTypes, returnExpectedType) = extractInputOutputTypesFromCallableReferenceExpectedType(expectedType, session) ?: return null
|
||||
val expectedArgumentsCount = inputTypes.size - unboundReceiverCount
|
||||
if (expectedArgumentsCount < 0) return null
|
||||
|
||||
val fakeArguments = createFakeArgumentsForReference(function, expectedArgumentsCount, inputTypes, unboundReceiverCount)
|
||||
val argumentMapping = mapArguments(fakeArguments, function)
|
||||
if (argumentMapping.diagnostics.any { !it.applicability.isSuccess }) return null
|
||||
|
||||
/**
|
||||
* (A, B, C) -> Unit
|
||||
* fun foo(a: A, b: B = B(), vararg c: C)
|
||||
*/
|
||||
var defaults = 0
|
||||
var varargMappingState = VarargMappingState.UNMAPPED
|
||||
val mappedArguments = linkedMapOf<FirValueParameter, ResolvedCallArgument>()
|
||||
val mappedVarargElements = linkedMapOf<FirValueParameter, MutableList<FirExpression>>()
|
||||
val mappedArgumentTypes = arrayOfNulls<ConeKotlinType?>(fakeArguments.size)
|
||||
|
||||
for ((valueParameter, resolvedArgument) in argumentMapping.parameterToCallArgumentMap) {
|
||||
for (fakeArgument in resolvedArgument.arguments) {
|
||||
val index = fakeArgument.index
|
||||
val substitutedParameter = function.valueParameters.getOrNull(function.indexOf(valueParameter)) ?: continue
|
||||
|
||||
val mappedArgument: ConeKotlinType?
|
||||
if (substitutedParameter.isVararg) {
|
||||
val (varargType, newVarargMappingState) = varargParameterTypeByExpectedParameter(
|
||||
inputTypes[index + unboundReceiverCount],
|
||||
substitutedParameter,
|
||||
varargMappingState
|
||||
)
|
||||
varargMappingState = newVarargMappingState
|
||||
mappedArgument = varargType
|
||||
|
||||
when (newVarargMappingState) {
|
||||
VarargMappingState.MAPPED_WITH_ARRAY -> {
|
||||
// If we've already mapped an argument to this value parameter, it'll always be a type mismatch.
|
||||
mappedArguments[valueParameter] = ResolvedCallArgument.SimpleArgument(fakeArgument)
|
||||
}
|
||||
VarargMappingState.MAPPED_WITH_PLAIN_ARGS -> {
|
||||
mappedVarargElements.getOrPut(valueParameter) { ArrayList() }.add(fakeArgument)
|
||||
}
|
||||
VarargMappingState.UNMAPPED -> {
|
||||
}
|
||||
}
|
||||
} else {
|
||||
mappedArgument = substitutedParameter.returnTypeRef.coneType
|
||||
mappedArguments[valueParameter] = resolvedArgument
|
||||
}
|
||||
|
||||
mappedArgumentTypes[index] = mappedArgument
|
||||
}
|
||||
if (resolvedArgument == ResolvedCallArgument.DefaultArgument) {
|
||||
defaults++
|
||||
mappedArguments[valueParameter] = resolvedArgument
|
||||
}
|
||||
}
|
||||
if (mappedArgumentTypes.any { it == null }) return null
|
||||
|
||||
for ((valueParameter, varargElements) in mappedVarargElements) {
|
||||
mappedArguments[valueParameter] = ResolvedCallArgument.VarargArgument(varargElements)
|
||||
}
|
||||
|
||||
for (valueParameter in function.valueParameters) {
|
||||
if (valueParameter.isVararg && valueParameter !in mappedArguments) {
|
||||
mappedArguments[valueParameter] = ResolvedCallArgument.VarargArgument(emptyList())
|
||||
}
|
||||
}
|
||||
|
||||
val coercionStrategy = if (returnExpectedType.isUnit && !function.returnTypeRef.isUnit)
|
||||
CoercionStrategy.COERCION_TO_UNIT
|
||||
else
|
||||
CoercionStrategy.NO_COERCION
|
||||
|
||||
val adaptedArguments = if (expectedType.isBaseTypeForNumberedReferenceTypes)
|
||||
emptyMap()
|
||||
else
|
||||
mappedArguments
|
||||
|
||||
val suspendConversionStrategy = if ((function as? FirSimpleFunction)?.isSuspend != true && expectedType.isSuspendFunctionType(session))
|
||||
SuspendConversionStrategy.SUSPEND_CONVERSION
|
||||
else
|
||||
SuspendConversionStrategy.NO_CONVERSION
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
return CallableReferenceAdaptation(
|
||||
mappedArgumentTypes as Array<ConeKotlinType>,
|
||||
coercionStrategy,
|
||||
defaults,
|
||||
adaptedArguments,
|
||||
suspendConversionStrategy
|
||||
)
|
||||
}
|
||||
|
||||
fun ConeKotlinType?.isPotentiallyArray(): Boolean =
|
||||
this != null && (this.arrayElementType() != null || this is ConeTypeVariableType)
|
||||
|
||||
private fun varargParameterTypeByExpectedParameter(
|
||||
expectedParameterType: ConeKotlinType,
|
||||
substitutedParameter: FirValueParameter,
|
||||
varargMappingState: VarargMappingState,
|
||||
): Pair<ConeKotlinType?, VarargMappingState> {
|
||||
val elementType = substitutedParameter.returnTypeRef.coneType.arrayElementType()
|
||||
?: error("Vararg parameter $substitutedParameter does not have vararg type")
|
||||
|
||||
return when (varargMappingState) {
|
||||
VarargMappingState.UNMAPPED -> {
|
||||
if (expectedParameterType.isPotentiallyArray()) {
|
||||
elementType.createOutArrayType() to VarargMappingState.MAPPED_WITH_ARRAY
|
||||
} else {
|
||||
elementType to VarargMappingState.MAPPED_WITH_PLAIN_ARGS
|
||||
}
|
||||
}
|
||||
VarargMappingState.MAPPED_WITH_PLAIN_ARGS -> {
|
||||
if (expectedParameterType.isPotentiallyArray())
|
||||
null to VarargMappingState.MAPPED_WITH_PLAIN_ARGS
|
||||
else
|
||||
elementType to VarargMappingState.MAPPED_WITH_PLAIN_ARGS
|
||||
}
|
||||
VarargMappingState.MAPPED_WITH_ARRAY ->
|
||||
null to VarargMappingState.MAPPED_WITH_ARRAY
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private enum class VarargMappingState {
|
||||
UNMAPPED, MAPPED_WITH_PLAIN_ARGS, MAPPED_WITH_ARRAY
|
||||
}
|
||||
|
||||
private fun FirFunction<*>.indexOf(valueParameter: FirValueParameter): Int = valueParameters.indexOf(valueParameter)
|
||||
|
||||
private val ConeKotlinType.isUnit: Boolean
|
||||
get() {
|
||||
val type = this.lowerBoundIfFlexible()
|
||||
if (type.isNullable) return false
|
||||
val classId = type.classId ?: return false
|
||||
return classId == StandardClassIds.Unit
|
||||
}
|
||||
|
||||
private val ConeKotlinType.isBaseTypeForNumberedReferenceTypes: Boolean
|
||||
get() {
|
||||
val classId = lowerBoundIfFlexible().classId ?: return false
|
||||
return when (classId) {
|
||||
StandardClassIds.KProperty,
|
||||
StandardClassIds.KMutableProperty,
|
||||
StandardClassIds.KCallable -> true
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
|
||||
private val FirExpression.index: Int
|
||||
get() = when (this) {
|
||||
is FirNamedArgumentExpression -> expression.index
|
||||
is FirFakeArgumentForCallableReference -> index
|
||||
else -> throw IllegalArgumentException()
|
||||
}
|
||||
|
||||
private fun createFakeArgumentsForReference(
|
||||
function: FirFunction<*>,
|
||||
expectedArgumentCount: Int,
|
||||
inputTypes: List<ConeKotlinType>,
|
||||
unboundReceiverCount: Int
|
||||
): List<FirExpression> {
|
||||
var afterVararg = false
|
||||
var varargComponentType: ConeKotlinType? = null
|
||||
var vararg = false
|
||||
return (0 until expectedArgumentCount).map { index ->
|
||||
val inputType = inputTypes.getOrNull(index + unboundReceiverCount)
|
||||
if (vararg && varargComponentType != inputType) {
|
||||
afterVararg = true
|
||||
}
|
||||
|
||||
val valueParameter = function.valueParameters.getOrNull(index)
|
||||
val name = if (afterVararg && valueParameter?.defaultValue != null)
|
||||
valueParameter.name
|
||||
else
|
||||
null
|
||||
|
||||
if (valueParameter?.isVararg == true) {
|
||||
varargComponentType = inputType
|
||||
vararg = true
|
||||
}
|
||||
if (name != null) {
|
||||
buildNamedArgumentExpression {
|
||||
expression = FirFakeArgumentForCallableReference(index)
|
||||
this.name = name
|
||||
isSpread = false
|
||||
}
|
||||
} else {
|
||||
FirFakeArgumentForCallableReference(index)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class FirFakeArgumentForCallableReference(
|
||||
val index: Int
|
||||
) : FirExpression() {
|
||||
override val source: FirSourceElement?
|
||||
get() = null
|
||||
|
||||
override val typeRef: FirTypeRef
|
||||
get() = error("should not be called")
|
||||
|
||||
override val annotations: List<FirAnnotationCall>
|
||||
get() = error("should not be called")
|
||||
|
||||
override fun replaceTypeRef(newTypeRef: FirTypeRef) {
|
||||
error("should not be called")
|
||||
}
|
||||
|
||||
override fun <D> transformAnnotations(transformer: FirTransformer<D>, data: D): FirNamedArgumentExpression {
|
||||
error("should not be called")
|
||||
}
|
||||
|
||||
override fun <R, D> acceptChildren(visitor: FirVisitor<R, D>, data: D) {
|
||||
error("should not be called")
|
||||
}
|
||||
|
||||
override fun <D> transformChildren(transformer: FirTransformer<D>, data: D): FirElement {
|
||||
error("should not be called")
|
||||
}
|
||||
}
|
||||
|
||||
fun ConeKotlinType.isKCallableType(): Boolean {
|
||||
return this.classId == StandardClassIds.KCallable
|
||||
}
|
||||
|
||||
private fun createKPropertyType(
|
||||
propertyOrField: FirVariable<*>,
|
||||
receiverType: ConeKotlinType?,
|
||||
returnTypeRef: FirResolvedTypeRef
|
||||
): ConeKotlinType {
|
||||
val propertyType = returnTypeRef.type
|
||||
return org.jetbrains.kotlin.fir.resolve.createKPropertyType(
|
||||
receiverType, propertyType, isMutable = propertyOrField.isVar
|
||||
)
|
||||
}
|
||||
+2
@@ -58,3 +58,5 @@ object HiddenCandidate : ResolutionDiagnostic(CandidateApplicability.HIDDEN)
|
||||
object ResolvedWithLowPriority : ResolutionDiagnostic(CandidateApplicability.RESOLVED_LOW_PRIORITY)
|
||||
|
||||
object InapplicableWrongReceiver : ResolutionDiagnostic(CandidateApplicability.INAPPLICABLE_WRONG_RECEIVER)
|
||||
|
||||
object LowerPriorityToPreserveCompatibilityDiagnostic : ResolutionDiagnostic(CandidateApplicability.RESOLVED_NEED_PRESERVE_COMPATIBILITY)
|
||||
|
||||
@@ -7,8 +7,6 @@ package org.jetbrains.kotlin.fir.resolve.calls
|
||||
|
||||
import org.jetbrains.kotlin.fir.*
|
||||
import org.jetbrains.kotlin.fir.declarations.*
|
||||
import org.jetbrains.kotlin.fir.diagnostics.ConeSimpleDiagnostic
|
||||
import org.jetbrains.kotlin.fir.diagnostics.DiagnosticKind
|
||||
import org.jetbrains.kotlin.fir.expressions.FirExpression
|
||||
import org.jetbrains.kotlin.fir.expressions.FirQualifiedAccessExpression
|
||||
import org.jetbrains.kotlin.fir.expressions.FirResolvedQualifier
|
||||
@@ -24,8 +22,6 @@ import org.jetbrains.kotlin.fir.types.*
|
||||
import org.jetbrains.kotlin.name.ClassId
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.resolve.calls.inference.ConstraintSystemOperation
|
||||
import org.jetbrains.kotlin.resolve.calls.inference.model.SimpleConstraintSystemConstraintPosition
|
||||
import org.jetbrains.kotlin.resolve.calls.tasks.ExplicitReceiverKind
|
||||
import org.jetbrains.kotlin.resolve.calls.tasks.ExplicitReceiverKind.*
|
||||
|
||||
@@ -196,165 +192,6 @@ internal object EagerResolveOfCallableReferences : CheckerStage() {
|
||||
}
|
||||
}
|
||||
|
||||
internal object CheckCallableReferenceExpectedType : CheckerStage() {
|
||||
override suspend fun check(candidate: Candidate, callInfo: CallInfo, sink: CheckerSink, context: ResolutionContext) {
|
||||
val outerCsBuilder = callInfo.outerCSBuilder ?: return
|
||||
val expectedType = callInfo.expectedType
|
||||
if (candidate.symbol !is FirCallableSymbol<*>) return
|
||||
|
||||
val resultingReceiverType = when (callInfo.lhs) {
|
||||
is DoubleColonLHS.Type -> callInfo.lhs.type.takeIf { callInfo.explicitReceiver !is FirResolvedQualifier }
|
||||
else -> null
|
||||
}
|
||||
|
||||
val fir: FirCallableDeclaration<*> = candidate.symbol.fir
|
||||
|
||||
val returnTypeRef = context.bodyResolveComponents.returnTypeCalculator.tryCalculateReturnType(fir)
|
||||
// If the expected type is a suspend function type and the current argument of interest is a function reference, we need to do
|
||||
// "suspend conversion." Here, during resolution, we bypass constraint system by making resulting type be KSuspendFunction.
|
||||
// Then, during conversion, we need to create an adapter function and replace the function reference created here with an adapted
|
||||
// callable reference.
|
||||
// TODO: should refer to LanguageVersionSettings.SuspendConversion
|
||||
val requireSuspendConversion = expectedType?.isSuspendFunctionType(callInfo.session) == true
|
||||
val resultingType: ConeKotlinType = when (fir) {
|
||||
is FirFunction -> callInfo.session.createAdaptedKFunctionType(
|
||||
fir,
|
||||
resultingReceiverType, returnTypeRef, expectedParameterTypes = expectedType?.typeArguments,
|
||||
isSuspend = (fir as? FirSimpleFunction)?.isSuspend == true || requireSuspendConversion,
|
||||
expectedReturnType = extractInputOutputTypesFromCallableReferenceExpectedType(expectedType, callInfo.session)?.outputType
|
||||
)
|
||||
is FirVariable<*> -> createKPropertyType(fir, resultingReceiverType, returnTypeRef)
|
||||
else -> ConeKotlinErrorType(ConeSimpleDiagnostic("Unknown callable kind: ${fir::class}", DiagnosticKind.UnknownCallableKind))
|
||||
}.let(candidate.substitutor::substituteOrSelf)
|
||||
candidate.usesSuspendConversion = requireSuspendConversion
|
||||
candidate.resultingTypeForCallableReference = resultingType
|
||||
candidate.outerConstraintBuilderEffect = fun ConstraintSystemOperation.() {
|
||||
addOtherSystem(candidate.system.asReadOnlyStorage())
|
||||
|
||||
val position = SimpleConstraintSystemConstraintPosition //TODO
|
||||
|
||||
if (expectedType != null) {
|
||||
addSubtypeConstraint(resultingType, expectedType, position)
|
||||
}
|
||||
|
||||
val declarationReceiverType: ConeKotlinType? =
|
||||
(fir as? FirCallableMemberDeclaration<*>)?.receiverTypeRef?.coneType
|
||||
?.let(candidate.substitutor::substituteOrSelf)
|
||||
|
||||
if (resultingReceiverType != null && declarationReceiverType != null) {
|
||||
addSubtypeConstraint(resultingReceiverType, declarationReceiverType, position)
|
||||
}
|
||||
}
|
||||
|
||||
var isApplicable = true
|
||||
|
||||
outerCsBuilder.runTransaction {
|
||||
candidate.outerConstraintBuilderEffect!!(this)
|
||||
|
||||
isApplicable = !hasContradiction
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
if (!isApplicable) {
|
||||
sink.yieldDiagnostic(InapplicableCandidate)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun createKPropertyType(
|
||||
propertyOrField: FirVariable<*>,
|
||||
receiverType: ConeKotlinType?,
|
||||
returnTypeRef: FirResolvedTypeRef
|
||||
): ConeKotlinType {
|
||||
val propertyType = returnTypeRef.type
|
||||
return createKPropertyType(
|
||||
receiverType, propertyType, isMutable = propertyOrField.isVar
|
||||
)
|
||||
}
|
||||
|
||||
private fun FirSession.createAdaptedKFunctionType(
|
||||
function: FirFunction<*>,
|
||||
receiverType: ConeKotlinType?,
|
||||
returnTypeRef: FirResolvedTypeRef,
|
||||
expectedParameterTypes: Array<out ConeTypeProjection>?,
|
||||
isSuspend: Boolean,
|
||||
expectedReturnType: ConeKotlinType?
|
||||
): ConeKotlinType {
|
||||
// The similar adaptations: defaults and coercion-to-unit happen at org.jetbrains.kotlin.resolve.calls.components.CallableReferencesCandidateFactory.getCallableReferenceAdaptation
|
||||
val parameterTypes = mutableListOf<ConeKotlinType>()
|
||||
val shift = if (receiverType != null) 1 else 0
|
||||
val expectedParameterNumber =
|
||||
if (expectedParameterTypes == null) null
|
||||
// Drop the last one: return type, and the first one: receiver type, if needed
|
||||
else expectedParameterTypes.size - 1 - shift
|
||||
|
||||
fun ConeKotlinType?.isPotentiallyArray(): Boolean =
|
||||
this != null && (this.arrayElementType() != null || this is ConeTypeVariableType)
|
||||
|
||||
var lastVarargParameter: FirValueParameter? = null
|
||||
for ((index, valueParameter) in function.valueParameters.withIndex()) {
|
||||
// Update the last vararg parameter in preparation for adaptation.
|
||||
if (valueParameter.isVararg) {
|
||||
lastVarargParameter = valueParameter
|
||||
}
|
||||
// Pack value parameters until the expected parameter number is met.
|
||||
if (expectedParameterNumber == null || index < expectedParameterNumber) {
|
||||
// But, if the value parameter is vararg, make sure it matches with the expected parameter type.
|
||||
if (expectedParameterTypes != null && valueParameter.isVararg) {
|
||||
val expectedParameterType = (expectedParameterTypes[index + shift] as? ConeKotlinTypeProjection)?.type
|
||||
if (!expectedParameterType.isPotentiallyArray()) {
|
||||
// Expect an element. Will spread vararg parameter later.
|
||||
continue
|
||||
}
|
||||
}
|
||||
parameterTypes += valueParameter.returnTypeRef.coneType
|
||||
continue
|
||||
}
|
||||
// After expected parameters are fulfilled, a value parameter which doesn't have a default value or isn't vararg should be added to
|
||||
// the resulting type (so that it can reject incompatible function reference). In either case, we can't assume no actual arguments
|
||||
// are given.
|
||||
if (valueParameter.defaultValue == null && !valueParameter.isVararg) {
|
||||
parameterTypes += valueParameter.returnTypeRef.coneType
|
||||
}
|
||||
}
|
||||
|
||||
// If a function with vararg is passed to a place where a spread of elements is expected, we can adapt the function reference to
|
||||
// literally spread such vararg argument. E.g., foo(vararg xs: Char): String => bar(::foo) where bar(f: (Char, Char) -> String)
|
||||
if (expectedParameterNumber != null &&
|
||||
expectedParameterTypes != null &&
|
||||
parameterTypes.size < expectedParameterNumber &&
|
||||
lastVarargParameter != null
|
||||
) {
|
||||
val varargArrayType = lastVarargParameter.returnTypeRef.coneType
|
||||
val varargElementType = varargArrayType.varargElementType()
|
||||
val expectedParameterType = (expectedParameterTypes[parameterTypes.size + shift] as? ConeKotlinTypeProjection)?.type
|
||||
// Expect an array or potentially array (i.e., type variable). Pass vararg parameter as-is.
|
||||
if (expectedParameterType.isPotentiallyArray()) {
|
||||
parameterTypes += varargArrayType
|
||||
} else {
|
||||
// Expect an element. Spread vararg parameter.
|
||||
while (parameterTypes.size < expectedParameterNumber) {
|
||||
parameterTypes += varargElementType
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val returnType =
|
||||
if (expectedReturnType != null && typeContext.run { expectedReturnType.isUnit() })
|
||||
expectedReturnType
|
||||
else
|
||||
returnTypeRef.type
|
||||
|
||||
return createFunctionalType(
|
||||
parameterTypes,
|
||||
receiverType = receiverType,
|
||||
rawReturnType = returnType,
|
||||
isKFunctionType = true,
|
||||
isSuspend = isSuspend
|
||||
)
|
||||
}
|
||||
|
||||
internal object DiscriminateSynthetics : CheckerStage() {
|
||||
override suspend fun check(candidate: Candidate, callInfo: CallInfo, sink: CheckerSink, context: ResolutionContext) {
|
||||
if (candidate.symbol is SyntheticSymbol) {
|
||||
|
||||
+6
@@ -5,7 +5,9 @@
|
||||
|
||||
package org.jetbrains.kotlin.fir.resolve.diagnostics
|
||||
|
||||
import org.jetbrains.kotlin.fir.declarations.FirCallableDeclaration
|
||||
import org.jetbrains.kotlin.fir.diagnostics.ConeDiagnostic
|
||||
import org.jetbrains.kotlin.fir.render
|
||||
import org.jetbrains.kotlin.fir.resolve.calls.Candidate
|
||||
import org.jetbrains.kotlin.fir.symbols.AbstractFirBasedSymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol
|
||||
@@ -80,6 +82,10 @@ class ConeInstanceAccessBeforeSuperCall(val target: String) : ConeDiagnostic() {
|
||||
override val reason: String get() = "Cannot access ''${target}'' before superclass constructor has been called"
|
||||
}
|
||||
|
||||
class ConeUnsupportedCallableReferenceTarget(val fir: FirCallableDeclaration<*>) : ConeDiagnostic() {
|
||||
override val reason: String get() = "Unsupported declaration for callable reference: ${fir.render()}"
|
||||
}
|
||||
|
||||
private fun describeSymbol(symbol: AbstractFirBasedSymbol<*>): String {
|
||||
return when (symbol) {
|
||||
is FirClassLikeSymbol<*> -> symbol.classId.asString()
|
||||
|
||||
@@ -7,6 +7,10 @@ package org.jetbrains.kotlin.fir.types
|
||||
|
||||
import org.jetbrains.kotlin.fir.symbols.StandardClassIds
|
||||
|
||||
fun ConeKotlinType.createOutArrayType(nullable: Boolean = false): ConeKotlinType {
|
||||
return ConeKotlinTypeProjectionOut(this).createArrayOf(nullable)
|
||||
}
|
||||
|
||||
fun ConeTypeProjection.createArrayOf(nullable: Boolean = false): ConeKotlinType {
|
||||
if (this is ConeKotlinTypeProjection) {
|
||||
val type = type.lowerBoundIfFlexible()
|
||||
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
/*
|
||||
* 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.resolve.calls.components
|
||||
|
||||
enum class SuspendConversionStrategy {
|
||||
SUSPEND_CONVERSION, NO_CONVERSION
|
||||
}
|
||||
-4
@@ -88,10 +88,6 @@ class CallableReferenceAdaptation(
|
||||
val suspendConversionStrategy: SuspendConversionStrategy
|
||||
)
|
||||
|
||||
enum class SuspendConversionStrategy {
|
||||
SUSPEND_CONVERSION, NO_CONVERSION
|
||||
}
|
||||
|
||||
/**
|
||||
* cases: class A {}, class B { companion object }, object C, enum class D { E }
|
||||
* A::foo <-> Type
|
||||
|
||||
-1
@@ -1,4 +1,3 @@
|
||||
// IGNORE_BACKEND_FIR: JVM_IR
|
||||
// FILE: test.kt
|
||||
|
||||
fun checkNotEqual(x: Any, y: Any) {
|
||||
|
||||
+2
-2
@@ -26,5 +26,5 @@ fun test() {
|
||||
|
||||
<!UNRESOLVED_REFERENCE!>B::bas<!>
|
||||
|
||||
::fas
|
||||
}
|
||||
<!UNRESOLVED_REFERENCE!>::fas<!>
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ fun test() {
|
||||
usePrimitiveIntArray(::intVararg)
|
||||
<!INAPPLICABLE_CANDIDATE!>useIntArray<!>(<!UNRESOLVED_REFERENCE!>::intVararg<!>)
|
||||
<!INAPPLICABLE_CANDIDATE!>useMixedStringArgs1<!>(<!UNRESOLVED_REFERENCE!>::stringVararg<!>)
|
||||
useMixedStringArgs2(::stringVararg)
|
||||
<!INAPPLICABLE_CANDIDATE!>useMixedStringArgs2<!>(<!UNRESOLVED_REFERENCE!>::stringVararg<!>)
|
||||
<!INAPPLICABLE_CANDIDATE!>useMixedStringArgs3<!>(<!UNRESOLVED_REFERENCE!>::stringVararg<!>)
|
||||
useTwoStringArrays(::stringVararg)
|
||||
<!INAPPLICABLE_CANDIDATE!>useTwoStringArrays<!>(<!UNRESOLVED_REFERENCE!>::stringVararg<!>)
|
||||
}
|
||||
|
||||
+8
-8
@@ -107,26 +107,26 @@ FILE fqName:<root> fileName:/caoWithAdaptationForSam.kt
|
||||
BLOCK type=kotlin.Unit origin=null
|
||||
VAR IR_TEMPORARY_VARIABLE name:tmp_0 type:<root>.A [val]
|
||||
GET_OBJECT 'CLASS OBJECT name:A modality:FINAL visibility:public superTypes:[kotlin.Any]' type=<root>.A
|
||||
VAR IR_TEMPORARY_VARIABLE name:tmp_1 type:kotlin.reflect.KFunction0<kotlin.Int> [val]
|
||||
FUNCTION_REFERENCE 'public final fun withVararg (vararg xs: kotlin.Int): kotlin.Int declared in <root>' type=kotlin.reflect.KFunction0<kotlin.Int> origin=null reflectionTarget=<same>
|
||||
VAR IR_TEMPORARY_VARIABLE name:tmp_1 type:kotlin.reflect.KFunction1<kotlin.IntArray, kotlin.Int> [val]
|
||||
FUNCTION_REFERENCE 'public final fun withVararg (vararg xs: kotlin.Int): kotlin.Int declared in <root>' type=kotlin.reflect.KFunction1<kotlin.IntArray, kotlin.Int> origin=null reflectionTarget=<same>
|
||||
ERROR_CALL 'Unresolved reference: <Inapplicable(INAPPLICABLE): /set>#' type=kotlin.Unit
|
||||
GET_VAR 'val tmp_1: kotlin.reflect.KFunction0<kotlin.Int> [val] declared in <root>.test1' type=kotlin.reflect.KFunction0<kotlin.Int> origin=null
|
||||
GET_VAR 'val tmp_1: kotlin.reflect.KFunction1<kotlin.IntArray, kotlin.Int> [val] declared in <root>.test1' type=kotlin.reflect.KFunction1<kotlin.IntArray, kotlin.Int> origin=null
|
||||
CALL 'public final fun plus (other: kotlin.Int): kotlin.Int [operator] declared in kotlin.Int' type=kotlin.Int origin=null
|
||||
$this: ERROR_CALL 'Unresolved reference: <Inapplicable(INAPPLICABLE): /get>#' type=kotlin.Int
|
||||
GET_VAR 'val tmp_1: kotlin.reflect.KFunction0<kotlin.Int> [val] declared in <root>.test1' type=kotlin.reflect.KFunction0<kotlin.Int> origin=null
|
||||
GET_VAR 'val tmp_1: kotlin.reflect.KFunction1<kotlin.IntArray, kotlin.Int> [val] declared in <root>.test1' type=kotlin.reflect.KFunction1<kotlin.IntArray, kotlin.Int> origin=null
|
||||
other: CONST Int type=kotlin.Int value=1
|
||||
FUN name:test2 visibility:public modality:FINAL <> () returnType:kotlin.Unit
|
||||
BLOCK_BODY
|
||||
BLOCK type=kotlin.Unit origin=null
|
||||
VAR IR_TEMPORARY_VARIABLE name:tmp_2 type:<root>.B [val]
|
||||
GET_OBJECT 'CLASS OBJECT name:B modality:FINAL visibility:public superTypes:[kotlin.Any]' type=<root>.B
|
||||
VAR IR_TEMPORARY_VARIABLE name:tmp_3 type:kotlin.reflect.KFunction0<kotlin.Int> [val]
|
||||
FUNCTION_REFERENCE 'public final fun withVararg (vararg xs: kotlin.Int): kotlin.Int declared in <root>' type=kotlin.reflect.KFunction0<kotlin.Int> origin=null reflectionTarget=<same>
|
||||
VAR IR_TEMPORARY_VARIABLE name:tmp_3 type:kotlin.reflect.KFunction1<kotlin.IntArray, kotlin.Int> [val]
|
||||
FUNCTION_REFERENCE 'public final fun withVararg (vararg xs: kotlin.Int): kotlin.Int declared in <root>' type=kotlin.reflect.KFunction1<kotlin.IntArray, kotlin.Int> origin=null reflectionTarget=<same>
|
||||
ERROR_CALL 'Unresolved reference: <Inapplicable(INAPPLICABLE): /set>#' type=kotlin.Unit
|
||||
GET_VAR 'val tmp_3: kotlin.reflect.KFunction0<kotlin.Int> [val] declared in <root>.test2' type=kotlin.reflect.KFunction0<kotlin.Int> origin=null
|
||||
GET_VAR 'val tmp_3: kotlin.reflect.KFunction1<kotlin.IntArray, kotlin.Int> [val] declared in <root>.test2' type=kotlin.reflect.KFunction1<kotlin.IntArray, kotlin.Int> origin=null
|
||||
CALL 'public final fun plus (other: kotlin.Int): kotlin.Int [operator] declared in kotlin.Int' type=kotlin.Int origin=null
|
||||
$this: ERROR_CALL 'Unresolved reference: <Inapplicable(INAPPLICABLE): /get>#' type=kotlin.Int
|
||||
GET_VAR 'val tmp_3: kotlin.reflect.KFunction0<kotlin.Int> [val] declared in <root>.test2' type=kotlin.reflect.KFunction0<kotlin.Int> origin=null
|
||||
GET_VAR 'val tmp_3: kotlin.reflect.KFunction1<kotlin.IntArray, kotlin.Int> [val] declared in <root>.test2' type=kotlin.reflect.KFunction1<kotlin.IntArray, kotlin.Int> origin=null
|
||||
other: CONST Int type=kotlin.Int value=1
|
||||
FUN name:test3 visibility:public modality:FINAL <> (fn:kotlin.Function1<kotlin.Int, kotlin.Unit>) returnType:kotlin.Unit
|
||||
VALUE_PARAMETER name:fn index:0 type:kotlin.Function1<kotlin.Int, kotlin.Unit>
|
||||
|
||||
-73
@@ -1,73 +0,0 @@
|
||||
FILE fqName:<root> fileName:/funWithDefaultParametersAsKCallableStar.kt
|
||||
FUN name:defaultsOnly visibility:public modality:FINAL <> (x:kotlin.String) returnType:kotlin.Int
|
||||
VALUE_PARAMETER name:x index:0 type:kotlin.String
|
||||
EXPRESSION_BODY
|
||||
CONST String type=kotlin.String value=""
|
||||
BLOCK_BODY
|
||||
RETURN type=kotlin.Nothing from='public final fun defaultsOnly (x: kotlin.String): kotlin.Int declared in <root>'
|
||||
CONST Int type=kotlin.Int value=1
|
||||
FUN name:regularAndDefaults visibility:public modality:FINAL <> (x1:kotlin.String, x2:kotlin.String) returnType:kotlin.Int
|
||||
VALUE_PARAMETER name:x1 index:0 type:kotlin.String
|
||||
VALUE_PARAMETER name:x2 index:1 type:kotlin.String
|
||||
EXPRESSION_BODY
|
||||
CONST String type=kotlin.String value=""
|
||||
BLOCK_BODY
|
||||
RETURN type=kotlin.Nothing from='public final fun regularAndDefaults (x1: kotlin.String, x2: kotlin.String): kotlin.Int declared in <root>'
|
||||
CONST Int type=kotlin.Int value=1
|
||||
FUN name:varargs visibility:public modality:FINAL <> (xs:kotlin.Array<out kotlin.String>) returnType:kotlin.Int
|
||||
VALUE_PARAMETER name:xs index:0 type:kotlin.Array<out kotlin.String> varargElementType:kotlin.String [vararg]
|
||||
BLOCK_BODY
|
||||
RETURN type=kotlin.Nothing from='public final fun varargs (vararg xs: kotlin.String): kotlin.Int declared in <root>'
|
||||
CONST Int type=kotlin.Int value=1
|
||||
CLASS CLASS name:C modality:FINAL visibility:public superTypes:[kotlin.Any]
|
||||
$this: VALUE_PARAMETER INSTANCE_RECEIVER name:<this> type:<root>.C
|
||||
CONSTRUCTOR visibility:public <> (x:kotlin.String) returnType:<root>.C [primary]
|
||||
VALUE_PARAMETER name:x index:0 type:kotlin.String
|
||||
EXPRESSION_BODY
|
||||
CONST String type=kotlin.String value=""
|
||||
BLOCK_BODY
|
||||
DELEGATING_CONSTRUCTOR_CALL 'public constructor <init> () [primary] declared in kotlin.Any'
|
||||
INSTANCE_INITIALIZER_CALL classDescriptor='CLASS CLASS name:C modality:FINAL visibility:public superTypes:[kotlin.Any]'
|
||||
PROPERTY name:x visibility:public modality:FINAL [val]
|
||||
FIELD PROPERTY_BACKING_FIELD name:x type:kotlin.String visibility:private [final]
|
||||
EXPRESSION_BODY
|
||||
GET_VAR 'x: kotlin.String declared in <root>.C.<init>' type=kotlin.String origin=INITIALIZE_PROPERTY_FROM_PARAMETER
|
||||
FUN DEFAULT_PROPERTY_ACCESSOR name:<get-x> visibility:public modality:FINAL <> ($this:<root>.C) returnType:kotlin.String
|
||||
correspondingProperty: PROPERTY name:x visibility:public modality:FINAL [val]
|
||||
$this: VALUE_PARAMETER name:<this> type:<root>.C
|
||||
BLOCK_BODY
|
||||
RETURN type=kotlin.Nothing from='public final fun <get-x> (): kotlin.String declared in <root>.C'
|
||||
GET_FIELD 'FIELD PROPERTY_BACKING_FIELD name:x type:kotlin.String visibility:private [final]' type=kotlin.String origin=null
|
||||
receiver: GET_VAR '<this>: <root>.C declared in <root>.C.<get-x>' type=<root>.C origin=null
|
||||
FUN FAKE_OVERRIDE name:equals visibility:public modality:OPEN <> ($this:kotlin.Any, other:kotlin.Any?) returnType:kotlin.Boolean [fake_override,operator]
|
||||
overridden:
|
||||
public open fun equals (other: kotlin.Any?): kotlin.Boolean [operator] declared in kotlin.Any
|
||||
$this: VALUE_PARAMETER name:<this> type:kotlin.Any
|
||||
VALUE_PARAMETER name:other index:0 type:kotlin.Any?
|
||||
FUN FAKE_OVERRIDE name:hashCode visibility:public modality:OPEN <> ($this:kotlin.Any) returnType:kotlin.Int [fake_override]
|
||||
overridden:
|
||||
public open fun hashCode (): kotlin.Int declared in kotlin.Any
|
||||
$this: VALUE_PARAMETER name:<this> type:kotlin.Any
|
||||
FUN FAKE_OVERRIDE name:toString visibility:public modality:OPEN <> ($this:kotlin.Any) returnType:kotlin.String [fake_override]
|
||||
overridden:
|
||||
public open fun toString (): kotlin.String declared in kotlin.Any
|
||||
$this: VALUE_PARAMETER name:<this> type:kotlin.Any
|
||||
FUN name:useKCallableStar visibility:public modality:FINAL <> (fn:kotlin.reflect.KCallable<*>) returnType:kotlin.Unit
|
||||
VALUE_PARAMETER name:fn index:0 type:kotlin.reflect.KCallable<*>
|
||||
BLOCK_BODY
|
||||
FUN name:testDefaultsOnlyStar visibility:public modality:FINAL <> () returnType:kotlin.Unit
|
||||
BLOCK_BODY
|
||||
CALL 'public final fun useKCallableStar (fn: kotlin.reflect.KCallable<*>): kotlin.Unit declared in <root>' type=kotlin.Unit origin=null
|
||||
fn: FUNCTION_REFERENCE 'public final fun defaultsOnly (x: kotlin.String): kotlin.Int declared in <root>' type=kotlin.reflect.KFunction0<kotlin.Int> origin=null reflectionTarget=<same>
|
||||
FUN name:testRegularAndDefaultsStar visibility:public modality:FINAL <> () returnType:kotlin.Unit
|
||||
BLOCK_BODY
|
||||
CALL 'public final fun useKCallableStar (fn: kotlin.reflect.KCallable<*>): kotlin.Unit declared in <root>' type=kotlin.Unit origin=null
|
||||
fn: FUNCTION_REFERENCE 'public final fun regularAndDefaults (x1: kotlin.String, x2: kotlin.String): kotlin.Int declared in <root>' type=kotlin.reflect.KFunction1<kotlin.String, kotlin.Int> origin=null reflectionTarget=<same>
|
||||
FUN name:testVarargsStar visibility:public modality:FINAL <> () returnType:kotlin.Unit
|
||||
BLOCK_BODY
|
||||
CALL 'public final fun useKCallableStar (fn: kotlin.reflect.KCallable<*>): kotlin.Unit declared in <root>' type=kotlin.Unit origin=null
|
||||
fn: FUNCTION_REFERENCE 'public final fun varargs (vararg xs: kotlin.String): kotlin.Int declared in <root>' type=kotlin.reflect.KFunction0<kotlin.Int> origin=null reflectionTarget=<same>
|
||||
FUN name:testCtorStar visibility:public modality:FINAL <> () returnType:kotlin.Unit
|
||||
BLOCK_BODY
|
||||
CALL 'public final fun useKCallableStar (fn: kotlin.reflect.KCallable<*>): kotlin.Unit declared in <root>' type=kotlin.Unit origin=null
|
||||
fn: FUNCTION_REFERENCE 'public constructor <init> (x: kotlin.String) [primary] declared in <root>.C' type=kotlin.reflect.KFunction0<<root>.C> origin=null reflectionTarget=<same>
|
||||
+1
@@ -1,3 +1,4 @@
|
||||
// FIR_IDENTICAL
|
||||
// WITH_REFLECT
|
||||
import kotlin.reflect.KCallable
|
||||
|
||||
|
||||
Reference in New Issue
Block a user