[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 ?: ResolutionMode.ContextIndependent
) )
return completionResult.takeIf { it.callCompleted }?.result return completionResult
} }
private fun createCandidateInfoProvider(resolutionParameters: ResolutionParameters): CandidateInfoProvider { 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>/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>/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|() R|<local>/y|.R|kotlin/Int.inc|()
^ Int(1) ^ Int(1)
} }
@@ -98,7 +98,7 @@ FILE: flowFromInplaceLambda.kt
^ (R|<local>/x| as R|kotlin/Int|) ^ (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>/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|() R|<local>/y|.R|kotlin/Int.inc|()
^ Int(1) ^ 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. // 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 }), id(exactlyOnce { y.<!UNRESOLVED_REFERENCE!>inc<!>(); x as Int }),
y as Int, y as Int,
exactlyOnce { x.inc(); y.inc(); 1 } exactlyOnce { x.<!UNRESOLVED_REFERENCE!>inc<!>(); y.inc(); 1 }
).inc() // OK ).inc() // OK
x.inc() // OK x.inc() // OK
y.inc() // OK y.inc() // OK
@@ -49,7 +49,7 @@ fun completedCallAtLeastOnce(x: Any?, y: Any?) {
select( select(
id(atLeastOnce { y.<!UNRESOLVED_REFERENCE!>inc<!>(); x as Int }), id(atLeastOnce { y.<!UNRESOLVED_REFERENCE!>inc<!>(); x as Int }),
y as Int, y as Int,
atLeastOnce { x.inc(); y.inc(); 1 } atLeastOnce { x.<!UNRESOLVED_REFERENCE!>inc<!>(); y.inc(); 1 }
).inc() // OK ).inc() // OK
x.inc() // OK x.inc() // OK
y.inc() // OK y.inc() // OK
@@ -112,8 +112,9 @@ finally {
20 -> {21}; 20 -> {21};
21 -> {22}; 21 -> {22};
22 -> {23}; 22 -> {23};
23 -> {24};
23 -> {17} [color=green style=dashed]; 23 -> {17} [color=green style=dashed];
23 -> {24} [color=green];
23 -> {31} [color=red];
24 -> {25}; 24 -> {25};
24 -> {27} [label="onUncaughtException"]; 24 -> {27} [label="onUncaughtException"];
25 -> {26}; 25 -> {26};
@@ -201,7 +202,8 @@ finally {
44 -> {45}; 44 -> {45};
45 -> {46}; 45 -> {46};
46 -> {47}; 46 -> {47};
48 -> {49}; 48 -> {49} [color=green];
48 -> {56} [color=red];
49 -> {50}; 49 -> {50};
49 -> {52} [label="onUncaughtException"]; 49 -> {52} [label="onUncaughtException"];
50 -> {51}; 50 -> {51};
@@ -323,7 +323,7 @@ private fun BodyResolveComponents.createExplicitReceiverForInvokeByCallable(
} }
source = info.fakeSourceForImplicitInvokeCallReceiver source = info.fakeSourceForImplicitInvokeCallReceiver
}.build().let { }.build().let {
callCompleter.completeCall(it, ResolutionMode.ReceiverResolution).result callCompleter.completeCall(it, ResolutionMode.ReceiverResolution)
}.let { }.let {
transformQualifiedAccessUsingSmartcastInfo(it) transformQualifiedAccessUsingSmartcastInfo(it)
} }
@@ -54,13 +54,10 @@ class FirCallCompleter(
val completer = ConstraintSystemCompleter(components, transformer.context) val completer = ConstraintSystemCompleter(components, transformer.context)
fun <T> completeCall(call: T, resolutionMode: ResolutionMode): T where T : FirResolvable, T : FirStatement {
data class CompletionResult<T>(val result: T, val callCompleted: Boolean)
fun <T> completeCall(call: T, resolutionMode: ResolutionMode): CompletionResult<T> where T : FirResolvable, T : FirStatement {
val typeRef = components.typeFromCallee(call) 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 candidate = reference.candidate
val initialType = typeRef.initialTypeOfCandidate(candidate) val initialType = typeRef.initialTypeOfCandidate(candidate)
@@ -104,10 +101,10 @@ class FirCallCompleter(
null null
) )
inferenceSession.addCompletedCall(completedCall, candidate) inferenceSession.addCompletedCall(completedCall, candidate)
CompletionResult(completedCall, true) completedCall
} else { } else {
inferenceSession.addPartiallyResolvedCall(call) inferenceSession.addPartiallyResolvedCall(call)
CompletionResult(call, false) call
} }
} }
@@ -120,7 +117,7 @@ class FirCallCompleter(
inferenceSession.addPartiallyResolvedCall(call) inferenceSession.addPartiallyResolvedCall(call)
} }
CompletionResult(call, false) call
} }
ConstraintSystemCompletionMode.UNTIL_FIRST_LAMBDA -> throw IllegalStateException() ConstraintSystemCompletionMode.UNTIL_FIRST_LAMBDA -> throw IllegalStateException()
@@ -188,7 +188,7 @@ class FirSyntheticCallGenerator(
this.argumentList = argumentList 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 completedCallableReference = result.argument as FirCallableReferenceAccess?
val callCalleeReference = result.calleeReference val callCalleeReference = result.calleeReference
@@ -25,9 +25,8 @@ import org.jetbrains.kotlin.fir.visitors.FirDefaultTransformer
*/ */
class FirArrayOfCallTransformer : FirDefaultTransformer<Nothing?>() { class FirArrayOfCallTransformer : FirDefaultTransformer<Nothing?>() {
private fun toArrayOfCall(functionCall: FirFunctionCall): FirArrayOfCall? { private fun toArrayOfCall(functionCall: FirFunctionCall): FirArrayOfCall? {
if (!functionCall.isArrayOfCall) { if (!functionCall.isArrayOfCall) return null
return null if (functionCall.calleeReference !is FirResolvedNamedReference) return null
}
return buildArrayOfCall { return buildArrayOfCall {
source = functionCall.source source = functionCall.source
annotations += functionCall.annotations 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 // 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 // we need having branches condition bes analyzed that is why we can't have call
// `whenExpression.transformSingle(whenExhaustivenessTransformer, null)` at the beginning // `whenExpression.transformSingle(whenExhaustivenessTransformer, null)` at the beginning
val callCompleted = when { if (completionNeeded) {
completionNeeded -> { val completionResult = callCompleter.completeCall(
val completionResult = callCompleter.completeCall( whenExpression,
whenExpression, // For non-exhaustive when expressions, we should complete then as independent because below
// 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
// 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
// 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
// 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,
if (whenExpression.isProperlyExhaustive) data else ResolutionMode.ContextIndependent, )
) whenExpression = completionResult
whenExpression = completionResult.result
completionResult.callCompleted
}
else -> false
} }
dataFlowAnalyzer.exitWhenExpression(whenExpression, data.forceFullCompletion)
dataFlowAnalyzer.exitWhenExpression(whenExpression, callCompleted)
whenExpression = whenExpression.replaceReturnTypeIfNotExhaustive() whenExpression = whenExpression.replaceReturnTypeIfNotExhaustive()
whenExpression whenExpression
} }
@@ -169,13 +163,13 @@ class FirControlFlowStatementsResolveTransformer(transformer: FirAbstractBodyRes
tryExpression.transformCatches(this, ResolutionMode.ContextDependent) tryExpression.transformCatches(this, ResolutionMode.ContextDependent)
val incomplete = syntheticCallGenerator.generateCalleeForTryExpression(tryExpression, resolutionContext) val incomplete = syntheticCallGenerator.generateCalleeForTryExpression(tryExpression, resolutionContext)
var (result, callCompleted) = callCompleter.completeCall(incomplete, data) var result = callCompleter.completeCall(incomplete, data)
if (result.finallyBlock != null) { if (result.finallyBlock != null) {
dataFlowAnalyzer.enterFinallyBlock() dataFlowAnalyzer.enterFinallyBlock()
result = result.transformFinallyBlock(transformer, ResolutionMode.ContextIndependent) result = result.transformFinallyBlock(transformer, ResolutionMode.ContextIndependent)
dataFlowAnalyzer.exitFinallyBlock() dataFlowAnalyzer.exitFinallyBlock()
} }
dataFlowAnalyzer.exitTryExpression(callCompleted) dataFlowAnalyzer.exitTryExpression(data.forceFullCompletion)
return result return result
} }
@@ -262,7 +256,7 @@ class FirControlFlowStatementsResolveTransformer(transformer: FirAbstractBodyRes
) )
elvisExpression.transformRhs(transformer, resolutionModeForRhs) elvisExpression.transformRhs(transformer, resolutionModeForRhs)
val (result, callCompleted) = callCompleter.completeCall( val result = callCompleter.completeCall(
syntheticCallGenerator.generateCalleeForElvisExpression(elvisExpression, resolutionContext), data 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 return result
} }
} }
@@ -166,7 +166,7 @@ open class FirExpressionsResolveTransformer(transformer: FirAbstractBodyResolveT
if (!transformedCallee.isAcceptableResolvedQualifiedAccess()) { if (!transformedCallee.isAcceptableResolvedQualifiedAccess()) {
return qualifiedAccessExpression return qualifiedAccessExpression
} }
callCompleter.completeCall(transformedCallee, data).result callCompleter.completeCall(transformedCallee, data)
} else { } else {
transformedCallee transformedCallee
} }
@@ -419,44 +419,39 @@ open class FirExpressionsResolveTransformer(transformer: FirAbstractBodyResolveT
functionCall.transformAnnotations(transformer, data) functionCall.transformAnnotations(transformer, data)
functionCall.replaceLambdaArgumentInvocationKinds(session) functionCall.replaceLambdaArgumentInvocationKinds(session)
functionCall.transformTypeArguments(transformer, ResolutionMode.ContextIndependent) functionCall.transformTypeArguments(transformer, ResolutionMode.ContextIndependent)
val (completeInference, callCompleted) = val initialExplicitReceiver = functionCall.explicitReceiver
run { val withTransformedArguments = if (!resolvingAugmentedAssignment) {
val initialExplicitReceiver = functionCall.explicitReceiver dataFlowAnalyzer.enterCallArguments(functionCall, functionCall.arguments)
val withTransformedArguments = if (!resolvingAugmentedAssignment) { // In provideDelegate mode the explicitReceiver is already resolved
dataFlowAnalyzer.enterCallArguments(functionCall, functionCall.arguments) // E.g. we have val some by someDelegate
// In provideDelegate mode the explicitReceiver is already resolved // At 1st stage of delegate inference we resolve someDelegate itself,
// E.g. we have val some by someDelegate // at 2nd stage in provideDelegate mode we are trying to resolve someDelegate.provideDelegate(),
// At 1st stage of delegate inference we resolve someDelegate itself, // and 'someDelegate' explicit receiver is resolved at 1st stage
// at 2nd stage in provideDelegate mode we are trying to resolve someDelegate.provideDelegate(), // See also FirDeclarationsResolveTransformer.transformWrappedDelegateExpression
// and 'someDelegate' explicit receiver is resolved at 1st stage val withResolvedExplicitReceiver = if (provideDelegate) functionCall else transformExplicitReceiver(functionCall)
// See also FirDeclarationsResolveTransformer.transformWrappedDelegateExpression withResolvedExplicitReceiver.also {
val withResolvedExplicitReceiver = if (provideDelegate) functionCall else transformExplicitReceiver(functionCall) it.replaceArgumentList(it.argumentList.transform(this, ResolutionMode.ContextDependent))
withResolvedExplicitReceiver.also { dataFlowAnalyzer.exitCallArguments()
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)
} }
} 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) val result = completeInference.transformToIntegerOperatorCallOrApproximateItIfNeeded(data)
if (!resolvingAugmentedAssignment) { if (!resolvingAugmentedAssignment) {
dataFlowAnalyzer.exitFunctionCall(result, callCompleted) dataFlowAnalyzer.exitFunctionCall(result, data.forceFullCompletion)
} }
addReceiversFromExtensions(result) addReceiversFromExtensions(result)
if (callCompleted) { if (enableArrayOfCallTransformation) {
if (enableArrayOfCallTransformation) { return arrayOfCallTransformer.transformFunctionCall(result, null)
return arrayOfCallTransformer.transformFunctionCall(result, null)
}
} }
return result return result
} }
@@ -970,11 +965,11 @@ open class FirExpressionsResolveTransformer(transformer: FirAbstractBodyResolveT
.transformAnnotations(transformer, ResolutionMode.ContextIndependent) .transformAnnotations(transformer, ResolutionMode.ContextIndependent)
.replaceArgumentList(checkNotNullCall.argumentList.transform(transformer, ResolutionMode.ContextDependent)) .replaceArgumentList(checkNotNullCall.argumentList.transform(transformer, ResolutionMode.ContextDependent))
val (result, callCompleted) = callCompleter.completeCall( val result = callCompleter.completeCall(
components.syntheticCallGenerator.generateCalleeForCheckNotNullCall(checkNotNullCall, resolutionContext), data components.syntheticCallGenerator.generateCalleeForCheckNotNullCall(checkNotNullCall, resolutionContext), data
) )
dataFlowAnalyzer.exitCheckNotNullCall(result, callCompleted) dataFlowAnalyzer.exitCheckNotNullCall(result, data.forceFullCompletion)
return result return result
} }
@@ -1328,8 +1323,8 @@ open class FirExpressionsResolveTransformer(transformer: FirAbstractBodyResolveT
// it seems that we may leave this code as is // it seems that we may leave this code as is
// without adding `context.withTowerDataContext(context.getTowerDataContextForConstructorResolution())` // without adding `context.withTowerDataContext(context.getTowerDataContextForConstructorResolution())`
val (result, callCompleted) = callCompleter.completeCall(resolvedCall, ResolutionMode.ContextIndependent) val result = callCompleter.completeCall(resolvedCall, ResolutionMode.ContextIndependent)
dataFlowAnalyzer.exitDelegatedConstructorCall(result, callCompleted) dataFlowAnalyzer.exitDelegatedConstructorCall(result, data.forceFullCompletion)
return result return result
} }
+2 -1
View File
@@ -706,8 +706,9 @@ digraph kt44814_kt {
191 -> {192}; 191 -> {192};
192 -> {193}; 192 -> {193};
193 -> {194}; 193 -> {194};
194 -> {195};
194 -> {182} [color=green style=dashed]; 194 -> {182} [color=green style=dashed];
194 -> {195} [color=green];
194 -> {202} [style=dotted];
195 -> {196}; 195 -> {196};
196 -> {197 201}; 196 -> {197 201};
197 -> {198}; 197 -> {198};