/*
* Copyright 2010-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.resolve.calls.model
import org.jetbrains.kotlin.descriptors.CallableDescriptor
import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor
import org.jetbrains.kotlin.renderer.DescriptorRenderer
import org.jetbrains.kotlin.resolve.calls.components.TypeArgumentsToParametersMapper
import org.jetbrains.kotlin.resolve.calls.inference.NewConstraintSystem
import org.jetbrains.kotlin.resolve.calls.inference.components.FreshVariableNewTypeSubstitutor
import org.jetbrains.kotlin.resolve.calls.inference.model.ConstraintStorage
import org.jetbrains.kotlin.resolve.calls.inference.model.NewConstraintSystemImpl
import org.jetbrains.kotlin.resolve.calls.tasks.ExplicitReceiverKind
import org.jetbrains.kotlin.resolve.calls.tower.*
import org.jetbrains.kotlin.types.TypeSubstitutor
import org.jetbrains.kotlin.types.UnwrappedType
abstract class ResolutionPart {
abstract fun KotlinResolutionCandidate.process(workIndex: Int)
open fun KotlinResolutionCandidate.workCount(): Int = 1
// helper functions
protected inline val KotlinResolutionCandidate.candidateDescriptor get() = resolvedCall.candidateDescriptor
protected inline val KotlinResolutionCandidate.kotlinCall get() = resolvedCall.atom
}
interface KotlinDiagnosticsHolder {
fun addDiagnostic(diagnostic: KotlinCallDiagnostic)
class SimpleHolder : KotlinDiagnosticsHolder {
private val diagnostics = arrayListOf<KotlinCallDiagnostic>()
override fun addDiagnostic(diagnostic: KotlinCallDiagnostic) {
diagnostics.add(diagnostic)
}
fun getDiagnostics(): List<KotlinCallDiagnostic> = diagnostics
}
}
fun KotlinDiagnosticsHolder.addDiagnosticIfNotNull(diagnostic: KotlinCallDiagnostic?) {
diagnostic?.let { addDiagnostic(it) }
}
/**
* baseSystem contains all information from arguments, i.e. it is union of all system of arguments
* Also by convention we suppose that baseSystem has no contradiction
*/
class KotlinResolutionCandidate(
val callComponents: KotlinCallComponents,
val scopeTower: ImplicitScopeTower,
private val baseSystem: ConstraintStorage,
val resolvedCall: MutableResolvedCallAtom,
val knownTypeParametersResultingSubstitutor: TypeSubstitutor? = null,
private val resolutionSequence: List<ResolutionPart> = resolvedCall.atom.callKind.resolutionSequence
) : Candidate, KotlinDiagnosticsHolder {
val diagnosticsFromResolutionParts = arrayListOf<KotlinCallDiagnostic>() // TODO: this is mutable list, take diagnostics only once!
private var newSystem: NewConstraintSystemImpl? = null
private var currentApplicability = ResolutionCandidateApplicability.RESOLVED
private var subResolvedAtoms: MutableList<ResolvedAtom> = arrayListOf()
private val stepCount = resolutionSequence.sumBy { it.run { workCount() } }
private var step = 0
fun getSystem(): NewConstraintSystem {
if (newSystem == null) {
newSystem = NewConstraintSystemImpl(callComponents.constraintInjector, callComponents.builtIns)
newSystem!!.addOtherSystem(baseSystem)
}
return newSystem!!
}
internal val csBuilder get() = getSystem().getBuilder()
override fun addDiagnostic(diagnostic: KotlinCallDiagnostic) {
diagnosticsFromResolutionParts.add(diagnostic)
currentApplicability = maxOf(diagnostic.candidateApplicability, currentApplicability)
}
fun addResolvedKtPrimitive(resolvedAtom: ResolvedAtom) {
subResolvedAtoms.add(resolvedAtom)
}
private fun processParts(stopOnFirstError: Boolean) {
if (stopOnFirstError && step > 0) return // error already happened
if (step == stepCount) return
var partIndex = 0
var workStep = step
while (workStep > 0) {
val workCount = resolutionSequence[partIndex].run { workCount() }
if (workStep >= workCount) {
partIndex++
workStep -= workCount
} else {
break
}
}
if (partIndex < resolutionSequence.size) {
if (processPart(resolutionSequence[partIndex], stopOnFirstError, workStep)) return
partIndex++
}
while (partIndex < resolutionSequence.size) {
if (processPart(resolutionSequence[partIndex], stopOnFirstError)) return
partIndex++
}
if (step == stepCount) {
resolvedCall.setAnalyzedResults(subResolvedAtoms)
}
}
// true if part was interrupted
private fun processPart(part: ResolutionPart, stopOnFirstError: Boolean, startWorkIndex: Int = 0): Boolean {
for (workIndex in startWorkIndex until (part.run { workCount() })) {
if (stopOnFirstError && !currentApplicability.isSuccess) return true
part.run { process(workIndex) }
step++
}
return false
}
val variableCandidateIfInvoke: KotlinResolutionCandidate?
get() = callComponents.statelessCallbacks.getVariableCandidateIfInvoke(resolvedCall.atom)
private val variableApplicability
get() = variableCandidateIfInvoke?.resultingApplicability ?: ResolutionCandidateApplicability.RESOLVED
override val isSuccessful: Boolean
get() {
processParts(stopOnFirstError = true)
return currentApplicability.isSuccess && variableApplicability.isSuccess && !getSystem().hasContradiction
}
override val resultingApplicability: ResolutionCandidateApplicability
get() {
processParts(stopOnFirstError = false)
val systemApplicability = getResultApplicability(getSystem().diagnostics)
return maxOf(currentApplicability, systemApplicability, variableApplicability)
}
override fun toString(): String {
val descriptor = DescriptorRenderer.COMPACT.render(resolvedCall.candidateDescriptor)
val okOrFail = if (currentApplicability.isSuccess) "OK" else "FAIL"
val step = "$step/$stepCount"
return "$okOrFail($step): $descriptor"
}
}
class MutableResolvedCallAtom(
override val atom: KotlinCall,
override val candidateDescriptor: CallableDescriptor, // original candidate descriptor
override val explicitReceiverKind: ExplicitReceiverKind,
override val dispatchReceiverArgument: SimpleKotlinCallArgument?,
override val extensionReceiverArgument: SimpleKotlinCallArgument?
) : ResolvedCallAtom() {
override lateinit var typeArgumentMappingByOriginal: TypeArgumentsToParametersMapper.TypeArgumentsMapping
override lateinit var argumentMappingByOriginal: Map<ValueParameterDescriptor, ResolvedCallArgument>
override lateinit var substitutor: FreshVariableNewTypeSubstitutor
lateinit var argumentToCandidateParameter: Map<KotlinCallArgument, ValueParameterDescriptor>
private var samAdapterMap: HashMap<KotlinCallArgument, SamConversionDescription>? = null
override val argumentsWithConversion: Map<KotlinCallArgument, SamConversionDescription>
get() = samAdapterMap ?: emptyMap()
fun registerArgumentWithSamConversion(argument: KotlinCallArgument, samConversionDescription: SamConversionDescription) {
if (samAdapterMap == null)
samAdapterMap = hashMapOf()
samAdapterMap!![argument] = samConversionDescription
}
override public fun setAnalyzedResults(subResolvedAtoms: List<ResolvedAtom>) {
super.setAnalyzedResults(subResolvedAtoms)
}
override fun toString(): String = "$atom, candidate = $candidateDescriptor"
}