[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 {
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 */
@@ -134,7 +134,7 @@ class IrBuiltIns(
val throwableClass = builtIns.throwable.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
val primitiveTypes = listOf(bool, char, byte, short, int, long, float, double)
@@ -5,9 +5,7 @@
package org.jetbrains.kotlin.resolve.calls.components
import org.jetbrains.kotlin.builtins.ReflectionTypes
import org.jetbrains.kotlin.builtins.getReturnTypeFromFunctionType
import org.jetbrains.kotlin.builtins.isFunctionType
import org.jetbrains.kotlin.builtins.*
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.annotations.Annotations
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.typeUtil.immediateSupertypes
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.addIfNotNull
@@ -223,9 +222,9 @@ class CallableReferencesCandidateFactory(
expectedType: UnwrappedType?,
unboundReceiverCount: 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
val fakeArguments = (0..(expectedArgumentCount - 1)).map { FakeKotlinCallArgumentForCallableReference(it) }
@@ -251,7 +250,7 @@ class CallableReferencesCandidateFactory(
if (mappedArguments.any { it == null }) return null
// 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
@@ -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
return if (expectedType.isFunctionType) {
expectedType
} else if (ReflectionTypes.isNumberedKFunctionOrKSuspendFunction(expectedType)) {
expectedType.immediateSupertypes().first { it.isFunctionType }.unwrap()
} else {
null
return when {
expectedType.isFunctionType ->
extractInputOutputTypesFromFunctionType(expectedType)
expectedType.isBaseTypeForNumberedReferenceTypes() ->
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
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.ValueParameterDescriptor
import org.jetbrains.kotlin.resolve.calls.components.CallableReferenceCandidate
import org.jetbrains.kotlin.resolve.calls.components.TypeArgumentsToParametersMapper
import org.jetbrains.kotlin.resolve.calls.components.getFunctionTypeFromCallableReferenceExpectedType
import org.jetbrains.kotlin.resolve.calls.components.*
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.TypeVariableForLambdaReturnType
@@ -132,18 +127,10 @@ class ResolvedCallableReferenceAtom(
}
override val inputTypes: Collection<UnwrappedType>
get() {
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
}
get() = extractInputOutputTypesFromCallableReferenceExpectedType(expectedType)?.inputTypes ?: listOfNotNull(expectedType)
override val outputType: UnwrappedType?
get() {
val functionType = getFunctionTypeFromCallableReferenceExpectedType(expectedType) ?: return null
return functionType.getReturnTypeFromFunctionType().unwrap()
}
get() = extractInputOutputTypesFromCallableReferenceExpectedType(expectedType)?.outputType
}
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");
}
@TestMetadata("subtypeArgumentFromRHSForReference.kt")
public void testSubtypeArgumentFromRHSForReference() throws Exception {
runTest("compiler/testData/diagnostics/tests/callableReference/subtypeArgumentFromRHSForReference.kt");
}
@TestMetadata("unused.kt")
public void testUnused() throws Exception {
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");
}
@TestMetadata("subtypeArgumentFromRHSForReference.kt")
public void testSubtypeArgumentFromRHSForReference() throws Exception {
runTest("compiler/testData/diagnostics/tests/callableReference/subtypeArgumentFromRHSForReference.kt");
}
@TestMetadata("unused.kt")
public void testUnused() throws Exception {
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 kMutableProperty1 = reflect("KMutableProperty1");
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 uShortFqName = fqName("UShort");
@@ -102,8 +102,13 @@ class ReflectionTypes(module: ModuleDescriptor, private val notFoundClasses: Not
isNumberedKPropertyType(type) || isNumberedKMutablePropertyType(type)
private fun isKCallableType(type: KotlinType): Boolean =
hasFqName(type.constructor, KotlinBuiltIns.FQ_NAMES.kCallable) ||
type.constructor.supertypes.any { isKCallableType(it) }
hasKCallableTypeFqName(type) || 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 {
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)
}
fun hasKPropertyTypeFqName(type: KotlinType): Boolean =
hasFqName(type.constructor, KotlinBuiltIns.FQ_NAMES.kPropertyFqName)
fun isNumberedKPropertyType(type: KotlinType): Boolean {
val descriptor = type.constructor.declarationDescriptor as? ClassDescriptor ?: return false
return hasFqName(descriptor, KotlinBuiltIns.FQ_NAMES.kProperty0) ||