[FIR] Union CFG edges from postponed lambdas only if outer call was analyzed independently

Before this change nodes unification was called if outer call was
  completed in the FULL mode, but it may happen even if this call is
  actually a part of some other outer call
This commit is contained in:
Dmitriy Novozhilov
2023-06-28 15:19:32 +03:00
committed by Space Team
parent cf949e8760
commit fc57f48c8f
12 changed files with 674 additions and 690 deletions
@@ -82,7 +82,7 @@ class SingleCandidateResolver(
?: ResolutionMode.ContextIndependent
)
return completionResult.takeIf { it.callCompleted }?.result
return completionResult
}
private fun createCandidateInfoProvider(resolutionParameters: ResolutionParameters): CandidateInfoProvider {
File diff suppressed because it is too large Load Diff
@@ -84,7 +84,7 @@ FILE: flowFromInplaceLambda.kt
^ (R|<local>/x| as R|kotlin/Int|)
}
)), (R|<local>/y| as R|kotlin/Int|), R|/exactlyOnce|<R|kotlin/Int|>(<L> = exactlyOnce@fun <anonymous>(): R|kotlin/Int| <inline=NoInline, kind=EXACTLY_ONCE> {
R|<local>/x|.R|kotlin/Int.inc|()
R|<local>/x|.<Unresolved name: inc>#()
R|<local>/y|.R|kotlin/Int.inc|()
^ Int(1)
}
@@ -98,7 +98,7 @@ FILE: flowFromInplaceLambda.kt
^ (R|<local>/x| as R|kotlin/Int|)
}
)), (R|<local>/y| as R|kotlin/Int|), R|/atLeastOnce|<R|kotlin/Int|>(<L> = atLeastOnce@fun <anonymous>(): R|kotlin/Int| <inline=NoInline, kind=AT_LEAST_ONCE> {
R|<local>/x|.R|kotlin/Int.inc|()
R|<local>/x|.<Unresolved name: inc>#()
R|<local>/y|.R|kotlin/Int.inc|()
^ Int(1)
}
@@ -39,7 +39,7 @@ fun completedCallExactlyOnce(x: Any?, y: Any?) {
// The value of the type argument is known, so the call is complete and the data can flow.
id(exactlyOnce { y.<!UNRESOLVED_REFERENCE!>inc<!>(); x as Int }),
y as Int,
exactlyOnce { x.inc(); y.inc(); 1 }
exactlyOnce { x.<!UNRESOLVED_REFERENCE!>inc<!>(); y.inc(); 1 }
).inc() // OK
x.inc() // OK
y.inc() // OK
@@ -49,7 +49,7 @@ fun completedCallAtLeastOnce(x: Any?, y: Any?) {
select(
id(atLeastOnce { y.<!UNRESOLVED_REFERENCE!>inc<!>(); x as Int }),
y as Int,
atLeastOnce { x.inc(); y.inc(); 1 }
atLeastOnce { x.<!UNRESOLVED_REFERENCE!>inc<!>(); y.inc(); 1 }
).inc() // OK
x.inc() // OK
y.inc() // OK
@@ -112,8 +112,9 @@ finally {
20 -> {21};
21 -> {22};
22 -> {23};
23 -> {24};
23 -> {17} [color=green style=dashed];
23 -> {24} [color=green];
23 -> {31} [color=red];
24 -> {25};
24 -> {27} [label="onUncaughtException"];
25 -> {26};
@@ -201,7 +202,8 @@ finally {
44 -> {45};
45 -> {46};
46 -> {47};
48 -> {49};
48 -> {49} [color=green];
48 -> {56} [color=red];
49 -> {50};
49 -> {52} [label="onUncaughtException"];
50 -> {51};
@@ -323,7 +323,7 @@ private fun BodyResolveComponents.createExplicitReceiverForInvokeByCallable(
}
source = info.fakeSourceForImplicitInvokeCallReceiver
}.build().let {
callCompleter.completeCall(it, ResolutionMode.ReceiverResolution).result
callCompleter.completeCall(it, ResolutionMode.ReceiverResolution)
}.let {
transformQualifiedAccessUsingSmartcastInfo(it)
}
@@ -54,13 +54,10 @@ class FirCallCompleter(
val completer = ConstraintSystemCompleter(components, transformer.context)
data class CompletionResult<T>(val result: T, val callCompleted: Boolean)
fun <T> completeCall(call: T, resolutionMode: ResolutionMode): CompletionResult<T> where T : FirResolvable, T : FirStatement {
fun <T> completeCall(call: T, resolutionMode: ResolutionMode): T where T : FirResolvable, T : FirStatement {
val typeRef = components.typeFromCallee(call)
val reference = call.calleeReference as? FirNamedReferenceWithCandidate ?: return CompletionResult(call, true)
val reference = call.calleeReference as? FirNamedReferenceWithCandidate ?: return call
val candidate = reference.candidate
val initialType = typeRef.initialTypeOfCandidate(candidate)
@@ -104,10 +101,10 @@ class FirCallCompleter(
null
)
inferenceSession.addCompletedCall(completedCall, candidate)
CompletionResult(completedCall, true)
completedCall
} else {
inferenceSession.addPartiallyResolvedCall(call)
CompletionResult(call, false)
call
}
}
@@ -120,7 +117,7 @@ class FirCallCompleter(
inferenceSession.addPartiallyResolvedCall(call)
}
CompletionResult(call, false)
call
}
ConstraintSystemCompletionMode.UNTIL_FIRST_LAMBDA -> throw IllegalStateException()
@@ -188,7 +188,7 @@ class FirSyntheticCallGenerator(
this.argumentList = argumentList
}
val result = components.callCompleter.completeCall(fakeCallElement, ResolutionMode.ContextIndependent).result
val result = components.callCompleter.completeCall(fakeCallElement, ResolutionMode.ContextIndependent)
val completedCallableReference = result.argument as FirCallableReferenceAccess?
val callCalleeReference = result.calleeReference
@@ -25,9 +25,8 @@ import org.jetbrains.kotlin.fir.visitors.FirDefaultTransformer
*/
class FirArrayOfCallTransformer : FirDefaultTransformer<Nothing?>() {
private fun toArrayOfCall(functionCall: FirFunctionCall): FirArrayOfCall? {
if (!functionCall.isArrayOfCall) {
return null
}
if (!functionCall.isArrayOfCall) return null
if (functionCall.calleeReference !is FirResolvedNamedReference) return null
return buildArrayOfCall {
source = functionCall.source
annotations += functionCall.annotations
@@ -94,24 +94,18 @@ class FirControlFlowStatementsResolveTransformer(transformer: FirAbstractBodyRes
// exhaustiveness is not yet computed there, but at the same time to compute it properly
// we need having branches condition bes analyzed that is why we can't have call
// `whenExpression.transformSingle(whenExhaustivenessTransformer, null)` at the beginning
val callCompleted = when {
completionNeeded -> {
val completionResult = callCompleter.completeCall(
whenExpression,
// For non-exhaustive when expressions, we should complete then as independent because below
// their type is artificially replaced with Unit, while candidate symbol's return type remains the same
// So when combining two when's the inner one was erroneously resolved as a normal dependent exhaustive sub-expression
// At the same time, it all looks suspicious and inconsistent, so we hope it would be investigated at KT-55175
if (whenExpression.isProperlyExhaustive) data else ResolutionMode.ContextIndependent,
)
whenExpression = completionResult.result
completionResult.callCompleted
}
else -> false
if (completionNeeded) {
val completionResult = callCompleter.completeCall(
whenExpression,
// For non-exhaustive when expressions, we should complete then as independent because below
// their type is artificially replaced with Unit, while candidate symbol's return type remains the same
// So when combining two when's the inner one was erroneously resolved as a normal dependent exhaustive sub-expression
// At the same time, it all looks suspicious and inconsistent, so we hope it would be investigated at KT-55175
if (whenExpression.isProperlyExhaustive) data else ResolutionMode.ContextIndependent,
)
whenExpression = completionResult
}
dataFlowAnalyzer.exitWhenExpression(whenExpression, callCompleted)
dataFlowAnalyzer.exitWhenExpression(whenExpression, data.forceFullCompletion)
whenExpression = whenExpression.replaceReturnTypeIfNotExhaustive()
whenExpression
}
@@ -169,13 +163,13 @@ class FirControlFlowStatementsResolveTransformer(transformer: FirAbstractBodyRes
tryExpression.transformCatches(this, ResolutionMode.ContextDependent)
val incomplete = syntheticCallGenerator.generateCalleeForTryExpression(tryExpression, resolutionContext)
var (result, callCompleted) = callCompleter.completeCall(incomplete, data)
var result = callCompleter.completeCall(incomplete, data)
if (result.finallyBlock != null) {
dataFlowAnalyzer.enterFinallyBlock()
result = result.transformFinallyBlock(transformer, ResolutionMode.ContextIndependent)
dataFlowAnalyzer.exitFinallyBlock()
}
dataFlowAnalyzer.exitTryExpression(callCompleted)
dataFlowAnalyzer.exitTryExpression(data.forceFullCompletion)
return result
}
@@ -262,7 +256,7 @@ class FirControlFlowStatementsResolveTransformer(transformer: FirAbstractBodyRes
)
elvisExpression.transformRhs(transformer, resolutionModeForRhs)
val (result, callCompleted) = callCompleter.completeCall(
val result = callCompleter.completeCall(
syntheticCallGenerator.generateCalleeForElvisExpression(elvisExpression, resolutionContext), data
)
@@ -291,7 +285,7 @@ class FirControlFlowStatementsResolveTransformer(transformer: FirAbstractBodyRes
}
}
dataFlowAnalyzer.exitElvis(elvisExpression, isLhsNotNull, callCompleted)
dataFlowAnalyzer.exitElvis(elvisExpression, isLhsNotNull, data.forceFullCompletion)
return result
}
}
@@ -166,7 +166,7 @@ open class FirExpressionsResolveTransformer(transformer: FirAbstractBodyResolveT
if (!transformedCallee.isAcceptableResolvedQualifiedAccess()) {
return qualifiedAccessExpression
}
callCompleter.completeCall(transformedCallee, data).result
callCompleter.completeCall(transformedCallee, data)
} else {
transformedCallee
}
@@ -419,44 +419,39 @@ open class FirExpressionsResolveTransformer(transformer: FirAbstractBodyResolveT
functionCall.transformAnnotations(transformer, data)
functionCall.replaceLambdaArgumentInvocationKinds(session)
functionCall.transformTypeArguments(transformer, ResolutionMode.ContextIndependent)
val (completeInference, callCompleted) =
run {
val initialExplicitReceiver = functionCall.explicitReceiver
val withTransformedArguments = if (!resolvingAugmentedAssignment) {
dataFlowAnalyzer.enterCallArguments(functionCall, functionCall.arguments)
// In provideDelegate mode the explicitReceiver is already resolved
// E.g. we have val some by someDelegate
// At 1st stage of delegate inference we resolve someDelegate itself,
// at 2nd stage in provideDelegate mode we are trying to resolve someDelegate.provideDelegate(),
// and 'someDelegate' explicit receiver is resolved at 1st stage
// See also FirDeclarationsResolveTransformer.transformWrappedDelegateExpression
val withResolvedExplicitReceiver = if (provideDelegate) functionCall else transformExplicitReceiver(functionCall)
withResolvedExplicitReceiver.also {
it.replaceArgumentList(it.argumentList.transform(this, ResolutionMode.ContextDependent))
dataFlowAnalyzer.exitCallArguments()
}
} else {
functionCall
}
val resultExpression = callResolver.resolveCallAndSelectCandidate(withTransformedArguments)
val resultExplicitReceiver = resultExpression.explicitReceiver?.unwrapSmartcastExpression()
if (initialExplicitReceiver !== resultExplicitReceiver && resultExplicitReceiver is FirQualifiedAccessExpression) {
// name.invoke() case
callCompleter.completeCall(resultExplicitReceiver, ResolutionMode.ContextIndependent)
}
callCompleter.completeCall(resultExpression, data)
val initialExplicitReceiver = functionCall.explicitReceiver
val withTransformedArguments = if (!resolvingAugmentedAssignment) {
dataFlowAnalyzer.enterCallArguments(functionCall, functionCall.arguments)
// In provideDelegate mode the explicitReceiver is already resolved
// E.g. we have val some by someDelegate
// At 1st stage of delegate inference we resolve someDelegate itself,
// at 2nd stage in provideDelegate mode we are trying to resolve someDelegate.provideDelegate(),
// and 'someDelegate' explicit receiver is resolved at 1st stage
// See also FirDeclarationsResolveTransformer.transformWrappedDelegateExpression
val withResolvedExplicitReceiver = if (provideDelegate) functionCall else transformExplicitReceiver(functionCall)
withResolvedExplicitReceiver.also {
it.replaceArgumentList(it.argumentList.transform(this, ResolutionMode.ContextDependent))
dataFlowAnalyzer.exitCallArguments()
}
} else {
functionCall
}
val resultExpression = callResolver.resolveCallAndSelectCandidate(withTransformedArguments)
val resultExplicitReceiver = resultExpression.explicitReceiver?.unwrapSmartcastExpression()
if (initialExplicitReceiver !== resultExplicitReceiver && resultExplicitReceiver is FirQualifiedAccessExpression) {
// name.invoke() case
callCompleter.completeCall(resultExplicitReceiver, ResolutionMode.ContextIndependent)
}
val completeInference = callCompleter.completeCall(resultExpression, data)
val result = completeInference.transformToIntegerOperatorCallOrApproximateItIfNeeded(data)
if (!resolvingAugmentedAssignment) {
dataFlowAnalyzer.exitFunctionCall(result, callCompleted)
dataFlowAnalyzer.exitFunctionCall(result, data.forceFullCompletion)
}
addReceiversFromExtensions(result)
if (callCompleted) {
if (enableArrayOfCallTransformation) {
return arrayOfCallTransformer.transformFunctionCall(result, null)
}
if (enableArrayOfCallTransformation) {
return arrayOfCallTransformer.transformFunctionCall(result, null)
}
return result
}
@@ -970,11 +965,11 @@ open class FirExpressionsResolveTransformer(transformer: FirAbstractBodyResolveT
.transformAnnotations(transformer, ResolutionMode.ContextIndependent)
.replaceArgumentList(checkNotNullCall.argumentList.transform(transformer, ResolutionMode.ContextDependent))
val (result, callCompleted) = callCompleter.completeCall(
val result = callCompleter.completeCall(
components.syntheticCallGenerator.generateCalleeForCheckNotNullCall(checkNotNullCall, resolutionContext), data
)
dataFlowAnalyzer.exitCheckNotNullCall(result, callCompleted)
dataFlowAnalyzer.exitCheckNotNullCall(result, data.forceFullCompletion)
return result
}
@@ -1328,8 +1323,8 @@ open class FirExpressionsResolveTransformer(transformer: FirAbstractBodyResolveT
// it seems that we may leave this code as is
// without adding `context.withTowerDataContext(context.getTowerDataContextForConstructorResolution())`
val (result, callCompleted) = callCompleter.completeCall(resolvedCall, ResolutionMode.ContextIndependent)
dataFlowAnalyzer.exitDelegatedConstructorCall(result, callCompleted)
val result = callCompleter.completeCall(resolvedCall, ResolutionMode.ContextIndependent)
dataFlowAnalyzer.exitDelegatedConstructorCall(result, data.forceFullCompletion)
return result
}
+2 -1
View File
@@ -706,8 +706,9 @@ digraph kt44814_kt {
191 -> {192};
192 -> {193};
193 -> {194};
194 -> {195};
194 -> {182} [color=green style=dashed];
194 -> {195} [color=green];
194 -> {202} [style=dotted];
195 -> {196};
196 -> {197 201};
197 -> {198};