From 240ed30c2f3d47ca37cfa3750ca258772720757b Mon Sep 17 00:00:00 2001 From: svtk Date: Tue, 11 Oct 2011 18:02:55 +0400 Subject: [PATCH] Calls to flowInformationProvider were removed from JetTypeInferrer --- .../jet/lang/JetSemanticServices.java | 4 +- .../jet/lang/cfg/JetControlFlowProcessor.java | 6 +- .../jet/lang/resolve/AnnotationResolver.java | 2 +- .../jet/lang/resolve/BodyResolver.java | 24 +- .../lang/resolve/ClassDescriptorResolver.java | 8 +- .../jet/lang/resolve/ControlFlowAnalyzer.java | 133 +++++++++ .../jet/lang/resolve/TopDownAnalyzer.java | 1 + .../lang/resolve/TypeHierarchyResolver.java | 4 +- .../jet/lang/types/JetTypeInferrer.java | 262 +++++++++++++----- .../jetbrains/jet/resolve/JetResolveTest.java | 2 +- .../jet/types/JetTypeCheckerTest.java | 6 +- 11 files changed, 349 insertions(+), 103 deletions(-) create mode 100644 compiler/frontend/src/org/jetbrains/jet/lang/resolve/ControlFlowAnalyzer.java diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/JetSemanticServices.java b/compiler/frontend/src/org/jetbrains/jet/lang/JetSemanticServices.java index 6d94973a3d4..6b5517ace57 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/JetSemanticServices.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/JetSemanticServices.java @@ -44,8 +44,8 @@ public class JetSemanticServices { } @NotNull - public JetTypeInferrer.Services getTypeInferrerServices(@NotNull BindingTrace trace, @NotNull JetFlowInformationProvider flowInformationProvider) { - return new JetTypeInferrer(flowInformationProvider, this).getServices(trace); + public JetTypeInferrer.Services getTypeInferrerServices(@NotNull BindingTrace trace) { + return new JetTypeInferrer(this).getServices(trace); } @NotNull diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/cfg/JetControlFlowProcessor.java b/compiler/frontend/src/org/jetbrains/jet/lang/cfg/JetControlFlowProcessor.java index 95b3aa92012..7c49cf6c627 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/cfg/JetControlFlowProcessor.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/cfg/JetControlFlowProcessor.java @@ -76,17 +76,17 @@ public class JetControlFlowProcessor { Stack stack = labeledElements.get(labelName); if (stack == null || stack.isEmpty()) { if (reportUnresolved) { - trace.report(UNRESOLVED_REFERENCE.on(labelExpression)); +// trace.report(UNRESOLVED_REFERENCE.on(labelExpression)); } return null; } else if (stack.size() > 1) { // trace.getErrorHandler().genericWarning(labelExpression.getNode(), "There is more than one label with such a name in this scope"); - trace.report(LABEL_NAME_CLASH.on(labelExpression)); +// trace.report(LABEL_NAME_CLASH.on(labelExpression)); } JetElement result = stack.peek(); - trace.record(BindingContext.LABEL_TARGET, labelExpression, result); +// trace.record(BindingContext.LABEL_TARGET, labelExpression, result); return result; } diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/AnnotationResolver.java b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/AnnotationResolver.java index f00643ae6cb..9f08e5871c6 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/AnnotationResolver.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/AnnotationResolver.java @@ -37,7 +37,7 @@ public class AnnotationResolver { this.trace = trace; // this.typeInferrer = new JetTypeInferrer(JetFlowInformationProvider.THROW_EXCEPTION, semanticServices); // this.services = typeInferrer.getServices(this.trace); - this.callResolver = new CallResolver(semanticServices, new JetTypeInferrer(JetFlowInformationProvider.THROW_EXCEPTION, semanticServices), DataFlowInfo.getEmpty()); + this.callResolver = new CallResolver(semanticServices, new JetTypeInferrer(semanticServices), DataFlowInfo.getEmpty()); } @NotNull diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/BodyResolver.java b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/BodyResolver.java index 4ee7c5c2fa7..b915bec7921 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/BodyResolver.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/BodyResolver.java @@ -125,7 +125,7 @@ public class BodyResolver { final JetScope scopeForConstructor = primaryConstructor == null ? null : getInnerScopeForConstructor(primaryConstructor, descriptor.getScopeForMemberResolution(), true); - final JetTypeInferrer.Services typeInferrer = context.getSemanticServices().getTypeInferrerServices(traceForConstructors, JetFlowInformationProvider.NONE); // TODO : flow + final JetTypeInferrer.Services typeInferrer = context.getSemanticServices().getTypeInferrerServices(traceForConstructors); // TODO : flow final Map supertypes = Maps.newLinkedHashMap(); JetVisitorVoid visitor = new JetVisitorVoid() { @@ -290,7 +290,7 @@ public class BodyResolver { ConstructorDescriptor primaryConstructor = classDescriptor.getUnsubstitutedPrimaryConstructor(); assert primaryConstructor != null; final JetScope scopeForConstructor = getInnerScopeForConstructor(primaryConstructor, classDescriptor.getScopeForMemberResolution(), true); - JetTypeInferrer.Services typeInferrer = context.getSemanticServices().getTypeInferrerServices(createFieldAssignTrackingTrace(), JetFlowInformationProvider.NONE); // TODO : flow + JetTypeInferrer.Services typeInferrer = context.getSemanticServices().getTypeInferrerServices(createFieldAssignTrackingTrace()); // TODO : flow for (JetClassInitializer anonymousInitializer : anonymousInitializers) { typeInferrer.getType(scopeForConstructor, anonymousInitializer.getBody(), NO_EXPECTED_TYPE); } @@ -316,7 +316,7 @@ public class BodyResolver { private void resolveSecondaryConstructorBody(JetConstructor declaration, final ConstructorDescriptor descriptor, final JetScope declaringScope) { final JetScope functionInnerScope = getInnerScopeForConstructor(descriptor, declaringScope, false); - final JetTypeInferrer.Services typeInferrerForInitializers = context.getSemanticServices().getTypeInferrerServices(traceForConstructors, JetFlowInformationProvider.NONE); + final JetTypeInferrer.Services typeInferrerForInitializers = context.getSemanticServices().getTypeInferrerServices(traceForConstructors); JetClass containingClass = PsiTreeUtil.getParentOfType(declaration, JetClass.class); assert containingClass != null : "This must be guaranteed by the parser"; @@ -377,11 +377,11 @@ public class BodyResolver { } JetExpression bodyExpression = declaration.getBodyExpression(); if (bodyExpression != null) { - context.getClassDescriptorResolver().computeFlowData(declaration, bodyExpression); - JetFlowInformationProvider flowInformationProvider = context.getClassDescriptorResolver().computeFlowData(declaration, bodyExpression); - JetTypeInferrer.Services typeInferrer = context.getSemanticServices().getTypeInferrerServices(traceForConstructors, flowInformationProvider); + //context.getClassDescriptorResolver().computeFlowData(declaration, bodyExpression); + //JetFlowInformationProvider flowInformationProvider = context.getClassDescriptorResolver().computeFlowData(declaration, bodyExpression); + JetTypeInferrer.Services typeInferrer = context.getSemanticServices().getTypeInferrerServices(traceForConstructors); - typeInferrer.checkFunctionReturnType(functionInnerScope, declaration, JetStandardClasses.getUnitType()); + typeInferrer.checkFunctionReturnType(functionInnerScope, declaration, descriptor, JetStandardClasses.getUnitType()); } } @@ -516,8 +516,8 @@ public class BodyResolver { } private void resolvePropertyInitializer(JetProperty property, PropertyDescriptor propertyDescriptor, JetExpression initializer, JetScope scope) { - JetFlowInformationProvider flowInformationProvider = context.getClassDescriptorResolver().computeFlowData(property, initializer); // TODO : flow JET-15 - JetTypeInferrer.Services typeInferrer = context.getSemanticServices().getTypeInferrerServices(traceForConstructors, flowInformationProvider); + //JetFlowInformationProvider flowInformationProvider = context.getClassDescriptorResolver().computeFlowData(property, initializer); // TODO : flow JET-15 + JetTypeInferrer.Services typeInferrer = context.getSemanticServices().getTypeInferrerServices(traceForConstructors); JetType type = typeInferrer.getType(getPropertyDeclarationInnerScope(scope, propertyDescriptor), initializer, NO_EXPECTED_TYPE); JetType expectedType = propertyDescriptor.getInType(); @@ -552,13 +552,13 @@ public class BodyResolver { JetExpression bodyExpression = function.getBodyExpression(); if (bodyExpression != null) { - JetFlowInformationProvider flowInformationProvider = context.getClassDescriptorResolver().computeFlowData(function.asElement(), bodyExpression); - JetTypeInferrer.Services typeInferrer = context.getSemanticServices().getTypeInferrerServices(trace, flowInformationProvider); + //JetFlowInformationProvider flowInformationProvider = context.getClassDescriptorResolver().computeFlowData(function.asElement(), bodyExpression); + JetTypeInferrer.Services typeInferrer = context.getSemanticServices().getTypeInferrerServices(trace); typeInferrer.checkFunctionReturnType(declaringScope, function, functionDescriptor); } - JetTypeInferrer.Services typeInferrer = context.getSemanticServices().getTypeInferrerServices(trace, JetFlowInformationProvider.THROW_EXCEPTION); + JetTypeInferrer.Services typeInferrer = context.getSemanticServices().getTypeInferrerServices(trace); List valueParameters = function.getValueParameters(); for (int i = 0; i < valueParameters.size(); i++) { ValueParameterDescriptor valueParameterDescriptor = functionDescriptor.getValueParameters().get(i); diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/ClassDescriptorResolver.java b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/ClassDescriptorResolver.java index 49f3ab24bc3..de6079ebb31 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/ClassDescriptorResolver.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/ClassDescriptorResolver.java @@ -178,8 +178,8 @@ public class ClassDescriptorResolver { returnType = DeferredType.create(trace, new LazyValueWithDefault(ErrorUtils.createErrorType("Recursive dependency")) { @Override protected JetType compute() { - JetFlowInformationProvider flowInformationProvider = computeFlowData(function, bodyExpression); - return semanticServices.getTypeInferrerServices(trace, flowInformationProvider).inferFunctionReturnType(scope, function, functionDescriptor); + //JetFlowInformationProvider flowInformationProvider = computeFlowData(function, bodyExpression); + return semanticServices.getTypeInferrerServices(trace).inferFunctionReturnType(scope, function, functionDescriptor); } }); } @@ -537,8 +537,8 @@ public class ClassDescriptorResolver { LazyValue lazyValue = new LazyValueWithDefault(ErrorUtils.createErrorType("Recursive dependency")) { @Override protected JetType compute() { - JetFlowInformationProvider flowInformationProvider = computeFlowData(property, initializer); - return semanticServices.getTypeInferrerServices(trace, flowInformationProvider).safeGetType(scope, initializer, JetTypeInferrer.NO_EXPECTED_TYPE); + //JetFlowInformationProvider flowInformationProvider = computeFlowData(property, initializer); + return semanticServices.getTypeInferrerServices(trace).safeGetType(scope, initializer, JetTypeInferrer.NO_EXPECTED_TYPE); } }; if (allowDeferred) { diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/ControlFlowAnalyzer.java b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/ControlFlowAnalyzer.java new file mode 100644 index 00000000000..9e85eadfd73 --- /dev/null +++ b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/ControlFlowAnalyzer.java @@ -0,0 +1,133 @@ +package org.jetbrains.jet.lang.resolve; + +import com.google.common.collect.Lists; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.jet.lang.cfg.JetFlowInformationProvider; +import org.jetbrains.jet.lang.descriptors.ConstructorDescriptor; +import org.jetbrains.jet.lang.descriptors.FunctionDescriptor; +import org.jetbrains.jet.lang.descriptors.FunctionDescriptorImpl; +import org.jetbrains.jet.lang.psi.*; +import org.jetbrains.jet.lang.resolve.scopes.JetScope; +import org.jetbrains.jet.lang.types.JetStandardClasses; +import org.jetbrains.jet.lang.types.JetType; +import org.jetbrains.jet.lang.types.JetTypeInferrer; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import static org.jetbrains.jet.lang.diagnostics.Errors.*; +import static org.jetbrains.jet.lang.types.JetTypeInferrer.NO_EXPECTED_TYPE; + +/** + * @author svtk + */ +public class ControlFlowAnalyzer { + private TopDownAnalysisContext context; + private JetTypeInferrer.Services typeInferrer; + + public ControlFlowAnalyzer(TopDownAnalysisContext context) { + this.context = context; + typeInferrer = context.getSemanticServices().getTypeInferrerServices(context.getTrace()); + } + + public void process() { + for (Map.Entry entry : context.getFunctions().entrySet()) { + JetNamedFunction function = entry.getKey(); + FunctionDescriptorImpl functionDescriptor = entry.getValue(); + + final JetType expectedReturnType = !function.hasBlockBody() && !function.hasDeclaredReturnType() ? NO_EXPECTED_TYPE : functionDescriptor.getReturnType(); + checkFunction(function, functionDescriptor, expectedReturnType); + } + + for (Map.Entry entry : this.context.getConstructors().entrySet()) { + JetDeclaration declaration = entry.getKey(); + assert declaration instanceof JetConstructor; + JetConstructor constructor = (JetConstructor) declaration; + ConstructorDescriptor descriptor = entry.getValue(); + + checkFunction(constructor, descriptor, JetStandardClasses.getUnitType()); + } + + for (JetProperty property : context.getProperties().keySet()) { + checkProperty(property); + } + } + + private void checkFunction(JetDeclarationWithBody function, FunctionDescriptor functionDescriptor, final @NotNull JetType expectedReturnType) { + JetExpression bodyExpression = function.getBodyExpression(); + if (bodyExpression == null) return; + + JetFlowInformationProvider flowInformationProvider = context.getClassDescriptorResolver().computeFlowData((JetElement)function, bodyExpression); + + final boolean blockBody = function.hasBlockBody(); + List unreachableElements = Lists.newArrayList(); + flowInformationProvider.collectUnreachableExpressions(function.asElement(), unreachableElements); + + // This is needed in order to highlight only '1 < 2' and not '1', '<' and '2' as well + final Set rootUnreachableElements = JetPsiUtil.findRootExpressions(unreachableElements); + + for (JetElement element : rootUnreachableElements) { + context.getTrace().report(UNREACHABLE_CODE.on(element)); + } + + List returnedExpressions = Lists.newArrayList(); + flowInformationProvider.collectReturnExpressions(function.asElement(), returnedExpressions); + + boolean nothingReturned = returnedExpressions.isEmpty(); + + returnedExpressions.remove(function); // This will be the only "expression" if the body is empty + + final JetScope scope = this.context.getDeclaringScopes().get(function); + + if (expectedReturnType != NO_EXPECTED_TYPE && !JetStandardClasses.isUnit(expectedReturnType) && returnedExpressions.isEmpty() && !nothingReturned) { + context.getTrace().report(RETURN_TYPE_MISMATCH.on(bodyExpression, expectedReturnType)); + } + + for (JetExpression returnedExpression : returnedExpressions) { + returnedExpression.accept(new JetVisitorVoid() { + @Override + public void visitReturnExpression(JetReturnExpression expression) { + if (!blockBody) { + context.getTrace().report(RETURN_IN_FUNCTION_WITH_EXPRESSION_BODY.on(expression)); + } + } + + @Override + public void visitExpression(JetExpression expression) { + if (blockBody && expectedReturnType != NO_EXPECTED_TYPE && !JetStandardClasses.isUnit(expectedReturnType) && !rootUnreachableElements.contains(expression)) { + JetType type = typeInferrer.getType(scope, expression, expectedReturnType); + if (type == null || !JetStandardClasses.isNothing(type)) { + context.getTrace().report(NO_RETURN_IN_FUNCTION_WITH_BLOCK_BODY.on(expression)); + } + } + } + }); + } + markDominatedExpressionsAsUnreachable(bodyExpression, scope, expectedReturnType, flowInformationProvider); + } + + private void markDominatedExpressionsAsUnreachable(JetExpression expression, JetScope scope, JetType expectedReturnType, JetFlowInformationProvider flowInformationProvider) { + JetType type = typeInferrer.getType(scope, expression, expectedReturnType); + if (type == null || !JetStandardClasses.isNothing(type) || type.isNullable()) { + return; + } + + List dominated = new ArrayList(); + flowInformationProvider.collectDominatedExpressions(expression, dominated); + Set rootExpressions = JetPsiUtil.findRootExpressions(dominated); + for (JetElement rootExpression : rootExpressions) { + context.getTrace().report(UNREACHABLE_BECAUSE_OF_NOTHING.on(rootExpression, expression.getText())); + } + } + + + private void checkProperty(JetProperty property) { + JetExpression initializer = property.getInitializer(); + if (initializer == null) return; + JetScope scope = this.context.getDeclaringScopes().get(property); + JetFlowInformationProvider flowInformationProvider = context.getClassDescriptorResolver().computeFlowData(property, initializer); + markDominatedExpressionsAsUnreachable(initializer, scope, NO_EXPECTED_TYPE, flowInformationProvider); + } +} diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/TopDownAnalyzer.java b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/TopDownAnalyzer.java index 42762dd3c5b..085f89bb0f1 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/TopDownAnalyzer.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/TopDownAnalyzer.java @@ -69,6 +69,7 @@ public class TopDownAnalyzer { new OverrideResolver(context).process(); new BodyResolver(context).resolveBehaviorDeclarationBodies(); new DeclarationsChecker(context).process(); + new ControlFlowAnalyzer(context).process(); } public static void processStandardLibraryNamespace( diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/TypeHierarchyResolver.java b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/TypeHierarchyResolver.java index 1a5d519e22c..5958a403d7e 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/TypeHierarchyResolver.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/TypeHierarchyResolver.java @@ -218,7 +218,7 @@ public class TypeHierarchyResolver { if (importDirective.isAllUnder()) { JetExpression importedReference = importDirective.getImportedReference(); if (importedReference != null) { - JetTypeInferrer.Services typeInferrerServices = context.getSemanticServices().getTypeInferrerServices(context.getTrace(), JetFlowInformationProvider.THROW_EXCEPTION); + JetTypeInferrer.Services typeInferrerServices = context.getSemanticServices().getTypeInferrerServices(context.getTrace()); JetType type = typeInferrerServices.getTypeWithNamespaces(namespaceScope, importedReference); if (type != null) { namespaceScope.importScope(type.getMemberScope()); @@ -232,7 +232,7 @@ public class TypeHierarchyResolver { JetExpression importedReference = importDirective.getImportedReference(); if (importedReference instanceof JetDotQualifiedExpression) { JetDotQualifiedExpression reference = (JetDotQualifiedExpression) importedReference; - JetType type = context.getSemanticServices().getTypeInferrerServices(context.getTrace(), JetFlowInformationProvider.THROW_EXCEPTION).getTypeWithNamespaces(namespaceScope, reference.getReceiverExpression()); + JetType type = context.getSemanticServices().getTypeInferrerServices(context.getTrace()).getTypeWithNamespaces(namespaceScope, reference.getReceiverExpression()); JetExpression selectorExpression = reference.getSelectorExpression(); if (selectorExpression != null) { referenceExpression = (JetSimpleNameExpression) selectorExpression; diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/types/JetTypeInferrer.java b/compiler/frontend/src/org/jetbrains/jet/lang/types/JetTypeInferrer.java index 542f9aae729..cec4b09de34 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/types/JetTypeInferrer.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/types/JetTypeInferrer.java @@ -12,7 +12,6 @@ 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.descriptors.*; import org.jetbrains.jet.lang.descriptors.annotations.AnnotationDescriptor; import org.jetbrains.jet.lang.diagnostics.Diagnostic; @@ -160,14 +159,16 @@ public class JetTypeInferrer { .build(); private final JetSemanticServices semanticServices; - private final JetFlowInformationProvider flowInformationProvider; +// private final JetFlowInformationProvider flowInformationProvider; private final Map patternsToDataFlowInfo = Maps.newHashMap(); private final Map> patternsToBoundVariableLists = Maps.newHashMap(); + + private final LabelsResolver labelsResolver = new LabelsResolver(); - public JetTypeInferrer(@NotNull JetFlowInformationProvider flowInformationProvider, @NotNull JetSemanticServices semanticServices) { + public JetTypeInferrer(@NotNull JetSemanticServices semanticServices) { this.semanticServices = semanticServices; - this.flowInformationProvider = flowInformationProvider; +// this.flowInformationProvider = flowInformationProvider; } public Services getServices(@NotNull BindingTrace trace) { @@ -254,7 +255,7 @@ public class JetTypeInferrer { expectedReturnType = NO_EXPECTED_TYPE; } JetScope functionInnerScope = FunctionDescriptorUtil.getFunctionInnerScope(outerScope, functionDescriptor, trace); - checkFunctionReturnType(functionInnerScope, function, expectedReturnType, dataFlowInfo); + checkFunctionReturnType(functionInnerScope, function, functionDescriptor, expectedReturnType, dataFlowInfo); // Map typeMap = collectReturnedExpressionsWithTypes(outerScope, function, functionDescriptor, expectedReturnType); // if (typeMap.isEmpty()) { // return; // The function returns Nothing @@ -288,11 +289,11 @@ public class JetTypeInferrer { // } } - public void checkFunctionReturnType(JetScope functionInnerScope, JetDeclarationWithBody function, @NotNull final JetType expectedReturnType) { - checkFunctionReturnType(functionInnerScope, function, expectedReturnType, DataFlowInfo.getEmpty()); + public void checkFunctionReturnType(JetScope functionInnerScope, JetDeclarationWithBody function, FunctionDescriptor functionDescriptor, @NotNull final JetType expectedReturnType) { + checkFunctionReturnType(functionInnerScope, function, functionDescriptor, expectedReturnType, DataFlowInfo.getEmpty()); } - private void checkFunctionReturnType(JetScope functionInnerScope, JetDeclarationWithBody function, @NotNull final JetType expectedReturnType, @NotNull DataFlowInfo dataFlowInfo) { + private void checkFunctionReturnType(JetScope functionInnerScope, JetDeclarationWithBody function, FunctionDescriptor functionDescriptor, @NotNull final JetType expectedReturnType, @NotNull DataFlowInfo dataFlowInfo) { JetExpression bodyExpression = function.getBodyExpression(); assert bodyExpression != null; @@ -310,56 +311,62 @@ public class JetTypeInferrer { typeInferrerVisitor.getType(bodyExpression, context); } - List unreachableElements = Lists.newArrayList(); - flowInformationProvider.collectUnreachableExpressions(function.asElement(), unreachableElements); +// List unreachableElements = Lists.newArrayList(); +// flowInformationProvider.collectUnreachableExpressions(function.asElement(), unreachableElements); // This is needed in order to highlight only '1 < 2' and not '1', '<' and '2' as well - final Set rootUnreachableElements = JetPsiUtil.findRootExpressions(unreachableElements); +// final Set rootUnreachableElements = JetPsiUtil.findRootExpressions(unreachableElements); // TODO : (return 1) || (return 2) -- only || and right of it is unreachable // TODO : try {return 1} finally {return 2}. Currently 'return 1' is reported as unreachable, // though it'd better be reported more specifically - for (JetElement element : rootUnreachableElements) { -// trace.getErrorHandler().genericError(element.getNode(), "Unreachable code"); - trace.report(UNREACHABLE_CODE.on(element)); - } +// for (JetElement element : rootUnreachableElements) { +// //trace.report(UNREACHABLE_CODE.on(element)); +// } - List returnedExpressions = Lists.newArrayList(); - flowInformationProvider.collectReturnExpressions(function.asElement(), returnedExpressions); +// List returnedExpressions = Lists.newArrayList(); +// flowInformationProvider.collectReturnExpressions(function.asElement(), returnedExpressions); - boolean nothingReturned = returnedExpressions.isEmpty(); +// boolean nothingReturned = returnedExpressions.isEmpty(); - returnedExpressions.remove(function); // This will be the only "expression" if the body is empty + //returnedExpressions.remove(function); // This will be the only "expression" if the body is empty +// Map typeMap = collectReturnedExpressionsWithTypes(trace, functionInnerScope, function, functionDescriptor); +// Set returnedExpressions = typeMap.keySet(); +// +// +// if (expectedReturnType != NO_EXPECTED_TYPE && !JetStandardClasses.isUnit(expectedReturnType) && returnedExpressions.isEmpty()) { +// trace.report(RETURN_TYPE_MISMATCH.on(bodyExpression, expectedReturnType)); +// } - if (expectedReturnType != NO_EXPECTED_TYPE && !JetStandardClasses.isUnit(expectedReturnType) && returnedExpressions.isEmpty() && !nothingReturned) { -// trace.getErrorHandler().genericError(bodyExpression.getNode(), "This function must return a value of type " + expectedReturnType); - trace.report(RETURN_TYPE_MISMATCH.on(bodyExpression, expectedReturnType)); - } - - for (JetExpression returnedExpression : returnedExpressions) { - returnedExpression.accept(new JetVisitorVoid() { - @Override - public void visitReturnExpression(JetReturnExpression expression) { - if (!blockBody) { -// trace.getErrorHandler().genericError(expression.getNode(), "Returns are not allowed for functions with expression body. Use block body in '{...}'"); - trace.report(RETURN_IN_FUNCTION_WITH_EXPRESSION_BODY.on(expression)); - } - } - - @Override - public void visitExpression(JetExpression expression) { - if (blockBody && !JetStandardClasses.isUnit(expectedReturnType) && !rootUnreachableElements.contains(expression)) { - //TODO move to pseudocode - JetType type = typeInferrerVisitor.getType(expression, context.replaceExpectedType(NO_EXPECTED_TYPE)); - if (type == null || !JetStandardClasses.isNothing(type)) { -// trace.getErrorHandler().genericError(expression.getNode(), "A 'return' expression required in a function with a block body ('{...}')"); - trace.report(NO_RETURN_IN_FUNCTION_WITH_BLOCK_BODY.on(expression)); - } - } - } - }); - } +// if (expectedReturnType != NO_EXPECTED_TYPE && !JetStandardClasses.isUnit(expectedReturnType) && returnedExpressions.isEmpty() && !nothingReturned) { +//// trace.getErrorHandler().genericError(bodyExpression.getNode(), "This function must return a value of type " + expectedReturnType); +// trace.report(RETURN_TYPE_MISMATCH.on(bodyExpression, expectedReturnType)); +// } +// +// for (JetExpression returnedExpression : returnedExpressions) { +// returnedExpression.accept(new JetVisitorVoid() { +// @Override +// public void visitReturnExpression(JetReturnExpression expression) { +// if (!blockBody) { +//// trace.getErrorHandler().genericError(expression.getNode(), "Returns are not allowed for functions with expression body. Use block body in '{...}'"); +// trace.report(RETURN_IN_FUNCTION_WITH_EXPRESSION_BODY.on(expression)); +// } +// } +// +// @Override +// public void visitExpression(JetExpression expression) { +// if (blockBody && !JetStandardClasses.isUnit(expectedReturnType) && !rootUnreachableElements.contains(expression)) { +// //TODO move to pseudocode +// JetType type = typeInferrerVisitor.getType(expression, context.replaceExpectedType(NO_EXPECTED_TYPE)); +// if (type == null || !JetStandardClasses.isNothing(type)) { +//// trace.getErrorHandler().genericError(expression.getNode(), "A 'return' expression required in a function with a block body ('{...}')"); +// trace.report(NO_RETURN_IN_FUNCTION_WITH_BLOCK_BODY.on(expression)); +// } +// } +// } +// }); +// } } @Nullable @@ -376,14 +383,14 @@ public class JetTypeInferrer { @NotNull public JetType inferFunctionReturnType(@NotNull JetScope outerScope, JetDeclarationWithBody function, FunctionDescriptor functionDescriptor) { - Map typeMap = collectReturnedExpressionsWithTypes(trace, outerScope, function, functionDescriptor); + Map typeMap = collectReturnedExpressionsWithTypes(trace, outerScope, function, functionDescriptor); Collection types = typeMap.values(); return types.isEmpty() ? JetStandardClasses.getNothingType() : semanticServices.getTypeChecker().commonSupertype(types); } - private Map collectReturnedExpressionsWithTypes( + private Map collectReturnedExpressionsWithTypes( @NotNull BindingTrace trace, JetScope outerScope, JetDeclarationWithBody function, @@ -392,10 +399,20 @@ public class JetTypeInferrer { assert bodyExpression != null; JetScope functionInnerScope = FunctionDescriptorUtil.getFunctionInnerScope(outerScope, functionDescriptor, trace); typeInferrerVisitor.getType(bodyExpression, newContext(trace, functionInnerScope, DataFlowInfo.getEmpty(), NO_EXPECTED_TYPE, FORBIDDEN)); - Collection returnedExpressions = new ArrayList(); - Collection elementsReturningUnit = new ArrayList(); - flowInformationProvider.collectReturnedInformation(function.asElement(), returnedExpressions, elementsReturningUnit); - Map typeMap = new HashMap(); + //todo function literals + final Collection returnedExpressions = new ArrayList(); + if (function.hasBlockBody()) { + bodyExpression.accept(new JetTreeVisitorVoid() { + @Override + public void visitReturnExpression(JetReturnExpression expression) { + returnedExpressions.add(expression); + } + }); + } + else { + returnedExpressions.add(bodyExpression); + } + Map typeMap = new HashMap(); for (JetExpression returnedExpression : returnedExpressions) { JetType cachedType = trace.getBindingContext().get(BindingContext.EXPRESSION_TYPE, returnedExpression); trace.record(STATEMENT, returnedExpression, false); @@ -403,9 +420,6 @@ public class JetTypeInferrer { typeMap.put(returnedExpression, cachedType); } } - for (JetElement jetElement : elementsReturningUnit) { - typeMap.put(jetElement, JetStandardClasses.getUnitType()); - } return typeMap; } @@ -718,7 +732,6 @@ public class JetTypeInferrer { } private class TypeInferrerVisitor extends JetVisitor { - protected DataFlowInfo resultDataFlowInfo; @Nullable @@ -767,10 +780,11 @@ public class JetTypeInferrer { } if (result != null) { context.trace.record(BindingContext.EXPRESSION_TYPE, expression, result); - if (JetStandardClasses.isNothing(result) && !result.isNullable()) { - markDominatedExpressionsAsUnreachable(expression, context); - } +// if (JetStandardClasses.isNothing(result) && !result.isNullable()) { +// markDominatedExpressionsAsUnreachable(expression, context); +// } } +// } } catch (ReenteringLazyValueComputationException e) { // context.trace.getErrorHandler().genericError(expression.getNode(), "Type checking has run into a recursive problem"); // TODO : message @@ -798,16 +812,16 @@ public class JetTypeInferrer { //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - private void markDominatedExpressionsAsUnreachable(JetExpression expression, TypeInferenceContext context) { - List dominated = new ArrayList(); - flowInformationProvider.collectDominatedExpressions(expression, dominated); - Set rootExpressions = JetPsiUtil.findRootExpressions(dominated); - for (JetElement rootExpression : rootExpressions) { -// context.trace.getErrorHandler().genericError(rootExpression.getNode(), -// "This code is unreachable, because '" + expression.getText() + "' never terminates normally"); - context.trace.report(UNREACHABLE_BECAUSE_OF_NOTHING.on(rootExpression, expression.getText())); - } - } +// private void markDominatedExpressionsAsUnreachable(JetExpression expression, TypeInferenceContext context) { +// List dominated = new ArrayList(); +// flowInformationProvider.collectDominatedExpressions(expression, dominated); +// Set rootExpressions = JetPsiUtil.findRootExpressions(dominated); +// for (JetElement rootExpression : rootExpressions) { +//// context.trace.getErrorHandler().genericError(rootExpression.getNode(), +//// "This code is unreachable, because '" + expression.getText() + "' never terminates normally"); +// context.trace.report(UNREACHABLE_BECAUSE_OF_NOTHING.on(rootExpression, expression.getText())); +// } +// } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -1029,7 +1043,7 @@ public class JetTypeInferrer { JetTypeReference returnTypeRef = functionLiteral.getReturnTypeRef(); if (returnTypeRef != null) { returnType = context.typeResolver.resolveType(context.scope, returnTypeRef); - context.services.checkFunctionReturnType(functionInnerScope, expression, returnType, context.dataFlowInfo); + context.services.checkFunctionReturnType(functionInnerScope, expression, functionDescriptor, returnType, context.dataFlowInfo); } else { if (functionTypeExpected) { @@ -1139,6 +1153,12 @@ public class JetTypeInferrer { @Override public JetType visitReturnExpression(JetReturnExpression expression, TypeInferenceContext context) { + JetSimpleNameExpression labelElement = expression.getTargetLabel(); + if (labelElement != null) { + String labelName = expression.getLabelName(); + assert labelName != null; + labelsResolver.resolveLabel(labelName, labelElement, true, context); + } if (context.expectedReturnType == FORBIDDEN) { // context.trace.getErrorHandler().genericError(expression.getNode(), "'return' is not allowed here"); context.trace.report(RETURN_NOT_ALLOWED.on(expression)); @@ -1161,11 +1181,13 @@ public class JetTypeInferrer { @Override public JetType visitBreakExpression(JetBreakExpression expression, TypeInferenceContext context) { + labelsResolver.resolveCorrespondingLoopLabel(expression, context); return context.services.checkType(JetStandardClasses.getNothingType(), expression, context); } @Override public JetType visitContinueExpression(JetContinueExpression expression, TypeInferenceContext context) { + labelsResolver.resolveCorrespondingLoopLabel(expression, context); return context.services.checkType(JetStandardClasses.getNothingType(), expression, context); } @@ -1306,6 +1328,8 @@ public class JetTypeInferrer { context.trace.record(REFERENCE_TARGET, expression.getThisReference(), declarationDescriptor); } else if (size == 0) { + //todo (first we put to the context, then get from it) + labelsResolver.resolveLabel(labelName, targetLabel, false, context); // This uses the info written by the control flow processor PsiElement psiElement = BindingContextUtils.resolveToDeclarationPsiElement(context.trace.getBindingContext(), targetLabel); if (psiElement instanceof JetFunctionLiteralExpression) { @@ -1860,19 +1884,21 @@ public class JetTypeInferrer { DataFlowInfo conditionInfo = condition == null ? context.dataFlowInfo : extractDataFlowInfoFromCondition(condition, true, scopeToExtend, context); getTypeWithNewScopeAndDataFlowInfo(scopeToExtend, body, conditionInfo, context); } - if (!isBreakable(expression)) { + if (!containsBreak(expression)) { // resultScope = newWritableScopeImpl(); resultDataFlowInfo = extractDataFlowInfoFromCondition(condition, false, null, context); } return context.services.checkType(JetStandardClasses.getUnitType(), expression, contextWithExpectedType); } - private boolean isBreakable(JetLoopExpression expression) { + private boolean containsBreak(JetLoopExpression expression) { final boolean[] result = new boolean[1]; result[0] = false; expression.accept(new JetTreeVisitorVoid() { @Override public void visitBreakExpression(JetBreakExpression expression) { + //todo get exact loop for this break, compare to expression + //expression.getLabeledExpression() result[0] = true; } }); @@ -1903,7 +1929,7 @@ public class JetTypeInferrer { } JetExpression condition = expression.getCondition(); checkCondition(conditionScope, condition, context); - if (!isBreakable(expression)) { + if (!containsBreak(expression)) { // resultScope = newWritableScopeImpl(); resultDataFlowInfo = extractDataFlowInfoFromCondition(condition, false, null, context); } @@ -2289,8 +2315,13 @@ public class JetTypeInferrer { if (baseExpression == null) return null; JetSimpleNameExpression operationSign = expression.getOperationSign(); if (JetTokens.LABELS.contains(operationSign.getReferencedNameElementType())) { + String referencedName = operationSign.getReferencedName(); + referencedName = referencedName == null ? " " : referencedName; + labelsResolver.enterLabeledElement(referencedName.substring(1), baseExpression); // TODO : Some processing for the label? - return context.services.checkType(getType(baseExpression, context.replaceExpectedReturnType(context.expectedType)), expression, context); + JetType type = context.services.checkType(getType(baseExpression, context.replaceExpectedReturnType(context.expectedType)), expression, context); + labelsResolver.exitLabeledElement(baseExpression); + return type; } IElementType operationType = operationSign.getReferencedNameElementType(); String name = unaryOperationNames.get(operationType); @@ -2331,6 +2362,7 @@ public class JetTypeInferrer { else { result = returnType; } + return context.services.checkType(result, expression, context); } @@ -2830,4 +2862,84 @@ public class JetTypeInferrer { return null; } } + + private class LabelsResolver { + private final Map> labeledElements = new HashMap>(); + + private void enterLabeledElement(@NotNull String labelName, @NotNull JetExpression labeledExpression) { + JetExpression deparenthesized = JetPsiUtil.deparenthesize(labeledExpression); + if (deparenthesized != null) { + Stack stack = labeledElements.get(labelName); + if (stack == null) { + stack = new Stack(); + labeledElements.put(labelName, stack); + } + stack.push(deparenthesized); + } + } + + private void exitLabeledElement(JetExpression expression) { + JetExpression deparenthesized = JetPsiUtil.deparenthesize(expression); + // TODO : really suboptimal + for (Iterator>> mapIter = labeledElements.entrySet().iterator(); mapIter.hasNext(); ) { + Map.Entry> entry = mapIter.next(); + Stack stack = entry.getValue(); + for (Iterator stackIter = stack.iterator(); stackIter.hasNext(); ) { + JetElement recorded = stackIter.next(); + if (recorded == deparenthesized) { + stackIter.remove(); + } + } + if (stack.isEmpty()) { + mapIter.remove(); + } + } + } + + private void resolveLabel(@NotNull String labelName, @NotNull JetSimpleNameExpression labelExpression, boolean reportUnresolved, TypeInferenceContext context) { + Collection declarationsByLabel = context.scope.getDeclarationsByLabel(labelName); + int size = declarationsByLabel.size(); + + if (size == 1) { + DeclarationDescriptor declarationDescriptor = declarationsByLabel.iterator().next(); + JetElement element; + if (declarationDescriptor instanceof ClassDescriptor) { + ClassDescriptor classDescriptor = (ClassDescriptor) declarationDescriptor; + element = (JetElement) context.trace.get(BindingContext.DESCRIPTOR_TO_DECLARATION, classDescriptor); + } + else if (declarationDescriptor instanceof FunctionDescriptor) { + FunctionDescriptor functionDescriptor = (FunctionDescriptor) declarationDescriptor; + element = (JetElement) context.trace.get(BindingContext.DESCRIPTOR_TO_DECLARATION, functionDescriptor); + } + else { + throw new UnsupportedOperationException(); // TODO + } + context.trace.record(LABEL_TARGET, labelExpression, element); + return; + } + + Stack stack = labeledElements.get(labelName); + if (stack == null || stack.isEmpty()) { + if (reportUnresolved) { + context.trace.report(UNRESOLVED_REFERENCE.on(labelExpression)); + } + return; + } + else if (stack.size() > 1) { + context.trace.report(LABEL_NAME_CLASH.on(labelExpression)); + } + + JetElement result = stack.peek(); + context.trace.record(BindingContext.LABEL_TARGET, labelExpression, result); + } + + private void resolveCorrespondingLoopLabel(JetLabelQualifiedExpression expression, TypeInferenceContext context) { + String labelName = expression.getLabelName(); + if (labelName != null) { + JetSimpleNameExpression targetLabel = expression.getTargetLabel(); + assert targetLabel != null; + resolveLabel(labelName, targetLabel, true, context); + } + } + } } diff --git a/idea/tests/org/jetbrains/jet/resolve/JetResolveTest.java b/idea/tests/org/jetbrains/jet/resolve/JetResolveTest.java index 058b742d5e6..b7786c8c126 100644 --- a/idea/tests/org/jetbrains/jet/resolve/JetResolveTest.java +++ b/idea/tests/org/jetbrains/jet/resolve/JetResolveTest.java @@ -106,7 +106,7 @@ public class JetResolveTest extends ExtensibleResolveTestCase { @NotNull private FunctionDescriptor standardFunction(ClassDescriptor classDescriptor, List typeArguments, String name, JetType... parameterType) { List parameterTypeList = Arrays.asList(parameterType); - JetTypeInferrer.Services typeInferrerServices = JetSemanticServices.createSemanticServices(getProject()).getTypeInferrerServices(new BindingTraceContext(), JetFlowInformationProvider.NONE); + JetTypeInferrer.Services typeInferrerServices = JetSemanticServices.createSemanticServices(getProject()).getTypeInferrerServices(new BindingTraceContext()); OverloadResolutionResults functions = typeInferrerServices.getCallResolver().resolveExactSignature( classDescriptor.getMemberScope(typeArguments), ReceiverDescriptor.NO_RECEIVER, name, parameterTypeList); diff --git a/idea/tests/org/jetbrains/jet/types/JetTypeCheckerTest.java b/idea/tests/org/jetbrains/jet/types/JetTypeCheckerTest.java index 1903d2b13f1..dd787a1cd9b 100644 --- a/idea/tests/org/jetbrains/jet/types/JetTypeCheckerTest.java +++ b/idea/tests/org/jetbrains/jet/types/JetTypeCheckerTest.java @@ -493,14 +493,14 @@ public class JetTypeCheckerTest extends LightDaemonAnalyzerTestCase { private void assertType(String expression, JetType expectedType) { Project project = getProject(); JetExpression jetExpression = JetPsiFactory.createExpression(project, expression); - JetType type = semanticServices.getTypeInferrerServices(JetTestUtils.DUMMY_TRACE, JetFlowInformationProvider.NONE).getType(scopeWithImports, jetExpression, JetTypeInferrer.NO_EXPECTED_TYPE); + JetType type = semanticServices.getTypeInferrerServices(JetTestUtils.DUMMY_TRACE).getType(scopeWithImports, jetExpression, JetTypeInferrer.NO_EXPECTED_TYPE); assertTrue(type + " != " + expectedType, type.equals(expectedType)); } private void assertErrorType(String expression) { Project project = getProject(); JetExpression jetExpression = JetPsiFactory.createExpression(project, expression); - JetType type = semanticServices.getTypeInferrerServices(JetTestUtils.DUMMY_TRACE, JetFlowInformationProvider.NONE).safeGetType(scopeWithImports, jetExpression, JetTypeInferrer.NO_EXPECTED_TYPE); + JetType type = semanticServices.getTypeInferrerServices(JetTestUtils.DUMMY_TRACE).safeGetType(scopeWithImports, jetExpression, JetTypeInferrer.NO_EXPECTED_TYPE); assertTrue("Error type expected but " + type + " returned", ErrorUtils.isErrorType(type)); } @@ -523,7 +523,7 @@ public class JetTypeCheckerTest extends LightDaemonAnalyzerTestCase { private void assertType(JetScope scope, String expression, String expectedTypeStr) { Project project = getProject(); JetExpression jetExpression = JetPsiFactory.createExpression(project, expression); - JetType type = semanticServices.getTypeInferrerServices(JetTestUtils.DUMMY_TRACE, JetFlowInformationProvider.NONE).getType(addImports(scope), jetExpression, JetTypeInferrer.NO_EXPECTED_TYPE); + JetType type = semanticServices.getTypeInferrerServices(JetTestUtils.DUMMY_TRACE).getType(addImports(scope), jetExpression, JetTypeInferrer.NO_EXPECTED_TYPE); JetType expectedType = expectedTypeStr == null ? null : makeType(expectedTypeStr); assertEquals(expectedType, type); }