[NI] Align builder inference with the old inference

#KT-29184 Fixed
This commit is contained in:
Mikhail Zarechenskiy
2019-03-26 15:32:39 +03:00
parent 1eb70ea19e
commit b10d19dc78
19 changed files with 110 additions and 66 deletions
@@ -55,6 +55,7 @@ public interface Errors {
DiagnosticFactory1<PsiElement, String> NEW_INFERENCE_ERROR = DiagnosticFactory1.create(ERROR);
DiagnosticFactory1<PsiElement, String> NEW_INFERENCE_DIAGNOSTIC = DiagnosticFactory1.create(WARNING);
DiagnosticFactory0<KtElement> NON_APPLICABLE_CALL_FOR_BUILDER_INFERENCE = DiagnosticFactory0.create(WARNING);
DiagnosticFactory1<PsiElement, Pair<LanguageFeature, LanguageVersionSettings>> UNSUPPORTED_FEATURE = DiagnosticFactory1.create(ERROR);
DiagnosticFactory1<PsiElement, Throwable> EXCEPTION_FROM_ANALYZER = DiagnosticFactory1.create(ERROR);
@@ -633,6 +633,7 @@ public class DefaultErrorMessages {
MAP.put(UNSUPPORTED, "Unsupported [{0}]", STRING);
MAP.put(NEW_INFERENCE_ERROR, "New inference error [{0}]", STRING);
MAP.put(NEW_INFERENCE_DIAGNOSTIC, "New inference [{0}]", STRING);
MAP.put(NON_APPLICABLE_CALL_FOR_BUILDER_INFERENCE, "Non-applicable call for builder inference");
MAP.put(UNSUPPORTED_FEATURE, "{0}", new LanguageFeatureMessageRenderer(LanguageFeatureMessageRenderer.Type.UNSUPPORTED));
MAP.put(EXPERIMENTAL_FEATURE_WARNING, "{0}", new LanguageFeatureMessageRenderer(LanguageFeatureMessageRenderer.Type.WARNING));
@@ -10,6 +10,7 @@ import org.jetbrains.kotlin.diagnostics.Errors
import org.jetbrains.kotlin.diagnostics.Errors.*
import org.jetbrains.kotlin.diagnostics.Errors.BadNamedArgumentsTarget.INVOKE_ON_FUNCTION_TYPE
import org.jetbrains.kotlin.diagnostics.Errors.BadNamedArgumentsTarget.NON_KOTLIN_FUNCTION
import org.jetbrains.kotlin.diagnostics.reportDiagnosticOnce
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
@@ -49,6 +50,10 @@ class DiagnosticReporterByTrackingStrategy(
)
InstantiationOfAbstractClass::class.java -> tracingStrategy.instantiationOfAbstractClass(trace)
AbstractSuperCall::class.java -> tracingStrategy.abstractSuperCall(trace)
NonApplicableCallForBuilderInferenceDiagnostic::class.java -> {
val reportOn = (diagnostic as NonApplicableCallForBuilderInferenceDiagnostic).kotlinCall
trace.reportDiagnosticOnce(Errors.NON_APPLICABLE_CALL_FOR_BUILDER_INFERENCE.on(reportOn.psiKotlinCall.psiCall.callElement))
}
}
}
@@ -7,7 +7,6 @@ package org.jetbrains.kotlin.resolve.calls.inference
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.descriptors.CallableDescriptor
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
import org.jetbrains.kotlin.resolve.BindingTrace
import org.jetbrains.kotlin.resolve.TemporaryBindingTrace
@@ -22,9 +21,7 @@ import org.jetbrains.kotlin.resolve.calls.inference.model.ConstraintKind
import org.jetbrains.kotlin.resolve.calls.inference.model.ConstraintStorage
import org.jetbrains.kotlin.resolve.calls.inference.model.NewConstraintSystemImpl
import org.jetbrains.kotlin.resolve.calls.inference.model.NewTypeVariable
import org.jetbrains.kotlin.resolve.calls.model.KotlinCallComponents
import org.jetbrains.kotlin.resolve.calls.model.KotlinResolutionCandidate
import org.jetbrains.kotlin.resolve.calls.model.ResolvedLambdaAtom
import org.jetbrains.kotlin.resolve.calls.model.*
import org.jetbrains.kotlin.resolve.calls.tower.*
import org.jetbrains.kotlin.resolve.deprecation.DeprecationResolver
import org.jetbrains.kotlin.types.StubType
@@ -51,19 +48,25 @@ class CoroutineInferenceSession(
) : ManyCandidatesResolver<CallableDescriptor>(
psiCallResolver, postponedArgumentsAnalyzer, kotlinConstraintSystemCompleter, callComponents, builtIns
) {
private val suspendCompletedCalls = arrayListOf<PSICompletedCallInfo>()
private val normalCompletedCalls = arrayListOf<PSICompletedCallInfo>()
private val commonCalls = arrayListOf<PSICompletedCallInfo>()
private val diagnostics = arrayListOf<KotlinCallDiagnostic>()
override fun shouldRunCompletion(candidate: KotlinResolutionCandidate): Boolean = true
override fun addCompletedCallInfo(callInfo: CompletedCallInfo) {
require(callInfo is PSICompletedCallInfo) { "Wrong instance of callInfo: $callInfo" }
val candidateDescriptor = callInfo.callResolutionResult.resultCallAtom.candidateDescriptor
if (candidateDescriptor is FunctionDescriptor && candidateDescriptor.isSuspend)
suspendCompletedCalls.add(callInfo)
else
normalCompletedCalls.add(callInfo)
commonCalls.add(callInfo)
val isApplicableCall =
callComponents.statelessCallbacks.isApplicableCallForBuilderInference(
callInfo.resolvedCall.resultingDescriptor,
callComponents.languageVersionSettings
)
if (!isApplicableCall) {
diagnostics.add(NonApplicableCallForBuilderInferenceDiagnostic(callInfo.callResolutionResult.resultCallAtom.atom))
}
}
override fun writeOnlyStubs(): Boolean {
@@ -140,8 +143,11 @@ class CoroutineInferenceSession(
integrateConstraints(commonSystem, initialStorage, nonFixedToVariablesSubstitutor)
for (suspendCall in suspendCompletedCalls) {
integrateConstraints(commonSystem, suspendCall.callResolutionResult.constraintSystem, nonFixedToVariablesSubstitutor)
for (call in commonCalls) {
integrateConstraints(commonSystem, call.callResolutionResult.constraintSystem, nonFixedToVariablesSubstitutor)
}
for (diagnostic in diagnostics) {
commonSystem.addError(diagnostic)
}
return commonSystem
@@ -155,7 +161,7 @@ class CoroutineInferenceSession(
val nonFixedTypesToResultSubstitutor = ComposedSubstitutor(commonSystemSubstitutor, nonFixedToVariablesSubstitutor)
for (completedCall in suspendCompletedCalls + normalCompletedCalls) {
for (completedCall in commonCalls) {
val resultCallAtom = completedCall.callResolutionResult.resultCallAtom
val call = resultCallAtom.atom.getResolvedPsiKotlinCall<CallableDescriptor>(trace) ?: continue
@@ -221,7 +221,7 @@ class CoroutineInferenceSupport(
if (!resultingCall.isReallySuccess()) return
val resultingDescriptor = resultingCall.resultingDescriptor
if (!isGoodCall(resultingDescriptor)) {
if (!isApplicableCallForBuilderInference(resultingDescriptor, languageVersionSettings)) {
inferenceData.badCallHappened()
}
@@ -250,32 +250,6 @@ class CoroutineInferenceSupport(
}
}
private fun KotlinType.containsTypeTemplate() = contains { it is TypeTemplate }
private fun isGoodCall(resultingDescriptor: CallableDescriptor): Boolean {
if (!languageVersionSettings.supportsFeature(LanguageFeature.ExperimentalBuilderInference)) {
return isGoodCallForOldCoroutines(resultingDescriptor)
}
if (resultingDescriptor.isExtension && !resultingDescriptor.hasBuilderInferenceAnnotation()) {
return resultingDescriptor.extensionReceiverParameter?.type?.containsTypeTemplate() == false
}
val returnType = resultingDescriptor.returnType ?: return false
return !returnType.containsTypeTemplate()
}
private fun isGoodCallForOldCoroutines(resultingDescriptor: CallableDescriptor): Boolean {
val returnType = resultingDescriptor.returnType ?: return false
if (returnType.containsTypeTemplate()) return false
if (resultingDescriptor !is FunctionDescriptor || resultingDescriptor.isSuspend) return true
if (resultingDescriptor.valueParameters.any { it.type.containsTypeTemplate() }) return false
return true
}
private class CoroutineTypeCheckerContext(
private val allowOnlyTrivialConstraints: Boolean
) : ClassicTypeCheckerContext(errorTypeEqualsToAnything = true) {
@@ -320,6 +294,32 @@ class CoroutineInferenceSupport(
}
}
private fun KotlinType.containsTypeTemplate() = contains { it is TypeTemplate || it is StubType }
fun isApplicableCallForBuilderInference(descriptor: CallableDescriptor, languageVersionSettings: LanguageVersionSettings): Boolean {
if (!languageVersionSettings.supportsFeature(LanguageFeature.ExperimentalBuilderInference)) {
return isGoodCallForOldCoroutines(descriptor)
}
if (descriptor.isExtension && !descriptor.hasBuilderInferenceAnnotation()) {
return descriptor.extensionReceiverParameter?.type?.containsTypeTemplate() == false
}
val returnType = descriptor.returnType ?: return false
return !returnType.containsTypeTemplate()
}
private fun isGoodCallForOldCoroutines(resultingDescriptor: CallableDescriptor): Boolean {
val returnType = resultingDescriptor.returnType ?: return false
if (returnType.containsTypeTemplate()) return false
if (resultingDescriptor !is FunctionDescriptor || resultingDescriptor.isSuspend) return true
if (resultingDescriptor.valueParameters.any { it.type.containsTypeTemplate() }) return false
return true
}
fun isCoroutineCallWithAdditionalInference(
parameterDescriptor: ValueParameterDescriptor,
argument: ValueArgument,
@@ -205,7 +205,7 @@ class KotlinResolutionCallbacksImpl(
}
override fun bindStubResolvedCallForCandidate(candidate: ResolvedCallAtom) {
kotlinToResolvedCallTransformer.createStubResolvedCallAndWriteItToTrace<CallableDescriptor>(candidate, trace, emptyList())
kotlinToResolvedCallTransformer.createStubResolvedCallAndWriteItToTrace(candidate, trace, emptyList(), substitutor = null)
}
override fun createReceiverWithSmartCastInfo(resolvedAtom: ResolvedCallAtom): ReceiverValueWithSmartCastInfo? {
@@ -73,4 +73,11 @@ class KotlinResolutionStatelessCallbacksImpl(
override fun isCoroutineCall(argument: KotlinCallArgument, parameter: ValueParameterDescriptor): Boolean =
isCoroutineCallWithAdditionalInference(parameter, argument.psiCallArgument.valueArgument, languageVersionSettings)
override fun isApplicableCallForBuilderInference(
descriptor: CallableDescriptor,
languageVersionSettings: LanguageVersionSettings
): Boolean {
return org.jetbrains.kotlin.resolve.calls.inference.isApplicableCallForBuilderInference(descriptor, languageVersionSettings)
}
}
@@ -100,26 +100,26 @@ class KotlinToResolvedCallTransformer(
PSIPartialCallInfo(baseResolvedCall, context, tracingStrategy)
)
createStubResolvedCallAndWriteItToTrace(candidate, context.trace, baseResolvedCall.diagnostics)
createStubResolvedCallAndWriteItToTrace(candidate, context.trace, baseResolvedCall.diagnostics, substitutor = null)
}
is CompletedCallResolutionResult, is ErrorCallResolutionResult -> {
val candidate = (baseResolvedCall as SingleCallResolutionResult).resultCallAtom
if (baseResolvedCall is CompletedCallResolutionResult) {
context.inferenceSession.addCompletedCallInfo(PSICompletedCallInfo(baseResolvedCall, context, tracingStrategy))
}
val resultSubstitutor = baseResolvedCall.constraintSystem.buildResultingSubstitutor()
if (context.inferenceSession.writeOnlyStubs()) {
return createStubResolvedCallAndWriteItToTrace(
val stub = createStubResolvedCallAndWriteItToTrace(
candidate,
context.trace,
baseResolvedCall.diagnostics,
completedCall = true
substitutor = resultSubstitutor
)
forwardCallToInferenceSession(baseResolvedCall, context, stub, tracingStrategy)
return stub as ResolvedCall<D>
}
val resultSubstitutor = baseResolvedCall.constraintSystem.buildResultingSubstitutor()
val ktPrimitiveCompleter = ResolvedAtomCompleter(
resultSubstitutor, context, this, expressionTypingServices, argumentTypeResolver,
doubleColonExpressionResolver, builtIns, deprecationResolver, moduleDescriptor, dataFlowValueFactory
@@ -129,7 +129,10 @@ class KotlinToResolvedCallTransformer(
ktPrimitiveCompleter.completeAll(subKtPrimitive)
}
ktPrimitiveCompleter.completeResolvedCall(candidate, baseResolvedCall.diagnostics) as ResolvedCall<D>
val resolvedCall = ktPrimitiveCompleter.completeResolvedCall(candidate, baseResolvedCall.diagnostics) as ResolvedCall<D>
forwardCallToInferenceSession(baseResolvedCall, context, resolvedCall, tracingStrategy)
resolvedCall
}
is SingleCallResolutionResult -> error("Call resolution result for one candidate didn't transformed: $baseResolvedCall")
@@ -137,13 +140,23 @@ class KotlinToResolvedCallTransformer(
}
}
private fun forwardCallToInferenceSession(
baseResolvedCall: CallResolutionResult,
context: BasicCallResolutionContext,
resolvedCall: ResolvedCall<*>,
tracingStrategy: TracingStrategy
) {
if (baseResolvedCall is CompletedCallResolutionResult) {
context.inferenceSession.addCompletedCallInfo(PSICompletedCallInfo(baseResolvedCall, context, resolvedCall, tracingStrategy))
}
}
fun <D : CallableDescriptor> createStubResolvedCallAndWriteItToTrace(
candidate: ResolvedCallAtom,
trace: BindingTrace,
diagnostics: Collection<KotlinCallDiagnostic>,
completedCall: Boolean = false
substitutor: NewTypeSubstitutor?
): ResolvedCall<D> {
val substitutor = if (completedCall) NewTypeSubstitutorByConstructorMap(emptyMap()) else null
val result = transformToResolvedCall<D>(candidate, trace, substitutor, diagnostics)
val psiKotlinCall = candidate.atom.psiKotlinCall
val tracing = psiKotlinCall.safeAs<PSIKotlinCallForInvoke>()?.baseCall?.tracingStrategy ?: psiKotlinCall.tracingStrategy
@@ -122,6 +122,7 @@ class PSIPartialCallInfo(
class PSICompletedCallInfo(
override val callResolutionResult: CompletedCallResolutionResult,
val context: BasicCallResolutionContext,
val resolvedCall: ResolvedCall<*>,
val tracingStrategy: TracingStrategy
) : CompletedCallInfo
@@ -5,6 +5,7 @@
package org.jetbrains.kotlin.resolve.calls.components
import org.jetbrains.kotlin.config.LanguageVersionSettings
import org.jetbrains.kotlin.descriptors.CallableDescriptor
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor
@@ -29,6 +30,7 @@ interface KotlinResolutionStatelessCallbacks {
fun getScopeTowerForCallableReferenceArgument(argument: CallableReferenceKotlinCallArgument): ImplicitScopeTower
fun getVariableCandidateIfInvoke(functionCall: KotlinCall): KotlinResolutionCandidate?
fun isCoroutineCall(argument: KotlinCallArgument, parameter: ValueParameterDescriptor): Boolean
fun isApplicableCallForBuilderInference(descriptor: CallableDescriptor, languageVersionSettings: LanguageVersionSettings): Boolean
}
// This components hold state (trace). Work with this carefully.
@@ -179,4 +179,10 @@ class ManyCandidatesCallDiagnostic(
override fun report(reporter: DiagnosticReporter) {
reporter.onCall(this)
}
}
class NonApplicableCallForBuilderInferenceDiagnostic(val kotlinCall: KotlinCall) : KotlinCallDiagnostic(CONVENTION_ERROR) {
override fun report(reporter: DiagnosticReporter) {
reporter.onCall(this)
}
}
@@ -53,7 +53,7 @@ val test6 = <!OI;TYPE_INFERENCE_NO_INFORMATION_FOR_PARAMETER!>generate<!> {
}
val test7 = <!OI;TYPE_INFERENCE_NO_INFORMATION_FOR_PARAMETER!>generate<!> {
yield("baz")
yield(<!NI;TYPE_MISMATCH!>"baz"<!>)
genericExtension<Int>()
}
@@ -5,9 +5,9 @@ public val test2: kotlin.Any?
public val test3: kotlin.Int
public val test4: kotlin.Any?
public val test5: kotlin.Int
public val test6: kotlin.String
public val test7: kotlin.String
public val test8: kotlin.Any?
public val test6: kotlin.Any?
public val test7: kotlin.Int
public val test8: kotlin.String
public fun </*0*/ S> generate(/*0*/ @kotlin.BuilderInference g: suspend Controller<S>.() -> kotlin.Unit): S
public fun Base.baseExtension(): kotlin.Unit
public fun </*0*/ S> Controller<S>.genericExtension(): kotlin.Unit
@@ -23,7 +23,7 @@ val memberWithoutAnn = <!NI;IMPLICIT_NOTHING_AS_TYPE_PARAMETER, OI;TYPE_INFERENC
}
val extension = <!OI;TYPE_INFERENCE_NO_INFORMATION_FOR_PARAMETER!>build<!> {
extensionAdd("foo")
<!NI;NON_APPLICABLE_CALL_FOR_BUILDER_INFERENCE!>extensionAdd("foo")<!>
}
val safeExtension = build {
@@ -1,6 +1,6 @@
package
public val extension: kotlin.collections.List<kotlin.Any?>
public val extension: kotlin.collections.List<kotlin.String>
public val member: kotlin.collections.List<kotlin.Int>
public val memberWithoutAnn: kotlin.collections.List<kotlin.Nothing>
public val safeExtension: kotlin.collections.List<kotlin.String>
@@ -30,7 +30,7 @@ val memberWithoutAnn = <!NI;IMPLICIT_NOTHING_AS_TYPE_PARAMETER, OI;TYPE_INFERENC
}
val extension = <!OI;TYPE_INFERENCE_NO_INFORMATION_FOR_PARAMETER!>build<!> {
extensionAdd("foo")
<!NI;NON_APPLICABLE_CALL_FOR_BUILDER_INFERENCE!>extensionAdd("foo")<!>
}
val safeExtension = build {
@@ -1,9 +1,9 @@
package
public val extension: kotlin.collections.List<kotlin.Any?>
public val member: kotlin.collections.List<kotlin.Any?>
public val extension: kotlin.collections.List<kotlin.String>
public val member: kotlin.collections.List<kotlin.Int>
public val memberWithoutAnn: kotlin.collections.List<kotlin.Nothing>
public val safeExtension: kotlin.collections.List<kotlin.Any?>
public val safeExtension: kotlin.collections.List<kotlin.String>
public fun </*0*/ S> build(/*0*/ @kotlin.BuilderInference g: Builder<S>.() -> kotlin.Unit): kotlin.collections.List<S>
public fun </*0*/ S> wrongBuild(/*0*/ g: Builder<S>.() -> kotlin.Unit): kotlin.collections.List<S>
public fun </*0*/ S> Builder<S>.extensionAdd(/*0*/ s: S): kotlin.Unit
@@ -26,7 +26,7 @@ val normal = generate {
}
val extension = <!OI;TYPE_INFERENCE_NO_INFORMATION_FOR_PARAMETER!>generate<!> {
extensionYield("foo")
<!NI;NON_APPLICABLE_CALL_FOR_BUILDER_INFERENCE!>extensionYield("foo")<!>
}
val safeExtension = generate {
@@ -1,6 +1,8 @@
// !LANGUAGE: -ReleaseCoroutines -ExperimentalBuilderInference
// !DIAGNOSTICS: -EXPERIMENTAL_FEATURE_WARNING
// !WITH_NEW_INFERENCE
// SKIP_TXT
@kotlin.coroutines.experimental.RestrictsSuspension
class RestrictedController<T> {
suspend fun yield(<!UNUSED_PARAMETER!>x<!>: T) {}
@@ -75,13 +77,13 @@ fun test() {
this@a.<!ILLEGAL_RESTRICTED_SUSPENDING_FUNCTION_CALL!>yield<!>(1)
this@a.<!ILLEGAL_RESTRICTED_SUSPENDING_FUNCTION_CALL!>yield2<!>(1)
with(this) {
<!NI;NON_APPLICABLE_CALL_FOR_BUILDER_INFERENCE!>with(this) {
<!ILLEGAL_RESTRICTED_SUSPENDING_FUNCTION_CALL!>yield<!>("")
this@with.<!ILLEGAL_RESTRICTED_SUSPENDING_FUNCTION_CALL!>yield<!>("")
<!ILLEGAL_RESTRICTED_SUSPENDING_FUNCTION_CALL!>yield2<!>("")
this@with.<!ILLEGAL_RESTRICTED_SUSPENDING_FUNCTION_CALL!>yield2<!>("")
}
}<!>
}
}