Effects: support called-in-place lambdas

Support initialization of local variables in capture, if that capture is
anonymous lambda that was passed to function which explicitly expressed
its intent to call closure "here and now"

Changes:
- LocalFunctionDeclarationInstruction made open

- Introduce InlinedLocalFunctionDeclarationInstruction, which is subtype
of LocalFunctionDeclarationInstruction with additional semantic: it
is statically known that this function will be called in-place and maybe
with some definite amount of invocations.

- Next-instruction of InlinedLocalFunctionDeclarationInstruction depends
on whether flow can exit its body normally, i.e. on wheter EXIT is
reachable. If flow can reach EXIT, then .next is just straight next
instruction, otherwise it's SINK.

- Take non-local instructions into consideration when analyzing
reachability. We didn't it before because no one ever needed proper
analysis. Now we have InlinedLocalFunctionDeclarationInstruction which
cares about proper reachability of EXIT.

- Pull control-flow information from
InlinedLocalFunctionDeclarationInstruction's EXIT into parent's
pseudocode, thus allowing it to participate in initialization/use
analysis.

- Change logic of 'isCapturedWrite' to not report
"CAPTURED_VAL_INITIALIZATION" if value is captured by the inline-lambda.

==========
Introduction of EffectSystem: 12/18
This commit is contained in:
Dmitry Savvinov
2017-10-03 15:02:44 +03:00
parent 0dd6d1f4c7
commit bdbb161cfa
12 changed files with 194 additions and 58 deletions
@@ -19,6 +19,7 @@ package org.jetbrains.kotlin.cfg
import org.jetbrains.kotlin.cfg.pseudocode.PseudoValue
import org.jetbrains.kotlin.cfg.pseudocode.Pseudocode
import org.jetbrains.kotlin.cfg.pseudocode.instructions.eval.*
import org.jetbrains.kotlin.contracts.description.InvocationKind
import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
@@ -27,9 +28,8 @@ import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue
interface ControlFlowBuilder {
// Subroutines
fun enterSubroutine(subroutine: KtElement)
fun exitSubroutine(subroutine: KtElement): Pseudocode
fun enterSubroutine(subroutine: KtElement, invocationKind: InvocationKind? = null)
fun exitSubroutine(subroutine: KtElement, invocationKind: InvocationKind? = null): Pseudocode
val currentSubroutine: KtElement
val returnSubroutine: KtElement
@@ -49,6 +49,8 @@ interface ControlFlowBuilder {
fun declareVariable(property: KtVariableDeclaration)
fun declareFunction(subroutine: KtElement, pseudocode: Pseudocode)
fun declareInlinedFunction(subroutine: KtElement, pseudocode: Pseudocode, invocationKind: InvocationKind)
fun declareEntryOrObject(entryOrObject: KtClassOrObject)
// Labels
@@ -19,6 +19,7 @@ package org.jetbrains.kotlin.cfg
import org.jetbrains.kotlin.cfg.pseudocode.PseudoValue
import org.jetbrains.kotlin.cfg.pseudocode.Pseudocode
import org.jetbrains.kotlin.cfg.pseudocode.instructions.eval.*
import org.jetbrains.kotlin.contracts.description.InvocationKind
import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
@@ -134,11 +135,12 @@ abstract class ControlFlowBuilderAdapter : ControlFlowBuilder {
delegateBuilder.exitTryFinally()
}
override fun enterSubroutine(subroutine: KtElement) {
delegateBuilder.enterSubroutine(subroutine)
override fun enterSubroutine(subroutine: KtElement, invocationKind: InvocationKind?) {
delegateBuilder.enterSubroutine(subroutine, invocationKind)
}
override fun exitSubroutine(subroutine: KtElement): Pseudocode = delegateBuilder.exitSubroutine(subroutine)
override fun exitSubroutine(subroutine: KtElement, invocationKind: InvocationKind?): Pseudocode =
delegateBuilder.exitSubroutine(subroutine, invocationKind)
override val currentSubroutine: KtElement
get() = delegateBuilder.currentSubroutine
@@ -178,6 +180,10 @@ abstract class ControlFlowBuilderAdapter : ControlFlowBuilder {
delegateBuilder.declareFunction(subroutine, pseudocode)
}
override fun declareInlinedFunction(subroutine: KtElement, pseudocode: Pseudocode, invocationKind: InvocationKind) {
delegateBuilder.declareInlinedFunction(subroutine, pseudocode, invocationKind)
}
override fun declareEntryOrObject(entryOrObject: KtClassOrObject) {
delegateBuilder.declareEntryOrObject(entryOrObject)
}
@@ -369,7 +369,8 @@ class ControlFlowInformationProvider private constructor(
// Do not consider top-level properties
if (containingDeclarationDescriptor is PackageFragmentDescriptor) return false
var parentDeclaration = getElementParentDeclaration(writeValueInstruction.element)
while (true) {
loop@ while (true) {
val context = trace.bindingContext
val parentDescriptor = getDeclarationDescriptorIncludingConstructors(context, parentDeclaration)
if (parentDescriptor == containingDeclarationDescriptor) {
@@ -381,6 +382,13 @@ class ControlFlowInformationProvider private constructor(
parentDeclaration = getElementParentDeclaration(parentDeclaration)
}
is KtDeclarationWithBody -> {
// If it is captured write in lambda that is called in-place, then skip it (treat as parent)
val maybeEnclosingLambdaExpr = parentDeclaration.parent
if (maybeEnclosingLambdaExpr is KtLambdaExpression && trace[BindingContext.LAMBDA_INVOCATIONS, maybeEnclosingLambdaExpr] != null) {
parentDeclaration = getElementParentDeclaration(parentDeclaration)
continue@loop
}
if (parentDeclaration is KtFunction && parentDeclaration.isLocal) return true
// miss non-local function or accessor just once
parentDeclaration = getElementParentDeclaration(parentDeclaration)
@@ -30,6 +30,9 @@ import org.jetbrains.kotlin.cfg.pseudocode.PseudocodeImpl
import org.jetbrains.kotlin.cfg.pseudocode.instructions.eval.AccessTarget
import org.jetbrains.kotlin.cfg.pseudocode.instructions.eval.InstructionWithValue
import org.jetbrains.kotlin.cfg.pseudocode.instructions.eval.MagicKind
import org.jetbrains.kotlin.contracts.description.InvocationKind
import org.jetbrains.kotlin.contracts.description.canBeRevisited
import org.jetbrains.kotlin.contracts.description.isDefinitelyVisited
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.impl.AnonymousFunctionDescriptor
import org.jetbrains.kotlin.diagnostics.Errors.*
@@ -58,18 +61,20 @@ import org.jetbrains.kotlin.types.expressions.OperatorConventions
import org.jetbrains.kotlin.util.slicedMap.ReadOnlySlice
import java.util.*
typealias DeferredGenerator = (ControlFlowBuilder) -> Unit
class ControlFlowProcessor(private val trace: BindingTrace) {
private val builder: ControlFlowBuilder = ControlFlowInstructionsGenerator()
fun generatePseudocode(subroutine: KtElement): Pseudocode {
val pseudocode = generate(subroutine)
val pseudocode = generate(subroutine, null)
(pseudocode as PseudocodeImpl).postProcess()
return pseudocode
}
private fun generate(subroutine: KtElement): Pseudocode {
builder.enterSubroutine(subroutine)
private fun generate(subroutine: KtElement, invocationKind: InvocationKind? = null): Pseudocode {
builder.enterSubroutine(subroutine, invocationKind)
val cfpVisitor = CFPVisitor(builder)
if (subroutine is KtDeclarationWithBody && subroutine !is KtSecondaryConstructor) {
val valueParameters = subroutine.valueParameters
@@ -87,7 +92,7 @@ class ControlFlowProcessor(private val trace: BindingTrace) {
else {
cfpVisitor.generateInstructions(subroutine)
}
return builder.exitSubroutine(subroutine)
return builder.exitSubroutine(subroutine, invocationKind)
}
private fun generateImplicitReturnValue(bodyExpression: KtExpression, subroutine: KtElement) {
@@ -105,7 +110,7 @@ class ControlFlowProcessor(private val trace: BindingTrace) {
val afterDeclaration = builder.createUnboundLabel("after local declaration")
builder.nondeterministicJump(afterDeclaration, subroutine, null)
generate(subroutine)
generate(subroutine, null)
builder.bindLabel(afterDeclaration)
}
@@ -115,6 +120,13 @@ class ControlFlowProcessor(private val trace: BindingTrace) {
private val catchFinallyStack = Stack<CatchFinallyLabels>()
// Some language constructs (e.g. inlined lambdas) should be partially processed before call
// (to provide argument for call itself), and partially - after (in case of inlined lambdas,
// their body should be generated after call). To do so, we store deferred generators, which
// will be called after call instruction is emitted.
// Stack is necessary to store generators across nested calls
private val deferredGeneratorsStack = Stack<MutableList<DeferredGenerator>>()
private val conditionVisitor = object : KtVisitorVoid() {
private fun getSubjectExpression(condition: KtWhenCondition): KtExpression? =
@@ -1020,14 +1032,42 @@ class ControlFlowProcessor(private val trace: BindingTrace) {
return parent.parent is KtDoWhileExpression
}
private fun visitFunction(function: KtFunction) {
processLocalDeclaration(function)
private fun visitFunction(function: KtFunction, invocationKind: InvocationKind? = null) {
if (invocationKind == null) {
processLocalDeclaration(function)
}
else {
visitInlinedFunction(function, invocationKind)
}
val isAnonymousFunction = function is KtFunctionLiteral || function.name == null
if (isAnonymousFunction || function.isLocal && function.parent !is KtBlockExpression) {
builder.createLambda(function)
}
}
private fun visitInlinedFunction(lambdaFunctionLiteral: KtFunction, invocationKind: InvocationKind) {
// Defer emitting of inlined declaration
deferredGeneratorsStack.peek().add({ builder ->
val beforeDeclaration = builder.createUnboundLabel("before inlined declaration")
val afterDeclaration = builder.createUnboundLabel("after inlined declaration")
builder.bindLabel(beforeDeclaration)
if (!invocationKind.isDefinitelyVisited()) {
builder.nondeterministicJump(afterDeclaration, lambdaFunctionLiteral, null)
}
generate(lambdaFunctionLiteral, invocationKind)
if (invocationKind.canBeRevisited()) {
builder.nondeterministicJump(beforeDeclaration, lambdaFunctionLiteral, null)
}
builder.bindLabel(afterDeclaration)
})
}
override fun visitNamedFunction(function: KtNamedFunction) {
visitFunction(function)
}
@@ -1035,7 +1075,11 @@ class ControlFlowProcessor(private val trace: BindingTrace) {
override fun visitLambdaExpression(lambdaExpression: KtLambdaExpression) {
mark(lambdaExpression)
val functionLiteral = lambdaExpression.functionLiteral
visitFunction(functionLiteral)
// NB. Behaviour here is implicitly controlled by the LanguageFeature 'CallsInPlaceEffect'
// If this feature is turned off, then slice LAMBDA_INVOCATIONS is never written and invocationKind
// in all subsequent calls always 'null', resulting in falling back to old behaviour
visitFunction(functionLiteral, trace[BindingContext.LAMBDA_INVOCATIONS, lambdaExpression])
copyValue(functionLiteral, lambdaExpression)
}
@@ -1485,6 +1529,8 @@ class ControlFlowProcessor(private val trace: BindingTrace) {
val receivers = getReceiverValues(resolvedCall)
deferredGeneratorsStack.push(mutableListOf())
var parameterValues = SmartFMap.emptyMap<PseudoValue, ValueParameterDescriptor>()
for (argument in resolvedCall.call.valueArguments) {
val argumentMapping = resolvedCall.getArgumentMapping(argument)
@@ -1507,7 +1553,10 @@ class ControlFlowProcessor(private val trace: BindingTrace) {
}
mark(resolvedCall.call.callElement)
return builder.call(callElement, resolvedCall, receivers, parameterValues)
val callInstruction = builder.call(callElement, resolvedCall, receivers, parameterValues)
val deferredGeneratorsForCall = deferredGeneratorsStack.pop()
deferredGeneratorsForCall.forEach { it.invoke(builder) }
return callInstruction
}
private fun getReceiverValues(resolvedCall: ResolvedCall<*>): Map<PseudoValue, ReceiverValue> {
@@ -19,6 +19,7 @@ package org.jetbrains.kotlin.cfg.pseudocodeTraverser
import org.jetbrains.kotlin.cfg.ControlFlowInfo
import org.jetbrains.kotlin.cfg.pseudocode.Pseudocode
import org.jetbrains.kotlin.cfg.pseudocode.instructions.Instruction
import org.jetbrains.kotlin.cfg.pseudocode.instructions.special.InlinedLocalFunctionDeclarationInstruction
import org.jetbrains.kotlin.cfg.pseudocode.instructions.special.LocalFunctionDeclarationInstruction
import org.jetbrains.kotlin.cfg.pseudocode.instructions.special.SubroutineEnterInstruction
import org.jetbrains.kotlin.cfg.pseudocode.instructions.special.SubroutineSinkInstruction
@@ -96,8 +97,13 @@ private fun <I : ControlFlowInfo<*, *>> Pseudocode.collectDataFromSubgraph(
if (instruction is LocalFunctionDeclarationInstruction) {
val subroutinePseudocode = instruction.body
subroutinePseudocode.collectDataFromSubgraph(
traversalOrder, edgesMap, mergeEdges, updateEdge, previousInstructions, changed, true)
val lastInstruction = subroutinePseudocode.getLastInstruction(traversalOrder)
traversalOrder, edgesMap, mergeEdges, updateEdge, previousInstructions, changed, true
)
// Special case for inlined functions: take flow from EXIT instructions (it contains flow which exits declaration normally)
val lastInstruction = if (instruction is InlinedLocalFunctionDeclarationInstruction && traversalOrder == FORWARD)
subroutinePseudocode.exitInstruction
else
subroutinePseudocode.getLastInstruction(traversalOrder)
val previousValue = edgesMap[instruction]
val newValue = edgesMap[lastInstruction]
val updatedValue = newValue?.let {
@@ -24,6 +24,7 @@ import org.jetbrains.kotlin.cfg.pseudocode.instructions.Instruction
import org.jetbrains.kotlin.cfg.pseudocode.instructions.eval.*
import org.jetbrains.kotlin.cfg.pseudocode.instructions.jumps.*
import org.jetbrains.kotlin.cfg.pseudocode.instructions.special.*
import org.jetbrains.kotlin.contracts.description.InvocationKind
import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
@@ -47,8 +48,8 @@ class ControlFlowInstructionsGenerator : ControlFlowBuilderAdapter() {
private val allBlocks = Stack<BlockInfo>()
private fun pushBuilder(scopingElement: KtElement, subroutine: KtElement) {
val worker = ControlFlowInstructionsGeneratorWorker(scopingElement, subroutine)
private fun pushBuilder(scopingElement: KtElement, subroutine: KtElement, shouldInline: Boolean) {
val worker = ControlFlowInstructionsGeneratorWorker(scopingElement, subroutine, shouldInline)
builders.push(worker)
builder = worker
}
@@ -64,32 +65,42 @@ class ControlFlowInstructionsGenerator : ControlFlowBuilderAdapter() {
return worker
}
override fun enterSubroutine(subroutine: KtElement) {
override fun enterSubroutine(subroutine: KtElement, invocationKind: InvocationKind?) {
val builder = builder
val shouldInlnie = invocationKind != null
if (builder != null && subroutine is KtFunctionLiteral) {
pushBuilder(subroutine, builder.returnSubroutine)
pushBuilder(subroutine, builder.returnSubroutine, shouldInlnie)
}
else {
pushBuilder(subroutine, subroutine)
pushBuilder(subroutine, subroutine, shouldInlnie)
}
delegateBuilder.enterBlockScope(subroutine)
delegateBuilder.enterSubroutine(subroutine)
}
override fun exitSubroutine(subroutine: KtElement): Pseudocode {
super.exitSubroutine(subroutine)
override fun exitSubroutine(subroutine: KtElement, invocationKind: InvocationKind?): Pseudocode {
super.exitSubroutine(subroutine, invocationKind)
delegateBuilder.exitBlockScope(subroutine)
val worker = popBuilder()
if (!builders.empty()) {
val builder = builders.peek()
builder.declareFunction(subroutine, worker.pseudocode)
if (invocationKind == null) {
builder.declareFunction(subroutine, worker.pseudocode)
}
else {
builder.declareInlinedFunction(subroutine, worker.pseudocode, invocationKind)
}
}
return worker.pseudocode
}
private inner class ControlFlowInstructionsGeneratorWorker(scopingElement: KtElement, override val returnSubroutine: KtElement) : ControlFlowBuilder {
private inner class ControlFlowInstructionsGeneratorWorker(
scopingElement: KtElement,
override val returnSubroutine: KtElement,
shouldInline: Boolean
) : ControlFlowBuilder {
val pseudocode: PseudocodeImpl = PseudocodeImpl(scopingElement)
val pseudocode: PseudocodeImpl = PseudocodeImpl(scopingElement, shouldInline)
private val error: Label = pseudocode.createLabel("error", null)
private val sink: Label = pseudocode.createLabel("sink", null)
@@ -145,7 +156,7 @@ class ControlFlowInstructionsGenerator : ControlFlowBuilderAdapter() {
override val currentLoop: KtLoopExpression?
get() = if (loopInfo.empty()) null else loopInfo.peek().element
override fun enterSubroutine(subroutine: KtElement) {
override fun enterSubroutine(subroutine: KtElement, invocationKind: InvocationKind?) {
val blockInfo = SubroutineInfo(
subroutine,
/* entry point */ createUnboundLabel(),
@@ -204,7 +215,7 @@ class ControlFlowInstructionsGenerator : ControlFlowBuilderAdapter() {
}
}
override fun exitSubroutine(subroutine: KtElement): Pseudocode {
override fun exitSubroutine(subroutine: KtElement, invocationKind: InvocationKind?): Pseudocode {
getSubroutineExitPoint(subroutine)?.let { bindLabel(it) }
pseudocode.addExitInstruction(SubroutineExitInstruction(subroutine, currentScope, false))
bindLabel(error)
@@ -261,6 +272,10 @@ class ControlFlowInstructionsGenerator : ControlFlowBuilderAdapter() {
add(LocalFunctionDeclarationInstruction(subroutine, pseudocode, currentScope))
}
override fun declareInlinedFunction(subroutine: KtElement, pseudocode: Pseudocode, invocationKind: InvocationKind) {
add(InlinedLocalFunctionDeclarationInstruction(subroutine, pseudocode, currentScope, invocationKind))
}
override fun declareEntryOrObject(entryOrObject: KtClassOrObject) {
add(VariableDeclarationInstruction(entryOrObject, currentScope))
}
@@ -45,6 +45,7 @@ interface Pseudocode {
val enterInstruction: SubroutineEnterInstruction
val isInlined: Boolean
val containsDoWhile: Boolean
val rootPseudocode: Pseudocode
@@ -27,10 +27,7 @@ import org.jetbrains.kotlin.cfg.pseudocode.instructions.eval.MergeInstruction
import org.jetbrains.kotlin.cfg.pseudocode.instructions.jumps.AbstractJumpInstruction
import org.jetbrains.kotlin.cfg.pseudocode.instructions.jumps.ConditionalJumpInstruction
import org.jetbrains.kotlin.cfg.pseudocode.instructions.jumps.NondeterministicJumpInstruction
import org.jetbrains.kotlin.cfg.pseudocode.instructions.special.LocalFunctionDeclarationInstruction
import org.jetbrains.kotlin.cfg.pseudocode.instructions.special.SubroutineEnterInstruction
import org.jetbrains.kotlin.cfg.pseudocode.instructions.special.SubroutineExitInstruction
import org.jetbrains.kotlin.cfg.pseudocode.instructions.special.SubroutineSinkInstruction
import org.jetbrains.kotlin.cfg.pseudocode.instructions.special.*
import org.jetbrains.kotlin.cfg.pseudocodeTraverser.TraversalOrder.BACKWARD
import org.jetbrains.kotlin.cfg.pseudocodeTraverser.TraversalOrder.FORWARD
import org.jetbrains.kotlin.cfg.pseudocodeTraverser.TraverseInstructionResult
@@ -38,7 +35,7 @@ import org.jetbrains.kotlin.cfg.pseudocodeTraverser.traverseFollowingInstruction
import org.jetbrains.kotlin.psi.KtElement
import java.util.*
class PseudocodeImpl(override val correspondingElement: KtElement) : Pseudocode {
class PseudocodeImpl(override val correspondingElement: KtElement, override val isInlined: Boolean) : Pseudocode {
internal val mutableInstructionList = ArrayList<Instruction>()
override val instructions = ArrayList<Instruction>()
@@ -56,6 +53,8 @@ class PseudocodeImpl(override val correspondingElement: KtElement) : Pseudocode
getLocalDeclarations(this)
}
val reachableInstructions = hashSetOf<Instruction>()
private val representativeInstructions = HashMap<KtElement, KtElementInstruction>()
private val labels = ArrayList<PseudocodeLabel>()
@@ -110,7 +109,7 @@ class PseudocodeImpl(override val correspondingElement: KtElement) : Pseudocode
override val reversedInstructions: List<Instruction>
get() {
val traversedInstructions = linkedSetOf<Instruction>()
traverseFollowingInstructions(sinkInstruction, traversedInstructions, BACKWARD, null)
traverseFollowingInstructions(if (this.isInlined) instructions.last() else sinkInstruction, traversedInstructions, BACKWARD, null)
if (traversedInstructions.size < instructions.size) {
val simplyReversedInstructions = instructions.reversed()
for (instruction in simplyReversedInstructions) {
@@ -224,22 +223,17 @@ class PseudocodeImpl(override val correspondingElement: KtElement) : Pseudocode
postPrecessed = true
errorInstruction.sink = sinkInstruction
exitInstruction.sink = sinkInstruction
for ((index, instruction) in mutableInstructionList.withIndex()) {
//recursively invokes 'postProcess' for local declarations
//recursively invokes 'postProcess' for local declarations, thus it needs global set of reachable instructions
instruction.processInstruction(index)
}
if (parent != null) return
// Collecting reachable instructions should be done after processing all instructions
// (including instructions in local declarations) to avoid being in incomplete state.
collectAndCacheReachableInstructions()
for (localFunctionDeclarationInstruction in localDeclarations) {
(localFunctionDeclarationInstruction.body as PseudocodeImpl).collectAndCacheReachableInstructions()
}
}
private fun collectAndCacheReachableInstructions() {
val reachableInstructions = collectReachableInstructions()
collectReachableInstructions()
for (instruction in mutableInstructionList) {
if (reachableInstructions.contains(instruction)) {
instructions.add(instruction)
@@ -287,6 +281,14 @@ class PseudocodeImpl(override val correspondingElement: KtElement) : Pseudocode
instruction.next = sinkInstruction
}
override fun visitInlinedLocalFunctionDeclarationInstruction(instruction: InlinedLocalFunctionDeclarationInstruction) {
val body = instruction.body as PseudocodeImpl
body.parent = this@PseudocodeImpl
body.postProcess()
// Don't add edge to next instruction if flow can't reach exit of inlined declaration
instruction.next = if (body.instructions.contains(body.exitInstruction)) getNextPosition(currentPosition) else sinkInstruction
}
override fun visitSubroutineExit(instruction: SubroutineExitInstruction) {
// Nothing
}
@@ -301,25 +303,25 @@ class PseudocodeImpl(override val correspondingElement: KtElement) : Pseudocode
})
}
private fun collectReachableInstructions(): Set<Instruction> {
val visited = hashSetOf<Instruction>()
traverseFollowingInstructions(enterInstruction, visited, FORWARD
private fun collectReachableInstructions() {
val reachableFromThisPseudocode = hashSetOf<Instruction>()
traverseFollowingInstructions(enterInstruction, reachableFromThisPseudocode, FORWARD
) { instruction ->
if (instruction is MagicInstruction && instruction.kind === MagicKind.EXHAUSTIVE_WHEN_ELSE) {
return@traverseFollowingInstructions TraverseInstructionResult.SKIP
}
TraverseInstructionResult.CONTINUE
}
if (!visited.contains(exitInstruction)) {
visited.add(exitInstruction)
// Don't force-add EXIT and ERROR for inlined pseudocodes because for such
// declarations those instructions has special semantic
if (!isInlined) {
reachableFromThisPseudocode.add(exitInstruction)
reachableFromThisPseudocode.add(errorInstruction)
reachableFromThisPseudocode.add(sinkInstruction)
}
if (!visited.contains(errorInstruction)) {
visited.add(errorInstruction)
}
if (!visited.contains(sinkInstruction)) {
visited.add(sinkInstruction)
}
return visited
reachableFromThisPseudocode.forEach { (it.owner as PseudocodeImpl).reachableInstructions.add(it) }
}
private fun markDeadInstructions() {
@@ -343,7 +345,7 @@ class PseudocodeImpl(override val correspondingElement: KtElement) : Pseudocode
}
override fun copy(): PseudocodeImpl {
val result = PseudocodeImpl(correspondingElement)
val result = PseudocodeImpl(correspondingElement, isInlined)
result.repeatWhole(this)
return result
}
@@ -33,6 +33,10 @@ open class InstructionVisitor {
visitInstructionWithNext(instruction)
}
open fun visitInlinedLocalFunctionDeclarationInstruction(instruction: InlinedLocalFunctionDeclarationInstruction) {
visitLocalFunctionDeclarationInstruction(instruction)
}
open fun visitVariableDeclarationInstruction(instruction: VariableDeclarationInstruction) {
visitInstructionWithNext(instruction)
}
@@ -30,6 +30,9 @@ abstract class InstructionVisitorWithResult<out R> {
open fun visitLocalFunctionDeclarationInstruction(instruction: LocalFunctionDeclarationInstruction): R =
visitInstructionWithNext(instruction)
open fun visitInlinedFunctionDeclarationInstruction(instruction: InlinedLocalFunctionDeclarationInstruction): R =
visitLocalFunctionDeclarationInstruction(instruction)
open fun visitVariableDeclarationInstruction(instruction: VariableDeclarationInstruction): R = visitInstructionWithNext(instruction)
open fun visitUnconditionalJump(instruction: UnconditionalJumpInstruction): R = visitJump(instruction)
@@ -0,0 +1,40 @@
/*
* 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.cfg.pseudocode.instructions.special
import org.jetbrains.kotlin.cfg.pseudocode.Pseudocode
import org.jetbrains.kotlin.cfg.pseudocode.instructions.BlockScope
import org.jetbrains.kotlin.cfg.pseudocode.instructions.InstructionImpl
import org.jetbrains.kotlin.cfg.pseudocode.instructions.InstructionVisitor
import org.jetbrains.kotlin.cfg.pseudocode.instructions.InstructionVisitorWithResult
import org.jetbrains.kotlin.contracts.description.InvocationKind
import org.jetbrains.kotlin.psi.KtElement
class InlinedLocalFunctionDeclarationInstruction(
element: KtElement,
body: Pseudocode,
blockScope: BlockScope,
val kind: InvocationKind
) : LocalFunctionDeclarationInstruction(element, body, blockScope) {
override fun createCopy(): InstructionImpl = InlinedLocalFunctionDeclarationInstruction(element, body, blockScope, kind)
override fun accept(visitor: InstructionVisitor) = visitor.visitInlinedLocalFunctionDeclarationInstruction(this)
override fun <R> accept(visitor: InstructionVisitorWithResult<R>): R = visitor.visitInlinedFunctionDeclarationInstruction(this)
override fun toString(): String = "inlined(${render(element)})"
}
@@ -25,7 +25,7 @@ import org.jetbrains.kotlin.cfg.pseudocode.instructions.InstructionVisitor
import org.jetbrains.kotlin.cfg.pseudocode.instructions.InstructionVisitorWithResult
import org.jetbrains.kotlin.cfg.pseudocode.instructions.InstructionImpl
class LocalFunctionDeclarationInstruction(
open class LocalFunctionDeclarationInstruction(
element: KtElement,
val body: Pseudocode,
blockScope: BlockScope