diff --git a/.idea/runConfigurations/All_Tests.xml b/.idea/runConfigurations/All_Tests.xml index fe9f201fd2d..80abd8cce40 100644 --- a/.idea/runConfigurations/All_Tests.xml +++ b/.idea/runConfigurations/All_Tests.xml @@ -16,7 +16,6 @@ - diff --git a/idea/src/org/jetbrains/jet/lang/cfg/JetFlowInformationProvider.java b/idea/src/org/jetbrains/jet/lang/cfg/JetFlowInformationProvider.java index d9d2840aca7..6531bdf05b3 100644 --- a/idea/src/org/jetbrains/jet/lang/cfg/JetFlowInformationProvider.java +++ b/idea/src/org/jetbrains/jet/lang/cfg/JetFlowInformationProvider.java @@ -3,7 +3,6 @@ package org.jetbrains.jet.lang.cfg; import org.jetbrains.annotations.NotNull; import org.jetbrains.jet.lang.psi.JetElement; import org.jetbrains.jet.lang.psi.JetExpression; -import org.jetbrains.jet.lang.psi.JetFunction; import java.util.Collection; @@ -13,10 +12,16 @@ import java.util.Collection; public interface JetFlowInformationProvider { JetFlowInformationProvider ERROR = new JetFlowInformationProvider() { @Override - public void collectReturnedInformation(@NotNull JetFunction function, Collection returnedExpressions, Collection elementsReturningUnit) { + public void collectReturnedInformation(@NotNull JetElement subroutine, Collection returnedExpressions, Collection elementsReturningUnit) { + throw new UnsupportedOperationException(); + } + + @Override + public void collectUnreachableExpressions(@NotNull JetElement subroutine, Collection unreachableElements) { throw new UnsupportedOperationException(); } }; - void collectReturnedInformation(@NotNull JetFunction function, Collection returnedExpressions, Collection elementsReturningUnit); + void collectReturnedInformation(@NotNull JetElement subroutine, Collection returnedExpressions, Collection elementsReturningUnit); + void collectUnreachableExpressions(@NotNull JetElement subroutine, Collection unreachableElements); } diff --git a/idea/src/org/jetbrains/jet/lang/cfg/pseudocode/AbstractJumpInstruction.java b/idea/src/org/jetbrains/jet/lang/cfg/pseudocode/AbstractJumpInstruction.java index 297b1d41c0a..521a28723ba 100644 --- a/idea/src/org/jetbrains/jet/lang/cfg/pseudocode/AbstractJumpInstruction.java +++ b/idea/src/org/jetbrains/jet/lang/cfg/pseudocode/AbstractJumpInstruction.java @@ -1,7 +1,11 @@ package org.jetbrains.jet.lang.cfg.pseudocode; +import org.jetbrains.annotations.NotNull; import org.jetbrains.jet.lang.cfg.Label; +import java.util.Collection; +import java.util.Collections; + /** * @author abreslav */ @@ -21,6 +25,12 @@ public abstract class AbstractJumpInstruction extends Instruction { return resolvedTarget; } + @NotNull + @Override + public Collection getNextInstructions() { + return Collections.singleton(getResolvedTarget()); + } + public void setResolvedTarget(Instruction resolvedTarget) { this.resolvedTarget = outgoingEdgeTo(resolvedTarget); } diff --git a/idea/src/org/jetbrains/jet/lang/cfg/pseudocode/ConditionalJumpInstruction.java b/idea/src/org/jetbrains/jet/lang/cfg/pseudocode/ConditionalJumpInstruction.java index 263d16667b5..c37a665ada6 100644 --- a/idea/src/org/jetbrains/jet/lang/cfg/pseudocode/ConditionalJumpInstruction.java +++ b/idea/src/org/jetbrains/jet/lang/cfg/pseudocode/ConditionalJumpInstruction.java @@ -1,7 +1,11 @@ package org.jetbrains.jet.lang.cfg.pseudocode; +import org.jetbrains.annotations.NotNull; import org.jetbrains.jet.lang.cfg.Label; +import java.util.Arrays; +import java.util.Collection; + /** * @author abreslav */ @@ -35,6 +39,12 @@ public class ConditionalJumpInstruction extends AbstractJumpInstruction { this.nextOnFalse = outgoingEdgeTo(nextOnFalse); } + @NotNull + @Override + public Collection getNextInstructions() { + return Arrays.asList(getNextOnFalse(), getNextOnTrue()); + } + @Override public void accept(InstructionVisitor visitor) { visitor.visitConditionalJump(this); diff --git a/idea/src/org/jetbrains/jet/lang/cfg/pseudocode/Instruction.java b/idea/src/org/jetbrains/jet/lang/cfg/pseudocode/Instruction.java index d5a73b7884f..d2ae263ddd2 100644 --- a/idea/src/org/jetbrains/jet/lang/cfg/pseudocode/Instruction.java +++ b/idea/src/org/jetbrains/jet/lang/cfg/pseudocode/Instruction.java @@ -1,5 +1,6 @@ package org.jetbrains.jet.lang.cfg.pseudocode; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.Collection; @@ -15,6 +16,9 @@ public abstract class Instruction { return previousInstructions; } + @NotNull + public abstract Collection getNextInstructions(); + @Nullable protected Instruction outgoingEdgeTo(@Nullable Instruction target) { if (target != null) { diff --git a/idea/src/org/jetbrains/jet/lang/cfg/pseudocode/InstructionWithNext.java b/idea/src/org/jetbrains/jet/lang/cfg/pseudocode/InstructionWithNext.java index d7c7b16c1a8..a50bbf2cb08 100644 --- a/idea/src/org/jetbrains/jet/lang/cfg/pseudocode/InstructionWithNext.java +++ b/idea/src/org/jetbrains/jet/lang/cfg/pseudocode/InstructionWithNext.java @@ -3,6 +3,9 @@ package org.jetbrains.jet.lang.cfg.pseudocode; import org.jetbrains.annotations.NotNull; import org.jetbrains.jet.lang.psi.JetElement; +import java.util.Collection; +import java.util.Collections; + /** * @author abreslav */ @@ -17,6 +20,12 @@ public abstract class InstructionWithNext extends JetElementInstruction { return next; } + @NotNull + @Override + public Collection getNextInstructions() { + return Collections.singleton(next); + } + public void setNext(Instruction next) { this.next = outgoingEdgeTo(next); } diff --git a/idea/src/org/jetbrains/jet/lang/cfg/pseudocode/NondeterministicJumpInstruction.java b/idea/src/org/jetbrains/jet/lang/cfg/pseudocode/NondeterministicJumpInstruction.java index f9a10afe6d4..20a34886dde 100644 --- a/idea/src/org/jetbrains/jet/lang/cfg/pseudocode/NondeterministicJumpInstruction.java +++ b/idea/src/org/jetbrains/jet/lang/cfg/pseudocode/NondeterministicJumpInstruction.java @@ -1,7 +1,11 @@ package org.jetbrains.jet.lang.cfg.pseudocode; +import org.jetbrains.annotations.NotNull; import org.jetbrains.jet.lang.cfg.Label; +import java.util.Arrays; +import java.util.Collection; + /** * @author abreslav */ @@ -26,6 +30,12 @@ public class NondeterministicJumpInstruction extends AbstractJumpInstruction { this.next = next; } + @NotNull + @Override + public Collection getNextInstructions() { + return Arrays.asList(getResolvedTarget(), getNext()); + } + @Override public String toString() { return "jmp?(" + getTargetLabel().getName() + ")"; diff --git a/idea/src/org/jetbrains/jet/lang/cfg/pseudocode/Pseudocode.java b/idea/src/org/jetbrains/jet/lang/cfg/pseudocode/Pseudocode.java index 4c912b8e1b4..26514ff8eb2 100644 --- a/idea/src/org/jetbrains/jet/lang/cfg/pseudocode/Pseudocode.java +++ b/idea/src/org/jetbrains/jet/lang/cfg/pseudocode/Pseudocode.java @@ -69,6 +69,11 @@ public class Pseudocode { return label; } + @NotNull + public Collection getInstructions() { + return instructions; + } + public void addInstruction(Instruction instruction) { instructions.add(instruction); if (instruction instanceof SubroutineExitInstruction) { @@ -83,6 +88,11 @@ public class Pseudocode { return exitInstruction; } + @NotNull + public SubroutineEnterInstruction getEnterInstruction() { + return (SubroutineEnterInstruction) instructions.get(0); + } + public void bindLabel(Label label) { ((PseudocodeLabel) label).setTargetInstructionIndex(instructions.size()); } diff --git a/idea/src/org/jetbrains/jet/lang/cfg/pseudocode/ReturnNoValueInstruction.java b/idea/src/org/jetbrains/jet/lang/cfg/pseudocode/ReturnNoValueInstruction.java index a479b00d5e6..169a51c048a 100644 --- a/idea/src/org/jetbrains/jet/lang/cfg/pseudocode/ReturnNoValueInstruction.java +++ b/idea/src/org/jetbrains/jet/lang/cfg/pseudocode/ReturnNoValueInstruction.java @@ -27,6 +27,6 @@ public class ReturnNoValueInstruction extends AbstractJumpInstruction { @Override public String toString() { - return "ret"; + return "ret " + getTargetLabel(); } } diff --git a/idea/src/org/jetbrains/jet/lang/cfg/pseudocode/ReturnValueInstruction.java b/idea/src/org/jetbrains/jet/lang/cfg/pseudocode/ReturnValueInstruction.java index 0779d3c24b4..88166b8da6d 100644 --- a/idea/src/org/jetbrains/jet/lang/cfg/pseudocode/ReturnValueInstruction.java +++ b/idea/src/org/jetbrains/jet/lang/cfg/pseudocode/ReturnValueInstruction.java @@ -18,6 +18,6 @@ public class ReturnValueInstruction extends AbstractJumpInstruction { @Override public String toString() { - return "ret(*)"; + return "ret(*) " + getTargetLabel(); } } diff --git a/idea/src/org/jetbrains/jet/lang/cfg/pseudocode/SubroutineExitInstruction.java b/idea/src/org/jetbrains/jet/lang/cfg/pseudocode/SubroutineExitInstruction.java index c9c4138af3e..e46750724e4 100644 --- a/idea/src/org/jetbrains/jet/lang/cfg/pseudocode/SubroutineExitInstruction.java +++ b/idea/src/org/jetbrains/jet/lang/cfg/pseudocode/SubroutineExitInstruction.java @@ -3,6 +3,9 @@ package org.jetbrains.jet.lang.cfg.pseudocode; import org.jetbrains.annotations.NotNull; import org.jetbrains.jet.lang.psi.JetElement; +import java.util.Collection; +import java.util.Collections; + /** * @author abreslav */ @@ -17,6 +20,12 @@ public class SubroutineExitInstruction extends Instruction { return subroutine; } + @NotNull + @Override + public Collection getNextInstructions() { + return Collections.emptyList(); + } + @Override public void accept(InstructionVisitor visitor) { visitor.visitSubroutineExit(this); diff --git a/idea/src/org/jetbrains/jet/lang/resolve/TopDownAnalyzer.java b/idea/src/org/jetbrains/jet/lang/resolve/TopDownAnalyzer.java index 773f969e38c..93073375829 100644 --- a/idea/src/org/jetbrains/jet/lang/resolve/TopDownAnalyzer.java +++ b/idea/src/org/jetbrains/jet/lang/resolve/TopDownAnalyzer.java @@ -268,21 +268,27 @@ public class TopDownAnalyzer { JetFunction function = (JetFunction) declaration; FunctionDescriptorImpl functionDescriptorImpl = (FunctionDescriptorImpl) descriptor; if (bodyExpression != null) { - JetFlowInformationProvider flowInformationProvider = computeFlowData(declaration, bodyExpression); + JetFlowInformationProvider flowInformationProvider = computeFlowData(function, bodyExpression); JetTypeInferrer typeInferrer = semanticServices.getTypeInferrer(trace, flowInformationProvider); assert readyToProcessExpressions : "Must be ready collecting types"; if (function.getReturnTypeRef() != null) { - typeInferrer.checkFunctionReturnType(declaringScope, function, descriptor); + typeInferrer.checkFunctionReturnType(declaringScope, function, functionDescriptorImpl); } else { - JetType returnType = typeInferrer.getFunctionReturnType(declaringScope, function, descriptor); + JetType returnType = typeInferrer.getFunctionReturnType(declaringScope, function, functionDescriptorImpl); if (returnType == null) { returnType = ErrorUtils.createErrorType("Unable to infer body type"); } functionDescriptorImpl.setUnsubstitutedReturnType(returnType); } + + List unreachableElements = new ArrayList(); + flowInformationProvider.collectUnreachableExpressions(function, unreachableElements); + for (JetElement unreachableElement : unreachableElements) { + semanticServices.getErrorHandler().genericError(unreachableElement.getNode(), "Unreachable code"); + } } else { if (function.getReturnTypeRef() == null) { @@ -327,17 +333,44 @@ public class TopDownAnalyzer { wrappedTrace.close(); return new JetFlowInformationProvider() { @Override - public void collectReturnedInformation(@NotNull JetFunction function, Collection returnedExpressions, Collection elementsReturningUnit) { - Pseudocode pseudocode = pseudocodeMap.get(function); + public void collectReturnedInformation(@NotNull JetElement subroutine, Collection returnedExpressions, Collection elementsReturningUnit) { + Pseudocode pseudocode = pseudocodeMap.get(subroutine); assert pseudocode != null; SubroutineExitInstruction exitInstruction = pseudocode.getExitInstruction(); - processPreviousInstructions(exitInstruction, returnedExpressions, elementsReturningUnit); + processPreviousInstructions(exitInstruction, new HashSet(), returnedExpressions, elementsReturningUnit); + } + + @Override + public void collectUnreachableExpressions(@NotNull JetElement subroutine, Collection unreachableElements) { + Pseudocode pseudocode = pseudocodeMap.get(subroutine); + assert pseudocode != null; + + SubroutineEnterInstruction enterInstruction = pseudocode.getEnterInstruction(); + Set visited = new HashSet(); + collectReachable(enterInstruction, visited); + + for (Instruction instruction : pseudocode.getInstructions()) { + if (!visited.contains(instruction) && instruction instanceof JetElementInstruction) { + unreachableElements.add(((JetElementInstruction) instruction).getElement()); + } + } + } }; } - private void processPreviousInstructions(Instruction previousFor, final Collection returnedExpressions, final Collection elementsReturningUnit) { + private void collectReachable(Instruction current, Set visited) { + if (!visited.add(current)) return; + + for (Instruction nextInstruction : current.getNextInstructions()) { + collectReachable(nextInstruction, visited); + } + } + + private void processPreviousInstructions(Instruction previousFor, final Set visited, final Collection returnedExpressions, final Collection elementsReturningUnit) { + if (!visited.add(previousFor)) return; + Collection previousInstructions = previousFor.getPreviousInstructions(); InstructionVisitor visitor = new InstructionVisitor() { @Override @@ -347,7 +380,7 @@ public class TopDownAnalyzer { @Override public void visitReturnValue(ReturnValueInstruction instruction) { - processPreviousInstructions(instruction, returnedExpressions, elementsReturningUnit); + processPreviousInstructions(instruction, visited, returnedExpressions, elementsReturningUnit); } @Override @@ -372,7 +405,7 @@ public class TopDownAnalyzer { @Override public void visitJump(AbstractJumpInstruction instruction) { - processPreviousInstructions(instruction, returnedExpressions, elementsReturningUnit); + processPreviousInstructions(instruction, visited, returnedExpressions, elementsReturningUnit); } @Override diff --git a/idea/src/org/jetbrains/jet/lang/types/JetTypeInferrer.java b/idea/src/org/jetbrains/jet/lang/types/JetTypeInferrer.java index a13080a35f1..dd763d70bc8 100644 --- a/idea/src/org/jetbrains/jet/lang/types/JetTypeInferrer.java +++ b/idea/src/org/jetbrains/jet/lang/types/JetTypeInferrer.java @@ -348,7 +348,9 @@ public class JetTypeInferrer { } private JetType getBlockReturnedTypeWithWritableScope(@NotNull WritableScope scope, @NotNull List extends JetElement> block) { - assert !block.isEmpty(); + if (block.isEmpty()) { + return JetStandardClasses.getUnitType(); + } TypeInferrerVisitorWithWritableScope blockLevelVisitor = new TypeInferrerVisitorWithWritableScope(scope, true); diff --git a/idea/testData/cfg/Nonlocals.instructions b/idea/testData/cfg/Nonlocals.instructions index 042f52d49af..baf838b0132 100644 --- a/idea/testData/cfg/Nonlocals.instructions +++ b/idea/testData/cfg/Nonlocals.instructions @@ -9,7 +9,7 @@ l6: r(b) jf(l8) r(1) - ret(*) + ret(*) l5 jmp(l9) l8: read (Unit) @@ -32,7 +32,7 @@ l4: r(2) r(5) - ret(*) + ret(*) l5 l5: ===================== @@ -60,7 +60,7 @@ l0: r(a) jf(l2) r(1) - ret(*) + ret(*) l1 jmp(l3) l2: read (Unit) @@ -84,7 +84,7 @@ l4: r(2) r(5) - ret(*) + ret(*) l5 l5: ===================== diff --git a/idea/testData/cfg/ReturnFromExpression.instructions b/idea/testData/cfg/ReturnFromExpression.instructions index 1051a3a5a15..43f3284ac47 100644 --- a/idea/testData/cfg/ReturnFromExpression.instructions +++ b/idea/testData/cfg/ReturnFromExpression.instructions @@ -1,17 +1,16 @@ == blockAndAndMismatch == fun blockAndAndMismatch() : Boolean { - (return true) || (return false) + false || (return false) } --------------------- l0: - r(true) - ret(*) + r(false) jt(l2) r(false) - ret(*) + ret(*) l1 l2: - r((return true) || (return false)) + r(false || (return false)) l1: ===================== diff --git a/idea/testData/cfg/ReturnFromExpression.jet b/idea/testData/cfg/ReturnFromExpression.jet index 2bc7603b44b..afbd008d8f5 100644 --- a/idea/testData/cfg/ReturnFromExpression.jet +++ b/idea/testData/cfg/ReturnFromExpression.jet @@ -1,3 +1,3 @@ fun blockAndAndMismatch() : Boolean { - (return true) || (return false) + false || (return false) } diff --git a/idea/testData/checker/UnreachableCode.jet b/idea/testData/checker/UnreachableCode.jet new file mode 100644 index 00000000000..4b6afe5dab1 --- /dev/null +++ b/idea/testData/checker/UnreachableCode.jet @@ -0,0 +1,38 @@ +fun t1() { + return + 1 +} + +fun t2() { + if (1 > 2) + return + else return + 1 +} + +fun t3() { + if (1 > 2) + return 2 + else return "" + 1 +} + +fun t4(a : Boolean) { + do { + return + } + while (a) + 1 +} + +//fun t5() { +// do { +// return +// } +// while (1 > 2) +// 1 +//} + +//fun blockAndAndMismatch() : Boolean { +// (return true) || (return false) +//}