diff --git a/idea/src/org/jetbrains/jet/lang/JetSemanticServices.java b/idea/src/org/jetbrains/jet/lang/JetSemanticServices.java index 91a88bbc39926a56f7af314d9bef9d5b4aff9971..121f666028b63f9e73c04881324f9747bcfcc6e1 100644 --- a/idea/src/org/jetbrains/jet/lang/JetSemanticServices.java +++ b/idea/src/org/jetbrains/jet/lang/JetSemanticServices.java @@ -2,6 +2,7 @@ package org.jetbrains.jet.lang; import com.intellij.openapi.project.Project; import org.jetbrains.annotations.NotNull; +import org.jetbrains.jet.lang.cfg.JetFlowInformationProvider; import org.jetbrains.jet.lang.resolve.ClassDescriptorResolver; import org.jetbrains.jet.lang.resolve.JetScope; import org.jetbrains.jet.lang.resolve.OverloadResolver; @@ -44,8 +45,8 @@ public class JetSemanticServices { } @NotNull - public JetTypeInferrer getTypeInferrer(BindingTrace trace) { - return new JetTypeInferrer(trace, this); + public JetTypeInferrer getTypeInferrer(BindingTrace trace, JetFlowInformationProvider flowInformationProvider) { + return new JetTypeInferrer(trace, flowInformationProvider, this); } @NotNull diff --git a/idea/src/org/jetbrains/jet/lang/cfg/JetControlFlowBuilder.java b/idea/src/org/jetbrains/jet/lang/cfg/JetControlFlowBuilder.java index eebc570e0585a2858fae1857c345abe1e0d5da78..361dd64f128f7206fa5dce3c3ad4d0f9f68b39f0 100644 --- a/idea/src/org/jetbrains/jet/lang/cfg/JetControlFlowBuilder.java +++ b/idea/src/org/jetbrains/jet/lang/cfg/JetControlFlowBuilder.java @@ -47,7 +47,7 @@ public interface JetControlFlowBuilder { JetElement getCurrentSubroutine(); void returnValue(@NotNull JetElement subroutine); - void returnNoValue(@NotNull JetElement subroutine); + void returnNoValue(@NotNull JetElement expression, @NotNull JetElement subroutine); void writeNode(@NotNull JetElement assignment, @NotNull JetElement lValue); diff --git a/idea/src/org/jetbrains/jet/lang/cfg/JetControlFlowBuilderAdapter.java b/idea/src/org/jetbrains/jet/lang/cfg/JetControlFlowBuilderAdapter.java index 41e3ece4a20f3857917956d458eef338b8afd358..df5c84c5749dac315d18731674097cbb7eb4a19f 100644 --- a/idea/src/org/jetbrains/jet/lang/cfg/JetControlFlowBuilderAdapter.java +++ b/idea/src/org/jetbrains/jet/lang/cfg/JetControlFlowBuilderAdapter.java @@ -110,8 +110,8 @@ public class JetControlFlowBuilderAdapter implements JetControlFlowBuilder { } @Override - public void returnNoValue(@NotNull JetElement subroutine) { - builder.returnNoValue(subroutine); + public void returnNoValue(@NotNull JetElement expression, @NotNull JetElement subroutine) { + builder.returnNoValue(expression, subroutine); } @Override diff --git a/idea/src/org/jetbrains/jet/lang/cfg/JetControlFlowProcessor.java b/idea/src/org/jetbrains/jet/lang/cfg/JetControlFlowProcessor.java index 666f2bebf2232dcc66b82cb4d83c6c492ad18aae..35137f65d30e4c45567dddaa2610b390b2cc7c96 100644 --- a/idea/src/org/jetbrains/jet/lang/cfg/JetControlFlowProcessor.java +++ b/idea/src/org/jetbrains/jet/lang/cfg/JetControlFlowProcessor.java @@ -366,7 +366,7 @@ public class JetControlFlowProcessor { } if (subroutine != null) { if (returnedExpression == null) { - builder.returnNoValue(subroutine); + builder.returnNoValue(expression, subroutine); } else { builder.returnValue(subroutine); diff --git a/idea/src/org/jetbrains/jet/lang/cfg/JetFlowInformationProvider.java b/idea/src/org/jetbrains/jet/lang/cfg/JetFlowInformationProvider.java new file mode 100644 index 0000000000000000000000000000000000000000..d9d2840aca7e582bff92b286bb6274b90e500c7a --- /dev/null +++ b/idea/src/org/jetbrains/jet/lang/cfg/JetFlowInformationProvider.java @@ -0,0 +1,22 @@ +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; + +/** + * @author abreslav + */ +public interface JetFlowInformationProvider { + JetFlowInformationProvider ERROR = new JetFlowInformationProvider() { + @Override + public void collectReturnedInformation(@NotNull JetFunction function, Collection returnedExpressions, Collection elementsReturningUnit) { + throw new UnsupportedOperationException(); + } + }; + + void collectReturnedInformation(@NotNull JetFunction function, Collection returnedExpressions, Collection elementsReturningUnit); +} diff --git a/idea/src/org/jetbrains/jet/lang/cfg/pseudocode/FunctionLiteralValueInstruction.java b/idea/src/org/jetbrains/jet/lang/cfg/pseudocode/FunctionLiteralValueInstruction.java index 6b7d7b804fd94f9280f02750f128c8a8aa47ffc5..f83c5bf6c1ee7d2cecd30ef67f577de7e58d7cd9 100644 --- a/idea/src/org/jetbrains/jet/lang/cfg/pseudocode/FunctionLiteralValueInstruction.java +++ b/idea/src/org/jetbrains/jet/lang/cfg/pseudocode/FunctionLiteralValueInstruction.java @@ -1,17 +1,17 @@ package org.jetbrains.jet.lang.cfg.pseudocode; import org.jetbrains.annotations.NotNull; -import org.jetbrains.jet.lang.psi.JetElement; +import org.jetbrains.jet.lang.psi.JetFunctionLiteralExpression; /** * @author abreslav */ -public class FunctionLiteralValueInstruction extends InstructionWithNext { +public class FunctionLiteralValueInstruction extends ReadValueInstruction { private Pseudocode body; - public FunctionLiteralValueInstruction(@NotNull JetElement element) { - super(element); + public FunctionLiteralValueInstruction(@NotNull JetFunctionLiteralExpression expression) { + super(expression); } public Pseudocode getBody() { diff --git a/idea/src/org/jetbrains/jet/lang/cfg/pseudocode/InstructionVisitor.java b/idea/src/org/jetbrains/jet/lang/cfg/pseudocode/InstructionVisitor.java index 3080875bed8d77c88879216cadeefc8b26b7763f..df589c62d82e58f505106ef6188aa15e5b4a160d 100644 --- a/idea/src/org/jetbrains/jet/lang/cfg/pseudocode/InstructionVisitor.java +++ b/idea/src/org/jetbrains/jet/lang/cfg/pseudocode/InstructionVisitor.java @@ -9,7 +9,7 @@ public class InstructionVisitor { } public void visitFunctionLiteralValue(FunctionLiteralValueInstruction instruction) { - visitInstructionWithNext(instruction); + visitReadValue(instruction); } public void visitUnconditionalJump(UnconditionalJumpInstruction instruction) { diff --git a/idea/src/org/jetbrains/jet/lang/cfg/pseudocode/JetControlFlowDataTraceFactory.java b/idea/src/org/jetbrains/jet/lang/cfg/pseudocode/JetControlFlowDataTraceFactory.java index e1c946ac0863181a27e769a816d1a493307fd873..76fe9026d256edcf165832341b6fb777fffa84a4 100644 --- a/idea/src/org/jetbrains/jet/lang/cfg/pseudocode/JetControlFlowDataTraceFactory.java +++ b/idea/src/org/jetbrains/jet/lang/cfg/pseudocode/JetControlFlowDataTraceFactory.java @@ -10,11 +10,11 @@ public interface JetControlFlowDataTraceFactory { JetControlFlowDataTraceFactory EMPTY = new JetControlFlowDataTraceFactory() { @NotNull @Override - public JetControlFlowDataTrace createTrace(JetElement element) { - return JetControlFlowDataTrace.EMPTY; + public JetPseudocodeTrace createTrace(JetElement element) { + return JetPseudocodeTrace.EMPTY; } }; @NotNull - JetControlFlowDataTrace createTrace(JetElement element); + JetPseudocodeTrace createTrace(JetElement element); } diff --git a/idea/src/org/jetbrains/jet/lang/cfg/pseudocode/JetControlFlowInstructionsGenerator.java b/idea/src/org/jetbrains/jet/lang/cfg/pseudocode/JetControlFlowInstructionsGenerator.java index fdface77708f1804cbdda1d6dd8f1ec2572b455c..a2c4f584646e3c29733f95f6becdf802119abbcc 100644 --- a/idea/src/org/jetbrains/jet/lang/cfg/pseudocode/JetControlFlowInstructionsGenerator.java +++ b/idea/src/org/jetbrains/jet/lang/cfg/pseudocode/JetControlFlowInstructionsGenerator.java @@ -7,6 +7,7 @@ import org.jetbrains.jet.lang.cfg.Label; import org.jetbrains.jet.lang.psi.JetBlockExpression; import org.jetbrains.jet.lang.psi.JetElement; import org.jetbrains.jet.lang.psi.JetExpression; +import org.jetbrains.jet.lang.psi.JetFunctionLiteralExpression; import java.util.*; @@ -21,9 +22,9 @@ public class JetControlFlowInstructionsGenerator extends JetControlFlowBuilderAd private int labelCount = 0; private final Stack builders = new Stack(); - private final JetControlFlowDataTrace trace; + private final JetPseudocodeTrace trace; - public JetControlFlowInstructionsGenerator(JetControlFlowDataTrace trace) { + public JetControlFlowInstructionsGenerator(JetPseudocodeTrace trace) { super(null); this.trace = trace; } @@ -60,7 +61,7 @@ public class JetControlFlowInstructionsGenerator extends JetControlFlowBuilderAd JetControlFlowInstructionsGeneratorWorker worker = popBuilder(subroutine); if (functionLiteral) { JetControlFlowInstructionsGeneratorWorker builder = builders.peek(); - FunctionLiteralValueInstruction instruction = new FunctionLiteralValueInstruction(subroutine); + FunctionLiteralValueInstruction instruction = new FunctionLiteralValueInstruction((JetFunctionLiteralExpression) subroutine); instruction.setBody(worker.getPseudocode()); builder.add(instruction); } @@ -155,8 +156,8 @@ public class JetControlFlowInstructionsGenerator extends JetControlFlowBuilderAd } @Override - public void returnNoValue(@NotNull JetElement subroutine) { - add(new ReturnNoValueInstruction(getExitPoint(subroutine))); + public void returnNoValue(@NotNull JetElement expression, @NotNull JetElement subroutine) { + add(new ReturnNoValueInstruction(expression, getExitPoint(subroutine))); } @Override diff --git a/idea/src/org/jetbrains/jet/lang/cfg/pseudocode/JetControlFlowDataTrace.java b/idea/src/org/jetbrains/jet/lang/cfg/pseudocode/JetPseudocodeTrace.java similarity index 81% rename from idea/src/org/jetbrains/jet/lang/cfg/pseudocode/JetControlFlowDataTrace.java rename to idea/src/org/jetbrains/jet/lang/cfg/pseudocode/JetPseudocodeTrace.java index b16a3454c679775cc2d6ede683737466e3a010fe..7cc5a86e6a995ed08d9dc39d411212fa98fdad23 100644 --- a/idea/src/org/jetbrains/jet/lang/cfg/pseudocode/JetControlFlowDataTrace.java +++ b/idea/src/org/jetbrains/jet/lang/cfg/pseudocode/JetPseudocodeTrace.java @@ -6,9 +6,9 @@ import org.jetbrains.jet.lang.psi.JetElement; /** * @author abreslav */ -public interface JetControlFlowDataTrace { +public interface JetPseudocodeTrace { - JetControlFlowDataTrace EMPTY = new JetControlFlowDataTrace() { + JetPseudocodeTrace EMPTY = new JetPseudocodeTrace() { @Override public void recordControlFlowData(@NotNull JetElement element, @NotNull Pseudocode pseudocode) { } 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 91cbf5f3fd0b545b525fbab7ef409a8a0c5bbb73..4c912b8e1b425c018ec113b621d1f72a5393ff63 100644 --- a/idea/src/org/jetbrains/jet/lang/cfg/pseudocode/Pseudocode.java +++ b/idea/src/org/jetbrains/jet/lang/cfg/pseudocode/Pseudocode.java @@ -52,6 +52,8 @@ public class Pseudocode { private final List labels = new ArrayList(); private final JetElement correspondingElement; + private SubroutineExitInstruction exitInstruction; + private boolean postPrecessed = false; public Pseudocode(JetElement correspondingElement) { this.correspondingElement = correspondingElement; @@ -69,6 +71,16 @@ public class Pseudocode { public void addInstruction(Instruction instruction) { instructions.add(instruction); + if (instruction instanceof SubroutineExitInstruction) { + SubroutineExitInstruction exitInstruction = (SubroutineExitInstruction) instruction; + assert this.exitInstruction == null; + this.exitInstruction = exitInstruction; + } + } + + @NotNull + public SubroutineExitInstruction getExitInstruction() { + return exitInstruction; } public void bindLabel(Label label) { @@ -76,6 +88,8 @@ public class Pseudocode { } public void postProcess() { + if (postPrecessed) return; + postPrecessed = true; for (int i = 0, instructionsSize = instructions.size(); i < instructionsSize; i++) { Instruction instruction = instructions.get(i); final int currentPosition = i; 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 f750e9e29d45c2db649926afbcb62a1c3c1429f2..a479b00d5e6e3d5f84e5c16e47692c046ccb08cb 100644 --- a/idea/src/org/jetbrains/jet/lang/cfg/pseudocode/ReturnNoValueInstruction.java +++ b/idea/src/org/jetbrains/jet/lang/cfg/pseudocode/ReturnNoValueInstruction.java @@ -1,14 +1,23 @@ package org.jetbrains.jet.lang.cfg.pseudocode; +import org.jetbrains.annotations.NotNull; import org.jetbrains.jet.lang.cfg.Label; +import org.jetbrains.jet.lang.psi.JetElement; /** * @author abreslav */ public class ReturnNoValueInstruction extends AbstractJumpInstruction { - public ReturnNoValueInstruction(Label targetLabel) { + private final JetElement element; + + public ReturnNoValueInstruction(@NotNull JetElement element, Label targetLabel) { super(targetLabel); + this.element = element; + } + + public JetElement getElement() { + return element; } @Override diff --git a/idea/src/org/jetbrains/jet/lang/resolve/ClassDescriptorResolver.java b/idea/src/org/jetbrains/jet/lang/resolve/ClassDescriptorResolver.java index 2054b82000f4c59b7c9834067357f0a1c92dabaa..89d0b2188980ce6f158ae18ed82792167dcccf6b 100644 --- a/idea/src/org/jetbrains/jet/lang/resolve/ClassDescriptorResolver.java +++ b/idea/src/org/jetbrains/jet/lang/resolve/ClassDescriptorResolver.java @@ -4,11 +4,15 @@ import com.intellij.lang.ASTNode; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.jet.lang.JetSemanticServices; +import org.jetbrains.jet.lang.cfg.JetFlowInformationProvider; import org.jetbrains.jet.lang.psi.*; import org.jetbrains.jet.lang.types.*; import org.jetbrains.jet.lexer.JetTokens; -import java.util.*; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; /** * @author abreslav @@ -305,7 +309,7 @@ public class ClassDescriptorResolver { type = ErrorUtils.createErrorType("No type, no body"); } else { // TODO : ??? Fix-point here: what if we have something like "val a = foo {a.bar()}" - type = semanticServices.getTypeInferrer(trace).getType(scope, initializer, false); + type = semanticServices.getTypeInferrer(trace, JetFlowInformationProvider.ERROR).getType(scope, initializer, false); } } else { type = typeResolver.resolveType(scope, propertyTypeRef); diff --git a/idea/src/org/jetbrains/jet/lang/resolve/TopDownAnalyzer.java b/idea/src/org/jetbrains/jet/lang/resolve/TopDownAnalyzer.java index a7dea9621c6c017d0aeeac3a97e7084236cd0c55..853bf3a9f50eb97e736c98217583a16654c2be0e 100644 --- a/idea/src/org/jetbrains/jet/lang/resolve/TopDownAnalyzer.java +++ b/idea/src/org/jetbrains/jet/lang/resolve/TopDownAnalyzer.java @@ -1,12 +1,10 @@ package org.jetbrains.jet.lang.resolve; import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; import org.jetbrains.jet.lang.JetSemanticServices; import org.jetbrains.jet.lang.cfg.JetControlFlowProcessor; -import org.jetbrains.jet.lang.cfg.pseudocode.JetControlFlowDataTrace; -import org.jetbrains.jet.lang.cfg.pseudocode.JetControlFlowDataTraceFactory; -import org.jetbrains.jet.lang.cfg.pseudocode.JetControlFlowInstructionsGenerator; +import org.jetbrains.jet.lang.cfg.JetFlowInformationProvider; +import org.jetbrains.jet.lang.cfg.pseudocode.*; import org.jetbrains.jet.lang.psi.*; import org.jetbrains.jet.lang.types.*; @@ -25,7 +23,6 @@ public class TopDownAnalyzer { private final JetSemanticServices semanticServices; private final ClassDescriptorResolver classDescriptorResolver; private final BindingTrace trace; - private final JetTypeInferrer typeInferrer; private final JetControlFlowDataTraceFactory flowDataTraceFactory; private boolean readyToProcessExpressions = false; @@ -33,7 +30,6 @@ public class TopDownAnalyzer { this.semanticServices = semanticServices; this.classDescriptorResolver = new ClassDescriptorResolver(semanticServices, bindingTrace); this.trace = bindingTrace; - this.typeInferrer = semanticServices.getTypeInferrer(trace); this.flowDataTraceFactory = flowDataTraceFactory; } @@ -41,7 +37,6 @@ public class TopDownAnalyzer { this.semanticServices = semanticServices; this.classDescriptorResolver = new ClassDescriptorResolver(semanticServices, bindingTrace); this.trace = bindingTrace; - this.typeInferrer = semanticServices.getTypeInferrer(trace); this.flowDataTraceFactory = JetControlFlowDataTraceFactory.EMPTY; } @@ -101,7 +96,7 @@ public class TopDownAnalyzer { if (importDirective.isAllUnder()) { JetExpression importedReference = importDirective.getImportedReference(); if (importedReference != null) { - JetType type = typeInferrer.getType(namespaceScope, importedReference, false); + JetType type = semanticServices.getTypeInferrer(trace, JetFlowInformationProvider.ERROR).getType(namespaceScope, importedReference, false); if (type != null) { namespaceScope.importScope(type.getMemberScope()); } @@ -263,71 +258,137 @@ public class TopDownAnalyzer { WritableScope declaringScope = declaringScopes.get(declaration); assert declaringScope != null; - WritableScope parameterScope = semanticServices.createWritableScope(declaringScope, descriptor); - for (TypeParameterDescriptor typeParameter : descriptor.getTypeParameters()) { - parameterScope.addTypeParameterDescriptor(typeParameter); - } - for (ValueParameterDescriptor valueParameterDescriptor : descriptor.getUnsubstitutedValueParameters()) { - parameterScope.addPropertyDescriptor(valueParameterDescriptor); - } - parameterScope.addLabeledDeclaration(descriptor); assert declaration instanceof JetFunction || declaration instanceof JetConstructor; JetDeclarationWithBody declarationWithBody = (JetDeclarationWithBody) declaration; JetExpression bodyExpression = declarationWithBody.getBodyExpression(); - if (bodyExpression != null) { - JetControlFlowDataTrace controlFlowDataTrace = flowDataTraceFactory.createTrace(declaration); - JetControlFlowInstructionsGenerator instructionsGenerator = new JetControlFlowInstructionsGenerator(controlFlowDataTrace); - new JetControlFlowProcessor(semanticServices, trace, instructionsGenerator).generate(declaration, bodyExpression); - controlFlowDataTrace.close(); - - boolean preferBlock = true; - FunctionDescriptorImpl functionDescriptorImpl = null; - if (declaration instanceof JetFunction) { - JetFunction jetFunction = (JetFunction) declaration; - preferBlock = jetFunction.hasBlockBody(); - functionDescriptorImpl = (FunctionDescriptorImpl) descriptor; - } + if (declaration instanceof JetFunction) { + JetFunction function = (JetFunction) declaration; + FunctionDescriptorImpl functionDescriptorImpl = (FunctionDescriptorImpl) descriptor; + if (bodyExpression != null) { + JetFlowInformationProvider flowInformationProvider = computeFlowData(declaration, bodyExpression); + JetTypeInferrer typeInferrer = semanticServices.getTypeInferrer(trace, flowInformationProvider); - JetType returnType = resolveExpression(parameterScope, bodyExpression, preferBlock, controlFlowDataTrace); + assert readyToProcessExpressions : "Must be ready collecting types"; - if (declaration instanceof JetFunction) { - JetFunction function = (JetFunction) declaration; if (function.getReturnTypeRef() != null) { - if (returnType != null) { - if (!semanticServices.getTypeChecker().isConvertibleTo(returnType, descriptor.getUnsubstitutedReturnType())) { - semanticServices.getErrorHandler().typeMismatch(bodyExpression, descriptor.getUnsubstitutedReturnType(), returnType); - } - } + typeInferrer.checkFunctionReturnType(declaringScope, function, descriptor); } else { - JetType safeReturnType = returnType; - if (safeReturnType == null) { - safeReturnType = ErrorUtils.createErrorType("Unable to infer body type"); + JetType returnType = typeInferrer.getFunctionReturnType(declaringScope, function, descriptor); + if (returnType == null) { + returnType = ErrorUtils.createErrorType("Unable to infer body type"); } - functionDescriptorImpl.setUnsubstitutedReturnType(safeReturnType); + functionDescriptorImpl.setUnsubstitutedReturnType(returnType); } } - } - else { - if (declaration instanceof JetFunction) { - JetFunction function = (JetFunction) declaration; + else { if (function.getReturnTypeRef() == null) { semanticServices.getErrorHandler().genericError(function.getNode(), "This function must either declare a return type or have a body element"); ((FunctionDescriptorImpl) descriptor).setUnsubstitutedReturnType(ErrorUtils.createErrorType("No type, no body")); } } } + else if (declaration instanceof JetConstructor) { + if (bodyExpression != null) { + computeFlowData(declaration, bodyExpression); + JetFlowInformationProvider flowInformationProvider = computeFlowData(declaration, bodyExpression); + JetTypeInferrer typeInferrer = semanticServices.getTypeInferrer(trace, flowInformationProvider); + typeInferrer.getType(FunctionDescriptorUtil.getFunctionInnerScope(declaringScope, descriptor, semanticServices), bodyExpression, true); + } + + } assert descriptor.getUnsubstitutedReturnType() != null; } } - @Nullable - private JetType resolveExpression(@NotNull JetScope scope, JetExpression expression, boolean preferBlock, JetControlFlowDataTrace controlFlowDataTrace) { - assert readyToProcessExpressions : "Must be ready collecting types"; - return typeInferrer.getType(scope, expression, preferBlock); + private JetFlowInformationProvider computeFlowData(@NotNull JetDeclaration declaration, @NotNull JetExpression bodyExpression) { + final JetPseudocodeTrace pseudocodeTrace = flowDataTraceFactory.createTrace(declaration); + final Map pseudocodeMap = new HashMap(); + JetPseudocodeTrace wrappedTrace = new JetPseudocodeTrace() { + @Override + public void recordControlFlowData(@NotNull JetElement element, @NotNull Pseudocode pseudocode) { + pseudocodeTrace.recordControlFlowData(element, pseudocode); + pseudocodeMap.put(element, pseudocode); + } + + @Override + public void close() { + pseudocodeTrace.close(); + for (Pseudocode pseudocode : pseudocodeMap.values()) { + pseudocode.postProcess(); + } + } + }; + JetControlFlowInstructionsGenerator instructionsGenerator = new JetControlFlowInstructionsGenerator(wrappedTrace); + new JetControlFlowProcessor(semanticServices, trace, instructionsGenerator).generate(declaration, bodyExpression); + wrappedTrace.close(); + return new JetFlowInformationProvider() { + @Override + public void collectReturnedInformation(@NotNull JetFunction function, Collection returnedExpressions, Collection elementsReturningUnit) { + Pseudocode pseudocode = pseudocodeMap.get(function); + assert pseudocode != null; + + SubroutineExitInstruction exitInstruction = pseudocode.getExitInstruction(); + processPreviousInstructions(exitInstruction, returnedExpressions, elementsReturningUnit); + } + }; + } + + private void processPreviousInstructions(Instruction previousFor, final Collection returnedExpressions, final Collection elementsReturningUnit) { + Collection previousInstructions = previousFor.getPreviousInstructions(); + InstructionVisitor visitor = new InstructionVisitor() { + @Override + public void visitReadValue(ReadValueInstruction instruction) { + returnedExpressions.add((JetExpression) instruction.getElement()); + } + + @Override + public void visitReturnValue(ReturnValueInstruction instruction) { + processPreviousInstructions(instruction, returnedExpressions, elementsReturningUnit); + } + + @Override + public void visitReturnNoValue(ReturnNoValueInstruction instruction) { + elementsReturningUnit.add(instruction.getElement()); + } + + @Override + public void visitSubroutineEnter(SubroutineEnterInstruction instruction) { + elementsReturningUnit.add(instruction.getSubroutine()); + } + + @Override + public void visitUnsupportedElementInstruction(UnsupportedElementInstruction instruction) { + semanticServices.getErrorHandler().genericError(instruction.getElement().getNode(), "Unsupported by control-flow builder"); + } + + @Override + public void visitWriteValue(WriteValueInstruction writeValueInstruction) { + elementsReturningUnit.add(writeValueInstruction.getElement()); + } + + @Override + public void visitJump(AbstractJumpInstruction instruction) { + processPreviousInstructions(instruction, returnedExpressions, elementsReturningUnit); + } + + @Override + public void visitInstruction(Instruction instruction) { + if (instruction instanceof JetElementInstruction) { + JetElementInstruction elementInstruction = (JetElementInstruction) instruction; + semanticServices.getErrorHandler().genericError(elementInstruction.getElement().getNode(), "Unsupported by control-flow builder " + elementInstruction.getElement()); + } + else { + throw new UnsupportedOperationException(instruction.toString()); + } + } + }; + for (Instruction previousInstruction : previousInstructions) { + previousInstruction.accept(visitor); + } } } diff --git a/idea/src/org/jetbrains/jet/lang/types/FunctionDescriptorUtil.java b/idea/src/org/jetbrains/jet/lang/types/FunctionDescriptorUtil.java index 98e36cc61cd1ff053992967167cc5e024609ac6a..1866fdeb2f5302bdeb87f19f3f409b22d3d8c5c7 100644 --- a/idea/src/org/jetbrains/jet/lang/types/FunctionDescriptorUtil.java +++ b/idea/src/org/jetbrains/jet/lang/types/FunctionDescriptorUtil.java @@ -2,6 +2,9 @@ package org.jetbrains.jet.lang.types; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import org.jetbrains.jet.lang.JetSemanticServices; +import org.jetbrains.jet.lang.resolve.JetScope; +import org.jetbrains.jet.lang.resolve.WritableScope; import java.util.*; @@ -116,4 +119,17 @@ public class FunctionDescriptorUtil { ); return substitutedDescriptor; } + + @NotNull + public static JetScope getFunctionInnerScope(@NotNull JetScope outerScope, @NotNull FunctionDescriptor descriptor, @NotNull JetSemanticServices semanticServices) { + WritableScope parameterScope = semanticServices.createWritableScope(outerScope, descriptor); + for (TypeParameterDescriptor typeParameter : descriptor.getTypeParameters()) { + parameterScope.addTypeParameterDescriptor(typeParameter); + } + for (ValueParameterDescriptor valueParameterDescriptor : descriptor.getUnsubstitutedValueParameters()) { + parameterScope.addPropertyDescriptor(valueParameterDescriptor); + } + parameterScope.addLabeledDeclaration(descriptor); + return parameterScope; + } } diff --git a/idea/src/org/jetbrains/jet/lang/types/JetTypeInferrer.java b/idea/src/org/jetbrains/jet/lang/types/JetTypeInferrer.java index 5ae05d07bd9567207ed36d99d22289d236c756fc..1d51ae9f245594bbf3eb000e5910905a9f3cacb7 100644 --- a/idea/src/org/jetbrains/jet/lang/types/JetTypeInferrer.java +++ b/idea/src/org/jetbrains/jet/lang/types/JetTypeInferrer.java @@ -6,6 +6,7 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.jet.JetNodeTypes; import org.jetbrains.jet.lang.JetSemanticServices; +import org.jetbrains.jet.lang.cfg.JetFlowInformationProvider; import org.jetbrains.jet.lang.psi.*; import org.jetbrains.jet.lang.resolve.*; import org.jetbrains.jet.lexer.JetTokens; @@ -60,16 +61,20 @@ public class JetTypeInferrer { assignmentOperationCounterparts.put(JetTokens.MINUSEQ, JetTokens.MINUS); } + private final Map typeCache = new HashMap(); + private final BindingTrace trace; private final JetSemanticServices semanticServices; private final TypeResolver typeResolver; private final ClassDescriptorResolver classDescriptorResolver; + private final JetFlowInformationProvider flowInformationProvider; - public JetTypeInferrer(BindingTrace trace, JetSemanticServices semanticServices) { - this.trace = trace; + public JetTypeInferrer(@NotNull BindingTrace trace, @NotNull JetFlowInformationProvider flowInformationProvider, @NotNull JetSemanticServices semanticServices) { + this.trace = new CachedBindingTrace(trace); this.semanticServices = semanticServices; this.typeResolver = new TypeResolver(trace, semanticServices); this.classDescriptorResolver = semanticServices.getClassDescriptorResolver(trace); + this.flowInformationProvider = flowInformationProvider; } @NotNull @@ -107,7 +112,6 @@ public class JetTypeInferrer { @NotNull JetReferenceExpression reference, @NotNull String name, @NotNull JetType receiverType, - @NotNull List argumentTypes, boolean reportUnresolved) { OverloadDomain overloadDomain = semanticServices.getOverloadResolver().getOverloadDomain(receiverType, scope, name); @@ -264,21 +268,75 @@ public class JetTypeInferrer { }; } + @NotNull + public JetType getFunctionReturnType(@NotNull JetScope outerScope, JetFunction function, FunctionDescriptor functionDescriptor) { + Map typeMap = getReturnedExpressions(outerScope, function, functionDescriptor); + Collection types = typeMap.values(); + return types.isEmpty() ? JetStandardClasses.getNothingType() : semanticServices.getTypeChecker().commonSupertype(types); + } + + private JetType getCachedType(@NotNull JetExpression expression) { +// assert typeCache.containsKey(expression) : "No type cached for " + expression.getText(); + return typeCache.get(expression); + } + + public void checkFunctionReturnType(@NotNull JetScope outerScope, @NotNull JetFunction function, @NotNull FunctionDescriptor functionDescriptor) { + Map typeMap = getReturnedExpressions(outerScope, function, functionDescriptor); + if (typeMap.isEmpty()) { + return; // The function returns Nothing + } + JetType expectedReturnType = functionDescriptor.getUnsubstitutedReturnType(); + for (Map.Entry entry : typeMap.entrySet()) { + JetType actualType = entry.getValue(); + JetElement element = entry.getKey(); + if (!semanticServices.getTypeChecker().isConvertibleTo(actualType, expectedReturnType)) { + if (element instanceof JetExpression) { + JetExpression expression = (JetExpression) element; + semanticServices.getErrorHandler().typeMismatch(expression, expectedReturnType, actualType); + } + else { + semanticServices.getErrorHandler().genericError(element.getNode(), "This function must return a value of type " + expectedReturnType); + } + } + } + } + + private Map getReturnedExpressions(JetScope outerScope, JetFunction function, FunctionDescriptor functionDescriptor) { + JetExpression bodyExpression = function.getBodyExpression(); + assert bodyExpression != null; + JetScope functionInnerScope = FunctionDescriptorUtil.getFunctionInnerScope(outerScope, functionDescriptor, semanticServices); + getType(functionInnerScope, bodyExpression, function.hasBlockBody()); + Collection returnedExpressions = new ArrayList(); + Collection elementsReturningUnit = new ArrayList(); + flowInformationProvider.collectReturnedInformation(function, returnedExpressions, elementsReturningUnit); + Map typeMap = new HashMap(); + for (JetExpression returnedExpression : returnedExpressions) { + JetType cachedType = getCachedType(returnedExpression); + if (cachedType != null) { + typeMap.put(returnedExpression, cachedType); + } + } + for (JetElement jetElement : elementsReturningUnit) { + typeMap.put(jetElement, JetStandardClasses.getUnitType()); + } + return typeMap; + } + @Nullable - private JetType getBlockReturnedType(@NotNull JetScope outerScope, @NotNull List block, @NotNull LabeledJumpDomain jumpDomain) { + private JetType getBlockReturnedType(@NotNull JetScope outerScope, @NotNull List block) { if (block.isEmpty()) { return JetStandardClasses.getUnitType(); } DeclarationDescriptor containingDescriptor = outerScope.getContainingDeclaration(); WritableScope scope = semanticServices.createWritableScope(outerScope, containingDescriptor); - return getBlockReturnedTypeWithWritableScope(scope, block, jumpDomain); + return getBlockReturnedTypeWithWritableScope(scope, block); } - private JetType getBlockReturnedTypeWithWritableScope(@NotNull WritableScope scope, @NotNull List block, @NotNull LabeledJumpDomain jumpDomain) { + private JetType getBlockReturnedTypeWithWritableScope(@NotNull WritableScope scope, @NotNull List block) { assert !block.isEmpty(); - TypeInferrerVisitorWithWritableScope blockLevelVisitor = new TypeInferrerVisitorWithWritableScope(scope, true, jumpDomain); + TypeInferrerVisitorWithWritableScope blockLevelVisitor = new TypeInferrerVisitorWithWritableScope(scope, true); JetType result = null; for (JetElement statement : block) { @@ -305,18 +363,12 @@ public class JetTypeInferrer { private class TypeInferrerVisitor extends JetVisitor { private final JetScope scope; private final boolean preferBlock; - private final LabeledJumpDomain jumpDomain; protected JetType result; - private TypeInferrerVisitor(@NotNull JetScope scope, boolean preferBlock, @NotNull LabeledJumpDomain jumpDomain) { + private TypeInferrerVisitor(@NotNull JetScope scope, boolean preferBlock) { this.scope = scope; this.preferBlock = preferBlock; - this.jumpDomain = jumpDomain; - } - - private TypeInferrerVisitor(JetScope scope, boolean preferBlock) { - this(scope, preferBlock, LabeledJumpDomain.EMPTY); } @Nullable @@ -379,7 +431,7 @@ public class JetTypeInferrer { public void visitFunctionLiteralExpression(JetFunctionLiteralExpression expression) { if (preferBlock && !expression.hasParameterSpecification()) { trace.recordBlock(expression); - result = getBlockReturnedType(scope, expression.getBody(), LabeledJumpDomain.ERROR); + result = getBlockReturnedType(scope, expression.getBody()); return; } @@ -416,7 +468,7 @@ public class JetTypeInferrer { writableScope.addPropertyDescriptor(propertyDescriptor); } writableScope.setThisType(receiverType); - returnType = getBlockReturnedType(writableScope, body, LabeledJumpDomain.ERROR); + returnType = getBlockReturnedType(writableScope, body); } JetType effectiveReceiverType = receiverTypeRef == null ? null : receiverType; JetType safeReturnType = returnType == null ? ErrorUtils.createErrorType("") : returnType; @@ -477,7 +529,6 @@ public class JetTypeInferrer { else { returnedType = JetStandardClasses.getUnitType(); } - jumpDomain.registerReturn(expression, returnedType); result = JetStandardClasses.getNothingType(); } @@ -485,15 +536,11 @@ public class JetTypeInferrer { @Override public void visitBreakExpression(JetBreakExpression expression) { result = JetStandardClasses.getNothingType(); - - jumpDomain.registerBreakOrContinue(expression); } @Override public void visitContinueExpression(JetContinueExpression expression) { result = JetStandardClasses.getNothingType(); - - jumpDomain.registerBreakOrContinue(expression); } @Override @@ -603,7 +650,7 @@ public class JetTypeInferrer { @Override public void visitBlockExpression(JetBlockExpression expression) { - result = getBlockReturnedType(scope, expression.getStatements(), jumpDomain); + result = getBlockReturnedType(scope, expression.getStatements()); } @Override @@ -692,7 +739,7 @@ public class JetTypeInferrer { if (!function.hasParameterSpecification()) { WritableScope writableScope = semanticServices.createWritableScope(scope, scope.getContainingDeclaration()); conditionScope = writableScope; - getBlockReturnedTypeWithWritableScope(writableScope, function.getBody(), LabeledJumpDomain.ERROR); // TODO + getBlockReturnedTypeWithWritableScope(writableScope, function.getBody()); trace.recordBlock(function); } else { getType(scope, body, true); @@ -701,7 +748,7 @@ public class JetTypeInferrer { else if (body != null) { WritableScope writableScope = semanticServices.createWritableScope(scope, scope.getContainingDeclaration()); conditionScope = writableScope; - getBlockReturnedTypeWithWritableScope(writableScope, Collections.singletonList(body), LabeledJumpDomain.ERROR); // TODO + getBlockReturnedTypeWithWritableScope(writableScope, Collections.singletonList(body)); } checkCondition(conditionScope, expression.getCondition()); result = JetStandardClasses.getUnitType(); @@ -1240,8 +1287,8 @@ public class JetTypeInferrer { private class TypeInferrerVisitorWithWritableScope extends TypeInferrerVisitor { private final WritableScope scope; - public TypeInferrerVisitorWithWritableScope(@NotNull WritableScope scope, boolean preferBlock, @NotNull LabeledJumpDomain jumpDomain) { - super(scope, preferBlock, jumpDomain); + public TypeInferrerVisitorWithWritableScope(@NotNull WritableScope scope, boolean preferBlock) { + super(scope, preferBlock); this.scope = scope; } @@ -1358,30 +1405,44 @@ public class JetTypeInferrer { } } - private interface LabeledJumpDomain { - LabeledJumpDomain EMPTY = new LabeledJumpDomain() { - @Override - public void registerReturn(@NotNull JetReturnExpression expression, JetType returnedExpressionType) { - } + private class CachedBindingTrace extends BindingTrace { + private final BindingTrace originalTrace; - @Override - public void registerBreakOrContinue(@NotNull JetLabelQualifiedExpression expression) { - } - }; + public CachedBindingTrace(BindingTrace originalTrace) { + this.originalTrace = originalTrace; + } - LabeledJumpDomain ERROR = new LabeledJumpDomain() { - @Override - public void registerReturn(@NotNull JetReturnExpression expression, JetType returnedExpressionType) { -// throw new UnsupportedOperationException(); - } + public void recordExpressionType(@NotNull JetExpression expression, @NotNull JetType type) { + originalTrace.recordExpressionType(expression, type); + typeCache.put(expression, type); + } - @Override - public void registerBreakOrContinue(@NotNull JetLabelQualifiedExpression expression) { -// throw new UnsupportedOperationException(); - } - }; + public void recordReferenceResolution(@NotNull JetReferenceExpression expression, @NotNull DeclarationDescriptor descriptor) { + originalTrace.recordReferenceResolution(expression, descriptor); + } + + public void recordLabelResolution(@NotNull JetReferenceExpression expression, @NotNull PsiElement element) { + originalTrace.recordLabelResolution(expression, element); + } + + public void recordDeclarationResolution(@NotNull PsiElement declaration, @NotNull DeclarationDescriptor descriptor) { + originalTrace.recordDeclarationResolution(declaration, descriptor); + } + + public void recordTypeResolution(@NotNull JetTypeReference typeReference, @NotNull JetType type) { + originalTrace.recordTypeResolution(typeReference, type); + } + + public void setToplevelScope(JetScope toplevelScope) { + originalTrace.setToplevelScope(toplevelScope); + } - void registerReturn(@NotNull JetReturnExpression expression, JetType returnedExpressionType); - void registerBreakOrContinue(@NotNull JetLabelQualifiedExpression expression); + public void recordBlock(JetFunctionLiteralExpression expression) { + originalTrace.recordBlock(expression); + } + + public void removeReferenceResolution(@NotNull JetReferenceExpression referenceExpression) { + originalTrace.removeReferenceResolution(referenceExpression); + } } } diff --git a/idea/testData/cfg/EmptyFunction.instructions b/idea/testData/cfg/EmptyFunction.instructions new file mode 100644 index 0000000000000000000000000000000000000000..599845fcbfbcce674f285bb96c7db1ed420dce6a --- /dev/null +++ b/idea/testData/cfg/EmptyFunction.instructions @@ -0,0 +1,8 @@ +== empty == +fun empty() {} +--------------------- +l0: + +l1: + +===================== diff --git a/idea/testData/cfg/EmptyFunction.jet b/idea/testData/cfg/EmptyFunction.jet new file mode 100644 index 0000000000000000000000000000000000000000..82980e475938c0a4dc5b6d8ecb44010da8fc21ef --- /dev/null +++ b/idea/testData/cfg/EmptyFunction.jet @@ -0,0 +1 @@ +fun empty() {} \ No newline at end of file diff --git a/idea/testData/cfg/ShortFunction.instructions b/idea/testData/cfg/ShortFunction.instructions new file mode 100644 index 0000000000000000000000000000000000000000..588f17b443e8068509f1df803dfc5e967039f08e --- /dev/null +++ b/idea/testData/cfg/ShortFunction.instructions @@ -0,0 +1,9 @@ +== short == +fun short() = 1 +--------------------- +l0: + + r(1) +l1: + +===================== diff --git a/idea/testData/cfg/ShortFunction.jet b/idea/testData/cfg/ShortFunction.jet new file mode 100644 index 0000000000000000000000000000000000000000..634b2c07274615619b77e35e916e5a0d96cd1e89 --- /dev/null +++ b/idea/testData/cfg/ShortFunction.jet @@ -0,0 +1 @@ +fun short() = 1 diff --git a/idea/tests/org/jetbrains/jet/cfg/JetControlFlowTest.java b/idea/tests/org/jetbrains/jet/cfg/JetControlFlowTest.java index 8d57695b88a5048eaa851801a6db3066c6f5eaf4..34b89515c3ba56ac246aabec2f48c69d812ca4dc 100644 --- a/idea/tests/org/jetbrains/jet/cfg/JetControlFlowTest.java +++ b/idea/tests/org/jetbrains/jet/cfg/JetControlFlowTest.java @@ -10,7 +10,7 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.jet.JetTestCaseBase; import org.jetbrains.jet.lang.ErrorHandler; import org.jetbrains.jet.lang.cfg.pseudocode.Instruction; -import org.jetbrains.jet.lang.cfg.pseudocode.JetControlFlowDataTrace; +import org.jetbrains.jet.lang.cfg.pseudocode.JetPseudocodeTrace; import org.jetbrains.jet.lang.cfg.pseudocode.JetControlFlowDataTraceFactory; import org.jetbrains.jet.lang.cfg.pseudocode.Pseudocode; import org.jetbrains.jet.lang.psi.JetElement; @@ -42,7 +42,7 @@ public class JetControlFlowTest extends JetTestCaseBase { JetFile file = (JetFile) getFile(); final Map data = new LinkedHashMap(); - final JetControlFlowDataTrace jetControlFlowDataTrace = new JetControlFlowDataTrace() { + final JetPseudocodeTrace pseudocodeTrace = new JetPseudocodeTrace() { @Override public void recordControlFlowData(@NotNull JetElement element, @NotNull Pseudocode pseudocode) { @@ -51,6 +51,9 @@ public class JetControlFlowTest extends JetTestCaseBase { @Override public void close() { + for (Pseudocode pseudocode : data.values()) { + pseudocode.postProcess(); + } } }; @@ -58,8 +61,8 @@ public class JetControlFlowTest extends JetTestCaseBase { AnalyzingUtils.analyzeNamespace(file.getRootNamespace(), ErrorHandler.THROW_EXCEPTION, new JetControlFlowDataTraceFactory() { @NotNull @Override - public JetControlFlowDataTrace createTrace(JetElement element) { - return jetControlFlowDataTrace; + public JetPseudocodeTrace createTrace(JetElement element) { + return pseudocodeTrace; } }); @@ -75,9 +78,6 @@ public class JetControlFlowTest extends JetTestCaseBase { private void processCFData(String name, Map data) throws IOException { Collection pseudocodes = data.values(); - for (Pseudocode pseudocode : pseudocodes) { - pseudocode.postProcess(); - } StringBuilder instructionDump = new StringBuilder(); int i = 0; diff --git a/idea/tests/org/jetbrains/jet/types/JetTypeCheckerTest.java b/idea/tests/org/jetbrains/jet/types/JetTypeCheckerTest.java index 90043a5ef55dcdc20c601cdb116d90c715b2b505..c7c6a6f54a0d6fc4593db9f98453000bb14f8993 100644 --- a/idea/tests/org/jetbrains/jet/types/JetTypeCheckerTest.java +++ b/idea/tests/org/jetbrains/jet/types/JetTypeCheckerTest.java @@ -6,6 +6,7 @@ import com.intellij.openapi.project.Project; import org.jetbrains.annotations.NotNull; import org.jetbrains.jet.lang.ErrorHandler; import org.jetbrains.jet.lang.JetSemanticServices; +import org.jetbrains.jet.lang.cfg.JetFlowInformationProvider; import org.jetbrains.jet.lang.psi.JetChangeUtil; import org.jetbrains.jet.lang.psi.JetClass; import org.jetbrains.jet.lang.psi.JetExpression; @@ -455,14 +456,14 @@ public class JetTypeCheckerTest extends LightDaemonAnalyzerTestCase { private void assertType(String expression, JetType expectedType) { Project project = getProject(); JetExpression jetExpression = JetChangeUtil.createExpression(project, expression); - JetType type = semanticServices.getTypeInferrer(BindingTrace.DUMMY).getType(classDefinitions.BASIC_SCOPE, jetExpression, false); + JetType type = semanticServices.getTypeInferrer(BindingTrace.DUMMY, JetFlowInformationProvider.ERROR).getType(classDefinitions.BASIC_SCOPE, jetExpression, false); assertTrue(type + " != " + expectedType, JetTypeImpl.equalTypes(type, expectedType)); } private void assertErrorType(String expression) { Project project = getProject(); JetExpression jetExpression = JetChangeUtil.createExpression(project, expression); - JetType type = semanticServices.getTypeInferrer(BindingTrace.DUMMY).safeGetType(classDefinitions.BASIC_SCOPE, jetExpression, false); + JetType type = semanticServices.getTypeInferrer(BindingTrace.DUMMY, JetFlowInformationProvider.ERROR).safeGetType(classDefinitions.BASIC_SCOPE, jetExpression, false); assertTrue("Error type expected but " + type + " returned", ErrorUtils.isErrorType(type)); } @@ -485,7 +486,7 @@ public class JetTypeCheckerTest extends LightDaemonAnalyzerTestCase { private void assertType(JetScope scope, String expression, String expectedTypeStr) { Project project = getProject(); JetExpression jetExpression = JetChangeUtil.createExpression(project, expression); - JetType type = semanticServices.getTypeInferrer(BindingTrace.DUMMY).getType(scope, jetExpression, false); + JetType type = semanticServices.getTypeInferrer(BindingTrace.DUMMY, JetFlowInformationProvider.ERROR).getType(scope, jetExpression, false); JetType expectedType = expectedTypeStr == null ? null : makeType(expectedTypeStr); assertEquals(expectedType, type); }