Type parameter as result type: more precise expected type definition #KT-5385 Fixed

Tests for KT-5385 KT-10223 KT-7800 KT-7817 + something related
This commit is contained in:
Mikhail Glukhikh
2015-11-27 18:59:02 +03:00
parent 51abb021bc
commit 5729855e4f
24 changed files with 352 additions and 13 deletions
@@ -52,21 +52,23 @@ public fun hasUnknownReturnType(type: KotlinType): Boolean {
return ErrorUtils.containsErrorType(getReturnTypeForCallable(type))
}
public fun replaceReturnTypeByUnknown(type: KotlinType): KotlinType {
public fun replaceReturnTypeForCallable(type: KotlinType, given: KotlinType): KotlinType {
assert(ReflectionTypes.isCallableType(type)) { "type $type is not a function or property" }
val newArguments = Lists.newArrayList<TypeProjection>()
newArguments.addAll(getParameterArgumentsOfCallableType(type))
newArguments.add(TypeProjectionImpl(Variance.INVARIANT, DONT_CARE))
newArguments.add(TypeProjectionImpl(Variance.INVARIANT, given))
return replaceTypeArguments(type, newArguments)
}
public fun replaceReturnTypeByUnknown(type: KotlinType) = replaceReturnTypeForCallable(type, DONT_CARE)
private fun replaceTypeArguments(type: KotlinType, newArguments: List<TypeProjection>) =
KotlinTypeImpl.create(type.getAnnotations(), type.getConstructor(), type.isMarkedNullable(), newArguments, type.getMemberScope())
private fun getParameterArgumentsOfCallableType(type: KotlinType) =
type.getArguments().dropLast(1)
private fun getReturnTypeForCallable(type: KotlinType) =
fun getReturnTypeForCallable(type: KotlinType) =
type.getArguments().last().getType()
private fun CallableDescriptor.hasReturnTypeDependentOnUninferredParams(constraintSystem: ConstraintSystem): Boolean {
@@ -210,7 +210,8 @@ class GenericCandidateResolver(private val argumentTypeResolver: ArgumentTypeRes
for (valueArgument in resolvedValueArgument.arguments) {
valueArgument.getArgumentExpression()?.let { argumentExpression ->
ArgumentTypeResolver.getFunctionLiteralArgumentIfAny(argumentExpression, context)?.let { functionLiteral ->
addConstraintForFunctionLiteral(functionLiteral, valueArgument, valueParameterDescriptor, constraintSystem, context)
addConstraintForFunctionLiteralArgument(functionLiteral, valueArgument, valueParameterDescriptor, constraintSystem, context,
resolvedCall.candidateDescriptor.returnType)
}
ArgumentTypeResolver.getCallableReferenceExpressionIfAny(argumentExpression, context)?.let { callableReference ->
addConstraintForCallableReference(callableReference, valueArgument, valueParameterDescriptor, constraintSystem, context)
@@ -223,12 +224,30 @@ class GenericCandidateResolver(private val argumentTypeResolver: ArgumentTypeRes
resolvedCall.setResultingSubstitutor(resultingSystem.resultingSubstitutor)
}
private fun <D : CallableDescriptor> addConstraintForFunctionLiteral(
// See KT-5385
// When literal returns T, and it's an argument of a function that also returns T,
// and we have some expected type Type, we can expected from literal to return Type
// Otherwise we do not care about literal's exact return type
private fun estimateLiteralReturnType(
context: CallCandidateResolutionContext<*>,
literalExpectedType: KotlinType,
ownerReturnType: KotlinType?
) = if (!TypeUtils.noExpectedType(context.expectedType) &&
ownerReturnType != null &&
TypeUtils.isTypeParameter(ownerReturnType) &&
KotlinBuiltIns.isFunctionOrExtensionFunctionType(literalExpectedType) &&
getReturnTypeForCallable(literalExpectedType) == ownerReturnType)
context.expectedType
else DONT_CARE
private fun <D : CallableDescriptor> addConstraintForFunctionLiteralArgument(
functionLiteral: KtFunction,
valueArgument: ValueArgument,
valueParameterDescriptor: ValueParameterDescriptor,
constraintSystem: ConstraintSystem.Builder,
context: CallCandidateResolutionContext<D>
context: CallCandidateResolutionContext<D>,
argumentOwnerReturnType: KotlinType?
) {
val argumentExpression = valueArgument.getArgumentExpression() ?: return
@@ -269,8 +288,9 @@ class GenericCandidateResolver(private val argumentTypeResolver: ArgumentTypeRes
return
}
}
val expectedTypeWithoutReturnType = replaceReturnTypeByUnknown(expectedType)
val newContext = context.replaceExpectedType(expectedTypeWithoutReturnType).replaceDataFlowInfo(dataFlowInfoForArgument)
val estimatedReturnType = estimateLiteralReturnType(context, effectiveExpectedType, argumentOwnerReturnType)
val expectedTypeWithEstimatedReturnType = replaceReturnTypeForCallable(expectedType, estimatedReturnType)
val newContext = context.replaceExpectedType(expectedTypeWithEstimatedReturnType).replaceDataFlowInfo(dataFlowInfoForArgument)
.replaceContextDependency(INDEPENDENT)
val type = argumentTypeResolver.getFunctionLiteralTypeInfo(argumentExpression, functionLiteral, newContext, RESOLVE_FUNCTION_ARGUMENTS).type
constraintSystem.addSubtypeConstraint(type, effectiveExpectedTypeInSystem, position)
@@ -0,0 +1,12 @@
class Foo(val s: String)
fun foo(): Foo? = Foo("OK")
fun <T> run(f: () -> T): T = f()
val foo: Foo = run {
val x = foo()
if (x == null) throw Exception()
x
}
fun box() = foo.s
@@ -11,6 +11,6 @@ fun test(bal: Array<Int>) {
val <!UNUSED_VARIABLE!>e<!>: Unit = run { bar += 4 }
val <!UNUSED_VARIABLE!>f<!>: Int = <!TYPE_INFERENCE_EXPECTED_TYPE_MISMATCH!>run { bar += 4 }<!>
val <!UNUSED_VARIABLE!>f<!>: Int = run { <!ASSIGNMENT_TYPE_MISMATCH!>bar += 4<!> }
}
fun <T> run(f: () -> T): T = f()
@@ -7,10 +7,10 @@ val a : (Int?) -> Int = l@ {
fun <R> let(<!UNUSED_PARAMETER!>f<!>: (Int?) -> R): R = null!!
val b: Int = <!TYPE_INFERENCE_EXPECTED_TYPE_MISMATCH!>let {
if (it != null) return@let it
val b: Int = let {
if (it != null) return@let <!DEBUG_INFO_SMARTCAST!>it<!>
5
}<!>
}
val c: Int = let {
if (it != null) <!DEBUG_INFO_SMARTCAST!>it<!> else 5
@@ -13,6 +13,6 @@ fun <T, G> A<T>.foo(x: (T)-> G): G {
fun main(args: Array<String>) {
val a = A(1)
val t: String = a.<!TYPE_INFERENCE_EXPECTED_TYPE_MISMATCH!>foo({p -> p})<!>
val t: String = a.<!TYPE_INFERENCE_EXPECTED_TYPE_MISMATCH!>foo({p -> <!TYPE_MISMATCH!>p<!>})<!>
checkSubtype<String>(t)
}
@@ -0,0 +1,12 @@
interface Foo
fun foo(): Foo? = null
fun <T> run(f: () -> T): T = f()
val foo: Foo = run {
run {
val x = foo()
if (x == null) throw Exception()
<!DEBUG_INFO_SMARTCAST!>x<!>
}
}
@@ -0,0 +1,11 @@
package
public val foo: Foo
public fun foo(): Foo?
public fun </*0*/ T> run(/*0*/ f: () -> T): T
public interface Foo {
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
}
@@ -0,0 +1,19 @@
class Indexed<T>(val x: T, val y: Int)
class Value<out T>(val x: T)
interface WithValue<out T> {
fun value(): Value<T>
}
class Singleton<T>(val x: T) : WithValue<T> {
override fun value() = Value(x)
}
class WithValueIndexed<T>(val f: () -> Value<T>) : WithValue<Indexed<T>> {
override fun value() = Value(Indexed(f().x, 0))
}
fun <T> Singleton<out T>.indexed(): WithValue<Indexed<T>> {
return WithValueIndexed { value() }
}
@@ -0,0 +1,45 @@
package
public fun </*0*/ T> Singleton<out T>.indexed(): WithValue<Indexed<T>>
public final class Indexed</*0*/ T> {
public constructor Indexed</*0*/ T>(/*0*/ x: T, /*1*/ y: kotlin.Int)
public final val x: T
public final val y: kotlin.Int
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 Singleton</*0*/ T> : WithValue<T> {
public constructor Singleton</*0*/ T>(/*0*/ x: T)
public final val x: 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 open override /*1*/ fun value(): Value<T>
}
public final class Value</*0*/ out T> {
public constructor Value</*0*/ out T>(/*0*/ x: T)
public final val x: 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 WithValue</*0*/ out 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 abstract fun value(): Value<T>
}
public final class WithValueIndexed</*0*/ T> : WithValue<Indexed<T>> {
public constructor WithValueIndexed</*0*/ T>(/*0*/ f: () -> Value<T>)
public final val f: () -> Value<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 open override /*1*/ fun value(): Value<Indexed<T>>
}
@@ -0,0 +1,25 @@
interface Foo
interface Bar : Foo
fun foo(): Foo? = null
fun bar(): Bar? = null
fun <T : Bar> run(f: () -> T): T = f()
val foo: Foo = run {
val x = bar()
if (x == null) throw Exception()
<!DEBUG_INFO_SMARTCAST!>x<!>
}
val foofoo: Foo = <!TYPE_INFERENCE_UPPER_BOUND_VIOLATED!>run<!> {
val x = foo()
if (x == null) throw Exception()
<!DEBUG_INFO_SMARTCAST!>x<!>
}
val bar: Bar = <!TYPE_INFERENCE_CONFLICTING_SUBSTITUTIONS!>run<!> {
val x = foo()
if (x == null) throw Exception()
<!TYPE_MISMATCH!>x<!>
}
@@ -0,0 +1,20 @@
package
public val bar: Bar
public val foo: Foo
public val foofoo: Foo
public fun bar(): Bar?
public fun foo(): Foo?
public fun </*0*/ T : Bar> run(/*0*/ f: () -> T): T
public interface Bar : Foo {
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 Foo {
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
}
@@ -0,0 +1,10 @@
fun <T> run(f: () -> T): T = f()
class My<T: Any>(val y: T?) {
fun get(): T = run {
val x = y
if (x == null) throw Exception()
<!DEBUG_INFO_SMARTCAST!>x<!>
}
}
@@ -0,0 +1,12 @@
package
public fun </*0*/ T> run(/*0*/ f: () -> T): T
public final class My</*0*/ T : kotlin.Any> {
public constructor My</*0*/ T : kotlin.Any>(/*0*/ y: T?)
public final val y: T?
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public final fun get(): T
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
@@ -0,0 +1,22 @@
// See KT-5385: no smart cast in a literal without given type arguments
interface Foo
fun foo(): Foo? = null
fun <T> run(f: () -> T): T = f()
val foo: Foo = run {
val x = foo()
if (x == null) throw Exception()
<!DEBUG_INFO_SMARTCAST!>x<!>
}
// Basic non-lambda case
fun <T> repeat(arg: T): T = arg
fun bar(): Foo {
val x = foo()
if (x == null) throw Exception()
return repeat(<!DEBUG_INFO_SMARTCAST!>x<!>)
}
@@ -0,0 +1,13 @@
package
public val foo: Foo
public fun bar(): Foo
public fun foo(): Foo?
public fun </*0*/ T> repeat(/*0*/ arg: T): T
public fun </*0*/ T> run(/*0*/ f: () -> T): T
public interface Foo {
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
}
@@ -0,0 +1,15 @@
// See also KT-7800
fun <T, R> T.let(f: (T) -> R): R = f(this)
fun foo(): Int {
val x: Int = 1.let {
val value: Int? = null
if (value == null) {
return@let 1
}
<!DEBUG_INFO_SMARTCAST!>value<!> // smart-cast should be here
}
return x
}
@@ -0,0 +1,4 @@
package
public fun foo(): kotlin.Int
public fun </*0*/ T, /*1*/ R> T.let(/*0*/ f: (T) -> R): R
@@ -0,0 +1,14 @@
// See also KT-7817
fun <R> synchronized(<!UNUSED_PARAMETER!>lock<!>: Any, block: () -> R): R = block()
class My {
val test: String
get() = synchronized(this) {
var x: String? = ""
if (x == null) {
x = "s"
}
<!DEBUG_INFO_SMARTCAST!>x<!>
}
}
@@ -0,0 +1,11 @@
package
public fun </*0*/ R> synchronized(/*0*/ lock: kotlin.Any, /*1*/ block: () -> R): R
public final class My {
public constructor My()
public final val test: kotlin.String
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
}
@@ -0,0 +1,13 @@
// See KT-10223
inline fun <T> using(input: Any?, f: (Any?) -> T): T = f(input)
val input: Any? = null
// Error:(32, 24) Type inference failed. Expected type mismatch: inferred type is kotlin.Any? but kotlin.String was expected
val test4: String = using(input) {
when (it) {
is String -> <!DEBUG_INFO_SMARTCAST!>it<!>
else -> throw RuntimeException()
}
}
@@ -0,0 +1,5 @@
package
public val input: kotlin.Any? = null
public val test4: kotlin.String
public inline fun </*0*/ T> using(/*0*/ input: kotlin.Any?, /*1*/ f: (kotlin.Any?) -> T): T
@@ -14700,6 +14700,12 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest {
doTest(fileName);
}
@TestMetadata("doubleLambdaArgument.kt")
public void testDoubleLambdaArgument() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/doubleLambdaArgument.kt");
doTest(fileName);
}
@TestMetadata("elvisExclExcl.kt")
public void testElvisExclExcl() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/elvisExclExcl.kt");
@@ -14880,6 +14886,48 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest {
doTest(fileName);
}
@TestMetadata("lambdaArgumentNoSubstitutedReturn.kt")
public void testLambdaArgumentNoSubstitutedReturn() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/lambdaArgumentNoSubstitutedReturn.kt");
doTest(fileName);
}
@TestMetadata("lambdaArgumentWithBoundWithoutType.kt")
public void testLambdaArgumentWithBoundWithoutType() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/lambdaArgumentWithBoundWithoutType.kt");
doTest(fileName);
}
@TestMetadata("lambdaArgumentWithExpectedGenericType.kt")
public void testLambdaArgumentWithExpectedGenericType() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/lambdaArgumentWithExpectedGenericType.kt");
doTest(fileName);
}
@TestMetadata("lambdaArgumentWithoutType.kt")
public void testLambdaArgumentWithoutType() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/lambdaArgumentWithoutType.kt");
doTest(fileName);
}
@TestMetadata("lambdaArgumentWithoutTypeIf.kt")
public void testLambdaArgumentWithoutTypeIf() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/lambdaArgumentWithoutTypeIf.kt");
doTest(fileName);
}
@TestMetadata("lambdaArgumentWithoutTypeIfMerge.kt")
public void testLambdaArgumentWithoutTypeIfMerge() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/lambdaArgumentWithoutTypeIfMerge.kt");
doTest(fileName);
}
@TestMetadata("lambdaArgumentWithoutTypeWhen.kt")
public void testLambdaArgumentWithoutTypeWhen() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/lambdaArgumentWithoutTypeWhen.kt");
doTest(fileName);
}
@TestMetadata("lambdaCall.kt")
public void testLambdaCall() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/lambdaCall.kt");
@@ -7270,6 +7270,12 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
doTest(fileName);
}
@TestMetadata("lambdaArgumentWithoutType.kt")
public void testLambdaArgumentWithoutType() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/smartCasts/lambdaArgumentWithoutType.kt");
doTest(fileName);
}
@TestMetadata("nullSmartCast.kt")
public void testNullSmartCast() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/smartCasts/nullSmartCast.kt");