[NI] Resolve receiver of provideDelegate independently

#KT-38259 Fixed
This commit is contained in:
Mikhail Zarechenskiy
2020-04-28 03:28:08 +03:00
parent 59f027e3e9
commit 2cee82a348
25 changed files with 211 additions and 53 deletions
@@ -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,
@@ -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