From 1c92c22deec38e96f8637c48e7fdfd4d19783488 Mon Sep 17 00:00:00 2001 From: Dmitriy Novozhilov Date: Thu, 14 Mar 2019 14:14:28 +0300 Subject: [PATCH] [NI] Add support `ExpectedTypeFromCast` to new inference. #KT-30405 Fixed --- .../resolve/calls/GenericCandidateResolver.kt | 42 ++----------------- .../tower/KotlinResolutionCallbacksImpl.kt | 26 ++++++++++-- .../kotlin/resolve/ktDescriptorUtil.kt | 26 ++++++++++++ .../jetbrains/kotlin/psi/psiUtil/ktPsiUtil.kt | 25 ++++++++++- .../calls/components/ExternalComponents.kt | 2 + .../calls/components/KotlinCallCompleter.kt | 12 ++++++ .../inference/expectedTypeWithGenerics.kt | 2 +- .../tests/inference/findViewById.kt | 6 +-- .../diagnostics/tests/inference/kt30405.kt | 16 +++++++ .../diagnostics/tests/inference/kt30405.txt | 4 ++ .../checkers/DiagnosticsTestGenerated.java | 5 +++ .../DiagnosticsUsingJavacTestGenerated.java | 5 +++ 12 files changed, 123 insertions(+), 48 deletions(-) create mode 100644 compiler/frontend/src/org/jetbrains/kotlin/resolve/ktDescriptorUtil.kt create mode 100644 compiler/testData/diagnostics/tests/inference/kt30405.kt create mode 100644 compiler/testData/diagnostics/tests/inference/kt30405.txt diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/GenericCandidateResolver.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/GenericCandidateResolver.kt index 7134af0a971..a295e998926 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/GenericCandidateResolver.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/GenericCandidateResolver.kt @@ -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() ?: 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() ?: 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() - } - - 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 diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/tower/KotlinResolutionCallbacksImpl.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/tower/KotlinResolutionCallbacksImpl.kt index 36f1fa339b5..b769fb45cdd 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/tower/KotlinResolutionCallbacksImpl.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/tower/KotlinResolutionCallbacksImpl.kt @@ -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()?.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 + } } diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/ktDescriptorUtil.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/ktDescriptorUtil.kt new file mode 100644 index 00000000000..f5e53a3efa8 --- /dev/null +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/ktDescriptorUtil.kt @@ -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 +} diff --git a/compiler/psi/src/org/jetbrains/kotlin/psi/psiUtil/ktPsiUtil.kt b/compiler/psi/src/org/jetbrains/kotlin/psi/psiUtil/ktPsiUtil.kt index e82c9d1bcbd..bcacd99d3d2 100644 --- a/compiler/psi/src/org/jetbrains/kotlin/psi/psiUtil/ktPsiUtil.kt +++ b/compiler/psi/src/org/jetbrains/kotlin/psi/psiUtil/ktPsiUtil.kt @@ -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 \ No newline at end of file +fun KtFile.getFileOrScriptDeclarations() = if (isScript()) script!!.declarations else declarations + +fun KtExpression.getBinaryWithTypeParent(): KtBinaryExpressionWithTypeRHS? { + val callExpression = parent.safeAs() ?: 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 +} \ No newline at end of file diff --git a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/ExternalComponents.kt b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/ExternalComponents.kt index abf573e8166..90938eea09e 100644 --- a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/ExternalComponents.kt +++ b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/ExternalComponents.kt @@ -49,6 +49,8 @@ interface KotlinResolutionCallbacks { fun isCompileTimeConstant(resolvedAtom: ResolvedCallAtom, expectedType: UnwrappedType): Boolean val inferenceSession: InferenceSession + + fun getExpectedTypeFromAsExpressionAndRecordItInTrace(resolvedAtom: ResolvedCallAtom): UnwrappedType? } interface SamConversionTransformer { diff --git a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/KotlinCallCompleter.kt b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/KotlinCallCompleter.kt index fba2d8bfa20..c63c6bd3226 100644 --- a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/KotlinCallCompleter.kt +++ b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/KotlinCallCompleter.kt @@ -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? diff --git a/compiler/testData/diagnostics/tests/inference/expectedTypeWithGenerics.kt b/compiler/testData/diagnostics/tests/inference/expectedTypeWithGenerics.kt index 47361a50d17..523db01ca2d 100644 --- a/compiler/testData/diagnostics/tests/inference/expectedTypeWithGenerics.kt +++ b/compiler/testData/diagnostics/tests/inference/expectedTypeWithGenerics.kt @@ -14,5 +14,5 @@ fun g() { val y = foo() as Int - val y2 = foo() as D + val y2 = foo() as D } \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/inference/findViewById.kt b/compiler/testData/diagnostics/tests/inference/findViewById.kt index b7846fb0494..f6b623dc3c1 100644 --- a/compiler/testData/diagnostics/tests/inference/findViewById.kt +++ b/compiler/testData/diagnostics/tests/inference/findViewById.kt @@ -31,7 +31,7 @@ val xCastExplicitType = Test().findViewById(0) as X val xSafeCastExplicitType = Test().findViewById(0) as? X val yExplicit: Y = Test().findViewById(0) -val yCast = Test().findViewById(0) as Y +val yCast = Test().findViewById(0) as Y class TestChild : Test() { @@ -39,7 +39,7 @@ class TestChild : Test() { val xCast = findViewById(0) as X val yExplicit: Y = findViewById(0) - val yCast = findViewById(0) as Y + val yCast = findViewById(0) as Y } fun test(t: Test) { @@ -47,7 +47,7 @@ fun test(t: Test) { val xCast = t.findViewById(0) as X val yExplicit: Y = t.findViewById(0) - val yCast = t.findViewById(0) as Y + val yCast = t.findViewById(0) as Y } fun test2(t: Test?) { diff --git a/compiler/testData/diagnostics/tests/inference/kt30405.kt b/compiler/testData/diagnostics/tests/inference/kt30405.kt new file mode 100644 index 00000000000..c618fc45235 --- /dev/null +++ b/compiler/testData/diagnostics/tests/inference/kt30405.kt @@ -0,0 +1,16 @@ +// !WITH_NEW_INFERENCE +// !LANGUAGE: +ExpectedTypeFromCast +// !CHECK_TYPE +// Issue: KT-30405 + +inline fun foo(): T { + TODO() +} + +fun test() { + val fooCall = foo() as String // T in foo should be inferred to String + fooCall checkType { _() } + + val safeFooCall = foo() as? String + safeFooCall checkType { _() } +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/inference/kt30405.txt b/compiler/testData/diagnostics/tests/inference/kt30405.txt new file mode 100644 index 00000000000..d9bdbeae39b --- /dev/null +++ b/compiler/testData/diagnostics/tests/inference/kt30405.txt @@ -0,0 +1,4 @@ +package + +public inline fun foo(): T +public fun test(): kotlin.Unit diff --git a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java index ac6d8c09f60..f1ab0a93ffa 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java @@ -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"); diff --git a/compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsUsingJavacTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsUsingJavacTestGenerated.java index 6588e3c76f4..662ee202133 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsUsingJavacTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsUsingJavacTestGenerated.java @@ -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");