From 931bbd8cecff035fb0c7614124682377c4a79da1 Mon Sep 17 00:00:00 2001 From: Mikhail Zarechenskiy Date: Mon, 6 May 2019 16:28:16 +0300 Subject: [PATCH] [NI] Support ad-hoc implicit integer coercion for Kotlin/Native --- .../DiagnosticReporterByTrackingStrategy.kt | 12 +++ .../tower/KotlinToResolvedCallTransformer.kt | 85 +++++++++++++------ .../calls/tower/ResolvedAtomCompleter.kt | 8 +- .../conversionOfSignedToUnsigned.kt | 5 +- 4 files changed, 81 insertions(+), 29 deletions(-) diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/DiagnosticReporterByTrackingStrategy.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/DiagnosticReporterByTrackingStrategy.kt index 92dbf0b0dd9..f81b0faa64a 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/DiagnosticReporterByTrackingStrategy.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/DiagnosticReporterByTrackingStrategy.kt @@ -5,6 +5,7 @@ package org.jetbrains.kotlin.resolve.calls +import org.jetbrains.kotlin.builtins.UnsignedTypes import org.jetbrains.kotlin.builtins.functions.FunctionInvokeDescriptor import org.jetbrains.kotlin.builtins.isExtensionFunctionType import org.jetbrains.kotlin.diagnostics.Errors @@ -23,6 +24,7 @@ import org.jetbrains.kotlin.resolve.calls.smartcasts.SmartCastManager import org.jetbrains.kotlin.resolve.calls.tasks.TracingStrategy import org.jetbrains.kotlin.resolve.calls.tower.* import org.jetbrains.kotlin.resolve.constants.CompileTimeConstantChecker +import org.jetbrains.kotlin.resolve.constants.TypedCompileTimeConstant import org.jetbrains.kotlin.resolve.constants.evaluate.ConstantExpressionEvaluator import org.jetbrains.kotlin.resolve.descriptorUtil.module import org.jetbrains.kotlin.resolve.scopes.receivers.ExpressionReceiver @@ -227,6 +229,16 @@ class DiagnosticReporterByTrackingStrategy( val expression = it.psiExpression ?: return val deparenthesized = KtPsiUtil.safeDeparenthesize(expression) if (reportConstantTypeMismatch(constraintError, deparenthesized)) return + + val compileTimeConstant = trace[BindingContext.COMPILE_TIME_VALUE, deparenthesized] as? TypedCompileTimeConstant + if (compileTimeConstant != null) { + val expressionType = trace[BindingContext.EXPRESSION_TYPE_INFO, expression]?.type + if (expressionType != null && + !UnsignedTypes.isUnsignedType(compileTimeConstant.type) && UnsignedTypes.isUnsignedType(expressionType) + ) { + return + } + } trace.report( Errors.TYPE_MISMATCH.on( deparenthesized, diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/tower/KotlinToResolvedCallTransformer.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/tower/KotlinToResolvedCallTransformer.kt index 7a363729cf8..75d02c765a5 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/tower/KotlinToResolvedCallTransformer.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/tower/KotlinToResolvedCallTransformer.kt @@ -7,6 +7,7 @@ package org.jetbrains.kotlin.resolve.calls.tower import org.jetbrains.kotlin.builtins.KotlinBuiltIns import org.jetbrains.kotlin.descriptors.* +import org.jetbrains.kotlin.descriptors.annotations.Annotations import org.jetbrains.kotlin.diagnostics.Diagnostic import org.jetbrains.kotlin.diagnostics.Errors import org.jetbrains.kotlin.psi.* @@ -35,7 +36,9 @@ import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowInfo import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowValueFactory import org.jetbrains.kotlin.resolve.calls.tasks.ExplicitReceiverKind import org.jetbrains.kotlin.resolve.calls.tasks.TracingStrategy +import org.jetbrains.kotlin.resolve.constants.CompileTimeConstant import org.jetbrains.kotlin.resolve.constants.IntegerLiteralTypeConstructor +import org.jetbrains.kotlin.resolve.constants.IntegerValueTypeConstant import org.jetbrains.kotlin.resolve.constants.evaluate.ConstantExpressionEvaluator import org.jetbrains.kotlin.resolve.deprecation.DeprecationResolver import org.jetbrains.kotlin.resolve.scopes.receivers.CastImplicitClassReceiver @@ -264,16 +267,22 @@ class KotlinToResolvedCallTransformer( for (valueArgument in resolvedCall.call.valueArguments) { val argumentMapping = resolvedCall.getArgumentMapping(valueArgument!!) + val parameter: ValueParameterDescriptor? val (expectedType, callPosition) = when (argumentMapping) { is ArgumentMatch -> { + parameter = argumentMapping.valueParameter + val expectedType = resolvedCall.getExpectedTypeForSamConvertedArgument(valueArgument) - ?: getEffectiveExpectedType(argumentMapping.valueParameter, valueArgument, context) + ?: getEffectiveExpectedType(argumentMapping.valueParameter, valueArgument, context) Pair( expectedType, CallPosition.ValueArgumentPosition(resolvedCall, argumentMapping.valueParameter, valueArgument) ) } - else -> Pair(TypeUtils.NO_EXPECTED_TYPE, CallPosition.Unknown) + else -> { + parameter = null + Pair(TypeUtils.NO_EXPECTED_TYPE, CallPosition.Unknown) + } } val newContext = context.replaceDataFlowInfo(resolvedCall.dataFlowInfoForArguments.getInfo(valueArgument)) @@ -284,13 +293,13 @@ class KotlinToResolvedCallTransformer( // todo external argument val argumentExpression = valueArgument.getArgumentExpression() ?: continue - updateRecordedType(argumentExpression, newContext, resolvedCall.isReallySuccess()) + updateRecordedType(argumentExpression, parameter, newContext, resolvedCall.isReallySuccess()) } - } fun updateRecordedType( expression: KtExpression, + parameter: ValueParameterDescriptor?, context: BasicCallResolutionContext, reportErrorForTypeMismatch: Boolean ): KotlinType? { @@ -309,13 +318,39 @@ class KotlinToResolvedCallTransformer( updatedType = argumentTypeResolver.updateResultArgumentTypeIfNotDenotable(context, deparenthesized) ?: updatedType } + + var reportErrorDuringTypeCheck = reportErrorForTypeMismatch + + if (parameter != null && ImplicitIntegerCoercion.isEnabledForParameter(parameter)) { + val argumentCompileTimeValue = context.trace[BindingContext.COMPILE_TIME_VALUE, deparenthesized] + if (argumentCompileTimeValue != null && argumentCompileTimeValue.parameters.isConvertableConstVal) { + val generalNumberType = createTypeForConvertableConstant(argumentCompileTimeValue) + if (generalNumberType != null) { + updatedType = argumentTypeResolver.updateResultArgumentTypeIfNotDenotable( + context.trace, context.statementFilter, context.expectedType, generalNumberType, expression + ) + reportErrorDuringTypeCheck = true + } + + } + } + updatedType = updateRecordedTypeForArgument(updatedType, recordedType, expression, context) - dataFlowAnalyzer.checkType(updatedType, deparenthesized, context, reportErrorForTypeMismatch) + dataFlowAnalyzer.checkType(updatedType, deparenthesized, context, reportErrorDuringTypeCheck) return updatedType } + private fun createTypeForConvertableConstant(constant: CompileTimeConstant<*>): SimpleType? { + val value = constant.getValue(TypeUtils.NO_EXPECTED_TYPE).safeAs()?.toLong() ?: return null + val typeConstructor = IntegerLiteralTypeConstructor(value, moduleDescriptor, constant.parameters) + return KotlinTypeFactory.simpleTypeWithNonTrivialMemberScope( + Annotations.EMPTY, typeConstructor, emptyList(), false, + ErrorUtils.createErrorScope("Scope for number value type ($typeConstructor)", true) + ) + } + private fun getResolvedCallForArgumentExpression(expression: KtExpression, context: BasicCallResolutionContext) = if (!ExpressionTypingUtils.dependsOnExpectedType(expression)) null @@ -374,36 +409,39 @@ class KotlinToResolvedCallTransformer( return expressionType != null && TypeUtils.isNullableType(expressionType) } - internal fun bindAndReport( + internal fun bind(trace: BindingTrace, resolvedCall: ResolvedCall<*>) { + resolvedCall.safeAs>()?.let { bind(trace, it) } + resolvedCall.safeAs()?.let { bind(trace, it) } + } + + fun reportDiagnostics( context: BasicCallResolutionContext, trace: BindingTrace, resolvedCall: ResolvedCall<*>, diagnostics: Collection ) { - resolvedCall.safeAs>()?.let { bindAndReport(context, trace, it, diagnostics) } - resolvedCall.safeAs()?.let { bindAndReport(context, trace, it, diagnostics) } + when (resolvedCall) { + is NewResolvedCallImpl -> + reportCallDiagnostic(context, trace, resolvedCall.resolvedCallAtom, resolvedCall.resultingDescriptor, diagnostics) + + is NewVariableAsFunctionResolvedCallImpl -> { + val variableCall = resolvedCall.variableCall + val functionCall = resolvedCall.functionCall + + reportCallDiagnostic(context, trace, variableCall.resolvedCallAtom, variableCall.resultingDescriptor, diagnostics) + reportCallDiagnostic(context, trace, functionCall.resolvedCallAtom, functionCall.resultingDescriptor, emptyList()) + } + } } - private fun bindAndReport( - context: BasicCallResolutionContext, - trace: BindingTrace, - simpleResolvedCall: NewResolvedCallImpl<*>, - diagnostics: Collection - ) { + private fun bind(trace: BindingTrace, simpleResolvedCall: NewResolvedCallImpl<*>) { val tracing = simpleResolvedCall.resolvedCallAtom.atom.psiKotlinCall.tracingStrategy tracing.bindReference(trace, simpleResolvedCall) tracing.bindResolvedCall(trace, simpleResolvedCall) - - reportCallDiagnostic(context, trace, simpleResolvedCall.resolvedCallAtom, simpleResolvedCall.resultingDescriptor, diagnostics) } - private fun bindAndReport( - context: BasicCallResolutionContext, - trace: BindingTrace, - variableAsFunction: NewVariableAsFunctionResolvedCallImpl, - diagnostics: Collection - ) { + private fun bind(trace: BindingTrace, variableAsFunction: NewVariableAsFunctionResolvedCallImpl) { val outerTracingStrategy = variableAsFunction.baseCall.tracingStrategy val variableCall = variableAsFunction.variableCall val functionCall = variableAsFunction.functionCall @@ -411,9 +449,6 @@ class KotlinToResolvedCallTransformer( outerTracingStrategy.bindReference(trace, variableCall) outerTracingStrategy.bindResolvedCall(trace, variableAsFunction) functionCall.kotlinCall.psiKotlinCall.tracingStrategy.bindReference(trace, functionCall) - - reportCallDiagnostic(context, trace, variableCall.resolvedCallAtom, variableCall.resultingDescriptor, diagnostics) - reportCallDiagnostic(context, trace, functionCall.resolvedCallAtom, functionCall.resultingDescriptor, emptyList()) } fun reportCallDiagnostic( diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/tower/ResolvedAtomCompleter.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/tower/ResolvedAtomCompleter.kt index 3375dc8c1a0..3a2fb6e34c2 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/tower/ResolvedAtomCompleter.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/tower/ResolvedAtomCompleter.kt @@ -102,12 +102,14 @@ class ResolvedAtomCompleter( else topLevelCallCheckerContext - kotlinToResolvedCallTransformer.bindAndReport(topLevelCallContext, topLevelTrace, resolvedCall, diagnostics) + kotlinToResolvedCallTransformer.bind(topLevelTrace, resolvedCall) kotlinToResolvedCallTransformer.runArgumentsChecks(topLevelCallContext, topLevelTrace, lastCall as NewResolvedCallImpl<*>) kotlinToResolvedCallTransformer.runCallCheckers(resolvedCall, callCheckerContext) kotlinToResolvedCallTransformer.runAdditionalReceiversCheckers(resolvedCall, topLevelCallContext) + kotlinToResolvedCallTransformer.reportDiagnostics(topLevelCallContext, topLevelTrace, resolvedCall, diagnostics) + return resolvedCall } @@ -153,7 +155,9 @@ class ResolvedAtomCompleter( .replaceBindingTrace(topLevelTrace) val argumentExpression = resultValueArgument.valueArgument.getArgumentExpression() ?: continue - kotlinToResolvedCallTransformer.updateRecordedType(argumentExpression, newContext, true) + kotlinToResolvedCallTransformer.updateRecordedType( + argumentExpression, parameter = null, context = newContext, reportErrorForTypeMismatch = true + ) } } diff --git a/compiler/testData/diagnostics/testsWithUnsignedTypes/conversions/conversionOfSignedToUnsigned.kt b/compiler/testData/diagnostics/testsWithUnsignedTypes/conversions/conversionOfSignedToUnsigned.kt index 6387689f38b..1f3f5ccb3b5 100644 --- a/compiler/testData/diagnostics/testsWithUnsignedTypes/conversions/conversionOfSignedToUnsigned.kt +++ b/compiler/testData/diagnostics/testsWithUnsignedTypes/conversions/conversionOfSignedToUnsigned.kt @@ -1,4 +1,5 @@ // !DIAGNOSTICS: -UNUSED_PARAMETER +// !WITH_NEW_INFERENCE // FILE: annotation.kt @@ -54,7 +55,7 @@ fun test() { takeUBytes(IMPLICIT_INT, EXPLICIT_INT, 42u) - takeLong(IMPLICIT_INT) + takeLong(IMPLICIT_INT) takeIntWithoutAnnotation(IMPLICIT_INT) @@ -62,7 +63,7 @@ fun test() { takeUByte(LONG_CONST) takeUByte(NON_CONST) - takeUByte(BIGGER_THAN_UBYTE) + takeUByte(BIGGER_THAN_UBYTE) takeUByte(UINT_CONST) takeUIntWithoutAnnotaion(IMPLICIT_INT) } \ No newline at end of file