diff --git a/compiler/frontend/src/org/jetbrains/kotlin/psi/JetPsiUtil.java b/compiler/frontend/src/org/jetbrains/kotlin/psi/JetPsiUtil.java index 2d95ad9d0e6..b2733c2d3cc 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/psi/JetPsiUtil.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/psi/JetPsiUtil.java @@ -41,6 +41,8 @@ import org.jetbrains.kotlin.name.Name; import org.jetbrains.kotlin.name.SpecialNames; import org.jetbrains.kotlin.parsing.JetExpressionParsing; import org.jetbrains.kotlin.psi.psiUtil.PsiUtilPackage; +import org.jetbrains.kotlin.resolve.ResolvePackage; +import org.jetbrains.kotlin.resolve.StatementFilter; import org.jetbrains.kotlin.types.expressions.OperatorConventions; import java.util.Collection; @@ -875,4 +877,24 @@ public class JetPsiUtil { return false; } + + @Nullable + public static JetExpression getLastElementDeparenthesized( + @Nullable JetExpression expression, + @NotNull StatementFilter statementFilter + ) { + JetExpression deparenthesizedExpression = JetPsiUtil.deparenthesize(expression, false); + if (deparenthesizedExpression instanceof JetBlockExpression) { + JetBlockExpression blockExpression = (JetBlockExpression) deparenthesizedExpression; + // todo + // This case is a temporary hack for 'if' branches. + // The right way to implement this logic is to interpret 'if' branches as function literals with explicitly-typed signatures + // (no arguments and no receiver) and therefore analyze them straight away (not in the 'complete' phase). + JetExpression lastStatementInABlock = ResolvePackage.getLastStatementInABlock(statementFilter, blockExpression); + if (lastStatementInABlock != null) { + return getLastElementDeparenthesized(lastStatementInABlock, statementFilter); + } + } + return deparenthesizedExpression; + } } diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/ArgumentTypeResolver.java b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/ArgumentTypeResolver.java index 1d6cbeaf985..fdd4f1a79d6 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/ArgumentTypeResolver.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/ArgumentTypeResolver.java @@ -53,6 +53,7 @@ import org.jetbrains.kotlin.types.expressions.typeInfoFactory.TypeInfoFactoryPac import java.util.Collections; import java.util.List; +import static org.jetbrains.kotlin.psi.JetPsiUtil.getLastElementDeparenthesized; import static org.jetbrains.kotlin.resolve.BindingContextUtils.getRecordedTypeInfo; import static org.jetbrains.kotlin.resolve.calls.callResolverUtil.ResolveArgumentsMode.RESOLVE_FUNCTION_ARGUMENTS; import static org.jetbrains.kotlin.resolve.calls.callResolverUtil.ResolveArgumentsMode.SHAPE_FUNCTION_ARGUMENTS; @@ -68,19 +69,22 @@ public class ArgumentTypeResolver { @NotNull private final ExpressionTypingServices expressionTypingServices; @NotNull private final KotlinBuiltIns builtIns; @NotNull private final ReflectionTypes reflectionTypes; + @NotNull private final ConstantExpressionEvaluator constantExpressionEvaluator; public ArgumentTypeResolver( @NotNull TypeResolver typeResolver, @NotNull CallResolver callResolver, @NotNull ExpressionTypingServices expressionTypingServices, @NotNull KotlinBuiltIns builtIns, - @NotNull ReflectionTypes reflectionTypes + @NotNull ReflectionTypes reflectionTypes, + @NotNull ConstantExpressionEvaluator constantExpressionEvaluator ) { this.typeResolver = typeResolver; this.callResolver = callResolver; this.expressionTypingServices = expressionTypingServices; this.builtIns = builtIns; this.reflectionTypes = reflectionTypes; + this.constantExpressionEvaluator = constantExpressionEvaluator; } public static boolean isSubtypeOfForArgumentType( @@ -171,26 +175,6 @@ public class ArgumentTypeResolver { return null; } - @Nullable - public static JetExpression getLastElementDeparenthesized( - @Nullable JetExpression expression, - @NotNull StatementFilter statementFilter - ) { - JetExpression deparenthesizedExpression = JetPsiUtil.deparenthesize(expression, false); - if (deparenthesizedExpression instanceof JetBlockExpression) { - JetBlockExpression blockExpression = (JetBlockExpression) deparenthesizedExpression; - // todo - // This case is a temporary hack for 'if' branches. - // The right way to implement this logic is to interpret 'if' branches as function literals with explicitly-typed signatures - // (no arguments and no receiver) and therefore analyze them straight away (not in the 'complete' phase). - JetExpression lastStatementInABlock = ResolvePackage.getLastStatementInABlock(statementFilter, blockExpression); - if (lastStatementInABlock != null) { - return getLastElementDeparenthesized(lastStatementInABlock, statementFilter); - } - } - return deparenthesizedExpression; - } - @Nullable public static JetCallableReferenceExpression getCallableReferenceExpressionIfAny( @NotNull JetExpression expression, @@ -356,7 +340,7 @@ public class ArgumentTypeResolver { } @Nullable - public static JetType updateResultArgumentTypeIfNotDenotable( + public JetType updateResultArgumentTypeIfNotDenotable( @NotNull ResolutionContext context, @NotNull JetExpression expression ) { @@ -365,30 +349,10 @@ public class ArgumentTypeResolver { if (type.getConstructor() instanceof IntegerValueTypeConstructor) { IntegerValueTypeConstructor constructor = (IntegerValueTypeConstructor) type.getConstructor(); JetType primitiveType = TypeUtils.getPrimitiveNumberType(constructor, context.expectedType); - updateNumberType(primitiveType, expression, context.statementFilter, context.trace); + constantExpressionEvaluator.updateNumberType(primitiveType, expression, context.statementFilter, context.trace); return primitiveType; } } return type; } - - public static void updateNumberType( - @NotNull JetType numberType, - @Nullable JetExpression expression, - @NotNull StatementFilter statementFilter, - @NotNull BindingTrace trace - ) { - if (expression == null) return; - BindingContextUtils.updateRecordedType(numberType, expression, trace, false); - - if (!(expression instanceof JetConstantExpression)) { - JetExpression deparenthesized = getLastElementDeparenthesized(expression, statementFilter); - if (deparenthesized != expression) { - updateNumberType(numberType, deparenthesized, statementFilter, trace); - } - return; - } - - ConstantExpressionEvaluator.evaluate(expression, trace, numberType); - } } diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/CallCompleter.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/CallCompleter.kt index 43496741dcd..90496d18f02 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/CallCompleter.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/CallCompleter.kt @@ -32,7 +32,9 @@ import org.jetbrains.kotlin.resolve.calls.context.CallCandidateResolutionContext import org.jetbrains.kotlin.resolve.calls.context.CheckArgumentTypesMode import org.jetbrains.kotlin.resolve.calls.inference.ConstraintSystemImpl import org.jetbrains.kotlin.resolve.calls.inference.InferenceErrorData -import org.jetbrains.kotlin.resolve.calls.inference.constraintPosition.ConstraintPositionKind.* +import org.jetbrains.kotlin.resolve.calls.inference.constraintPosition.ConstraintPositionKind.EXPECTED_TYPE_POSITION +import org.jetbrains.kotlin.resolve.calls.inference.constraintPosition.ConstraintPositionKind.FROM_COMPLETER +import org.jetbrains.kotlin.resolve.calls.inference.constraintPosition.ConstraintPositionKind.TYPE_BOUND_POSITION import org.jetbrains.kotlin.resolve.calls.model.* import org.jetbrains.kotlin.resolve.calls.results.OverloadResolutionResultsImpl import org.jetbrains.kotlin.resolve.calls.results.ResolutionStatus @@ -235,7 +237,7 @@ public class CallCompleter( if (valueArgument.isExternal()) return val expression = valueArgument.getArgumentExpression() ?: return - val deparenthesized = ArgumentTypeResolver.getLastElementDeparenthesized(expression, context.statementFilter) ?: return + val deparenthesized = JetPsiUtil.getLastElementDeparenthesized(expression, context.statementFilter) ?: return val recordedType = expression.let { context.trace.getType(it) } var updatedType: JetType? = recordedType @@ -249,7 +251,7 @@ public class CallCompleter( // For the cases like 'foo(1)' the type of '1' depends on expected type (it can be Int, Byte, etc.), // so while the expected type is not known, it's IntegerValueType(1), and should be updated when the expected type is known. if (recordedType != null && !recordedType.getConstructor().isDenotable()) { - updatedType = ArgumentTypeResolver.updateResultArgumentTypeIfNotDenotable(context, expression) + updatedType = argumentTypeResolver.updateResultArgumentTypeIfNotDenotable(context, expression) } updatedType = updateRecordedTypeForArgument(updatedType, recordedType, expression, context.trace) 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 60aefd312c8..ad57ec8d6c0 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/GenericCandidateResolver.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/GenericCandidateResolver.kt @@ -21,7 +21,6 @@ import org.jetbrains.kotlin.descriptors.CallableDescriptor import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.resolve.FunctionDescriptorUtil -import org.jetbrains.kotlin.resolve.calls.ArgumentTypeResolver.getLastElementDeparenthesized import org.jetbrains.kotlin.resolve.calls.callResolverUtil.* import org.jetbrains.kotlin.resolve.calls.callResolverUtil.ResolveArgumentsMode.RESOLVE_FUNCTION_ARGUMENTS import org.jetbrains.kotlin.resolve.calls.callResolverUtil.ResolveArgumentsMode.SHAPE_FUNCTION_ARGUMENTS @@ -34,10 +33,8 @@ import org.jetbrains.kotlin.resolve.calls.context.TemporaryTraceAndCache import org.jetbrains.kotlin.resolve.calls.inference.ConstraintSystem import org.jetbrains.kotlin.resolve.calls.inference.ConstraintSystemImpl import org.jetbrains.kotlin.resolve.calls.inference.constraintPosition.ConstraintPosition -import org.jetbrains.kotlin.resolve.calls.inference.constraintPosition.ConstraintPositionKind import org.jetbrains.kotlin.resolve.calls.inference.constraintPosition.ConstraintPositionKind.RECEIVER_POSITION import org.jetbrains.kotlin.resolve.calls.inference.constraintPosition.ConstraintPositionKind.VALUE_PARAMETER_POSITION -import org.jetbrains.kotlin.resolve.calls.inference.constraintPosition.ConstraintPositionKind.EXPECTED_TYPE_POSITION 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 @@ -184,7 +181,7 @@ class GenericCandidateResolver( argumentExpression: JetExpression?, context: ResolutionContext<*> ): JetType? { - val deparenthesizedArgument = getLastElementDeparenthesized(argumentExpression, context.statementFilter) + val deparenthesizedArgument = JetPsiUtil.getLastElementDeparenthesized(argumentExpression, context.statementFilter) if (deparenthesizedArgument == null || type == null) return type val dataFlowValue = DataFlowValueFactory.createDataFlowValue(deparenthesizedArgument, type, context) diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/constants/evaluate/ConstantExpressionEvaluator.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/constants/evaluate/ConstantExpressionEvaluator.kt index 6f7d09b2ade..6fa64faccbf 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/constants/evaluate/ConstantExpressionEvaluator.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/constants/evaluate/ConstantExpressionEvaluator.kt @@ -29,7 +29,6 @@ import org.jetbrains.kotlin.lexer.JetTokens import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType import org.jetbrains.kotlin.resolve.* -import org.jetbrains.kotlin.resolve.calls.ArgumentTypeResolver import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall import org.jetbrains.kotlin.resolve.calls.model.ResolvedValueArgument @@ -48,6 +47,25 @@ public class ConstantExpressionEvaluator( internal val constantValueFactory: ConstantValueFactory, internal val builtIns: KotlinBuiltIns ) { + public fun updateNumberType( + numberType: JetType, + expression: JetExpression?, + statementFilter: StatementFilter, + trace: BindingTrace + ) { + if (expression == null) return + BindingContextUtils.updateRecordedType(numberType, expression, trace, false) + + if (expression !is JetConstantExpression) { + val deparenthesized = JetPsiUtil.getLastElementDeparenthesized(expression, statementFilter) + if (deparenthesized !== expression) { + updateNumberType(numberType, deparenthesized, statementFilter, trace) + } + return + } + + evaluateExpression(expression, trace, numberType) + } internal fun resolveAnnotationArguments( resolvedCall: ResolvedCall<*>, @@ -160,10 +178,10 @@ public class ConstantExpressionEvaluator( val constants = ArrayList>() for (argument in resolvedValueArgument.getArguments()) { val argumentExpression = argument.getArgumentExpression() ?: continue - val constant = ConstantExpressionEvaluator.evaluate(argumentExpression, trace, expectedType) + val constant = evaluateExpression(argumentExpression, trace, expectedType) if (constant is IntegerValueTypeConstant) { val defaultType = constant.getType(expectedType) - ArgumentTypeResolver.updateNumberType(defaultType, argumentExpression, StatementFilter.NONE, trace) + updateNumberType(defaultType, argumentExpression, StatementFilter.NONE, trace) } if (constant != null) { constants.add(constant) @@ -184,17 +202,15 @@ public class ConstantExpressionEvaluator( } companion object { - platformStatic public fun evaluate(expression: JetExpression, trace: BindingTrace, expectedType: JetType? = TypeUtils.NO_EXPECTED_TYPE): CompileTimeConstant<*>? { - val builtIns = KotlinBuiltIns.getInstance() - return ConstantExpressionEvaluator(ConstantValueFactory(builtIns), builtIns).evaluateExpression(expression, trace, expectedType) - } - platformStatic public fun evaluateToConstantValue( expression: JetExpression, trace: BindingTrace, expectedType: JetType ): ConstantValue<*>? { - return evaluate(expression, trace, expectedType)?.toConstantValue(expectedType) + val builtIns = KotlinBuiltIns.getInstance() + return ConstantExpressionEvaluator(ConstantValueFactory(builtIns), builtIns). + evaluateExpression(expression, trace, expectedType)?. + toConstantValue(expectedType) } platformStatic public fun getConstant(expression: JetExpression, bindingContext: BindingContext): CompileTimeConstant<*>? { diff --git a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/DataFlowAnalyzer.java b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/DataFlowAnalyzer.java index 9b27e50a892..bedf7ef725e 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/DataFlowAnalyzer.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/DataFlowAnalyzer.java @@ -50,9 +50,14 @@ import static org.jetbrains.kotlin.types.TypeUtils.*; public class DataFlowAnalyzer { private final Iterable additionalTypeCheckers; + private final ConstantExpressionEvaluator constantExpressionEvaluator; - public DataFlowAnalyzer(@NotNull Iterable additionalTypeCheckers) { + public DataFlowAnalyzer( + @NotNull Iterable additionalTypeCheckers, + @NotNull ConstantExpressionEvaluator constantExpressionEvaluator + ) { this.additionalTypeCheckers = additionalTypeCheckers; + this.constantExpressionEvaluator = constantExpressionEvaluator; } @NotNull @@ -293,7 +298,7 @@ public class DataFlowAnalyzer { IntegerValueTypeConstant integerValueTypeConstant = (IntegerValueTypeConstant) value; if (context.contextDependency == INDEPENDENT) { expressionType = integerValueTypeConstant.getType(context.expectedType); - ArgumentTypeResolver.updateNumberType(expressionType, expression, context.statementFilter, context.trace); + constantExpressionEvaluator.updateNumberType(expressionType, expression, context.statementFilter, context.trace); } else { expressionType = integerValueTypeConstant.getUnknownIntegerType(); diff --git a/compiler/tests/org/jetbrains/kotlin/resolve/constants/evaluate/AbstractEvaluateExpressionTest.kt b/compiler/tests/org/jetbrains/kotlin/resolve/constants/evaluate/AbstractEvaluateExpressionTest.kt index 7f53fbfad86..f9ae3baebd7 100644 --- a/compiler/tests/org/jetbrains/kotlin/resolve/constants/evaluate/AbstractEvaluateExpressionTest.kt +++ b/compiler/tests/org/jetbrains/kotlin/resolve/constants/evaluate/AbstractEvaluateExpressionTest.kt @@ -17,6 +17,7 @@ package org.jetbrains.kotlin.resolve.constants.evaluate import com.intellij.openapi.util.io.FileUtil +import org.jetbrains.kotlin.builtins.KotlinBuiltIns import org.jetbrains.kotlin.descriptors.VariableDescriptor import org.jetbrains.kotlin.psi.JetProperty import org.jetbrains.kotlin.resolve.BindingContext @@ -24,6 +25,7 @@ import org.jetbrains.kotlin.resolve.DelegatingBindingTrace import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils import org.jetbrains.kotlin.resolve.annotation.AbstractAnnotationDescriptorResolveTest import org.jetbrains.kotlin.resolve.constants.CompileTimeConstant +import org.jetbrains.kotlin.resolve.constants.ConstantValueFactory import org.jetbrains.kotlin.resolve.constants.StringValue import org.jetbrains.kotlin.test.InTextDirectivesUtils import org.jetbrains.kotlin.test.JetTestUtils @@ -64,7 +66,8 @@ public abstract class AbstractEvaluateExpressionTest : AbstractAnnotationDescrip private fun evaluateInitializer(context: BindingContext, property: VariableDescriptor): CompileTimeConstant<*>? { val propertyDeclaration = DescriptorToSourceUtils.descriptorToDeclaration(property) as JetProperty - val compileTimeConstant = ConstantExpressionEvaluator.evaluate( + val builtIns = KotlinBuiltIns.getInstance() + val compileTimeConstant = ConstantExpressionEvaluator(ConstantValueFactory(builtIns), builtIns).evaluateExpression( propertyDeclaration.getInitializer()!!, DelegatingBindingTrace(context, "trace for evaluating compile time constant"), property.getType()