[NI] Fix input/output types for callable reference atom

Input and output types are crucial for type variable fixation order and
 analysis of postponed arguments (callable references, lambdas).

 Specifically, if there is non-fixed type variable inside input types of
 a callable reference, then we'll postpone resolution for such callable
 reference.

 Initial example with the expected type `KMutableProperty1<*, F>` caused
 problems because input types were computed incorrectly (while there
 aren't input types here)

 #KT-25431 Fixed
This commit is contained in:
Mikhail Zarechenskiy
2018-12-05 12:44:09 +03:00
parent 6ebbb6eae3
commit e8a8318ead
10 changed files with 139 additions and 35 deletions
@@ -169,7 +169,7 @@ class DelegatedPropertyResolver(
} }
private fun KtPsiFactory.createExpressionForProperty(): KtExpression { private fun KtPsiFactory.createExpressionForProperty(): KtExpression {
return createExpression("null as ${KotlinBuiltIns.FQ_NAMES.kProperty.asSingleFqName().asString()}<*>") return createExpression("null as ${KotlinBuiltIns.FQ_NAMES.kPropertyFqName.asString()}<*>")
} }
/* Resolve getValue() or setValue() methods from delegate */ /* Resolve getValue() or setValue() methods from delegate */
@@ -134,7 +134,7 @@ class IrBuiltIns(
val throwableClass = builtIns.throwable.toIrSymbol() val throwableClass = builtIns.throwable.toIrSymbol()
val kCallableClass = builtIns.getBuiltInClassByFqName(KotlinBuiltIns.FQ_NAMES.kCallable.toSafe()).toIrSymbol() val kCallableClass = builtIns.getBuiltInClassByFqName(KotlinBuiltIns.FQ_NAMES.kCallable.toSafe()).toIrSymbol()
val kPropertyClass = builtIns.getBuiltInClassByFqName(KotlinBuiltIns.FQ_NAMES.kProperty.asSingleFqName()).toIrSymbol() val kPropertyClass = builtIns.getBuiltInClassByFqName(KotlinBuiltIns.FQ_NAMES.kPropertyFqName.toSafe()).toIrSymbol()
// TODO switch to IrType // TODO switch to IrType
val primitiveTypes = listOf(bool, char, byte, short, int, long, float, double) val primitiveTypes = listOf(bool, char, byte, short, int, long, float, double)
@@ -5,9 +5,7 @@
package org.jetbrains.kotlin.resolve.calls.components package org.jetbrains.kotlin.resolve.calls.components
import org.jetbrains.kotlin.builtins.ReflectionTypes import org.jetbrains.kotlin.builtins.*
import org.jetbrains.kotlin.builtins.getReturnTypeFromFunctionType
import org.jetbrains.kotlin.builtins.isFunctionType
import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.annotations.Annotations import org.jetbrains.kotlin.descriptors.annotations.Annotations
import org.jetbrains.kotlin.resolve.calls.components.CreateFreshVariablesSubstitutor.createToFreshVariableSubstitutorAndAddInitialConstraints import org.jetbrains.kotlin.resolve.calls.components.CreateFreshVariablesSubstitutor.createToFreshVariableSubstitutorAndAddInitialConstraints
@@ -28,6 +26,7 @@ import org.jetbrains.kotlin.types.*
import org.jetbrains.kotlin.types.expressions.CoercionStrategy import org.jetbrains.kotlin.types.expressions.CoercionStrategy
import org.jetbrains.kotlin.types.typeUtil.immediateSupertypes import org.jetbrains.kotlin.types.typeUtil.immediateSupertypes
import org.jetbrains.kotlin.types.typeUtil.isUnit import org.jetbrains.kotlin.types.typeUtil.isUnit
import org.jetbrains.kotlin.types.typeUtil.supertypes
import org.jetbrains.kotlin.utils.SmartList import org.jetbrains.kotlin.utils.SmartList
import org.jetbrains.kotlin.utils.addIfNotNull import org.jetbrains.kotlin.utils.addIfNotNull
@@ -223,9 +222,9 @@ class CallableReferencesCandidateFactory(
expectedType: UnwrappedType?, expectedType: UnwrappedType?,
unboundReceiverCount: Int unboundReceiverCount: Int
): Triple<Array<KotlinType>, CoercionStrategy, Int>? { ): Triple<Array<KotlinType>, CoercionStrategy, Int>? {
val functionType = getFunctionTypeFromCallableReferenceExpectedType(expectedType) ?: return null val inputOutputTypes = extractInputOutputTypesFromCallableReferenceExpectedType(expectedType) ?: return null
val expectedArgumentCount = functionType.arguments.size - unboundReceiverCount - 1 // 1 -- return type val expectedArgumentCount = inputOutputTypes.inputTypes.size - unboundReceiverCount
if (expectedArgumentCount < 0) return null if (expectedArgumentCount < 0) return null
val fakeArguments = (0..(expectedArgumentCount - 1)).map { FakeKotlinCallArgumentForCallableReference(it) } val fakeArguments = (0..(expectedArgumentCount - 1)).map { FakeKotlinCallArgumentForCallableReference(it) }
@@ -251,7 +250,7 @@ class CallableReferencesCandidateFactory(
if (mappedArguments.any { it == null }) return null if (mappedArguments.any { it == null }) return null
// lower(Unit!) = Unit // lower(Unit!) = Unit
val returnExpectedType = functionType.getReturnTypeFromFunctionType().lowerIfFlexible() val returnExpectedType = inputOutputTypes.outputType
val coercion = if (returnExpectedType.isUnit()) CoercionStrategy.COERCION_TO_UNIT else CoercionStrategy.NO_COERCION val coercion = if (returnExpectedType.isUnit()) CoercionStrategy.COERCION_TO_UNIT else CoercionStrategy.NO_COERCION
@@ -342,14 +341,43 @@ class CallableReferencesCandidateFactory(
} }
} }
fun getFunctionTypeFromCallableReferenceExpectedType(expectedType: UnwrappedType?): UnwrappedType? { data class InputOutputTypes(val inputTypes: List<UnwrappedType>, val outputType: UnwrappedType)
fun extractInputOutputTypesFromCallableReferenceExpectedType(expectedType: UnwrappedType?): InputOutputTypes? {
if (expectedType == null) return null if (expectedType == null) return null
return if (expectedType.isFunctionType) { return when {
expectedType expectedType.isFunctionType ->
} else if (ReflectionTypes.isNumberedKFunctionOrKSuspendFunction(expectedType)) { extractInputOutputTypesFromFunctionType(expectedType)
expectedType.immediateSupertypes().first { it.isFunctionType }.unwrap()
} else { expectedType.isBaseTypeForNumberedReferenceTypes() ->
null InputOutputTypes(emptyList(), expectedType.arguments.single().type.unwrap())
ReflectionTypes.isNumberedKFunctionOrKSuspendFunction(expectedType) -> {
val functionFromSupertype = expectedType.immediateSupertypes().first { it.isFunctionType }.unwrap()
extractInputOutputTypesFromFunctionType(functionFromSupertype)
}
ReflectionTypes.isNumberedKPropertyOrKMutablePropertyType(expectedType) -> {
val functionFromSupertype = expectedType.supertypes().first { it.isFunctionType }.unwrap()
extractInputOutputTypesFromFunctionType(functionFromSupertype)
}
else -> null
} }
} }
private fun UnwrappedType.isBaseTypeForNumberedReferenceTypes(): Boolean =
ReflectionTypes.hasKPropertyTypeFqName(this) ||
ReflectionTypes.hasKMutablePropertyTypeFqName(this) ||
ReflectionTypes.hasKCallableTypeFqName(this)
private fun extractInputOutputTypesFromFunctionType(functionType: UnwrappedType): InputOutputTypes {
val receiver = functionType.getReceiverTypeFromFunctionType()?.unwrap()
val parameters = functionType.getValueParameterTypesFromFunctionType().map { it.type.unwrap() }
val inputTypes = listOfNotNull(receiver) + parameters
val outputType = functionType.getReturnTypeFromFunctionType().unwrap()
return InputOutputTypes(inputTypes, outputType)
}
@@ -5,14 +5,9 @@
package org.jetbrains.kotlin.resolve.calls.model package org.jetbrains.kotlin.resolve.calls.model
import org.jetbrains.kotlin.builtins.getReceiverTypeFromFunctionType
import org.jetbrains.kotlin.builtins.getReturnTypeFromFunctionType
import org.jetbrains.kotlin.builtins.getValueParameterTypesFromFunctionType
import org.jetbrains.kotlin.descriptors.CallableDescriptor import org.jetbrains.kotlin.descriptors.CallableDescriptor
import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor
import org.jetbrains.kotlin.resolve.calls.components.CallableReferenceCandidate import org.jetbrains.kotlin.resolve.calls.components.*
import org.jetbrains.kotlin.resolve.calls.components.TypeArgumentsToParametersMapper
import org.jetbrains.kotlin.resolve.calls.components.getFunctionTypeFromCallableReferenceExpectedType
import org.jetbrains.kotlin.resolve.calls.inference.components.FreshVariableNewTypeSubstitutor import org.jetbrains.kotlin.resolve.calls.inference.components.FreshVariableNewTypeSubstitutor
import org.jetbrains.kotlin.resolve.calls.inference.model.ConstraintStorage import org.jetbrains.kotlin.resolve.calls.inference.model.ConstraintStorage
import org.jetbrains.kotlin.resolve.calls.inference.model.TypeVariableForLambdaReturnType import org.jetbrains.kotlin.resolve.calls.inference.model.TypeVariableForLambdaReturnType
@@ -132,18 +127,10 @@ class ResolvedCallableReferenceAtom(
} }
override val inputTypes: Collection<UnwrappedType> override val inputTypes: Collection<UnwrappedType>
get() { get() = extractInputOutputTypesFromCallableReferenceExpectedType(expectedType)?.inputTypes ?: listOfNotNull(expectedType)
val functionType = getFunctionTypeFromCallableReferenceExpectedType(expectedType) ?: return listOfNotNull(expectedType)
val parameters = functionType.getValueParameterTypesFromFunctionType().map { it.type.unwrap() }
val receiver = functionType.getReceiverTypeFromFunctionType()?.unwrap()
return receiver?.let { parameters + it } ?: parameters
}
override val outputType: UnwrappedType? override val outputType: UnwrappedType?
get() { get() = extractInputOutputTypesFromCallableReferenceExpectedType(expectedType)?.outputType
val functionType = getFunctionTypeFromCallableReferenceExpectedType(expectedType) ?: return null
return functionType.getReturnTypeFromFunctionType().unwrap()
}
} }
class ResolvedCollectionLiteralAtom( class ResolvedCollectionLiteralAtom(
@@ -0,0 +1,27 @@
// !LANGUAGE: +NewInference
// !DIAGNOSTICS: -UNUSED_PARAMETER
import kotlin.reflect.*
interface Parent
class Child : Parent
class ChildHolder(var child: Child)
interface Inv<T>
class Form {
fun <F> get0(field: KMutableProperty<F>): Inv<F> = TODO()
fun <F> get1(field: KProperty<F>): Inv<F> = TODO()
fun <F> get2(field: KCallable<F>): Inv<F> = TODO()
fun <F> get3(field: KMutableProperty1<*, F>): Inv<F> = TODO()
}
fun <T : Parent> radio(field: Inv<T>) {}
fun test(form: Form) {
radio(form.get0(ChildHolder::child))
radio(form.get1(ChildHolder::child))
radio(form.get2(ChildHolder::child))
radio(form.get3(ChildHolder::child))
}
@@ -0,0 +1,42 @@
package
public fun </*0*/ T : Parent> radio(/*0*/ field: Inv<T>): kotlin.Unit
public fun test(/*0*/ form: Form): kotlin.Unit
public final class Child : Parent {
public constructor Child()
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
public final class ChildHolder {
public constructor ChildHolder(/*0*/ child: Child)
public final var child: Child
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
public final class Form {
public constructor Form()
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public final fun </*0*/ F> get0(/*0*/ field: kotlin.reflect.KMutableProperty<F>): Inv<F>
public final fun </*0*/ F> get1(/*0*/ field: kotlin.reflect.KProperty<F>): Inv<F>
public final fun </*0*/ F> get2(/*0*/ field: kotlin.reflect.KCallable<F>): Inv<F>
public final fun </*0*/ F> get3(/*0*/ field: kotlin.reflect.KMutableProperty1<*, F>): Inv<F>
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
public interface Inv</*0*/ T> {
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
public interface Parent {
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
@@ -1681,6 +1681,11 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest {
runTest("compiler/testData/diagnostics/tests/callableReference/sam.kt"); runTest("compiler/testData/diagnostics/tests/callableReference/sam.kt");
} }
@TestMetadata("subtypeArgumentFromRHSForReference.kt")
public void testSubtypeArgumentFromRHSForReference() throws Exception {
runTest("compiler/testData/diagnostics/tests/callableReference/subtypeArgumentFromRHSForReference.kt");
}
@TestMetadata("unused.kt") @TestMetadata("unused.kt")
public void testUnused() throws Exception { public void testUnused() throws Exception {
runTest("compiler/testData/diagnostics/tests/callableReference/unused.kt"); runTest("compiler/testData/diagnostics/tests/callableReference/unused.kt");
@@ -1681,6 +1681,11 @@ public class DiagnosticsUsingJavacTestGenerated extends AbstractDiagnosticsUsing
runTest("compiler/testData/diagnostics/tests/callableReference/sam.kt"); runTest("compiler/testData/diagnostics/tests/callableReference/sam.kt");
} }
@TestMetadata("subtypeArgumentFromRHSForReference.kt")
public void testSubtypeArgumentFromRHSForReference() throws Exception {
runTest("compiler/testData/diagnostics/tests/callableReference/subtypeArgumentFromRHSForReference.kt");
}
@TestMetadata("unused.kt") @TestMetadata("unused.kt")
public void testUnused() throws Exception { public void testUnused() throws Exception {
runTest("compiler/testData/diagnostics/tests/callableReference/unused.kt"); runTest("compiler/testData/diagnostics/tests/callableReference/unused.kt");
@@ -322,7 +322,9 @@ public abstract class KotlinBuiltIns {
public final FqNameUnsafe kMutableProperty0 = reflect("KMutableProperty0"); public final FqNameUnsafe kMutableProperty0 = reflect("KMutableProperty0");
public final FqNameUnsafe kMutableProperty1 = reflect("KMutableProperty1"); public final FqNameUnsafe kMutableProperty1 = reflect("KMutableProperty1");
public final FqNameUnsafe kMutableProperty2 = reflect("KMutableProperty2"); public final FqNameUnsafe kMutableProperty2 = reflect("KMutableProperty2");
public final ClassId kProperty = ClassId.topLevel(reflect("KProperty").toSafe()); public final FqNameUnsafe kPropertyFqName = reflect("KProperty");
public final FqNameUnsafe kMutablePropertyFqName = reflect("KMutableProperty");
public final ClassId kProperty = ClassId.topLevel(kPropertyFqName.toSafe());
public final FqName uByteFqName = fqName("UByte"); public final FqName uByteFqName = fqName("UByte");
public final FqName uShortFqName = fqName("UShort"); public final FqName uShortFqName = fqName("UShort");
@@ -102,8 +102,13 @@ class ReflectionTypes(module: ModuleDescriptor, private val notFoundClasses: Not
isNumberedKPropertyType(type) || isNumberedKMutablePropertyType(type) isNumberedKPropertyType(type) || isNumberedKMutablePropertyType(type)
private fun isKCallableType(type: KotlinType): Boolean = private fun isKCallableType(type: KotlinType): Boolean =
hasFqName(type.constructor, KotlinBuiltIns.FQ_NAMES.kCallable) || hasKCallableTypeFqName(type) || type.constructor.supertypes.any { isKCallableType(it) }
type.constructor.supertypes.any { isKCallableType(it) }
fun hasKCallableTypeFqName(type: KotlinType): Boolean =
hasFqName(type.constructor, KotlinBuiltIns.FQ_NAMES.kCallable)
fun hasKMutablePropertyTypeFqName(type: KotlinType): Boolean =
hasFqName(type.constructor, KotlinBuiltIns.FQ_NAMES.kMutablePropertyFqName)
fun isNumberedKMutablePropertyType(type: KotlinType): Boolean { fun isNumberedKMutablePropertyType(type: KotlinType): Boolean {
val descriptor = type.constructor.declarationDescriptor as? ClassDescriptor ?: return false val descriptor = type.constructor.declarationDescriptor as? ClassDescriptor ?: return false
@@ -112,6 +117,9 @@ class ReflectionTypes(module: ModuleDescriptor, private val notFoundClasses: Not
hasFqName(descriptor, KotlinBuiltIns.FQ_NAMES.kMutableProperty2) hasFqName(descriptor, KotlinBuiltIns.FQ_NAMES.kMutableProperty2)
} }
fun hasKPropertyTypeFqName(type: KotlinType): Boolean =
hasFqName(type.constructor, KotlinBuiltIns.FQ_NAMES.kPropertyFqName)
fun isNumberedKPropertyType(type: KotlinType): Boolean { fun isNumberedKPropertyType(type: KotlinType): Boolean {
val descriptor = type.constructor.declarationDescriptor as? ClassDescriptor ?: return false val descriptor = type.constructor.declarationDescriptor as? ClassDescriptor ?: return false
return hasFqName(descriptor, KotlinBuiltIns.FQ_NAMES.kProperty0) || return hasFqName(descriptor, KotlinBuiltIns.FQ_NAMES.kProperty0) ||