diff --git a/compiler/fir/cones/src/org/jetbrains/kotlin/fir/symbols/StandardClassIds.kt b/compiler/fir/cones/src/org/jetbrains/kotlin/fir/symbols/StandardClassIds.kt index a0acab10fc5..e0a900d4ef7 100644 --- a/compiler/fir/cones/src/org/jetbrains/kotlin/fir/symbols/StandardClassIds.kt +++ b/compiler/fir/cones/src/org/jetbrains/kotlin/fir/symbols/StandardClassIds.kt @@ -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() diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/CallableReferenceResolution.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/CallableReferenceResolution.kt new file mode 100644 index 00000000000..9f017e4e17b --- /dev/null +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/CallableReferenceResolution.kt @@ -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 { + 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() + + 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, + val coercionStrategy: CoercionStrategy, + val defaults: Int, + val mappedArguments: Map, + 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() + val mappedVarargElements = linkedMapOf>() + val mappedArgumentTypes = arrayOfNulls(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, + 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 { + 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, + unboundReceiverCount: Int +): List { + 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 + get() = error("should not be called") + + override fun replaceTypeRef(newTypeRef: FirTypeRef) { + error("should not be called") + } + + override fun transformAnnotations(transformer: FirTransformer, data: D): FirNamedArgumentExpression { + error("should not be called") + } + + override fun acceptChildren(visitor: FirVisitor, data: D) { + error("should not be called") + } + + override fun transformChildren(transformer: FirTransformer, 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 + ) +} diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/ResolutionDiagnostic.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/ResolutionDiagnostic.kt index eec75c61f95..3e6e23a6798 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/ResolutionDiagnostic.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/ResolutionDiagnostic.kt @@ -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) diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/ResolutionStages.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/ResolutionStages.kt index 1507c68cc56..fe739ec3ab0 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/ResolutionStages.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/ResolutionStages.kt @@ -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?, - 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() - 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) { diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/diagnostics/FirDiagnostics.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/diagnostics/FirDiagnostics.kt index 39a779cc0ab..209f3c17e30 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/diagnostics/FirDiagnostics.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/diagnostics/FirDiagnostics.kt @@ -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() diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/types/ArrayUtils.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/types/ArrayUtils.kt index e653e332dfc..63ac3569a2c 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/types/ArrayUtils.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/types/ArrayUtils.kt @@ -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() diff --git a/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/components/SuspendConversionStrategy.kt b/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/components/SuspendConversionStrategy.kt new file mode 100644 index 00000000000..349450c18b3 --- /dev/null +++ b/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/components/SuspendConversionStrategy.kt @@ -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 +} diff --git a/compiler/resolution/src/org/jetbrains/kotlin/types/expressions/CoercionStrategy.java b/compiler/resolution.common/src/org/jetbrains/kotlin/types/expressions/CoercionStrategy.java similarity index 100% rename from compiler/resolution/src/org/jetbrains/kotlin/types/expressions/CoercionStrategy.java rename to compiler/resolution.common/src/org/jetbrains/kotlin/types/expressions/CoercionStrategy.java diff --git a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/CallableReferenceResolution.kt b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/CallableReferenceResolution.kt index b455fd75f9c..9c22e82402d 100644 --- a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/CallableReferenceResolution.kt +++ b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/CallableReferenceResolution.kt @@ -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 diff --git a/compiler/testData/codegen/box/callableReference/equality/varargAsArrayWithDefaults.kt b/compiler/testData/codegen/box/callableReference/equality/varargAsArrayWithDefaults.kt index bec2e0d7716..5b7f9a6d41b 100644 --- a/compiler/testData/codegen/box/callableReference/equality/varargAsArrayWithDefaults.kt +++ b/compiler/testData/codegen/box/callableReference/equality/varargAsArrayWithDefaults.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND_FIR: JVM_IR // FILE: test.kt fun checkNotEqual(x: Any, y: Any) { diff --git a/compiler/testData/diagnostics/tests/callableReference/resolve/overloads.fir.kt b/compiler/testData/diagnostics/tests/callableReference/resolve/overloads.fir.kt index d67e86ca269..cbd6c75128d 100644 --- a/compiler/testData/diagnostics/tests/callableReference/resolve/overloads.fir.kt +++ b/compiler/testData/diagnostics/tests/callableReference/resolve/overloads.fir.kt @@ -26,5 +26,5 @@ fun test() { B::bas - ::fas -} \ No newline at end of file + ::fas +} diff --git a/compiler/testData/diagnostics/tests/varargs/varargViewedAsArray.fir.kt b/compiler/testData/diagnostics/tests/varargs/varargViewedAsArray.fir.kt index 44ae0640029..e8c30e4d3bb 100644 --- a/compiler/testData/diagnostics/tests/varargs/varargViewedAsArray.fir.kt +++ b/compiler/testData/diagnostics/tests/varargs/varargViewedAsArray.fir.kt @@ -23,7 +23,7 @@ fun test() { usePrimitiveIntArray(::intVararg) useIntArray(::intVararg) useMixedStringArgs1(::stringVararg) - useMixedStringArgs2(::stringVararg) + useMixedStringArgs2(::stringVararg) useMixedStringArgs3(::stringVararg) - useTwoStringArrays(::stringVararg) + useTwoStringArrays(::stringVararg) } diff --git a/compiler/testData/ir/irText/expressions/callableReferences/caoWithAdaptationForSam.fir.txt b/compiler/testData/ir/irText/expressions/callableReferences/caoWithAdaptationForSam.fir.txt index 012bc43aff6..1c43268e701 100644 --- a/compiler/testData/ir/irText/expressions/callableReferences/caoWithAdaptationForSam.fir.txt +++ b/compiler/testData/ir/irText/expressions/callableReferences/caoWithAdaptationForSam.fir.txt @@ -107,26 +107,26 @@ FILE fqName: fileName:/caoWithAdaptationForSam.kt BLOCK type=kotlin.Unit origin=null VAR IR_TEMPORARY_VARIABLE name:tmp_0 type:.A [val] GET_OBJECT 'CLASS OBJECT name:A modality:FINAL visibility:public superTypes:[kotlin.Any]' type=.A - VAR IR_TEMPORARY_VARIABLE name:tmp_1 type:kotlin.reflect.KFunction0 [val] - FUNCTION_REFERENCE 'public final fun withVararg (vararg xs: kotlin.Int): kotlin.Int declared in ' type=kotlin.reflect.KFunction0 origin=null reflectionTarget= + VAR IR_TEMPORARY_VARIABLE name:tmp_1 type:kotlin.reflect.KFunction1 [val] + FUNCTION_REFERENCE 'public final fun withVararg (vararg xs: kotlin.Int): kotlin.Int declared in ' type=kotlin.reflect.KFunction1 origin=null reflectionTarget= ERROR_CALL 'Unresolved reference: #' type=kotlin.Unit - GET_VAR 'val tmp_1: kotlin.reflect.KFunction0 [val] declared in .test1' type=kotlin.reflect.KFunction0 origin=null + GET_VAR 'val tmp_1: kotlin.reflect.KFunction1 [val] declared in .test1' type=kotlin.reflect.KFunction1 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: #' type=kotlin.Int - GET_VAR 'val tmp_1: kotlin.reflect.KFunction0 [val] declared in .test1' type=kotlin.reflect.KFunction0 origin=null + GET_VAR 'val tmp_1: kotlin.reflect.KFunction1 [val] declared in .test1' type=kotlin.reflect.KFunction1 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:.B [val] GET_OBJECT 'CLASS OBJECT name:B modality:FINAL visibility:public superTypes:[kotlin.Any]' type=.B - VAR IR_TEMPORARY_VARIABLE name:tmp_3 type:kotlin.reflect.KFunction0 [val] - FUNCTION_REFERENCE 'public final fun withVararg (vararg xs: kotlin.Int): kotlin.Int declared in ' type=kotlin.reflect.KFunction0 origin=null reflectionTarget= + VAR IR_TEMPORARY_VARIABLE name:tmp_3 type:kotlin.reflect.KFunction1 [val] + FUNCTION_REFERENCE 'public final fun withVararg (vararg xs: kotlin.Int): kotlin.Int declared in ' type=kotlin.reflect.KFunction1 origin=null reflectionTarget= ERROR_CALL 'Unresolved reference: #' type=kotlin.Unit - GET_VAR 'val tmp_3: kotlin.reflect.KFunction0 [val] declared in .test2' type=kotlin.reflect.KFunction0 origin=null + GET_VAR 'val tmp_3: kotlin.reflect.KFunction1 [val] declared in .test2' type=kotlin.reflect.KFunction1 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: #' type=kotlin.Int - GET_VAR 'val tmp_3: kotlin.reflect.KFunction0 [val] declared in .test2' type=kotlin.reflect.KFunction0 origin=null + GET_VAR 'val tmp_3: kotlin.reflect.KFunction1 [val] declared in .test2' type=kotlin.reflect.KFunction1 origin=null other: CONST Int type=kotlin.Int value=1 FUN name:test3 visibility:public modality:FINAL <> (fn:kotlin.Function1) returnType:kotlin.Unit VALUE_PARAMETER name:fn index:0 type:kotlin.Function1 diff --git a/compiler/testData/ir/irText/expressions/callableReferences/funWithDefaultParametersAsKCallableStar.fir.txt b/compiler/testData/ir/irText/expressions/callableReferences/funWithDefaultParametersAsKCallableStar.fir.txt deleted file mode 100644 index 900c3b98109..00000000000 --- a/compiler/testData/ir/irText/expressions/callableReferences/funWithDefaultParametersAsKCallableStar.fir.txt +++ /dev/null @@ -1,73 +0,0 @@ -FILE fqName: 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 ' - 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 ' - CONST Int type=kotlin.Int value=1 - FUN name:varargs visibility:public modality:FINAL <> (xs:kotlin.Array) returnType:kotlin.Int - VALUE_PARAMETER name:xs index:0 type:kotlin.Array varargElementType:kotlin.String [vararg] - BLOCK_BODY - RETURN type=kotlin.Nothing from='public final fun varargs (vararg xs: kotlin.String): kotlin.Int declared in ' - CONST Int type=kotlin.Int value=1 - CLASS CLASS name:C modality:FINAL visibility:public superTypes:[kotlin.Any] - $this: VALUE_PARAMETER INSTANCE_RECEIVER name: type:.C - CONSTRUCTOR visibility:public <> (x:kotlin.String) returnType:.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 () [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 .C.' type=kotlin.String origin=INITIALIZE_PROPERTY_FROM_PARAMETER - FUN DEFAULT_PROPERTY_ACCESSOR name: visibility:public modality:FINAL <> ($this:.C) returnType:kotlin.String - correspondingProperty: PROPERTY name:x visibility:public modality:FINAL [val] - $this: VALUE_PARAMETER name: type:.C - BLOCK_BODY - RETURN type=kotlin.Nothing from='public final fun (): kotlin.String declared in .C' - GET_FIELD 'FIELD PROPERTY_BACKING_FIELD name:x type:kotlin.String visibility:private [final]' type=kotlin.String origin=null - receiver: GET_VAR ': .C declared in .C.' type=.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: 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: 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: 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 ' type=kotlin.Unit origin=null - fn: FUNCTION_REFERENCE 'public final fun defaultsOnly (x: kotlin.String): kotlin.Int declared in ' type=kotlin.reflect.KFunction0 origin=null reflectionTarget= - 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 ' type=kotlin.Unit origin=null - fn: FUNCTION_REFERENCE 'public final fun regularAndDefaults (x1: kotlin.String, x2: kotlin.String): kotlin.Int declared in ' type=kotlin.reflect.KFunction1 origin=null reflectionTarget= - 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 ' type=kotlin.Unit origin=null - fn: FUNCTION_REFERENCE 'public final fun varargs (vararg xs: kotlin.String): kotlin.Int declared in ' type=kotlin.reflect.KFunction0 origin=null reflectionTarget= - 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 ' type=kotlin.Unit origin=null - fn: FUNCTION_REFERENCE 'public constructor (x: kotlin.String) [primary] declared in .C' type=kotlin.reflect.KFunction0<.C> origin=null reflectionTarget= diff --git a/compiler/testData/ir/irText/expressions/callableReferences/funWithDefaultParametersAsKCallableStar.kt b/compiler/testData/ir/irText/expressions/callableReferences/funWithDefaultParametersAsKCallableStar.kt index 5455d83efa3..89d24482ed8 100644 --- a/compiler/testData/ir/irText/expressions/callableReferences/funWithDefaultParametersAsKCallableStar.kt +++ b/compiler/testData/ir/irText/expressions/callableReferences/funWithDefaultParametersAsKCallableStar.kt @@ -1,3 +1,4 @@ +// FIR_IDENTICAL // WITH_REFLECT import kotlin.reflect.KCallable