[NI] Support ad-hoc implicit integer coercion for Kotlin/Native

This commit is contained in:
Mikhail Zarechenskiy
2019-05-06 16:28:16 +03:00
parent c8a4fa58cd
commit 931bbd8cec
4 changed files with 81 additions and 29 deletions
@@ -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,
@@ -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<Number>()?.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<NewResolvedCallImpl<*>>()?.let { bind(trace, it) }
resolvedCall.safeAs<NewVariableAsFunctionResolvedCallImpl>()?.let { bind(trace, it) }
}
fun reportDiagnostics(
context: BasicCallResolutionContext,
trace: BindingTrace,
resolvedCall: ResolvedCall<*>,
diagnostics: Collection<KotlinCallDiagnostic>
) {
resolvedCall.safeAs<NewResolvedCallImpl<*>>()?.let { bindAndReport(context, trace, it, diagnostics) }
resolvedCall.safeAs<NewVariableAsFunctionResolvedCallImpl>()?.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<KotlinCallDiagnostic>
) {
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<KotlinCallDiagnostic>
) {
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(
@@ -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
)
}
}
@@ -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(<!NI;TYPE_MISMATCH!>IMPLICIT_INT<!>)
takeIntWithoutAnnotation(IMPLICIT_INT)
@@ -62,7 +63,7 @@ fun test() {
takeUByte(<!TYPE_MISMATCH!>LONG_CONST<!>)
takeUByte(<!TYPE_MISMATCH!>NON_CONST<!>)
takeUByte(<!TYPE_MISMATCH!>BIGGER_THAN_UBYTE<!>)
takeUByte(<!NI;TYPE_MISMATCH, TYPE_MISMATCH!>BIGGER_THAN_UBYTE<!>)
takeUByte(<!TYPE_MISMATCH!>UINT_CONST<!>)
takeUIntWithoutAnnotaion(<!TYPE_MISMATCH!>IMPLICIT_INT<!>)
}