Pseudocode: Generate additional returns for implicit return values (lambdas, functions with expression bodies)

This commit is contained in:
Alexey Sedunov
2014-06-27 13:42:40 +04:00
parent 3e3918ab6e
commit 0a8fa404bf
21 changed files with 73 additions and 68 deletions
@@ -74,7 +74,7 @@ public interface JetControlFlowBuilder {
void nondeterministicJump(@NotNull List<Label> label, @NotNull JetElement element); void nondeterministicJump(@NotNull List<Label> label, @NotNull JetElement element);
void jumpToError(@NotNull JetElement element); void jumpToError(@NotNull JetElement element);
void returnValue(@NotNull JetReturnExpression returnExpression, @NotNull PseudoValue returnValue, @NotNull JetElement subroutine); void returnValue(@NotNull JetExpression returnExpression, @NotNull PseudoValue returnValue, @NotNull JetElement subroutine);
void returnNoValue(@NotNull JetReturnExpression returnExpression, @NotNull JetElement subroutine); void returnNoValue(@NotNull JetReturnExpression returnExpression, @NotNull JetElement subroutine);
void throwException(@NotNull JetThrowExpression throwExpression, @NotNull PseudoValue thrownValue); void throwException(@NotNull JetThrowExpression throwExpression, @NotNull PseudoValue thrownValue);
@@ -233,7 +233,7 @@ public abstract class JetControlFlowBuilderAdapter implements JetControlFlowBuil
} }
@Override @Override
public void returnValue(@NotNull JetReturnExpression returnExpression, @NotNull PseudoValue returnValue, @NotNull JetElement subroutine) { public void returnValue(@NotNull JetExpression returnExpression, @NotNull PseudoValue returnValue, @NotNull JetElement subroutine) {
getDelegateBuilder().returnValue(returnExpression, returnValue, subroutine); getDelegateBuilder().returnValue(returnExpression, returnValue, subroutine);
} }
@@ -87,6 +87,9 @@ public class JetControlFlowProcessor {
JetExpression bodyExpression = declarationWithBody.getBodyExpression(); JetExpression bodyExpression = declarationWithBody.getBodyExpression();
if (bodyExpression != null) { if (bodyExpression != null) {
cfpVisitor.generateInstructions(bodyExpression); cfpVisitor.generateInstructions(bodyExpression);
if (!declarationWithBody.hasBlockBody()) {
generateImplicitReturnValue(bodyExpression, subroutine);
}
} }
} else { } else {
cfpVisitor.generateInstructions(subroutine); cfpVisitor.generateInstructions(subroutine);
@@ -94,6 +97,13 @@ public class JetControlFlowProcessor {
return builder.exitSubroutine(subroutine); return builder.exitSubroutine(subroutine);
} }
private void generateImplicitReturnValue(@NotNull JetExpression bodyExpression, @NotNull JetElement subroutine) {
PseudoValue returnValue = builder.getBoundValue(bodyExpression);
if (returnValue == null) return;
builder.returnValue(bodyExpression, returnValue, subroutine);
}
private void processLocalDeclaration(@NotNull JetDeclaration subroutine) { private void processLocalDeclaration(@NotNull JetDeclaration subroutine) {
JetElement parent = PsiTreeUtil.getParentOfType(subroutine, JetElement.class); JetElement parent = PsiTreeUtil.getParentOfType(subroutine, JetElement.class);
assert parent != null; assert parent != null;
@@ -284,9 +284,7 @@ public class JetControlFlowInstructionsGenerator extends JetControlFlowBuilderAd
} }
@Override @Override
public void returnValue( public void returnValue(@NotNull JetExpression returnExpression, @NotNull PseudoValue returnValue, @NotNull JetElement subroutine) {
@NotNull JetReturnExpression returnExpression, @NotNull PseudoValue returnValue, @NotNull JetElement subroutine
) {
Label exitPoint = getExitPoint(subroutine); Label exitPoint = getExitPoint(subroutine);
handleJumpInsideTryFinally(exitPoint); handleJumpInsideTryFinally(exitPoint);
add(new ReturnValueInstruction(returnExpression, getCurrentScope(), exitPoint, returnValue)); add(new ReturnValueInstruction(returnExpression, getCurrentScope(), exitPoint, returnValue));
@@ -23,6 +23,7 @@ import java.util.Collections
import org.jetbrains.jet.lang.cfg.pseudocode.instructions.LexicalScope import org.jetbrains.jet.lang.cfg.pseudocode.instructions.LexicalScope
import org.jetbrains.jet.lang.cfg.pseudocode.instructions.InstructionVisitor import org.jetbrains.jet.lang.cfg.pseudocode.instructions.InstructionVisitor
import org.jetbrains.jet.lang.cfg.pseudocode.instructions.InstructionVisitorWithResult import org.jetbrains.jet.lang.cfg.pseudocode.instructions.InstructionVisitorWithResult
import org.jetbrains.jet.lang.psi.JetReturnExpression
public class ReturnValueInstruction( public class ReturnValueInstruction(
returnExpression: JetExpression, returnExpression: JetExpression,
@@ -47,4 +48,8 @@ public class ReturnValueInstruction(
override fun createCopy(newLabel: Label, lexicalScope: LexicalScope): AbstractJumpInstruction { override fun createCopy(newLabel: Label, lexicalScope: LexicalScope): AbstractJumpInstruction {
return ReturnValueInstruction((element as JetExpression), lexicalScope, newLabel, returnedValue) return ReturnValueInstruction((element as JetExpression), lexicalScope, newLabel, returnedValue)
} }
public val resultExpression: JetExpression =
element.let{ if (it is JetReturnExpression) it.getReturnedExpression()!! else element as JetExpression }
public val returnExpressionIfAny: JetReturnExpression? = element as? JetReturnExpression
} }
@@ -32,37 +32,8 @@ import org.jetbrains.jet.lang.resolve.OverridingUtil
import org.jetbrains.jet.lang.types.TypeUtils import org.jetbrains.jet.lang.types.TypeUtils
import org.jetbrains.jet.lang.types.JetType import org.jetbrains.jet.lang.types.JetType
fun JetExpression.isStatement(pseudocode: Pseudocode): Boolean { fun JetExpression.isStatement(pseudocode: Pseudocode): Boolean =
val value = pseudocode.getElementValue(this); pseudocode.getUsages(pseudocode.getElementValue(this)).isEmpty()
if (value == null) return true
fun considerUsedIfCreatedBeforeExit(): Boolean {
return when {
(getParent() as? JetFunction)?.getBodyExpression() == this ->
true
value.implicitReturnValue ->
true
else ->
false
}
}
val instruction = value.createdAt
if (considerUsedIfCreatedBeforeExit() && instruction.nextInstructions.any { it == pseudocode.getExitInstruction() }) return false
return pseudocode.getUsages(pseudocode.getElementValue(this)).isEmpty()
}
val PseudoValue.implicitReturnValue: Boolean
get() {
val pseudocode = createdAt.owner
val function = pseudocode.getCorrespondingElement() as? JetDeclarationWithBody
if (function is JetFunctionLiteral || (function != null && !function.hasBlockBody())) {
return pseudocode.getElementValue(function.getBodyExpression()) == this
}
return false
}
fun getReceiverTypePredicate(resolvedCall: ResolvedCall<*>, receiverValue: ReceiverValue): TypePredicate? { fun getReceiverTypePredicate(resolvedCall: ResolvedCall<*>, receiverValue: ReceiverValue): TypePredicate? {
val callableDescriptor = resolvedCall.getResultingDescriptor() val callableDescriptor = resolvedCall.getResultingDescriptor()
@@ -91,17 +62,15 @@ fun getExpectedTypePredicate(value: PseudoValue, bindingContext: BindingContext)
fun addSubtypesOf(jetType: JetType?) = typePredicates.add(jetType?.getSubtypesPredicate()) fun addSubtypesOf(jetType: JetType?) = typePredicates.add(jetType?.getSubtypesPredicate())
fun addTypePredicates(value: PseudoValue) { fun addTypePredicates(value: PseudoValue) {
if (value.implicitReturnValue) {
val function = value.createdAt.owner.getCorrespondingElement() as? JetDeclarationWithBody
val functionDescriptor = bindingContext[BindingContext.DECLARATION_TO_DESCRIPTOR, function] as? FunctionDescriptor
addSubtypesOf(functionDescriptor?.getReturnType())
}
pseudocode.getUsages(value).forEach { pseudocode.getUsages(value).forEach {
when (it) { when (it) {
is ReturnValueInstruction -> { is ReturnValueInstruction -> {
val functionDescriptor = (it.element as JetReturnExpression).getTargetFunctionDescriptor(bindingContext) val returnElement = it.element
addSubtypesOf(functionDescriptor?.getReturnType()) val functionDescriptor = when(returnElement) {
is JetReturnExpression -> returnElement.getTargetFunctionDescriptor(bindingContext)
else -> bindingContext[BindingContext.DECLARATION_TO_DESCRIPTOR, value.createdAt.owner.getCorrespondingElement()]
}
addSubtypesOf((functionDescriptor as? CallableDescriptor)?.getReturnType())
} }
is ConditionalJumpInstruction -> is ConditionalJumpInstruction ->
@@ -45,6 +45,7 @@ L0:
magic(vararg a: Any?) -> <v0> INIT: in: {a=D} out: {a=D} magic(vararg a: Any?) -> <v0> INIT: in: {a=D} out: {a=D}
w(a|<v0>) INIT: in: {a=D} out: {a=ID} USE: in: {a=READ} out: {a=READ} w(a|<v0>) INIT: in: {a=D} out: {a=ID} USE: in: {a=READ} out: {a=READ}
r(a) -> <v1> INIT: in: {a=ID} out: {a=ID} USE: in: {} out: {a=READ} r(a) -> <v1> INIT: in: {a=ID} out: {a=ID} USE: in: {} out: {a=READ}
ret(*|<v1>) L1
L1: L1:
<END> <END>
error: error:
@@ -48,8 +48,9 @@ L3:
r(a) -> <v4> INIT: in: {x=ID, y=ID} out: {x=ID, y=ID} USE: in: {} out: {a=READ} r(a) -> <v4> INIT: in: {x=ID, y=ID} out: {x=ID, y=ID} USE: in: {} out: {a=READ}
mark(use(a)) mark(use(a))
call(use(a), use|<v4>) -> <v5> call(use(a), use|<v4>) -> <v5>
3 ret(*|<v5>) L4 INIT: in: {x=ID} out: {x=ID}
L4: L4:
3 <END> INIT: in: {x=ID} out: {x=ID} <END>
error: error:
<ERROR> INIT: in: {} out: {} <ERROR> INIT: in: {} out: {}
sink: sink:
@@ -64,6 +65,7 @@ L0:
magic(vararg a: Any?) -> <v0> INIT: in: {a=D} out: {a=D} magic(vararg a: Any?) -> <v0> INIT: in: {a=D} out: {a=D}
w(a|<v0>) INIT: in: {a=D} out: {a=ID} USE: in: {a=READ} out: {a=READ} w(a|<v0>) INIT: in: {a=D} out: {a=ID} USE: in: {a=READ} out: {a=READ}
r(a) -> <v1> INIT: in: {a=ID} out: {a=ID} USE: in: {} out: {a=READ} r(a) -> <v1> INIT: in: {a=ID} out: {a=ID} USE: in: {} out: {a=READ}
ret(*|<v1>) L1
L1: L1:
<END> <END>
error: error:
@@ -55,6 +55,7 @@ L0:
magic(a: Int) -> <v0> INIT: in: {a=D} out: {a=D} magic(a: Int) -> <v0> INIT: in: {a=D} out: {a=D}
w(a|<v0>) INIT: in: {a=D} out: {a=ID} USE: in: {a=READ} out: {a=READ} w(a|<v0>) INIT: in: {a=D} out: {a=ID} USE: in: {a=READ} out: {a=READ}
r(a) -> <v1> INIT: in: {a=ID} out: {a=ID} USE: in: {} out: {a=READ} r(a) -> <v1> INIT: in: {a=ID} out: {a=ID} USE: in: {} out: {a=READ}
ret(*|<v1>) L1
L1: L1:
<END> <END>
error: error:
@@ -43,8 +43,9 @@ L3:
r(x) -> <v7> USE: in: {} out: {x=READ} r(x) -> <v7> USE: in: {} out: {x=READ}
mark(sum(x - 1) + x) mark(sum(x - 1) + x)
call(sum(x - 1) + x, plus|<v6>, <v7>) -> <v8> call(sum(x - 1) + x, plus|<v6>, <v7>) -> <v8>
2 ret(*|<v8>) L4
L4: L4:
2 <END> <END>
error: error:
<ERROR> INIT: in: {} out: {} <ERROR> INIT: in: {} out: {}
sink: sink:
@@ -68,6 +68,7 @@ L0:
magic(vararg a: Any?) -> <v0> INIT: in: {a=D} out: {a=D} magic(vararg a: Any?) -> <v0> INIT: in: {a=D} out: {a=D}
w(a|<v0>) INIT: in: {a=D} out: {a=ID} USE: in: {a=READ} out: {a=READ} w(a|<v0>) INIT: in: {a=D} out: {a=ID} USE: in: {a=READ} out: {a=READ}
r(a) -> <v1> INIT: in: {a=ID} out: {a=ID} USE: in: {} out: {a=READ} r(a) -> <v1> INIT: in: {a=ID} out: {a=ID} USE: in: {} out: {a=READ}
ret(*|<v1>) L1
L1: L1:
<END> <END>
error: error:
@@ -38,6 +38,7 @@ L3:
r(b) -> <v2> USE: in: {} out: {b=READ} r(b) -> <v2> USE: in: {} out: {b=READ}
mark(x + b) mark(x + b)
call(x + b, plus|<v1>, <v2>) -> <v3> call(x + b, plus|<v1>, <v2>) -> <v3>
ret(*|<v3>) L4
L4: L4:
<END> <END>
error: error:
@@ -79,12 +79,13 @@ L3:
3 <START> 3 <START>
4 mark(1) 4 mark(1)
r(1) -> <v0> r(1) -> <v0>
3 ret(*|<v0>) L4
L4: L4:
3 <END> NEXT:[<SINK>] <END> NEXT:[<SINK>]
error: error:
<ERROR> PREV:[] <ERROR> PREV:[]
sink: sink:
<SINK> PREV:[<ERROR>, <END>] <SINK> PREV:[<ERROR>, <END>]
===================== =====================
== foo == == foo ==
fun foo(a : Boolean, b : Int) : Unit {} fun foo(a : Boolean, b : Int) : Unit {}
@@ -4,10 +4,11 @@ fun short() = 1
L0: L0:
1 <START> 1 <START>
r(1) -> <v0> r(1) -> <v0>
ret(*|<v0>) L1
L1: L1:
<END> NEXT:[<SINK>] <END> NEXT:[<SINK>]
error: error:
<ERROR> PREV:[] <ERROR> PREV:[]
sink: sink:
<SINK> PREV:[<ERROR>, <END>] <SINK> PREV:[<ERROR>, <END>]
===================== =====================
@@ -45,12 +45,13 @@ override fun foo() = 10
L0: L0:
1 <START> 1 <START>
r(10) -> <v0> r(10) -> <v0>
ret(*|<v0>) L1
L1: L1:
<END> NEXT:[<SINK>] <END> NEXT:[<SINK>]
error: error:
<ERROR> PREV:[] <ERROR> PREV:[]
sink: sink:
<SINK> PREV:[<ERROR>, <END>] <SINK> PREV:[<ERROR>, <END>]
===================== =====================
== foo == == foo ==
fun foo(b: B) : Int { fun foo(b: B) : Int {
@@ -19,12 +19,13 @@ fun component1() = 1
L0: L0:
1 <START> 1 <START>
r(1) -> <v0> r(1) -> <v0>
ret(*|<v0>) L1
L1: L1:
<END> NEXT:[<SINK>] <END> NEXT:[<SINK>]
error: error:
<ERROR> PREV:[] <ERROR> PREV:[]
sink: sink:
<SINK> PREV:[<ERROR>, <END>] <SINK> PREV:[<ERROR>, <END>]
===================== =====================
== component2 == == component2 ==
fun component2() = 2 fun component2() = 2
@@ -32,12 +33,13 @@ fun component2() = 2
L0: L0:
1 <START> 1 <START>
r(2) -> <v0> r(2) -> <v0>
ret(*|<v0>) L1
L1: L1:
<END> NEXT:[<SINK>] <END> NEXT:[<SINK>]
error: error:
<ERROR> PREV:[] <ERROR> PREV:[]
sink: sink:
<SINK> PREV:[<ERROR>, <END>] <SINK> PREV:[<ERROR>, <END>]
===================== =====================
== test == == test ==
fun test(c: C) { fun test(c: C) {
@@ -24,6 +24,7 @@ L0:
magic(p: PropertyMetadata) -> <v1> magic(p: PropertyMetadata) -> <v1>
w(p|<v1>) w(p|<v1>)
r(0) -> <v2> r(0) -> <v2>
ret(*|<v2>) L1
L1: L1:
<END> NEXT:[<SINK>] <END> NEXT:[<SINK>]
error: error:
@@ -26,6 +26,7 @@ L0:
r($bar|<v1>) -> <v2> r($bar|<v1>) -> <v2>
mark("foo" + this.$bar) mark("foo" + this.$bar)
call("foo" + this.$bar, plus|<v0>, <v2>) -> <v3> call("foo" + this.$bar, plus|<v0>, <v2>) -> <v3>
ret(*|<v3>) L1
L1: L1:
<END> NEXT:[<SINK>] <END> NEXT:[<SINK>]
error: error:
@@ -17,6 +17,7 @@ L2 [after default value for parameter i]:
r(j) -> <v5> r(j) -> <v5>
mark(i + j) mark(i + j)
call(i + j, plus|<v4>, <v5>) -> <v6> call(i + j, plus|<v4>, <v5>) -> <v6>
ret(*|<v6>) L1
L1: L1:
<END> NEXT:[<SINK>] <END> NEXT:[<SINK>]
error: error:
@@ -16,14 +16,17 @@
package org.jetbrains.jet.cfg; package org.jetbrains.jet.cfg;
import junit.framework.Assert;
import junit.framework.Test; import junit.framework.Test;
import junit.framework.TestSuite; import junit.framework.TestSuite;
import java.io.File;
import java.util.regex.Pattern;
import org.jetbrains.jet.JetTestUtils; import org.jetbrains.jet.JetTestUtils;
import org.jetbrains.jet.test.InnerTestClasses; import org.jetbrains.jet.test.InnerTestClasses;
import org.jetbrains.jet.test.TestMetadata; import org.jetbrains.jet.test.TestMetadata;
import java.io.File; import org.jetbrains.jet.cfg.AbstractPseudoValueTest;
import java.util.regex.Pattern;
/** This class is generated by {@link org.jetbrains.jet.generators.tests.TestsPackage}. DO NOT MODIFY MANUALLY */ /** This class is generated by {@link org.jetbrains.jet.generators.tests.TestsPackage}. DO NOT MODIFY MANUALLY */
@SuppressWarnings("all") @SuppressWarnings("all")
@@ -90,7 +90,7 @@ private fun List<Instruction>.getResultType(
fun instructionToType(instruction: Instruction): JetType? { fun instructionToType(instruction: Instruction): JetType? {
val expression = when (instruction) { val expression = when (instruction) {
is ReturnValueInstruction -> is ReturnValueInstruction ->
(instruction.element as JetReturnExpression).getReturnedExpression() instruction.resultExpression
is InstructionWithValue -> is InstructionWithValue ->
instruction.outputValue?.element as? JetExpression instruction.outputValue?.element as? JetExpression
else -> null else -> null
@@ -145,10 +145,15 @@ private fun List<Instruction>.analyzeControlFlow(
} }
when (insn) { when (insn) {
is ReturnValueInstruction -> is ReturnValueInstruction -> {
if (isCurrentFunctionReturn(insn.element as JetReturnExpression)) { val returnExpression = insn.returnExpressionIfAny
valuedReturnExits.add(insn) if (returnExpression == null) {
} defaultExits.add(insn)
}
else if (isCurrentFunctionReturn(returnExpression)) {
valuedReturnExits.add(insn)
}
}
is AbstractJumpInstruction -> { is AbstractJumpInstruction -> {
val element = insn.element val element = insn.element