Control-Flow Analysis: Compute STATEMENT slice using control-flow information

This commit is contained in:
Alexey Sedunov
2014-07-11 19:30:25 +04:00
parent ffd4af872c
commit 00dd1dbb0b
19 changed files with 99 additions and 70 deletions
@@ -1252,8 +1252,6 @@ public class JetControlFlowProcessor {
generateInstructions(subjectExpression);
}
boolean hasElse = false;
List<JetExpression> branches = new ArrayList<JetExpression>();
Label doneLabel = builder.createUnboundLabel();
@@ -1265,7 +1263,6 @@ public class JetControlFlowProcessor {
boolean isElse = whenEntry.isElse();
if (isElse) {
hasElse = true;
if (iterator.hasNext()) {
trace.report(ELSE_MISPLACED_IN_WHEN.on(whenEntry));
}
@@ -1300,9 +1297,6 @@ public class JetControlFlowProcessor {
}
}
builder.bindLabel(doneLabel);
if (!hasElse && WhenChecker.mustHaveElse(expression, trace)) {
trace.report(NO_ELSE_IN_WHEN.on(expression));
}
mergeValues(branches, expression);
}
@@ -1407,6 +1401,11 @@ public class JetControlFlowProcessor {
builder.magic(specifier, specifier, arguments, PseudocodePackage.expectedTypeFor(expectedTypePredicate, arguments), MagicKind.VALUE_CONSUMER);
}
@Override
public void visitDelegationSpecifierList(@NotNull JetDelegationSpecifierList list) {
list.acceptChildren(this);
}
@Override
public void visitJetFile(@NotNull JetFile file) {
for (JetDeclaration declaration : file.getDeclarations()) {
@@ -29,6 +29,7 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.lang.cfg.PseudocodeVariablesData.VariableInitState;
import org.jetbrains.jet.lang.cfg.PseudocodeVariablesData.VariableUseState;
import org.jetbrains.jet.lang.cfg.pseudocode.PseudoValue;
import org.jetbrains.jet.lang.cfg.pseudocode.Pseudocode;
import org.jetbrains.jet.lang.cfg.pseudocode.PseudocodeUtil;
import org.jetbrains.jet.lang.cfg.pseudocode.instructions.Instruction;
@@ -121,6 +122,10 @@ public class JetFlowInformationProvider {
markUnusedVariables();
markUnusedLiteralsInBlock();
markStatements();
markWhenWithoutElse();
}
public void checkFunction(@Nullable JetType expectedReturnType) {
@@ -697,6 +702,48 @@ public class JetFlowInformationProvider {
);
}
////////////////////////////////////////////////////////////////////////////////
// Statements
public void markStatements() {
PseudocodeTraverserPackage.traverse(
pseudocode, FORWARD, new JetFlowInformationProvider.FunctionVoid1<Instruction>() {
@Override
public void execute(@NotNull Instruction instruction) {
PseudoValue value = instruction instanceof InstructionWithValue
? ((InstructionWithValue) instruction).getOutputValue()
: null;
Pseudocode pseudocode = instruction.getOwner();
boolean isUsedAsExpression = !pseudocode.getUsages(value).isEmpty();
for (JetElement element : pseudocode.getValueElements(value)) {
trace.record(BindingContext.USED_AS_EXPRESSION, element, isUsedAsExpression);
}
}
}
);
}
public void markWhenWithoutElse() {
PseudocodeTraverserPackage.traverse(
pseudocode, FORWARD, new JetFlowInformationProvider.FunctionVoid1<Instruction>() {
@Override
public void execute(@NotNull Instruction instruction) {
PseudoValue value = instruction instanceof InstructionWithValue
? ((InstructionWithValue) instruction).getOutputValue()
: null;
for (JetElement element : instruction.getOwner().getValueElements(value)) {
if (element instanceof JetWhenExpression) {
JetWhenExpression whenExpression = (JetWhenExpression) element;
if (whenExpression.getElseExpression() == null && WhenChecker.mustHaveElse(whenExpression, trace)) {
trace.report(NO_ELSE_IN_WHEN.on(whenExpression));
}
}
}
}
}
);
}
////////////////////////////////////////////////////////////////////////////////
// Tail calls
@@ -24,6 +24,7 @@ import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor;
import org.jetbrains.jet.lang.psi.*;
import org.jetbrains.jet.lang.resolve.BindingContext;
import org.jetbrains.jet.lang.resolve.BindingTrace;
import org.jetbrains.jet.lang.resolve.bindingContextUtil.BindingContextUtilPackage;
import org.jetbrains.jet.lang.types.JetType;
import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns;
@@ -37,7 +38,7 @@ public final class WhenChecker {
JetType expectedType = trace.get(BindingContext.EXPECTED_EXPRESSION_TYPE, expression);
boolean isUnit = expectedType != null && KotlinBuiltIns.getInstance().isUnit(expectedType);
// Some "statements" are actually expressions returned from lambdas, their expected types are non-null
boolean isStatement = Boolean.TRUE.equals(trace.get(BindingContext.STATEMENT, expression)) && expectedType == null;
boolean isStatement = BindingContextUtilPackage.isUsedAsStatement(expression, trace.getBindingContext()) && expectedType == null;
return !isUnit && !isStatement && !isWhenExhaustive(expression, trace);
}
@@ -33,9 +33,6 @@ import org.jetbrains.jet.lang.types.TypeUtils
import org.jetbrains.jet.lang.types.JetType
import org.jetbrains.jet.lang.cfg.pseudocode.instructions.special.LocalFunctionDeclarationInstruction
fun JetExpression.isStatement(pseudocode: Pseudocode): Boolean =
pseudocode.getUsages(pseudocode.getElementValue(this)).isEmpty()
fun getReceiverTypePredicate(resolvedCall: ResolvedCall<*>, receiverValue: ReceiverValue): TypePredicate? {
val callableDescriptor = resolvedCall.getResultingDescriptor()
if (callableDescriptor == null) return null
@@ -136,7 +136,7 @@ public interface BindingContext {
* Has type of current expression has been already resolved
*/
WritableSlice<JetExpression, Boolean> PROCESSED = Slices.createSimpleSetSlice();
WritableSlice<JetElement, Boolean> STATEMENT = Slices.createRemovableSetSlice();
WritableSlice<JetElement, Boolean> USED_AS_EXPRESSION = Slices.createRemovableSetSlice();
WritableSlice<VariableDescriptor, CaptureKind> CAPTURED_IN_CLOSURE = new BasicWritableSlice<VariableDescriptor, CaptureKind>(DO_NOTHING);
@@ -139,3 +139,6 @@ public fun JetReturnExpression.getTargetFunctionDescriptor(context: BindingConte
.dropWhile { it is AnonymousFunctionDescriptor }
.firstOrNull()
}
public fun JetExpression.isUsedAsExpression(context: BindingContext): Boolean = context[BindingContext.USED_AS_EXPRESSION, this]!!
public fun JetExpression.isUsedAsStatement(context: BindingContext): Boolean = !isUsedAsExpression(context)
@@ -49,7 +49,6 @@ import javax.inject.Inject;
import java.util.Iterator;
import java.util.List;
import static org.jetbrains.jet.lang.resolve.BindingContext.STATEMENT;
import static org.jetbrains.jet.lang.types.TypeUtils.*;
import static org.jetbrains.jet.lang.types.expressions.CoercionStrategy.COERCION_TO_UNIT;
@@ -253,7 +252,6 @@ public class ExpressionTypingServices {
);
JetTypeInfo typeInfo = expressionTypingFacade.getTypeInfo(bodyExpression, context, function.hasBlockBody());
trace.record(STATEMENT, bodyExpression, false);
JetType type = typeInfo.getType();
if (type != null) {
return type;
@@ -283,7 +281,6 @@ public class ExpressionTypingServices {
if (!(statement instanceof JetExpression)) {
continue;
}
trace.record(STATEMENT, statement);
JetExpression statementExpression = (JetExpression) statement;
if (!iterator.hasNext()) {
result = getTypeOfLastExpressionInBlock(
@@ -71,10 +71,6 @@ public class PatternMatchingTypingVisitor extends ExpressionTypingVisitor {
}
public JetTypeInfo visitWhenExpression(JetWhenExpression expression, ExpressionTypingContext contextWithExpectedType, boolean isStatement) {
if (isStatement) {
contextWithExpectedType.trace.record(BindingContext.STATEMENT, expression);
}
DataFlowUtils.recordExpectedType(contextWithExpectedType.trace, expression, contextWithExpectedType.expectedType);
ExpressionTypingContext context = contextWithExpectedType.replaceExpectedType(NO_EXPECTED_TYPE).replaceContextDependency(INDEPENDENT);