[FE 1.0] Introduce builder inference stub types checker which may report more precise and clear errors due to resolution ambiguity

^KT-49828 Fixed
This commit is contained in:
Victor Petukhov
2022-01-10 14:16:42 +03:00
committed by teamcity
parent dcc42d66c3
commit 455b3143e7
25 changed files with 884 additions and 71 deletions
@@ -27,8 +27,10 @@ import org.jetbrains.kotlin.name.Name;
import org.jetbrains.kotlin.psi.*;
import org.jetbrains.kotlin.resolve.VarianceConflictDiagnosticData;
import org.jetbrains.kotlin.resolve.calls.inference.InferenceErrorData;
import org.jetbrains.kotlin.resolve.calls.inference.model.TypeVariableTypeConstructor;
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall;
import org.jetbrains.kotlin.resolve.calls.tower.WrongResolutionToClassifier;
import org.jetbrains.kotlin.resolve.calls.util.BuilderLambdaLabelingInfo;
import org.jetbrains.kotlin.resolve.deprecation.DescriptorBasedDeprecationInfo;
import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualCompatibility.Incompatible;
import org.jetbrains.kotlin.serialization.deserialization.IncompatibleVersionErrorData;
@@ -839,6 +841,9 @@ public interface Errors {
DiagnosticFactory0<KtExpression> NOT_A_CLASS = DiagnosticFactory0.create(ERROR);
DiagnosticFactory1<PsiElement, Collection<? extends ResolvedCall<?>>> OVERLOAD_RESOLUTION_AMBIGUITY = DiagnosticFactory1.create(ERROR);
DiagnosticFactory3<PsiElement, String, String, String> OVERLOAD_RESOLUTION_AMBIGUITY_BECAUSE_OF_STUB_TYPES = DiagnosticFactory3.create(ERROR);
DiagnosticFactory3<PsiElement, KotlinType, String, String> STUB_TYPE_IN_ARGUMENT_CAUSES_AMBIGUITY = DiagnosticFactory3.create(ERROR);
DiagnosticFactory4<PsiElement, KotlinType, String, String, BuilderLambdaLabelingInfo> STUB_TYPE_IN_RECEIVER_CAUSES_AMBIGUITY = DiagnosticFactory4.create(ERROR);
DiagnosticFactory1<PsiElement, Collection<? extends ResolvedCall<?>>> NONE_APPLICABLE = DiagnosticFactory1.create(ERROR);
DiagnosticFactory1<PsiElement, Collection<? extends ResolvedCall<?>>> CANNOT_COMPLETE_RESOLVE = DiagnosticFactory1.create(ERROR);
DiagnosticFactory1<PsiElement, Collection<? extends ResolvedCall<?>>> UNRESOLVED_REFERENCE_WRONG_RECEIVER =
@@ -917,6 +917,11 @@ public class DefaultErrorMessages {
ELEMENT_TEXT, STRING, ELEMENT_TEXT);
MAP.put(OVERLOAD_RESOLUTION_AMBIGUITY, "Overload resolution ambiguity: {0}", AMBIGUOUS_CALLS);
MAP.put(OVERLOAD_RESOLUTION_AMBIGUITY_BECAUSE_OF_STUB_TYPES, "The builder `{0}` you are using has a type argument for type parameter(s) `{1}` that was not explicitly specified. " +
"Without knowing the type of `{1}` compiler cannot choose which overloaded function `{2}` to call here. " +
"Please, either specify the type `{1}` explicitly in `{0}` builder or use explicit cast to a specific type for parameter or receiver (see specific errors on them).", STRING, STRING, STRING);
MAP.put(STUB_TYPE_IN_ARGUMENT_CAUSES_AMBIGUITY, "Type of an argument hasn't inferred yet. To disambiguate this call, please use explicit cast for the parameter to {1} if you rely a type argument for type parameter(s) {2} to be inferred to {3}", RENDER_TYPE, STRING, STRING);
MAP.put(STUB_TYPE_IN_RECEIVER_CAUSES_AMBIGUITY, "Type of a receiver hasn't inferred yet. To disambiguate this call, please use explicit cast for the receiver to {1} if you rely a type argument for type parameter(s) {2} to be inferred to {3}", RENDER_TYPE, STRING, STRING, null);
MAP.put(NONE_APPLICABLE, "None of the following functions can be called with the arguments supplied: {0}", AMBIGUOUS_CALLS);
MAP.put(CANNOT_COMPLETE_RESOLVE, "Cannot choose among the following candidates without completing type inference: {0}", AMBIGUOUS_CALLS);
MAP.put(UNRESOLVED_REFERENCE_WRONG_RECEIVER, "Unresolved reference. None of the following candidates is applicable because of receiver type mismatch: {0}", AMBIGUOUS_CALLS);
@@ -0,0 +1,22 @@
/*
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.resolve.calls.checkers
import org.jetbrains.kotlin.resolve.calls.components.KotlinResolutionCallbacks
import org.jetbrains.kotlin.resolve.calls.context.BasicCallResolutionContext
import org.jetbrains.kotlin.resolve.calls.results.OverloadResolutionResults
import org.jetbrains.kotlin.resolve.calls.tower.ImplicitScopeTower
import org.jetbrains.kotlin.types.UnwrappedType
interface CallCheckerWithAdditionalResolve {
fun check(
overloadResolutionResults: OverloadResolutionResults<*>,
scopeTower: ImplicitScopeTower,
resolutionCallbacks: KotlinResolutionCallbacks,
expectedType: UnwrappedType?,
context: BasicCallResolutionContext,
)
}
@@ -217,11 +217,13 @@ class BuilderInferenceSession(
override fun currentConstraintSystem() = ConstraintStorage.Empty
fun getNotFixedToInferredTypesSubstitutor(): NewTypeSubstitutor {
val currentSubstitutor =
commonSystem.buildCurrentSubstitutor().cast<NewTypeSubstitutor>().takeIf { !it.isEmpty } ?: return EmptySubstitutor
return ComposedSubstitutor(currentSubstitutor, createNonFixedTypeToVariableSubstitutor())
}
fun getNotFixedToInferredTypesSubstitutor(): NewTypeSubstitutor =
ComposedSubstitutor(getCurrentSubstitutor(), createNonFixedTypeToVariableSubstitutor())
fun getUsedStubTypes(): Set<StubTypeForBuilderInference> = stubsForPostponedVariables.values.toSet()
fun getCurrentSubstitutor(): NewTypeSubstitutor =
commonSystem.buildCurrentSubstitutor().cast<NewTypeSubstitutor>().takeIf { !it.isEmpty } ?: EmptySubstitutor
override fun initializeLambda(lambda: ResolvedLambdaAtom) {
this.lambda = lambda
@@ -208,13 +208,11 @@ class KotlinResolutionCallbacksImpl(
kotlinToResolvedCallTransformer, expressionTypingServices, argumentTypeResolver,
doubleColonExpressionResolver, deprecationResolver, moduleDescriptor, typeApproximator,
missingSupertypesResolver, lambdaArgument
)
).apply { lambdaArgument.builderInferenceSession = this }
} else {
null
}
lambdaArgument.builderInferenceSession = builderInferenceSession
val temporaryTrace = if (builderInferenceSession != null)
TemporaryBindingTrace.create(trace, "Trace to resolve builder inference lambda: $lambdaArgument")
else
@@ -39,6 +39,7 @@ import org.jetbrains.kotlin.resolve.calls.tasks.OldResolutionCandidate
import org.jetbrains.kotlin.resolve.calls.tasks.TracingStrategy
import org.jetbrains.kotlin.resolve.calls.util.*
import org.jetbrains.kotlin.resolve.checkers.PassingProgressionAsCollectionCallChecker
import org.jetbrains.kotlin.resolve.checkers.ResolutionWithStubTypesChecker
import org.jetbrains.kotlin.resolve.constants.evaluate.ConstantExpressionEvaluator
import org.jetbrains.kotlin.resolve.deprecation.DeprecationResolver
import org.jetbrains.kotlin.resolve.descriptorUtil.builtIns
@@ -78,7 +79,10 @@ class PSICallResolver(
private val missingSupertypesResolver: MissingSupertypesResolver,
private val resultTypeResolver: ResultTypeResolver,
) {
private val callCheckersWithAdditionalResolve = listOf(PassingProgressionAsCollectionCallChecker(kotlinCallResolver))
private val callCheckersWithAdditionalResolve = listOf(
PassingProgressionAsCollectionCallChecker(kotlinCallResolver),
ResolutionWithStubTypesChecker(kotlinCallResolver)
)
private val givenCandidatesName = Name.special("<given candidates>")
@@ -29,7 +29,7 @@ abstract class StubTypesBasedInferenceSession<D : CallableDescriptor>(
val builtIns: KotlinBuiltIns
) : InferenceSession {
protected val partiallyResolvedCallsInfo = arrayListOf<PSIPartialCallInfo>()
private val errorCallsInfo = arrayListOf<PSIErrorCallInfo<D>>()
val errorCallsInfo = arrayListOf<PSIErrorCallInfo<D>>()
private val completedCalls = hashSetOf<ResolvedAtom>()
protected val nestedInferenceSessions = hashSetOf<StubTypesBasedInferenceSession<*>>()
@@ -19,26 +19,34 @@ import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType
import org.jetbrains.kotlin.resolve.BindingTrace
import org.jetbrains.kotlin.resolve.calls.CallTransformer
import org.jetbrains.kotlin.resolve.calls.components.KotlinResolutionCallbacks
import org.jetbrains.kotlin.resolve.calls.components.isVararg
import org.jetbrains.kotlin.resolve.calls.context.BasicCallResolutionContext
import org.jetbrains.kotlin.resolve.calls.context.ResolutionContext
import org.jetbrains.kotlin.resolve.calls.inference.ComposedSubstitutor
import org.jetbrains.kotlin.resolve.calls.inference.ConstraintSystem
import org.jetbrains.kotlin.resolve.calls.inference.components.EmptySubstitutor
import org.jetbrains.kotlin.resolve.calls.inference.components.NewTypeSubstitutor
import org.jetbrains.kotlin.resolve.calls.inference.constraintPosition.ConstraintPositionKind.EXPECTED_TYPE_POSITION
import org.jetbrains.kotlin.resolve.calls.inference.getNestedTypeVariables
import org.jetbrains.kotlin.resolve.calls.model.ResolvedValueArgument
import org.jetbrains.kotlin.resolve.calls.model.*
import org.jetbrains.kotlin.resolve.calls.tasks.ExplicitReceiverKind
import org.jetbrains.kotlin.resolve.calls.tasks.OldResolutionCandidate
import org.jetbrains.kotlin.resolve.calls.tower.*
import org.jetbrains.kotlin.resolve.descriptorUtil.isParameterOfAnnotation
import org.jetbrains.kotlin.resolve.scopes.LexicalScope
import org.jetbrains.kotlin.resolve.scopes.SyntheticScopes
import org.jetbrains.kotlin.resolve.scopes.collectSyntheticConstructors
import org.jetbrains.kotlin.resolve.scopes.receivers.ExpressionReceiver
import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue
import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValueWithSmartCastInfo
import org.jetbrains.kotlin.resolve.scopes.utils.getImplicitReceiversHierarchy
import org.jetbrains.kotlin.types.*
import org.jetbrains.kotlin.types.TypeUtils.DONT_CARE
import org.jetbrains.kotlin.types.checker.KotlinTypeChecker
import org.jetbrains.kotlin.types.expressions.OperatorConventions
import org.jetbrains.kotlin.types.typeUtil.contains
import org.jetbrains.kotlin.util.buildNotFixedVariablesToPossibleResultType
import org.jetbrains.kotlin.utils.SmartList
enum class ResolveArgumentsMode {
@@ -328,3 +336,43 @@ internal fun PsiElement.reportOnElement() =
?.takeIf { isImplicit }
?.let { getStrictParentOfType<KtSecondaryConstructor>()!! }
?: this
internal fun List<KotlinCallArgument>.replaceTypes(
context: BasicCallResolutionContext,
resolutionCallbacks: KotlinResolutionCallbacks,
replace: (Int, UnwrappedType) -> UnwrappedType?,
): List<KotlinCallArgument> = mapIndexed { i, argument ->
if (argument !is SimpleKotlinCallArgument) return@mapIndexed argument
val psiExpression = argument.psiExpression ?: return@mapIndexed argument
val argumentSubstitutor = if (argument is SubKotlinCallArgument) {
val notFixedVariablesSubstitutor =
argument.callResult.constraintSystem.buildNotFixedVariablesToPossibleResultType(resolutionCallbacks) as NewTypeSubstitutor
val fixedVariablesSubstitutor =
argument.callResult.constraintSystem.getBuilder().buildCurrentSubstitutor() as NewTypeSubstitutor
ComposedSubstitutor(notFixedVariablesSubstitutor, fixedVariablesSubstitutor)
} else EmptySubstitutor
val newType = replace(i, argumentSubstitutor.safeSubstitute(argument.receiver.receiverValue.type.unwrap()))
?: return@mapIndexed argument
ExpressionKotlinCallArgumentImpl(
argument.psiCallArgument.valueArgument,
argument.psiCallArgument.dataFlowInfoBeforeThisArgument,
argument.psiCallArgument.dataFlowInfoAfterThisArgument,
ReceiverValueWithSmartCastInfo(
ExpressionReceiver.create(psiExpression, newType, context.trace.bindingContext),
typesFromSmartCasts = emptySet(),
isStable = true
)
)
}
internal fun PSIKotlinCall.replaceArguments(
newArguments: List<KotlinCallArgument>,
newReceiverArgument: ReceiverExpressionKotlinCallArgument? = null,
): PSIKotlinCall = PSIKotlinCallImpl(
callKind, psiCall, tracingStrategy, newReceiverArgument, dispatchReceiverForInvokeExtension, name, typeArguments, newArguments,
externalArgument, startingDataFlowInfo, resultDataFlowInfo, dataFlowInfoForArguments, isForImplicitInvoke
)
@@ -0,0 +1,18 @@
/*
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.resolve.calls.util
import org.jetbrains.kotlin.psi.KtLambdaExpression
class BuilderLambdaLabelingInfo(val builderLambda: KtLambdaExpression?) {
companion object {
val EMPTY = BuilderLambdaLabelingInfo(null)
}
override fun toString(): String {
return builderLambda.toString()
}
}
@@ -7,21 +7,18 @@ package org.jetbrains.kotlin.resolve.checkers
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.config.LanguageFeature
import org.jetbrains.kotlin.descriptors.annotations.Annotations
import org.jetbrains.kotlin.diagnostics.Errors.PROGRESSIONS_CHANGING_RESOLVE
import org.jetbrains.kotlin.resolve.BindingTrace
import org.jetbrains.kotlin.resolve.calls.KotlinCallResolver
import org.jetbrains.kotlin.resolve.calls.checkers.CallCheckerWithAdditionalResolve
import org.jetbrains.kotlin.resolve.calls.components.KotlinResolutionCallbacks
import org.jetbrains.kotlin.resolve.calls.context.BasicCallResolutionContext
import org.jetbrains.kotlin.resolve.calls.model.KotlinCallArgument
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
import org.jetbrains.kotlin.resolve.calls.model.SimpleKotlinCallArgument
import org.jetbrains.kotlin.resolve.calls.results.OverloadResolutionResults
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowInfo
import org.jetbrains.kotlin.resolve.calls.tower.*
import org.jetbrains.kotlin.resolve.calls.util.replaceArguments
import org.jetbrains.kotlin.resolve.calls.util.replaceTypes
import org.jetbrains.kotlin.resolve.descriptorUtil.builtIns
import org.jetbrains.kotlin.resolve.scopes.receivers.ExpressionReceiver
import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValueWithSmartCastInfo
import org.jetbrains.kotlin.types.*
import org.jetbrains.kotlin.types.checker.ClassicTypeCheckerState
import org.jetbrains.kotlin.types.checker.ClassicTypeCheckerStateInternals
@@ -33,7 +30,7 @@ import org.jetbrains.kotlin.types.typeUtil.isTypeParameter
Please don't use similar logic for other checkers
*/
@OptIn(ClassicTypeCheckerStateInternals::class)
class PassingProgressionAsCollectionCallChecker(private val kotlinCallResolver: KotlinCallResolver) {
class PassingProgressionAsCollectionCallChecker(private val kotlinCallResolver: KotlinCallResolver) : CallCheckerWithAdditionalResolve {
private val typeCheckerState = ClassicTypeCheckerState(isErrorTypeEqualsToAnything = false)
private val iterableProgressions = listOf(
@@ -67,14 +64,21 @@ class PassingProgressionAsCollectionCallChecker(private val kotlinCallResolver:
if (progressionOrRangeArgumentTypes.all { it == null }) return
val builtIns = resolvedCall.candidateDescriptor.builtIns
val newArguments = replaceArgumentsWithCollectionIfNeeded(valueArguments, progressionOrRangeArgumentTypes, context.trace, builtIns)
val newCall = PSIKotlinCallImpl(
kotlinCall.callKind, kotlinCall.psiCall, kotlinCall.tracingStrategy, kotlinCall.explicitReceiver,
kotlinCall.dispatchReceiverForInvokeExtension, kotlinCall.name, kotlinCall.typeArguments, newArguments,
kotlinCall.externalArgument, kotlinCall.startingDataFlowInfo, kotlinCall.resultDataFlowInfo,
kotlinCall.dataFlowInfoForArguments, kotlinCall.isForImplicitInvoke
)
val newArguments = valueArguments.replaceTypes(context, resolutionCallbacks) { i, type ->
val progressionOrRangeElementType = progressionOrRangeArgumentTypes[i] ?: return@replaceTypes null
intersectTypes(
listOf(
KotlinTypeFactory.simpleNotNullType(
TypeAttributes.Empty,
builtIns.collection,
listOf(TypeProjectionImpl(progressionOrRangeElementType))
),
type
)
)
}
val newCall = kotlinCall.replaceArguments(newArguments)
val candidateForCollectionReplacedArgument = kotlinCallResolver.resolveCall(
scopeTower, resolutionCallbacks, newCall, expectedType, context.collectAllCandidates
@@ -129,7 +133,7 @@ class PassingProgressionAsCollectionCallChecker(private val kotlinCallResolver:
}
}
fun check(
override fun check(
overloadResolutionResults: OverloadResolutionResults<*>,
scopeTower: ImplicitScopeTower,
resolutionCallbacks: KotlinResolutionCallbacks,
@@ -149,36 +153,4 @@ class PassingProgressionAsCollectionCallChecker(private val kotlinCallResolver:
builtIns.collection,
listOf(TypeProjectionImpl(builtIns.nullableAnyType))
)
private fun replaceArgumentsWithCollectionIfNeeded(
valueArguments: List<KotlinCallArgument>,
progressionOrRangeArgumentTypes: List<KotlinType?>,
trace: BindingTrace,
builtIns: KotlinBuiltIns
): List<KotlinCallArgument> = valueArguments.mapIndexed { i, argument ->
if (argument !is ExpressionKotlinCallArgumentImpl) return@mapIndexed argument
val progressionOrRangeElementType = progressionOrRangeArgumentTypes[i] ?: return@mapIndexed argument
val psiExpression = argument.psiExpression ?: return@mapIndexed argument
val newType = intersectTypes(
listOf(
KotlinTypeFactory.simpleNotNullType(
TypeAttributes.Empty,
builtIns.collection,
listOf(TypeProjectionImpl(progressionOrRangeElementType))
),
argument.receiver.receiverValue.type.unwrap()
)
)
ExpressionKotlinCallArgumentImpl(
argument.psiCallArgument.valueArgument,
DataFlowInfo.EMPTY,
DataFlowInfo.EMPTY,
ReceiverValueWithSmartCastInfo(
ExpressionReceiver.create(psiExpression, newType, trace.bindingContext),
emptySet(),
true
)
)
}
}
@@ -0,0 +1,200 @@
/*
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.resolve.checkers
import org.jetbrains.kotlin.diagnostics.Errors.*
import org.jetbrains.kotlin.psi.KtLambdaExpression
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.calls.KotlinCallResolver
import org.jetbrains.kotlin.resolve.calls.checkers.CallCheckerWithAdditionalResolve
import org.jetbrains.kotlin.resolve.calls.components.KotlinResolutionCallbacks
import org.jetbrains.kotlin.resolve.calls.components.stableType
import org.jetbrains.kotlin.resolve.calls.context.BasicCallResolutionContext
import org.jetbrains.kotlin.resolve.calls.inference.BuilderInferenceSession
import org.jetbrains.kotlin.resolve.calls.inference.components.NewTypeSubstitutor
import org.jetbrains.kotlin.resolve.calls.inference.components.NewTypeSubstitutorByConstructorMap
import org.jetbrains.kotlin.resolve.calls.model.*
import org.jetbrains.kotlin.resolve.calls.results.OverloadResolutionResults
import org.jetbrains.kotlin.resolve.calls.tower.*
import org.jetbrains.kotlin.resolve.calls.util.BuilderLambdaLabelingInfo
import org.jetbrains.kotlin.resolve.calls.util.replaceArguments
import org.jetbrains.kotlin.resolve.calls.util.replaceTypes
import org.jetbrains.kotlin.resolve.scopes.receivers.*
import org.jetbrains.kotlin.types.*
import org.jetbrains.kotlin.types.checker.NewTypeVariableConstructor
class ResolutionWithStubTypesChecker(private val kotlinCallResolver: KotlinCallResolver) : CallCheckerWithAdditionalResolve {
override fun check(
overloadResolutionResults: OverloadResolutionResults<*>,
scopeTower: ImplicitScopeTower,
resolutionCallbacks: KotlinResolutionCallbacks,
expectedType: UnwrappedType?,
context: BasicCallResolutionContext,
) {
// Don't check builder inference lambdas if the entire builder call itself has resolution ambiguity
if (!overloadResolutionResults.isSingleResult) return
val builderResolvedCall = overloadResolutionResults.resultingCall as? NewAbstractResolvedCall<*> ?: return
val builderLambdas = (builderResolvedCall.psiKotlinCall.argumentsInParenthesis + builderResolvedCall.psiKotlinCall.externalArgument)
.filterIsInstance<LambdaKotlinCallArgument>()
.filter { it.hasBuilderInferenceAnnotation }
for (lambda in builderLambdas) {
val builderInferenceSession = lambda.builderInferenceSession as? BuilderInferenceSession ?: continue
val errorCalls = builderInferenceSession.errorCallsInfo
for (errorCall in errorCalls) {
val resolutionResult = errorCall.result
if (resolutionResult.isAmbiguity) {
val firstResolvedCall = resolutionResult.resultingCalls.first() as? NewAbstractResolvedCall<*> ?: continue
processResolutionAmbiguityError(context, firstResolvedCall, lambda, resolutionCallbacks, expectedType, scopeTower)
}
}
}
}
private fun processResolutionAmbiguityError(
context: BasicCallResolutionContext,
firstResolvedCall: NewAbstractResolvedCall<*>,
lambda: LambdaKotlinCallArgument,
resolutionCallbacks: KotlinResolutionCallbacks,
expectedType: UnwrappedType?,
scopeTower: ImplicitScopeTower,
) {
val kotlinCall = firstResolvedCall.psiKotlinCall
val calleeExpression = kotlinCall.psiCall.calleeExpression
val builderCalleeExpression = context.call.calleeExpression
if (calleeExpression == null || builderCalleeExpression == null) return
val receiverValue = firstResolvedCall.extensionReceiver
val valueArguments = kotlinCall.argumentsInParenthesis
val builderInferenceSession = lambda.builderInferenceSession as BuilderInferenceSession
val stubVariablesSubstitutor = builderInferenceSession.getNotFixedToInferredTypesSubstitutor()
val variablesForUsedStubTypes = builderInferenceSession.getUsedStubTypes().map { it.originalTypeVariable }
val typeVariablesSubstitutionMap = (builderInferenceSession.getCurrentSubstitutor() as NewTypeSubstitutorByConstructorMap).map
.filterKeys { it in variablesForUsedStubTypes }
val newReceiverArgument = receiverValue?.buildSubstitutedReceiverArgument(stubVariablesSubstitutor, context)
val newArguments = valueArguments.replaceTypes(context, resolutionCallbacks) { _, type ->
stubVariablesSubstitutor.safeSubstitute(type)
}
if (newReceiverArgument == null && valueArguments == newArguments) return
val newCall = kotlinCall.replaceArguments(newArguments, newReceiverArgument)
val candidatesForSubstitutedCall = kotlinCallResolver.resolveCall(
scopeTower, resolutionCallbacks, newCall, expectedType, context.collectAllCandidates
)
// It means we can't disambiguate the call with substituted receiver and arguments
if (candidatesForSubstitutedCall.size != 1) return
val typeVariablesCausedAmbiguity = reportStubTypeCausesAmbiguityOnArgumentsIfNeeded(
valueArguments, newArguments, context, typeVariablesSubstitutionMap
).toMutableSet()
val newReceiverValue = newReceiverArgument?.receiverValue
if (receiverValue != null && newReceiverValue != null) {
typeVariablesCausedAmbiguity.addAll(
reportStubTypeCausesAmbiguityOnReceiverIfNeeded(
receiverValue, newReceiverValue, kotlinCall, lambda, context, typeVariablesSubstitutionMap
)
)
}
if (typeVariablesCausedAmbiguity.isNotEmpty()) {
context.trace.report(
OVERLOAD_RESOLUTION_AMBIGUITY_BECAUSE_OF_STUB_TYPES.on(
calleeExpression,
builderCalleeExpression.toString(),
typeVariablesCausedAmbiguity.toString(),
calleeExpression.toString()
)
)
}
}
private fun reportStubTypeCausesAmbiguityOnReceiverIfNeeded(
receiver: ReceiverValue,
newReceiver: ReceiverValue,
kotlinCall: PSIKotlinCall,
lambda: LambdaKotlinCallArgument,
context: BasicCallResolutionContext,
substitutionMap: Map<TypeConstructor, UnwrappedType>
): Set<NewTypeVariableConstructor> = buildSet {
val receiverType = receiver.type
val newReceiverType = newReceiver.type
val relatedLambdaToLabel = (lambda.psiExpression as? KtLambdaExpression)?.takeIf {
val lexicalScope = context.trace.bindingContext[BindingContext.LEXICAL_SCOPE, kotlinCall.psiCall.callElement]
val nearestScopeDescriptor = lexicalScope?.ownerDescriptor
// Don't need to store lambda psi element if it can be accessed though unmarked `this`
nearestScopeDescriptor != null && nearestScopeDescriptor != (receiver as? ExtensionReceiver)?.declarationDescriptor
}
if (receiverType != newReceiverType) {
val typeVariables = substitutionMap.map { it.key as NewTypeVariableConstructor }
val typeParameters = typeVariables.joinToString { (it.originalTypeParameter?.name ?: it).toString() }
val inferredTypes = substitutionMap.values.joinToString()
addAll(typeVariables)
context.trace.report(
STUB_TYPE_IN_RECEIVER_CAUSES_AMBIGUITY.on(
kotlinCall.explicitReceiver?.psiExpression ?: kotlinCall.psiCall.callElement,
newReceiverType, typeParameters, inferredTypes,
if (relatedLambdaToLabel != null) BuilderLambdaLabelingInfo(relatedLambdaToLabel) else BuilderLambdaLabelingInfo.EMPTY
)
)
}
}
private fun reportStubTypeCausesAmbiguityOnArgumentsIfNeeded(
valueArguments: List<KotlinCallArgument>,
newArguments: List<KotlinCallArgument>,
context: BasicCallResolutionContext,
substitutionMap: Map<TypeConstructor, UnwrappedType>
): Set<NewTypeVariableConstructor> = buildSet {
for ((i, valueArgument) in valueArguments.withIndex()) {
if (valueArgument !is SimpleKotlinCallArgument) continue
val substitutedValueArgument = newArguments[i] as? SimpleKotlinCallArgument ?: continue
val originalType = valueArgument.receiver.stableType
val substitutedType = substitutedValueArgument.receiver.stableType
if (originalType != substitutedType) {
val psiExpression = valueArgument.psiExpression ?: continue
val typeVariables = substitutionMap.map { it.key as NewTypeVariableConstructor }
val typeParameters = typeVariables.joinToString { (it.originalTypeParameter?.name ?: it).toString() }
val inferredTypes = substitutionMap.values.joinToString()
addAll(typeVariables)
context.trace.report(
STUB_TYPE_IN_ARGUMENT_CAUSES_AMBIGUITY.on(psiExpression, substitutedType, typeParameters, inferredTypes)
)
}
}
}
private fun ReceiverValue.buildSubstitutedReceiverArgument(
substitutor: NewTypeSubstitutor,
context: BasicCallResolutionContext,
): ReceiverExpressionKotlinCallArgument? {
val newType = substitutor.safeSubstitute(type.unwrap())
val receiverValue = when (this) {
is ExpressionReceiver -> ExpressionReceiver.create(expression, newType, context.trace.bindingContext)
is ExtensionReceiver -> ExtensionReceiver(declarationDescriptor, newType, original)
else -> return null
}
return ReceiverExpressionKotlinCallArgument(
ReceiverValueWithSmartCastInfo(receiverValue, typesFromSmartCasts = emptySet(), true)
)
}
}
@@ -373,7 +373,7 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor {
return;
}
if (!CastDiagnosticsUtil.isCastPossible(actualType, targetType, components.platformToKotlinClassMapper) && !TypeUtilsKt.isStubType(actualType)) {
if (!TypeUtilsKt.isStubType(actualType) && !CastDiagnosticsUtil.isCastPossible(actualType, targetType, components.platformToKotlinClassMapper)) {
context.trace.report(CAST_NEVER_SUCCEEDS.on(expression.getOperationReference()));
return;
}