[NI] Add support ExpectedTypeFromCast to new inference. #KT-30405 Fixed

This commit is contained in:
Dmitriy Novozhilov
2019-03-14 14:14:28 +03:00
parent 9062811231
commit 1c92c22dee
12 changed files with 123 additions and 48 deletions
@@ -15,6 +15,7 @@ import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor
import org.jetbrains.kotlin.descriptors.impl.TypeAliasConstructorDescriptor
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.getBinaryWithTypeParent
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.FunctionDescriptorUtil
import org.jetbrains.kotlin.resolve.TemporaryBindingTrace
@@ -40,12 +41,12 @@ import org.jetbrains.kotlin.resolve.calls.results.ResolutionStatus
import org.jetbrains.kotlin.resolve.calls.results.ResolutionStatus.INCOMPLETE_TYPE_INFERENCE
import org.jetbrains.kotlin.resolve.calls.results.ResolutionStatus.OTHER_ERROR
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowValueFactory
import org.jetbrains.kotlin.resolve.isFunctionForExpectTypeFromCastFeature
import org.jetbrains.kotlin.resolve.scopes.receivers.ExpressionReceiver
import org.jetbrains.kotlin.types.*
import org.jetbrains.kotlin.types.TypeUtils.DONT_CARE
import org.jetbrains.kotlin.types.expressions.ControlStructureTypingUtils.ResolveConstruct
import org.jetbrains.kotlin.types.expressions.ExpressionTypingUtils
import org.jetbrains.kotlin.types.typeUtil.contains
import org.jetbrains.kotlin.types.typeUtil.makeNullable
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
@@ -124,21 +125,6 @@ class GenericCandidateResolver(
typeVariableSubstitutors[call.toHandle()]?.substitute(it, Variance.INVARIANT)
}
private fun FunctionDescriptor.isFunctionForExpectTypeFromCastFeature(): Boolean {
val typeParameter = typeParameters.singleOrNull() ?: return false
val returnType = returnType ?: return false
if (returnType is DeferredType && returnType.isComputing) return false
if (returnType.constructor != typeParameter.typeConstructor) return false
fun KotlinType.isBadType() = contains { it.constructor == typeParameter.typeConstructor }
if (valueParameters.any { it.type.isBadType() } || extensionReceiverParameter?.type?.isBadType() == true) return false
return true
}
private fun addExpectedTypeForExplicitCast(
context: CallCandidateResolutionContext<*>,
builder: ConstraintSystem.Builder
@@ -149,7 +135,7 @@ class GenericCandidateResolver(
val candidateDescriptor = context.candidateCall.candidateDescriptor.safeAs<FunctionDescriptor>() ?: return
val binaryParent = getBinaryWithTypeParent(context.call.calleeExpression) ?: return
val binaryParent = context.call.calleeExpression?.getBinaryWithTypeParent() ?: return
val operationType = binaryParent.operationReference.getReferencedNameElementType().takeIf {
it == KtTokens.AS_KEYWORD || it == KtTokens.AS_SAFE
} ?: return
@@ -165,28 +151,6 @@ class GenericCandidateResolver(
builder.addSubtypeConstraint(typeInSystem, expectedType, ConstraintPositionKind.SPECIAL.position())
}
private fun getBinaryWithTypeParent(calleeExpression: KtExpression?): KtBinaryExpressionWithTypeRHS? {
val callExpression = calleeExpression?.parent.safeAs<KtCallExpression>() ?: return null
val possibleQualifiedExpression = callExpression.parent
val targetExpression = if (possibleQualifiedExpression is KtQualifiedExpression) {
if (possibleQualifiedExpression.selectorExpression != callExpression) return null
possibleQualifiedExpression
} else {
callExpression
}
return targetExpression.topParenthesizedParentOrMe().parent.safeAs<KtBinaryExpressionWithTypeRHS>()
}
private fun KtExpression.topParenthesizedParentOrMe(): KtExpression {
var result: KtExpression = this
while (KtPsiUtil.deparenthesizeOnce(result.parent.safeAs()) == result) {
result = result.parent.safeAs() ?: break
}
return result
}
private fun addValidityConstraintsForConstituentTypes(builder: ConstraintSystem.Builder, type: KotlinType) {
val typeConstructor = type.constructor
if (typeConstructor.declarationDescriptor is TypeParameterDescriptor) return
@@ -11,16 +11,16 @@ import org.jetbrains.kotlin.builtins.KotlinBuiltIns.isUnderKotlinPackage
import org.jetbrains.kotlin.builtins.createFunctionType
import org.jetbrains.kotlin.config.LanguageVersionSettings
import org.jetbrains.kotlin.descriptors.CallableDescriptor
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
import org.jetbrains.kotlin.descriptors.annotations.Annotations
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.psi.KtExpression
import org.jetbrains.kotlin.psi.KtPsiUtil
import org.jetbrains.kotlin.psi.KtReturnExpression
import org.jetbrains.kotlin.psi.psiUtil.getBinaryWithTypeParent
import org.jetbrains.kotlin.psi.psiUtil.lastBlockStatementOrThis
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.BindingTrace
import org.jetbrains.kotlin.resolve.TemporaryBindingTrace
import org.jetbrains.kotlin.resolve.TypeResolver
import org.jetbrains.kotlin.resolve.*
import org.jetbrains.kotlin.resolve.calls.ArgumentTypeResolver
import org.jetbrains.kotlin.resolve.calls.components.InferenceSession
import org.jetbrains.kotlin.resolve.calls.components.KotlinResolutionCallbacks
@@ -45,6 +45,7 @@ import org.jetbrains.kotlin.types.expressions.DoubleColonExpressionResolver
import org.jetbrains.kotlin.types.expressions.ExpressionTypingServices
import org.jetbrains.kotlin.types.expressions.KotlinTypeInfo
import org.jetbrains.kotlin.types.typeUtil.isUnit
import org.jetbrains.kotlin.types.typeUtil.makeNullable
import org.jetbrains.kotlin.utils.addIfNotNull
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
@@ -244,4 +245,21 @@ class KotlinResolutionCallbacksImpl(
if (receiver == null) return callElement
return PsiTreeUtil.findCommonParent(callElement, receiver.psiExpression)?.safeAs() ?: callElement
}
override fun getExpectedTypeFromAsExpressionAndRecordItInTrace(resolvedAtom: ResolvedCallAtom): UnwrappedType? {
val candidateDescriptor = resolvedAtom.candidateDescriptor as? FunctionDescriptor ?: return null
val call = resolvedAtom.atom.safeAs<PSIKotlinCall>()?.psiCall ?: return null
if (call.typeArgumentList != null || !candidateDescriptor.isFunctionForExpectTypeFromCastFeature()) return null
val binaryParent = call.calleeExpression?.getBinaryWithTypeParent() ?: return null
val operationType = binaryParent.operationReference.getReferencedNameElementType().takeIf {
it == KtTokens.AS_KEYWORD || it == KtTokens.AS_SAFE
} ?: return null
val leftType = trace.get(BindingContext.TYPE, binaryParent.right ?: return null) ?: return null
val expectedType = if (operationType == KtTokens.AS_SAFE) leftType.makeNullable() else leftType
val resultType = expectedType.unwrap()
trace.record(BindingContext.CAST_TYPE_USED_AS_EXPECTED_TYPE, binaryParent)
return resultType
}
}
@@ -0,0 +1,26 @@
/*
* Copyright 2010-2019 JetBrains s.r.o. 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
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.types.DeferredType
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.typeUtil.contains
fun FunctionDescriptor.isFunctionForExpectTypeFromCastFeature(): Boolean {
val typeParameter = typeParameters.singleOrNull() ?: return false
val returnType = returnType ?: return false
if (returnType is DeferredType && returnType.isComputing) return false
if (returnType.constructor != typeParameter.typeConstructor) return false
fun KotlinType.isBadType() = contains { it.constructor == typeParameter.typeConstructor }
if (valueParameters.any { it.type.isBadType() } || extensionReceiverParameter?.type?.isBadType() == true) return false
return true
}
@@ -35,6 +35,7 @@ import org.jetbrains.kotlin.name.SpecialNames
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.stubs.KotlinClassOrObjectStub
import org.jetbrains.kotlin.types.expressions.OperatorConventions
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
import java.util.*
// NOTE: in this file we collect only Kotlin-specific methods working with PSI and not modifying it
@@ -645,4 +646,26 @@ fun KtModifierKeywordToken.toVisibility(): Visibility {
}
}
fun KtFile.getFileOrScriptDeclarations() = if (isScript()) script!!.declarations else declarations
fun KtFile.getFileOrScriptDeclarations() = if (isScript()) script!!.declarations else declarations
fun KtExpression.getBinaryWithTypeParent(): KtBinaryExpressionWithTypeRHS? {
val callExpression = parent.safeAs<KtCallExpression>() ?: return null
val possibleQualifiedExpression = callExpression.parent
val targetExpression = if (possibleQualifiedExpression is KtQualifiedExpression) {
if (possibleQualifiedExpression.selectorExpression != callExpression) return null
possibleQualifiedExpression
} else {
callExpression
}
return targetExpression.topParenthesizedParentOrMe().parent as? KtBinaryExpressionWithTypeRHS
}
fun KtExpression.topParenthesizedParentOrMe(): KtExpression {
var result: KtExpression = this
while (KtPsiUtil.deparenthesizeOnce(result.parent.safeAs()) == result) {
result = result.parent.safeAs() ?: break
}
return result
}
@@ -49,6 +49,8 @@ interface KotlinResolutionCallbacks {
fun isCompileTimeConstant(resolvedAtom: ResolvedCallAtom, expectedType: UnwrappedType): Boolean
val inferenceSession: InferenceSession
fun getExpectedTypeFromAsExpressionAndRecordItInTrace(resolvedAtom: ResolvedCallAtom): UnwrappedType?
}
interface SamConversionTransformer {
@@ -5,6 +5,7 @@
package org.jetbrains.kotlin.resolve.calls.components
import org.jetbrains.kotlin.config.LanguageFeature
import org.jetbrains.kotlin.resolve.calls.inference.NewConstraintSystem
import org.jetbrains.kotlin.resolve.calls.inference.components.KotlinConstraintSystemCompleter
import org.jetbrains.kotlin.resolve.calls.inference.components.KotlinConstraintSystemCompleter.ConstraintSystemCompletionMode
@@ -42,6 +43,7 @@ class KotlinCallCompleter(
val returnType = candidate.returnTypeWithSmartCastInfo(resolutionCallbacks)
candidate.addExpectedTypeConstraint(returnType, expectedType, resolutionCallbacks)
candidate.addExpectedTypeFromCastConstraint(returnType, resolutionCallbacks)
return if (resolutionCallbacks.inferenceSession.shouldRunCompletion(candidate))
candidate.runCompletion(
@@ -155,6 +157,16 @@ class KotlinCallCompleter(
}
}
private fun KotlinResolutionCandidate.addExpectedTypeFromCastConstraint(
returnType: UnwrappedType?,
resolutionCallbacks: KotlinResolutionCallbacks
) {
if (!callComponents.languageVersionSettings.supportsFeature(LanguageFeature.ExpectedTypeFromCast)) return
if (returnType == null) return
val expectedType = resolutionCallbacks.getExpectedTypeFromAsExpressionAndRecordItInTrace(resolvedCall) ?: return
csBuilder.addSubtypeConstraint(returnType, expectedType, ExpectedTypeConstraintPosition(resolvedCall.atom))
}
private fun KotlinResolutionCandidate.computeCompletionMode(
expectedType: UnwrappedType?,
currentReturnType: UnwrappedType?
@@ -14,5 +14,5 @@ fun <S, D: S> g() {
val <!UNUSED_VARIABLE!>y<!> = <!OI;TYPE_INFERENCE_UPPER_BOUND_VIOLATED!>foo<!>() as Int
val <!UNUSED_VARIABLE!>y2<!> = foo() <!NI;UNCHECKED_CAST!>as D<!>
val <!UNUSED_VARIABLE!>y2<!> = foo() as D
}
@@ -31,7 +31,7 @@ val xCastExplicitType = Test().findViewById<X>(0) as X
val xSafeCastExplicitType = Test().findViewById<X>(0) <!USELESS_CAST!>as? X<!>
val yExplicit: Y<String> = Test().findViewById(0)
val yCast = Test().findViewById(0) <!NI;UNCHECKED_CAST!>as Y<String><!>
val yCast = Test().findViewById(0) as Y<String>
class TestChild : Test() {
@@ -39,7 +39,7 @@ class TestChild : Test() {
val xCast = findViewById(0) as X
val yExplicit: Y<String> = findViewById(0)
val yCast = findViewById(0) <!NI;UNCHECKED_CAST!>as Y<String><!>
val yCast = findViewById(0) as Y<String>
}
fun test(t: Test) {
@@ -47,7 +47,7 @@ fun test(t: Test) {
val xCast = t.findViewById(0) as X
val yExplicit: Y<String> = t.findViewById(0)
val yCast = t.findViewById(0) <!NI;UNCHECKED_CAST!>as Y<String><!>
val yCast = t.findViewById(0) as Y<String>
}
fun test2(t: Test?) {
@@ -0,0 +1,16 @@
// !WITH_NEW_INFERENCE
// !LANGUAGE: +ExpectedTypeFromCast
// !CHECK_TYPE
// Issue: KT-30405
inline fun <reified T> foo(): T {
TODO()
}
fun test() {
val fooCall = foo() as String // T in foo should be inferred to String
fooCall checkType { _<String>() }
val safeFooCall = foo() as? String
safeFooCall checkType { _<String?>() }
}
@@ -0,0 +1,4 @@
package
public inline fun </*0*/ reified T> foo(): T
public fun test(): kotlin.Unit
@@ -9545,6 +9545,11 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest {
runTest("compiler/testData/diagnostics/tests/inference/kt28654.kt");
}
@TestMetadata("kt30405.kt")
public void testKt30405() throws Exception {
runTest("compiler/testData/diagnostics/tests/inference/kt30405.kt");
}
@TestMetadata("kt3184.kt")
public void testKt3184() throws Exception {
runTest("compiler/testData/diagnostics/tests/inference/kt3184.kt");
@@ -9540,6 +9540,11 @@ public class DiagnosticsUsingJavacTestGenerated extends AbstractDiagnosticsUsing
runTest("compiler/testData/diagnostics/tests/inference/kt28654.kt");
}
@TestMetadata("kt30405.kt")
public void testKt30405() throws Exception {
runTest("compiler/testData/diagnostics/tests/inference/kt30405.kt");
}
@TestMetadata("kt3184.kt")
public void testKt3184() throws Exception {
runTest("compiler/testData/diagnostics/tests/inference/kt3184.kt");