[NI] Support ad-hoc implicit integer coercion for Kotlin/Native
This commit is contained in:
+12
@@ -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,
|
||||
|
||||
+60
-25
@@ -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(
|
||||
|
||||
+6
-2
@@ -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
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Vendored
+3
-2
@@ -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<!>)
|
||||
}
|
||||
Reference in New Issue
Block a user