From d47130eff8bb9fb2cacfaf9cc9e2b0d785131884 Mon Sep 17 00:00:00 2001 From: Mikhail Zarechenskiy Date: Wed, 6 Dec 2017 20:38:58 +0300 Subject: [PATCH] [NI] Don't check type for constants too early to avoid mismatch --- .../tower/KotlinResolutionCallbacksImpl.kt | 34 +++++++++++++++++-- .../resolve/calls/tower/NewCallArguments.kt | 13 ++++--- .../resolve/calls/tower/PSICallResolver.kt | 7 ++-- .../calls/components/ExternalComponents.kt | 2 ++ .../calls/components/KotlinCallCompleter.kt | 2 +- .../testData/diagnostics/tests/Properties.kt | 4 +-- .../evaluate/binaryMinusIndependentExpType.kt | 32 ++++++++--------- .../tests/evaluate/parentesized.kt | 12 +++---- 8 files changed, 67 insertions(+), 39 deletions(-) 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 8a46f6bd273..9081b166d94 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 @@ -16,6 +16,9 @@ package org.jetbrains.kotlin.resolve.calls.tower +import com.intellij.psi.util.PsiTreeUtil +import org.jetbrains.kotlin.builtins.KotlinBuiltIns.isPrimitiveTypeOrNullablePrimitiveType +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 @@ -26,15 +29,18 @@ import org.jetbrains.kotlin.psi.KtReturnExpression 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.calls.ArgumentTypeResolver import org.jetbrains.kotlin.resolve.calls.components.KotlinResolutionCallbacks import org.jetbrains.kotlin.resolve.calls.context.BasicCallResolutionContext import org.jetbrains.kotlin.resolve.calls.context.ContextDependency import org.jetbrains.kotlin.resolve.calls.model.LambdaKotlinCallArgument +import org.jetbrains.kotlin.resolve.calls.model.ReceiverKotlinCallArgument import org.jetbrains.kotlin.resolve.calls.model.ResolvedCallAtom import org.jetbrains.kotlin.resolve.calls.model.SimpleKotlinCallArgument import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowInfo import org.jetbrains.kotlin.resolve.calls.util.CallMaker +import org.jetbrains.kotlin.resolve.constants.evaluate.ConstantExpressionEvaluator import org.jetbrains.kotlin.resolve.descriptorUtil.builtIns import org.jetbrains.kotlin.resolve.scopes.receivers.ExpressionReceiver import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValueWithSmartCastInfo @@ -49,12 +55,13 @@ import org.jetbrains.kotlin.utils.addIfNotNull import org.jetbrains.kotlin.utils.addToStdlib.safeAs class KotlinResolutionCallbacksImpl( - val topLevelCallContext: BasicCallResolutionContext, + topLevelCallContext: BasicCallResolutionContext, val expressionTypingServices: ExpressionTypingServices, val typeApproximator: TypeApproximator, val argumentTypeResolver: ArgumentTypeResolver, val languageVersionSettings: LanguageVersionSettings, - val kotlinToResolvedCallTransformer: KotlinToResolvedCallTransformer + val kotlinToResolvedCallTransformer: KotlinToResolvedCallTransformer, + val constantExpressionEvaluator: ConstantExpressionEvaluator ): KotlinResolutionCallbacks { val trace: BindingTrace = topLevelCallContext.trace @@ -160,4 +167,27 @@ class KotlinResolutionCallbacksImpl( languageVersionSettings ) } + + override fun isCompileTimeConstant(resolvedAtom: ResolvedCallAtom, expectedType: UnwrappedType): Boolean { + val descriptor = resolvedAtom.candidateDescriptor + + if (!isUnderKotlinPackage(descriptor)) return false + + val returnType = descriptor.returnType ?: return false + if (!isPrimitiveTypeOrNullablePrimitiveType(returnType) || !isPrimitiveTypeOrNullablePrimitiveType(expectedType)) return false + + val callElement = resolvedAtom.atom.psiKotlinCall.psiCall.callElement.safeAs() ?: return false + val expression = findCommonParent(callElement, resolvedAtom.atom.psiKotlinCall.explicitReceiver) + + val temporaryBindingTrace = TemporaryBindingTrace.create( + trace, + "Trace to check if some expression is constant, we have to avoid writing probably wrong COMPILE_TIME_VALUE slice" + ) + return constantExpressionEvaluator.evaluateExpression(expression, temporaryBindingTrace, expectedType) != null + } + + private fun findCommonParent(callElement: KtExpression, receiver: ReceiverKotlinCallArgument?): KtExpression { + if (receiver == null) return callElement + return PsiTreeUtil.findCommonParent(callElement, receiver.psiExpression)?.safeAs() ?: callElement + } } \ No newline at end of file diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/tower/NewCallArguments.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/tower/NewCallArguments.kt index 75c54be9a1d..a01494afca1 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/tower/NewCallArguments.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/tower/NewCallArguments.kt @@ -28,13 +28,11 @@ import org.jetbrains.kotlin.resolve.calls.callUtil.getCall import org.jetbrains.kotlin.resolve.calls.context.BasicCallResolutionContext import org.jetbrains.kotlin.resolve.calls.model.* import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowInfo -import org.jetbrains.kotlin.resolve.scopes.receivers.ExpressionReceiver -import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValueWithSmartCastInfo -import org.jetbrains.kotlin.resolve.scopes.receivers.TransientReceiver -import org.jetbrains.kotlin.resolve.scopes.receivers.prepareReceiverRegardingCaptureTypes +import org.jetbrains.kotlin.resolve.scopes.receivers.* import org.jetbrains.kotlin.types.ErrorUtils import org.jetbrains.kotlin.types.UnwrappedType import org.jetbrains.kotlin.types.expressions.KotlinTypeInfo +import org.jetbrains.kotlin.utils.addToStdlib.safeAs class SimpleTypeArgumentImpl( val typeReference: KtTypeReference, @@ -61,10 +59,11 @@ val KotlinCallArgument.psiCallArgument: PSIKotlinCallArgument get() { } val KotlinCallArgument.psiExpression: KtExpression? get() { - if (this is ReceiverExpressionKotlinCallArgument) { - return (receiver.receiverValue as? ExpressionReceiver)?.expression + return when (this) { + is ReceiverExpressionKotlinCallArgument -> receiver.receiverValue.safeAs()?.expression + is QualifierReceiverKotlinCallArgument -> receiver.safeAs()?.expression + else -> psiCallArgument.valueArgument.getArgumentExpression() } - return psiCallArgument.valueArgument.getArgumentExpression() } class ParseErrorKotlinCallArgument( diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/tower/PSICallResolver.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/tower/PSICallResolver.kt index e5251072737..1b5af6ad871 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/tower/PSICallResolver.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/tower/PSICallResolver.kt @@ -45,6 +45,7 @@ import org.jetbrains.kotlin.resolve.calls.tasks.DynamicCallableDescriptors import org.jetbrains.kotlin.resolve.calls.tasks.ResolutionCandidate import org.jetbrains.kotlin.resolve.calls.tasks.TracingStrategy import org.jetbrains.kotlin.resolve.calls.util.CallMaker +import org.jetbrains.kotlin.resolve.constants.evaluate.ConstantExpressionEvaluator import org.jetbrains.kotlin.resolve.descriptorUtil.builtIns import org.jetbrains.kotlin.resolve.lazy.ForceResolveUtil import org.jetbrains.kotlin.resolve.scopes.LexicalScope @@ -69,7 +70,8 @@ class PSICallResolver( private val kotlinCallResolver: KotlinCallResolver, private val typeApproximator: TypeApproximator, private val argumentTypeResolver: ArgumentTypeResolver, - private val effectSystem: EffectSystem + private val effectSystem: EffectSystem, + private val constantExpressionEvaluator: ConstantExpressionEvaluator ) { private val GIVEN_CANDIDATES_NAME = Name.special("") @@ -151,7 +153,8 @@ class PSICallResolver( private fun createResolutionCallbacks(context: BasicCallResolutionContext) = KotlinResolutionCallbacksImpl(context, expressionTypingServices, typeApproximator, - argumentTypeResolver, languageVersionSettings, kotlinToResolvedCallTransformer) + argumentTypeResolver, languageVersionSettings, kotlinToResolvedCallTransformer, + constantExpressionEvaluator) private fun calculateExpectedType(context: BasicCallResolutionContext): UnwrappedType? { val expectedType = context.expectedType.unwrap() 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 4cf622137dc..625927f355e 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 @@ -48,4 +48,6 @@ interface KotlinResolutionCallbacks { fun bindStubResolvedCallForCandidate(candidate: ResolvedCallAtom) fun createReceiverWithSmartCastInfo(resolvedAtom: ResolvedCallAtom): ReceiverValueWithSmartCastInfo? + + fun isCompileTimeConstant(resolvedAtom: ResolvedCallAtom, expectedType: UnwrappedType): Boolean } \ No newline at end of file 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 7cb1e4c03ed..cb3bcc8c9f3 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 @@ -141,7 +141,7 @@ class KotlinCallCompleter( val actualType = withSmartCastInfo?.stableType ?: unsubstitutedReturnType val returnType = resolvedCall.substitutor.substituteKeepAnnotations(actualType) - if (expectedType != null && !TypeUtils.noExpectedType(expectedType)) { + if (expectedType != null && !TypeUtils.noExpectedType(expectedType) && !resolutionCallbacks.isCompileTimeConstant(resolvedCall, expectedType)) { csBuilder.addSubtypeConstraint(returnType, expectedType, ExpectedTypeConstraintPosition(resolvedCall.atom)) } diff --git a/compiler/testData/diagnostics/tests/Properties.kt b/compiler/testData/diagnostics/tests/Properties.kt index d954f011eef..0da9f1cc61b 100644 --- a/compiler/testData/diagnostics/tests/Properties.kt +++ b/compiler/testData/diagnostics/tests/Properties.kt @@ -1,10 +1,8 @@ -// !WITH_NEW_INFERENCE - var x : Int = 1 + x get() : Int = 1 set(value : Long) { field = value.toInt() - field = 1.toLong() + field = 1.toLong() } val xx : Int = 1 + x diff --git a/compiler/testData/diagnostics/tests/evaluate/binaryMinusIndependentExpType.kt b/compiler/testData/diagnostics/tests/evaluate/binaryMinusIndependentExpType.kt index 987b45a3504..ae12df8f6b6 100644 --- a/compiler/testData/diagnostics/tests/evaluate/binaryMinusIndependentExpType.kt +++ b/compiler/testData/diagnostics/tests/evaluate/binaryMinusIndependentExpType.kt @@ -1,26 +1,24 @@ -// !WITH_NEW_INFERENCE - val p1: Int = 1 - 1 -val p2: Long = 1 - 1 -val p3: Byte = 1 - 1 -val p4: Short = 1 - 1 +val p2: Long = 1 - 1 +val p3: Byte = 1 - 1 +val p4: Short = 1 - 1 val l1: Long = 1 - 1.toLong() -val l2: Byte = 1 - 1.toLong() -val l3: Int = 1 - 1.toLong() -val l4: Short = 1 - 1.toLong() +val l2: Byte = 1 - 1.toLong() +val l3: Int = 1 - 1.toLong() +val l4: Short = 1 - 1.toLong() -val b1: Byte = 1 - 1.toByte() +val b1: Byte = 1 - 1.toByte() val b2: Int = 1 - 1.toByte() -val b3: Long = 1 - 1.toByte() -val b4: Short = 1 - 1.toByte() +val b3: Long = 1 - 1.toByte() +val b4: Short = 1 - 1.toByte() -val i1: Byte = 1 - 1.toInt() +val i1: Byte = 1 - 1.toInt() val i2: Int = 1 - 1.toInt() -val i3: Long = 1 - 1.toInt() -val i4: Short = 1 - 1.toInt() +val i3: Long = 1 - 1.toInt() +val i4: Short = 1 - 1.toInt() -val s1: Byte = 1 - 1.toShort() +val s1: Byte = 1 - 1.toShort() val s2: Int = 1 - 1.toShort() -val s3: Long = 1 - 1.toShort() -val s4: Short = 1 - 1.toShort() \ No newline at end of file +val s3: Long = 1 - 1.toShort() +val s4: Short = 1 - 1.toShort() \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/evaluate/parentesized.kt b/compiler/testData/diagnostics/tests/evaluate/parentesized.kt index 449602f4add..8070e900c7d 100644 --- a/compiler/testData/diagnostics/tests/evaluate/parentesized.kt +++ b/compiler/testData/diagnostics/tests/evaluate/parentesized.kt @@ -1,12 +1,10 @@ -// !WITH_NEW_INFERENCE - -val p1: Byte = (1 + 2) * 2 -val p2: Short = (1 + 2) * 2 +val p1: Byte = (1 + 2) * 2 +val p2: Short = (1 + 2) * 2 val p3: Int = (1 + 2) * 2 -val p4: Long = (1 + 2) * 2 +val p4: Long = (1 + 2) * 2 -val b1: Byte = (1.toByte() + 2) * 2 -val b2: Short = (1.toShort() + 2) * 2 +val b1: Byte = (1.toByte() + 2) * 2 +val b2: Short = (1.toShort() + 2) * 2 val b3: Int = (1.toInt() + 2) * 2 val b4: Long = (1.toLong() + 2) * 2