[NI] Add support ExpectedTypeFromCast to new inference. #KT-30405 Fixed
This commit is contained in:
+3
-39
@@ -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
|
||||
|
||||
+22
-4
@@ -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
|
||||
}
|
||||
+2
@@ -49,6 +49,8 @@ interface KotlinResolutionCallbacks {
|
||||
fun isCompileTimeConstant(resolvedAtom: ResolvedCallAtom, expectedType: UnwrappedType): Boolean
|
||||
|
||||
val inferenceSession: InferenceSession
|
||||
|
||||
fun getExpectedTypeFromAsExpressionAndRecordItInTrace(resolvedAtom: ResolvedCallAtom): UnwrappedType?
|
||||
}
|
||||
|
||||
interface SamConversionTransformer {
|
||||
|
||||
+12
@@ -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");
|
||||
|
||||
Generated
+5
@@ -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");
|
||||
|
||||
Reference in New Issue
Block a user