[NI] Resolve receiver of provideDelegate independently
#KT-38259 Fixed
This commit is contained in:
+4
-23
@@ -90,17 +90,9 @@ class DelegatedPropertyInferenceSession(
|
||||
override fun shouldCompleteResolvedSubAtomsOf(resolvedCallAtom: ResolvedCallAtom) = true
|
||||
}
|
||||
|
||||
object InferenceSessionForExistingCandidates : InferenceSession {
|
||||
class InferenceSessionForExistingCandidates(private val resolveReceiverIndependently: Boolean) : InferenceSession {
|
||||
override fun shouldRunCompletion(candidate: KotlinResolutionCandidate): Boolean {
|
||||
if (ErrorUtils.isError(candidate.resolvedCall.candidateDescriptor)) return false
|
||||
|
||||
val builder = candidate.getSystem().getBuilder()
|
||||
for ((_, variable) in builder.currentStorage().notFixedTypeVariables) {
|
||||
val hasProperConstraint = variable.constraints.any { candidate.getSystem().getBuilder().isProperType(it.type) }
|
||||
if (hasProperConstraint) return true
|
||||
}
|
||||
|
||||
return false
|
||||
return !ErrorUtils.isError(candidate.resolvedCall.candidateDescriptor)
|
||||
}
|
||||
|
||||
override fun addPartialCallInfo(callInfo: PartialCallInfo) {}
|
||||
@@ -122,18 +114,7 @@ object InferenceSessionForExistingCandidates : InferenceSession {
|
||||
|
||||
override fun computeCompletionMode(
|
||||
candidate: KotlinResolutionCandidate
|
||||
): KotlinConstraintSystemCompleter.ConstraintSystemCompletionMode? {
|
||||
// this is a hack to mitigate questionable behavior in delegated properties design, namely:
|
||||
// see test DelegatedProperty.ProvideDelegate#testHostAndReceiver2
|
||||
): KotlinConstraintSystemCompleter.ConstraintSystemCompletionMode? = null
|
||||
|
||||
// Normally, provideDelegate should be analyzed in INDEPENDENT mode, but now in OI there is one corner case, which
|
||||
// doesn't fit into this design
|
||||
val builder = candidate.getSystem().getBuilder()
|
||||
for ((_, variable) in builder.currentStorage().notFixedTypeVariables) {
|
||||
val hasProperConstraint = variable.constraints.any { candidate.getSystem().getBuilder().isProperType(it.type) }
|
||||
if (hasProperConstraint) return null
|
||||
}
|
||||
|
||||
return KotlinConstraintSystemCompleter.ConstraintSystemCompletionMode.PARTIAL
|
||||
}
|
||||
override fun resolveReceiverIndependently(): Boolean = resolveReceiverIndependently
|
||||
}
|
||||
|
||||
@@ -26,7 +26,9 @@ import org.jetbrains.kotlin.resolve.calls.components.PostponedArgumentsAnalyzer
|
||||
import org.jetbrains.kotlin.resolve.calls.context.ContextDependency
|
||||
import org.jetbrains.kotlin.resolve.calls.inference.ConstraintSystem
|
||||
import org.jetbrains.kotlin.resolve.calls.inference.ConstraintSystemCompleter
|
||||
import org.jetbrains.kotlin.resolve.calls.inference.components.EmptySubstitutor
|
||||
import org.jetbrains.kotlin.resolve.calls.inference.components.KotlinConstraintSystemCompleter
|
||||
import org.jetbrains.kotlin.resolve.calls.inference.components.NewTypeSubstitutorByConstructorMap
|
||||
import org.jetbrains.kotlin.resolve.calls.inference.constraintPosition.ConstraintPositionKind.FROM_COMPLETER
|
||||
import org.jetbrains.kotlin.resolve.calls.inference.model.TypeVariableTypeConstructor
|
||||
import org.jetbrains.kotlin.resolve.calls.inference.toHandle
|
||||
@@ -45,6 +47,7 @@ import org.jetbrains.kotlin.types.*
|
||||
import org.jetbrains.kotlin.types.TypeUtils.NO_EXPECTED_TYPE
|
||||
import org.jetbrains.kotlin.types.TypeUtils.noExpectedType
|
||||
import org.jetbrains.kotlin.types.checker.KotlinTypeChecker
|
||||
import org.jetbrains.kotlin.types.checker.NewTypeVariableConstructor
|
||||
import org.jetbrains.kotlin.types.expressions.ExpressionTypingContext
|
||||
import org.jetbrains.kotlin.types.expressions.ExpressionTypingServices
|
||||
import org.jetbrains.kotlin.types.expressions.ExpressionTypingUtils.createFakeExpressionOfType
|
||||
@@ -549,20 +552,35 @@ class DelegatedPropertyResolver(
|
||||
delegateType = delegateTypeConstructor.getApproximatedType()
|
||||
|
||||
if (languageVersionSettings.supportsFeature(LanguageFeature.OperatorProvideDelegate)) {
|
||||
val traceForProvideDelegate = TemporaryBindingTrace.create(traceToResolveDelegatedProperty, "Trace to resolve provide delegate")
|
||||
|
||||
val substitutionMap: Map<UnwrappedType, UnwrappedType>? = buildSubstitutionMapOfNonFixedVariables(delegateType)
|
||||
val nonFixedVariablesToStubTypesSubstitutor =
|
||||
if (substitutionMap != null)
|
||||
NewTypeSubstitutorByConstructorMap(substitutionMap.mapKeys { it.key.constructor })
|
||||
else
|
||||
EmptySubstitutor
|
||||
|
||||
val delegateTypeWithoutNonFixedVariables = nonFixedVariablesToStubTypesSubstitutor.safeSubstitute(delegateType.unwrap())
|
||||
|
||||
val contextForProvideDelegate = createContextForProvideDelegateMethod(
|
||||
scopeForDelegate, delegateDataFlow, traceToResolveDelegatedProperty, InferenceSessionForExistingCandidates
|
||||
scopeForDelegate, delegateDataFlow, traceForProvideDelegate, InferenceSessionForExistingCandidates(substitutionMap != null)
|
||||
)
|
||||
|
||||
val provideDelegateResults = getProvideDelegateMethod(
|
||||
variableDescriptor, delegateExpression, delegateType, contextForProvideDelegate
|
||||
variableDescriptor, delegateExpression, delegateTypeWithoutNonFixedVariables, contextForProvideDelegate
|
||||
)
|
||||
|
||||
if (conventionMethodFound(provideDelegateResults)) {
|
||||
val provideDelegateDescriptor = provideDelegateResults.resultingDescriptor
|
||||
if (provideDelegateDescriptor.isOperator) {
|
||||
delegateType = provideDelegateDescriptor.returnType ?: return null
|
||||
delegateType = inverseSubstitution(provideDelegateDescriptor.returnType, substitutionMap) ?: return null
|
||||
delegateDataFlow = provideDelegateResults.resultingCall.dataFlowInfoForArguments.resultInfo
|
||||
}
|
||||
trace.record(PROVIDE_DELEGATE_RESOLVED_CALL, variableDescriptor, provideDelegateResults.resultingCall)
|
||||
if (substitutionMap == null) {
|
||||
traceForProvideDelegate.record(PROVIDE_DELEGATE_RESOLVED_CALL, variableDescriptor, provideDelegateResults.resultingCall)
|
||||
traceForProvideDelegate.commit() // otherwise we have to reanalyze provideDelegate with good delegate type
|
||||
}
|
||||
}
|
||||
}
|
||||
return inferDelegateTypeFromGetSetValueMethods(
|
||||
@@ -570,6 +588,38 @@ class DelegatedPropertyResolver(
|
||||
)
|
||||
}
|
||||
|
||||
private fun inverseSubstitution(type: KotlinType?, substitutionMap: Map<UnwrappedType, UnwrappedType>?): UnwrappedType? {
|
||||
if (type == null) return null
|
||||
if (substitutionMap == null) return type.unwrap()
|
||||
|
||||
val invertedMap = hashMapOf<TypeConstructor, UnwrappedType>()
|
||||
for ((variable, stubType) in substitutionMap) {
|
||||
invertedMap[stubType.constructor] = variable
|
||||
}
|
||||
|
||||
return NewTypeSubstitutorByConstructorMap(invertedMap).safeSubstitute(type.unwrap())
|
||||
}
|
||||
|
||||
private fun buildSubstitutionMapOfNonFixedVariables(type: KotlinType): Map<UnwrappedType, UnwrappedType>? {
|
||||
// This is an exception for delegated properties that return just type variable
|
||||
if (type.constructor is NewTypeVariableConstructor) return null
|
||||
|
||||
var substitutionMap: MutableMap<UnwrappedType, UnwrappedType>? = null
|
||||
type.contains { innerType ->
|
||||
val constructor = innerType.constructor
|
||||
if (constructor is NewTypeVariableConstructor) {
|
||||
if (substitutionMap == null) substitutionMap = hashMapOf()
|
||||
if (innerType !in substitutionMap!!) {
|
||||
substitutionMap!![innerType] = StubTypeForProvideDelegateReceiver(constructor, innerType.isMarkedNullable)
|
||||
}
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
return substitutionMap
|
||||
}
|
||||
|
||||
private fun inferDelegateTypeFromGetSetValueMethods(
|
||||
delegateExpression: KtExpression,
|
||||
variableDescriptor: VariableDescriptorWithAccessors,
|
||||
|
||||
+2
@@ -159,6 +159,8 @@ abstract class ManyCandidatesResolver<D : CallableDescriptor>(
|
||||
|
||||
override fun computeCompletionMode(candidate: KotlinResolutionCandidate) = null
|
||||
|
||||
override fun resolveReceiverIndependently(): Boolean = false
|
||||
|
||||
private fun PartialCallInfo.asCallResolutionResult(
|
||||
diagnosticsHolder: KotlinDiagnosticsHolder.SimpleHolder,
|
||||
commonSystem: NewConstraintSystem
|
||||
|
||||
Reference in New Issue
Block a user