diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/cfg/JetFlowInformationProvider.java b/compiler/frontend/src/org/jetbrains/jet/lang/cfg/JetFlowInformationProvider.java index e38b2e8ca1c..84d0b036d6b 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/cfg/JetFlowInformationProvider.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/cfg/JetFlowInformationProvider.java @@ -1,23 +1,29 @@ package org.jetbrains.jet.lang.cfg; import com.google.common.collect.Maps; +import com.google.common.collect.Sets; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.jet.lang.cfg.pseudocode.*; -import org.jetbrains.jet.lang.psi.JetElement; -import org.jetbrains.jet.lang.psi.JetExpression; +import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor; +import org.jetbrains.jet.lang.descriptors.LocalVariableDescriptor; +import org.jetbrains.jet.lang.diagnostics.Errors; +import org.jetbrains.jet.lang.psi.*; +import org.jetbrains.jet.lang.resolve.BindingContext; import org.jetbrains.jet.lang.resolve.BindingTrace; import java.util.*; /** -* @author svtk -*/ + * @author svtk + */ public class JetFlowInformationProvider { private final Map pseudocodeMap; + private BindingTrace trace; public JetFlowInformationProvider(@NotNull JetElement declaration, @NotNull final JetExpression bodyExpression, @NotNull JetControlFlowDataTraceFactory flowDataTraceFactory, @NotNull BindingTrace trace) { + this.trace = trace; final JetPseudocodeTrace pseudocodeTrace = flowDataTraceFactory.createTrace(declaration); pseudocodeMap = new HashMap(); final Map representativeInstructions = new HashMap(); @@ -86,6 +92,13 @@ public class JetFlowInformationProvider { // Nothing } + @Override + public void visitUnconditionalJump(UnconditionalJumpInstruction instruction) { + for (Instruction previousInstruction : instruction.getPreviousInstructions()) { + previousInstruction.accept(this); + } + } + @Override public void visitInstruction(Instruction instruction) { if (instruction instanceof JetElementInstruction) { @@ -233,4 +246,100 @@ public class JetFlowInformationProvider { // previousInstruction.accept(visitor); // } // } + + private Map traverseInstructionGraphUntilFactsStabilization(Collection instructions, InstructionsMergeHandler instructionsMergeHandler, D initialDataValue, boolean straightDirection) { + + Map dataMap = Maps.newHashMap(); + for (Instruction instruction : instructions) { + dataMap.put(instruction, initialDataValue); + } + + boolean changed = true; + while (changed) { + changed = false; + for (Instruction instruction : instructions) { + D previousDataValue = dataMap.get(instruction); + + Collection previousInstructions = straightDirection + ? instruction.getPreviousInstructions() + : instruction.getNextInstructions(); + Collection incomingEdgesData = Sets.newHashSet(); + for (Instruction previousInstruction : previousInstructions) { + incomingEdgesData.add(dataMap.get(previousInstruction)); + } + D mergedData = instructionsMergeHandler.merge(instruction, previousDataValue, incomingEdgesData); + if (!mergedData.equals(previousDataValue)) { + changed = true; + dataMap.put(instruction, mergedData); + } + } + } + return dataMap; + } + + public void markUninitializedVariables(@NotNull JetElement subroutine) { + Pseudocode pseudocode = pseudocodeMap.get(subroutine); + assert pseudocode != null; + + Collection instructions = pseudocode.getInstructions(); + InstructionsMergeHandler> instructionsMergeHandler = new InstructionsMergeHandler>() { + @Override + public Set merge(Instruction instruction, Set previousDataValue, Collection> incomingEdgesData) { + Set initializedVariables = Sets.newHashSet(); + initializedVariables.addAll(previousDataValue); + for (Set edgeDataValue : incomingEdgesData) { + initializedVariables.addAll(edgeDataValue); + } + + if (instruction instanceof WriteValueInstruction) { + JetElement element = ((WriteValueInstruction) instruction).getElement(); + DeclarationDescriptor descriptor = null; + if (element instanceof JetBinaryExpression) { + JetExpression left = ((JetBinaryExpression) element).getLeft(); + if (left instanceof JetSimpleNameExpression) { + descriptor = trace.get(BindingContext.REFERENCE_TARGET, (JetSimpleNameExpression) left); + } + } + else if (element instanceof JetProperty) { + descriptor = trace.get(BindingContext.DECLARATION_TO_DESCRIPTOR, element); + } + if (descriptor instanceof LocalVariableDescriptor) { + initializedVariables.add((LocalVariableDescriptor) descriptor); + } + } + return initializedVariables; + } + }; + Map> dataMap = traverseInstructionGraphUntilFactsStabilization(instructions, instructionsMergeHandler, Collections.emptySet(), true); + + for (Instruction instruction : instructions) { + Set initializedVariables = dataMap.get(instruction); + if (instruction instanceof ReadValueInstruction) { + JetElement element = ((ReadValueInstruction) instruction).getElement(); + if (element instanceof JetSimpleNameExpression) { + DeclarationDescriptor descriptor = trace.get(BindingContext.REFERENCE_TARGET, (JetSimpleNameExpression) element); + if (descriptor instanceof LocalVariableDescriptor) { + if (!initializedVariables.contains(descriptor)) { + trace.report(Errors.UNINITIALIZED_VARIABLE.on((JetSimpleNameExpression) element, descriptor)); + } + } + } + } + else if (instruction instanceof WriteValueInstruction) { + JetElement element = ((WriteValueInstruction) instruction).getElement(); + if (element instanceof JetSimpleNameExpression) { + DeclarationDescriptor descriptor = trace.get(BindingContext.REFERENCE_TARGET, (JetSimpleNameExpression) element); + if (descriptor instanceof LocalVariableDescriptor) { + if (initializedVariables.contains(descriptor) && ((LocalVariableDescriptor) descriptor).isVar()) { + trace.report(Errors.VAL_REASSIGNMENT.on((JetSimpleNameExpression) element, descriptor)); + } + } + } + } + } + } + + interface InstructionsMergeHandler { + D merge(Instruction instruction, D previousDataValue, Collection incomingEdgesData); + } } diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/diagnostics/Errors.java b/compiler/frontend/src/org/jetbrains/jet/lang/diagnostics/Errors.java index b91883012bc..861e2cd92a2 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/diagnostics/Errors.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/diagnostics/Errors.java @@ -142,6 +142,9 @@ public interface Errors { }; PsiElementOnlyDiagnosticFactory3 VIRTUAL_MEMBER_HIDDEN = PsiElementOnlyDiagnosticFactory3.create(ERROR, "''{0}'' hides ''{1}'' in class {2} and needs 'override' modifier", DescriptorRenderer.TEXT); + PsiElementOnlyDiagnosticFactory1 UNINITIALIZED_VARIABLE = PsiElementOnlyDiagnosticFactory1.create(ERROR, "Variable ''{0}'' must be initialized", NAME); + PsiElementOnlyDiagnosticFactory1 VAL_REASSIGNMENT = PsiElementOnlyDiagnosticFactory1.create(ERROR, "Val can not be reassigned", NAME); + SimpleDiagnosticFactory UNREACHABLE_CODE = SimpleDiagnosticFactory.create(ERROR, "Unreachable code"); SimpleDiagnosticFactory MANY_CLASS_OBJECTS = SimpleDiagnosticFactory.create(ERROR, "Only one class object is allowed per class"); diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/ControlFlowAnalyzer.java b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/ControlFlowAnalyzer.java index 80525b8e113..3bb6bbc7b49 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/ControlFlowAnalyzer.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/ControlFlowAnalyzer.java @@ -108,6 +108,8 @@ public class ControlFlowAnalyzer { } }); } + + //flowInformationProvider.markUninitializedVariables(function.asElement()); } private void checkProperty(JetProperty property) {