diff --git a/idea/src/org/jetbrains/jet/lang/JetSemanticServices.java b/idea/src/org/jetbrains/jet/lang/JetSemanticServices.java index 64bad278863..626c611bdf6 100644 --- a/idea/src/org/jetbrains/jet/lang/JetSemanticServices.java +++ b/idea/src/org/jetbrains/jet/lang/JetSemanticServices.java @@ -47,8 +47,8 @@ public class JetSemanticServices { } @NotNull - public JetTypeInferrer getTypeInferrer(@NotNull BindingTrace trace, @NotNull JetFlowInformationProvider flowInformationProvider) { - return new JetTypeInferrer(trace, flowInformationProvider, this); + public JetTypeInferrer.Services getTypeInferrerServices(@NotNull BindingTrace trace, @NotNull JetFlowInformationProvider flowInformationProvider) { + return new JetTypeInferrer(flowInformationProvider, this).getServices(trace); } // @NotNull diff --git a/idea/src/org/jetbrains/jet/lang/resolve/ClassDescriptorResolver.java b/idea/src/org/jetbrains/jet/lang/resolve/ClassDescriptorResolver.java index 41e0369bc09..aec5ee12569 100644 --- a/idea/src/org/jetbrains/jet/lang/resolve/ClassDescriptorResolver.java +++ b/idea/src/org/jetbrains/jet/lang/resolve/ClassDescriptorResolver.java @@ -197,7 +197,7 @@ public class ClassDescriptorResolver { @Override protected JetType compute() { JetFlowInformationProvider flowInformationProvider = computeFlowData(function, bodyExpression); - return semanticServices.getTypeInferrer(trace, flowInformationProvider).inferFunctionReturnType(scope, function, functionDescriptor); + return semanticServices.getTypeInferrerServices(trace, flowInformationProvider).inferFunctionReturnType(scope, function, functionDescriptor); } }); } @@ -525,7 +525,7 @@ public class ClassDescriptorResolver { LazyValue lazyValue = new LazyValue() { @Override protected JetType compute() { - return semanticServices.getTypeInferrer(trace, JetFlowInformationProvider.THROW_EXCEPTION).safeGetType(scope, initializer, false, JetTypeInferrer.NO_EXPECTED_TYPE); + return semanticServices.getTypeInferrerServices(trace, JetFlowInformationProvider.THROW_EXCEPTION).safeGetType(scope, initializer, false, JetTypeInferrer.NO_EXPECTED_TYPE); } }; if (allowDeferred) { diff --git a/idea/src/org/jetbrains/jet/lang/resolve/TopDownAnalyzer.java b/idea/src/org/jetbrains/jet/lang/resolve/TopDownAnalyzer.java index 8d179ca681c..304a14ac748 100644 --- a/idea/src/org/jetbrains/jet/lang/resolve/TopDownAnalyzer.java +++ b/idea/src/org/jetbrains/jet/lang/resolve/TopDownAnalyzer.java @@ -280,7 +280,7 @@ public class TopDownAnalyzer { if (importDirective.isAllUnder()) { JetExpression importedReference = importDirective.getImportedReference(); if (importedReference != null) { - JetType type = semanticServices.getTypeInferrer(trace, JetFlowInformationProvider.THROW_EXCEPTION).getTypeWithNamespaces(namespaceScope, importedReference, false); + JetType type = semanticServices.getTypeInferrerServices(trace, JetFlowInformationProvider.THROW_EXCEPTION).getTypeWithNamespaces(namespaceScope, importedReference, false); if (type != null) { namespaceScope.importScope(type.getMemberScope()); } @@ -293,7 +293,7 @@ public class TopDownAnalyzer { JetExpression importedReference = importDirective.getImportedReference(); if (importedReference instanceof JetDotQualifiedExpression) { JetDotQualifiedExpression reference = (JetDotQualifiedExpression) importedReference; - JetType type = semanticServices.getTypeInferrer(trace, JetFlowInformationProvider.THROW_EXCEPTION).getTypeWithNamespaces(namespaceScope, reference.getReceiverExpression(), false); + JetType type = semanticServices.getTypeInferrerServices(trace, JetFlowInformationProvider.THROW_EXCEPTION).getTypeWithNamespaces(namespaceScope, reference.getReceiverExpression(), false); JetExpression selectorExpression = reference.getSelectorExpression(); if (selectorExpression != null) { referenceExpression = (JetSimpleNameExpression) selectorExpression; @@ -563,7 +563,7 @@ public class TopDownAnalyzer { final JetScope scopeForConstructor = primaryConstructor == null ? null : getInnerScopeForConstructor(primaryConstructor, descriptor.getScopeForMemberResolution(), true); - final JetTypeInferrer typeInferrer = semanticServices.getTypeInferrer(traceForConstructors, JetFlowInformationProvider.NONE); // TODO : flow + final JetTypeInferrer.Services typeInferrer = semanticServices.getTypeInferrerServices(traceForConstructors, JetFlowInformationProvider.NONE); // TODO : flow for (JetDelegationSpecifier delegationSpecifier : jetClass.getDelegationSpecifiers()) { delegationSpecifier.accept(new JetVisitor() { @@ -642,7 +642,7 @@ public class TopDownAnalyzer { ConstructorDescriptor primaryConstructor = classDescriptor.getUnsubstitutedPrimaryConstructor(); assert primaryConstructor != null; final JetScope scopeForConstructor = getInnerScopeForConstructor(primaryConstructor, classDescriptor.getScopeForMemberResolution(), true); - JetTypeInferrer typeInferrer = semanticServices.getTypeInferrer(traceForConstructors, JetFlowInformationProvider.NONE); // TODO : flow + JetTypeInferrer.Services typeInferrer = semanticServices.getTypeInferrerServices(traceForConstructors, JetFlowInformationProvider.NONE); // TODO : flow for (JetClassInitializer anonymousInitializer : anonymousInitializers) { typeInferrer.getType(scopeForConstructor, anonymousInitializer.getBody(), true, JetTypeInferrer.NO_EXPECTED_TYPE); } @@ -668,7 +668,7 @@ public class TopDownAnalyzer { private void resolveSecondaryConstructorBody(JetConstructor declaration, final ConstructorDescriptor descriptor, final JetScope declaringScope) { final JetScope functionInnerScope = getInnerScopeForConstructor(descriptor, declaringScope, false); - final JetTypeInferrer typeInferrerForInitializers = semanticServices.getTypeInferrer(traceForConstructors, JetFlowInformationProvider.NONE); + final JetTypeInferrer.Services typeInferrerForInitializers = semanticServices.getTypeInferrerServices(traceForConstructors, JetFlowInformationProvider.NONE); JetClass containingClass = PsiTreeUtil.getParentOfType(declaration, JetClass.class); assert containingClass != null : "This must be guaranteed by the parser"; @@ -728,7 +728,7 @@ public class TopDownAnalyzer { if (bodyExpression != null) { classDescriptorResolver.computeFlowData(declaration, bodyExpression); JetFlowInformationProvider flowInformationProvider = classDescriptorResolver.computeFlowData(declaration, bodyExpression); - JetTypeInferrer typeInferrer = semanticServices.getTypeInferrer(traceForConstructors, flowInformationProvider); + JetTypeInferrer.Services typeInferrer = semanticServices.getTypeInferrerServices(traceForConstructors, flowInformationProvider); typeInferrer.checkFunctionReturnType(functionInnerScope, declaration, descriptor, JetStandardClasses.getUnitType()); } @@ -859,7 +859,7 @@ public class TopDownAnalyzer { private void resolvePropertyInitializer(JetProperty property, PropertyDescriptor propertyDescriptor, JetExpression initializer, JetScope scope) { JetFlowInformationProvider flowInformationProvider = classDescriptorResolver.computeFlowData(property, initializer); // TODO : flow JET-15 - JetTypeInferrer typeInferrer = semanticServices.getTypeInferrer(traceForConstructors, flowInformationProvider); + JetTypeInferrer.Services typeInferrer = semanticServices.getTypeInferrerServices(traceForConstructors, flowInformationProvider); JetType type = typeInferrer.getType(getPropertyDeclarationInnerScope(scope, propertyDescriptor), initializer, false, JetTypeInferrer.NO_EXPECTED_TYPE); JetType expectedType; @@ -902,7 +902,7 @@ public class TopDownAnalyzer { if (bodyExpression != null) { JetFlowInformationProvider flowInformationProvider = classDescriptorResolver.computeFlowData(function.asElement(), bodyExpression); - JetTypeInferrer typeInferrer = semanticServices.getTypeInferrer(trace, flowInformationProvider); + JetTypeInferrer.Services typeInferrer = semanticServices.getTypeInferrerServices(trace, flowInformationProvider); typeInferrer.checkFunctionReturnType(declaringScope, function, functionDescriptor); } diff --git a/idea/src/org/jetbrains/jet/lang/types/JetTypeInferrer.java b/idea/src/org/jetbrains/jet/lang/types/JetTypeInferrer.java index 83dffca2bef..e34db0b9037 100644 --- a/idea/src/org/jetbrains/jet/lang/types/JetTypeInferrer.java +++ b/idea/src/org/jetbrains/jet/lang/types/JetTypeInferrer.java @@ -122,602 +122,607 @@ public class JetTypeInferrer { .put(JetTokens.MINUSEQ, JetTokens.MINUS) .build(); - private final BindingTrace trace; private final JetSemanticServices semanticServices; - private final TypeResolver typeResolver; - private final ClassDescriptorResolver classDescriptorResolver; private final JetFlowInformationProvider flowInformationProvider; private final Map patternsToDataFlowInfo = Maps.newHashMap(); private final Map> patternsToBoundVariableLists = Maps.newHashMap(); - public JetTypeInferrer(@NotNull BindingTrace trace, @NotNull JetFlowInformationProvider flowInformationProvider, @NotNull JetSemanticServices semanticServices) { - this.trace = trace; //new CachedBindingTrace(trace); + public JetTypeInferrer(@NotNull JetFlowInformationProvider flowInformationProvider, @NotNull JetSemanticServices semanticServices) { this.semanticServices = semanticServices; - this.typeResolver = new TypeResolver(semanticServices, trace, true); - this.classDescriptorResolver = semanticServices.getClassDescriptorResolver(trace); this.flowInformationProvider = flowInformationProvider; } - @NotNull - public JetType safeGetType(@NotNull final JetScope scope, @NotNull JetExpression expression, boolean preferBlock, @NotNull JetType expectedType) { - JetType type = getType(scope, expression, preferBlock, expectedType); - if (type != null) { - return type; - } - return ErrorUtils.createErrorType("Type for " + expression.getText()); + public Services getServices(@NotNull BindingTrace trace) { + return new Services(trace); } - @Nullable - public JetType getType(@NotNull final JetScope scope, @NotNull JetExpression expression, boolean preferBlock, @NotNull JetType expectedType) { - return new TypeInferrerVisitor(scope, preferBlock, DataFlowInfo.getEmpty(), expectedType, FORBIDDEN).getType(expression); - } + public class Services { + private final BindingTrace trace; + private final ClassDescriptorResolver classDescriptorResolver; + private final TypeResolver typeResolver; - public JetType getTypeWithNamespaces(@NotNull final JetScope scope, @NotNull JetExpression expression, boolean preferBlock) { - return new TypeInferrerVisitorWithNamespaces(scope, preferBlock, DataFlowInfo.getEmpty(), NO_EXPECTED_TYPE, null).getType(expression); - } - - @Nullable - private FunctionDescriptor lookupFunction( - @NotNull JetScope scope, - @NotNull JetReferenceExpression reference, - @NotNull String name, - @NotNull JetType receiverType, - @NotNull List argumentTypes, - boolean reportUnresolved) { - OverloadDomain overloadDomain = semanticServices.getOverloadResolver().getOverloadDomain(receiverType, scope, name); - // No generics. Guaranteed - overloadDomain = wrapForTracing(overloadDomain, reference, null, reportUnresolved); - OverloadResolutionResult resolutionResult = overloadDomain.getFunctionDescriptorForPositionedArguments(Collections.emptyList(), argumentTypes); - return resolutionResult.isSuccess() ? resolutionResult.getFunctionDescriptor() : null; - } - - @NotNull - private OverloadResolutionResult resolveNoParametersFunction(@NotNull JetType receiverType, @NotNull JetScope scope, @NotNull String name) { - OverloadDomain overloadDomain = semanticServices.getOverloadResolver().getOverloadDomain(receiverType, scope, name); - // No generics. Guaranteed - return overloadDomain.getFunctionDescriptorForPositionedArguments(Collections.emptyList(), Collections.emptyList()); - } - - private OverloadDomain getOverloadDomain( - @Nullable final JetType receiverType, - @NotNull final JetScope scope, - @NotNull JetExpression calleeExpression, - @Nullable PsiElement argumentList - ) { - final OverloadDomain[] result = new OverloadDomain[1]; - final JetSimpleNameExpression[] reference = new JetSimpleNameExpression[1]; - calleeExpression.accept(new JetVisitor() { - - @Override - public void visitHashQualifiedExpression(JetHashQualifiedExpression expression) { - // a#b -- create a domain for all overloads of b in a - throw new UnsupportedOperationException(); // TODO - } - - @Override - public void visitPredicateExpression(JetPredicateExpression expression) { - // overload lookup for checking, but the type is receiver's type + nullable - throw new UnsupportedOperationException(); // TODO - } - - @Override - public void visitQualifiedExpression(JetQualifiedExpression expression) { - trace.getErrorHandler().genericError(expression.getNode(), "Unsupported [JetTypeInferrer]"); - - // . or ?. -// JetType receiverType = getType(scope, expression.getReceiverExpression(), false); -// checkNullSafety(receiverType, expression.getOperationTokenNode()); -// -// JetExpression selectorExpression = expression.getSelectorExpression(); -// if (selectorExpression instanceof JetSimpleNameExpression) { -// JetSimpleNameExpression referenceExpression = (JetSimpleNameExpression) selectorExpression; -// String referencedName = referenceExpression.getReferencedName(); -// -// if (receiverType != null && referencedName != null) { -// // No generics. Guaranteed -// result[0] = semanticServices.getOverloadResolver().getOverloadDomain(receiverType, scope, referencedName); -// reference[0] = referenceExpression; -// } -// } else { -// throw new UnsupportedOperationException(); // TODO -// } - } - - @Override - public void visitSimpleNameExpression(JetSimpleNameExpression expression) { - // a -- create a hierarchical lookup domain for this.a - String referencedName = expression.getReferencedName(); - if (referencedName != null) { - // No generics. Guaranteed - result[0] = semanticServices.getOverloadResolver().getOverloadDomain(receiverType, scope, referencedName); - reference[0] = expression; - } - } - - @Override - public void visitExpression(JetExpression expression) { - // create a dummy domain for the type of e - throw new UnsupportedOperationException(expression.getText()); // TODO - } - - @Override - public void visitJetElement(JetElement element) { - trace.getErrorHandler().genericError(element.getNode(), "Unsupported in call element"); // TODO : Message - } - }); - return wrapForTracing(result[0], reference[0], argumentList, true); - } - - private void checkNullSafety(@Nullable JetType receiverType, @NotNull ASTNode operationTokenNode, @Nullable FunctionDescriptor callee) { - if (receiverType != null && callee != null) { - boolean namespaceType = receiverType instanceof NamespaceType; - JetType calleeReceiverType = callee.getReceiverType(); - boolean nullableReceiver = !namespaceType && receiverType.isNullable(); - boolean calleeForbidsNullableReceiver = calleeReceiverType == null || !calleeReceiverType.isNullable(); - - IElementType operationSign = operationTokenNode.getElementType(); - if (nullableReceiver && calleeForbidsNullableReceiver && operationSign == JetTokens.DOT) { - trace.getErrorHandler().genericError(operationTokenNode, "Only safe calls (?.) are allowed on a nullable receiver of type " + receiverType); - } - else if ((!nullableReceiver || !calleeForbidsNullableReceiver) && operationSign == JetTokens.SAFE_ACCESS) { - if (namespaceType) { - trace.getErrorHandler().genericError(operationTokenNode, "Safe calls are not allowed on namespaces"); - } - else { - trace.getErrorHandler().genericWarning(operationTokenNode, "Unnecessary safe call on a non-null receiver of type " + receiverType); - } - } - } - } - - private OverloadDomain wrapForTracing( - @NotNull final OverloadDomain overloadDomain, - @NotNull final JetReferenceExpression referenceExpression, - @Nullable final PsiElement argumentList, - final boolean reportErrors) { - return new OverloadDomain() { - @NotNull - @Override - public OverloadResolutionResult getFunctionDescriptorForNamedArguments(@NotNull List typeArguments, @NotNull Map valueArgumentTypes, @Nullable JetType functionLiteralArgumentType) { - OverloadResolutionResult resolutionResult = overloadDomain.getFunctionDescriptorForNamedArguments(typeArguments, valueArgumentTypes, functionLiteralArgumentType); - report(resolutionResult); - return resolutionResult; - } - - @NotNull - @Override - public OverloadResolutionResult getFunctionDescriptorForPositionedArguments(@NotNull List typeArguments, @NotNull List positionedValueArgumentTypes) { - OverloadResolutionResult resolutionResult = overloadDomain.getFunctionDescriptorForPositionedArguments(typeArguments, positionedValueArgumentTypes); - report(resolutionResult); - return resolutionResult; - } - - private void report(OverloadResolutionResult resolutionResult) { - if (resolutionResult.isSuccess() || resolutionResult.singleFunction()) { - trace.recordReferenceResolution(referenceExpression, resolutionResult.getFunctionDescriptor()); - } - if (reportErrors) { - switch (resolutionResult.getResultCode()) { - case NAME_NOT_FOUND: - trace.getErrorHandler().unresolvedReference(referenceExpression); - break; - case SINGLE_FUNCTION_ARGUMENT_MISMATCH: - if (argumentList != null) { - // TODO : More helpful message. NOTE: there's a separate handling for this for constructors - trace.getErrorHandler().genericError(argumentList.getNode(), "Arguments do not match " + DescriptorRenderer.TEXT.render(resolutionResult.getFunctionDescriptor())); - } - else { - trace.getErrorHandler().unresolvedReference(referenceExpression); - } - break; - case AMBIGUITY: - if (argumentList != null) { - // TODO : More helpful message. NOTE: there's a separate handling for this for constructors - trace.getErrorHandler().genericError(argumentList.getNode(), "Overload ambiguity [TODO : more helpful message]"); - } - else { - trace.getErrorHandler().unresolvedReference(referenceExpression); - } - break; - default: - // Not a success - } - } - } - - @Override - public boolean isEmpty() { - return overloadDomain.isEmpty(); - } - }; - } - - public void checkFunctionReturnType(@NotNull JetScope outerScope, @NotNull JetDeclarationWithBody function, @NotNull FunctionDescriptor functionDescriptor) { - final JetType expectedReturnType = functionDescriptor.getUnsubstitutedReturnType(); - JetScope functionInnerScope = FunctionDescriptorUtil.getFunctionInnerScope(outerScope, functionDescriptor, trace); - checkFunctionReturnType(functionInnerScope, function, functionDescriptor, expectedReturnType); -// Map typeMap = collectReturnedExpressionsWithTypes(outerScope, function, functionDescriptor, expectedReturnType); -// if (typeMap.isEmpty()) { -// return; // The function returns Nothing -// } -// for (Map.Entry entry : typeMap.entrySet()) { -// JetType actualType = entry.getValue(); -// JetElement element = entry.getKey(); -// JetTypeChecker typeChecker = semanticServices.getTypeChecker(); -// if (!typeChecker.isSubtypeOf(actualType, expectedReturnType)) { -// if (typeChecker.isConvertibleBySpecialConversion(actualType, expectedReturnType)) { -// if (expectedReturnType.getConstructor().equals(JetStandardClasses.getUnitType().getConstructor()) -// && element.getParent() instanceof JetReturnExpression) { -// trace.getErrorHandler().genericError(element.getNode(), "This function must return a value of type Unit"); -// } -// } -// else { -// if (element == function) { -// JetExpression bodyExpression = function.getBodyExpression(); -// assert bodyExpression != null; -// trace.getErrorHandler().genericError(bodyExpression.getNode(), "This function must return a value of type " + expectedReturnType); -// } -// else if (element instanceof JetExpression) { -// JetExpression expression = (JetExpression) element; -// trace.getErrorHandler().typeMismatch(expression, expectedReturnType, actualType); -// } -// else { -// trace.getErrorHandler().genericError(element.getNode(), "This function must return a value of type " + expectedReturnType); -// } -// } -// } -// } - } - - public void checkFunctionReturnType(JetScope functionInnerScope, JetDeclarationWithBody function, FunctionDescriptor functionDescriptor, @Nullable final JetType expectedReturnType) { - JetExpression bodyExpression = function.getBodyExpression(); - assert bodyExpression != null; - new TypeInferrerVisitor(functionInnerScope, function.hasBlockBody(), DataFlowInfo.getEmpty(), NO_EXPECTED_TYPE, expectedReturnType).getType(bodyExpression); - - 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); - - // 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"); + private Services(BindingTrace trace) { + this.trace = trace; + this.typeResolver = new TypeResolver(semanticServices, trace, true); + this.classDescriptorResolver = semanticServices.getClassDescriptorResolver(trace); } - final boolean blockBody = function.hasBlockBody(); - 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 - - if (!JetStandardClasses.isUnit(expectedReturnType) && returnedExpressions.isEmpty() && !nothingReturned) { - trace.getErrorHandler().genericError(bodyExpression.getNode(), "This function must return a value of type " + expectedReturnType); + @NotNull + public JetType safeGetType(@NotNull final JetScope scope, @NotNull JetExpression expression, boolean preferBlock, @NotNull JetType expectedType) { + JetType type = getType(scope, expression, preferBlock, expectedType); + if (type != null) { + return type; + } + return ErrorUtils.createErrorType("Type for " + expression.getText()); } - for (JetExpression returnedExpression : returnedExpressions) { - returnedExpression.accept(new JetVisitor() { + @Nullable + public JetType getType(@NotNull final JetScope scope, @NotNull JetExpression expression, boolean preferBlock, @NotNull JetType expectedType) { + return new TypeInferrerVisitor(new TypeInferenceContext(trace, scope, preferBlock, DataFlowInfo.getEmpty(), expectedType, FORBIDDEN)).getType(expression); + } + + public JetType getTypeWithNamespaces(@NotNull final JetScope scope, @NotNull JetExpression expression, boolean preferBlock) { + return new TypeInferrerVisitorWithNamespaces(new TypeInferenceContext(trace, scope, preferBlock, DataFlowInfo.getEmpty(), NO_EXPECTED_TYPE, null)).getType(expression); + } + + @Nullable + private FunctionDescriptor lookupFunction( + @NotNull JetScope scope, + @NotNull JetReferenceExpression reference, + @NotNull String name, + @NotNull JetType receiverType, + @NotNull List argumentTypes, + boolean reportUnresolved) { + OverloadDomain overloadDomain = semanticServices.getOverloadResolver().getOverloadDomain(receiverType, scope, name); + // No generics. Guaranteed + overloadDomain = wrapForTracing(overloadDomain, reference, null, reportUnresolved); + OverloadResolutionResult resolutionResult = overloadDomain.getFunctionDescriptorForPositionedArguments(Collections.emptyList(), argumentTypes); + return resolutionResult.isSuccess() ? resolutionResult.getFunctionDescriptor() : null; + } + + @NotNull + private OverloadResolutionResult resolveNoParametersFunction(@NotNull JetType receiverType, @NotNull JetScope scope, @NotNull String name) { + OverloadDomain overloadDomain = semanticServices.getOverloadResolver().getOverloadDomain(receiverType, scope, name); + // No generics. Guaranteed + return overloadDomain.getFunctionDescriptorForPositionedArguments(Collections.emptyList(), Collections.emptyList()); + } + + private OverloadDomain getOverloadDomain( + @Nullable final JetType receiverType, + @NotNull final JetScope scope, + @NotNull JetExpression calleeExpression, + @Nullable PsiElement argumentList + ) { + final OverloadDomain[] result = new OverloadDomain[1]; + final JetSimpleNameExpression[] reference = new JetSimpleNameExpression[1]; + calleeExpression.accept(new JetVisitor() { + @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 '{...}'"); + public void visitHashQualifiedExpression(JetHashQualifiedExpression expression) { + // a#b -- create a domain for all overloads of b in a + throw new UnsupportedOperationException(); // TODO + } + + @Override + public void visitPredicateExpression(JetPredicateExpression expression) { + // overload lookup for checking, but the type is receiver's type + nullable + throw new UnsupportedOperationException(); // TODO + } + + @Override + public void visitQualifiedExpression(JetQualifiedExpression expression) { + trace.getErrorHandler().genericError(expression.getNode(), "Unsupported [JetTypeInferrer]"); + + // . or ?. + // JetType receiverType = getType(scope, expression.getReceiverExpression(), false); + // checkNullSafety(receiverType, expression.getOperationTokenNode()); + // + // JetExpression selectorExpression = expression.getSelectorExpression(); + // if (selectorExpression instanceof JetSimpleNameExpression) { + // JetSimpleNameExpression referenceExpression = (JetSimpleNameExpression) selectorExpression; + // String referencedName = referenceExpression.getReferencedName(); + // + // if (receiverType != null && referencedName != null) { + // // No generics. Guaranteed + // result[0] = semanticServices.getOverloadResolver().getOverloadDomain(receiverType, scope, referencedName); + // reference[0] = referenceExpression; + // } + // } else { + // throw new UnsupportedOperationException(); // TODO + // } + } + + @Override + public void visitSimpleNameExpression(JetSimpleNameExpression expression) { + // a -- create a hierarchical lookup domain for this.a + String referencedName = expression.getReferencedName(); + if (referencedName != null) { + // No generics. Guaranteed + result[0] = semanticServices.getOverloadResolver().getOverloadDomain(receiverType, scope, referencedName); + reference[0] = expression; } } @Override public void visitExpression(JetExpression expression) { - if (blockBody && !JetStandardClasses.isUnit(expectedReturnType) && !rootUnreachableElements.contains(expression)) { - trace.getErrorHandler().genericError(expression.getNode(), "A 'return' expression required in a function with a block body ('{...}')"); - } + // create a dummy domain for the type of e + throw new UnsupportedOperationException(expression.getText()); // TODO + } + + @Override + public void visitJetElement(JetElement element) { + trace.getErrorHandler().genericError(element.getNode(), "Unsupported in call element"); // TODO : Message } }); - } - } - - @NotNull - public JetType inferFunctionReturnType(@NotNull JetScope outerScope, JetDeclarationWithBody function, FunctionDescriptor functionDescriptor) { - Map typeMap = collectReturnedExpressionsWithTypes(outerScope, function, functionDescriptor); - Collection types = typeMap.values(); - return types.isEmpty() - ? JetStandardClasses.getNothingType() - : semanticServices.getTypeChecker().commonSupertype(types); - } - - private Map collectReturnedExpressionsWithTypes( - JetScope outerScope, - JetDeclarationWithBody function, - FunctionDescriptor functionDescriptor) { - JetExpression bodyExpression = function.getBodyExpression(); - assert bodyExpression != null; - JetScope functionInnerScope = FunctionDescriptorUtil.getFunctionInnerScope(outerScope, functionDescriptor, trace); - new TypeInferrerVisitor(functionInnerScope, function.hasBlockBody(), DataFlowInfo.getEmpty(), NO_EXPECTED_TYPE, FORBIDDEN).getType(bodyExpression); - Collection returnedExpressions = new ArrayList(); - Collection elementsReturningUnit = new ArrayList(); - flowInformationProvider.collectReturnedInformation(function.asElement(), returnedExpressions, elementsReturningUnit); - Map typeMap = new HashMap(); - for (JetExpression returnedExpression : returnedExpressions) { - JetType cachedType = trace.getBindingContext().getExpressionType(returnedExpression); - trace.removeStatementRecord(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, DataFlowInfo dataFlowInfo, JetType extectedReturnType) { - if (block.isEmpty()) { - return JetStandardClasses.getUnitType(); + return wrapForTracing(result[0], reference[0], argumentList, true); } - DeclarationDescriptor containingDescriptor = outerScope.getContainingDeclaration(); - WritableScope scope = new WritableScopeImpl(outerScope, containingDescriptor, trace.getErrorHandler()).setDebugName("getBlockReturnedType"); - return getBlockReturnedTypeWithWritableScope(scope, block, dataFlowInfo, extectedReturnType); - } + private void checkNullSafety(@Nullable JetType receiverType, @NotNull ASTNode operationTokenNode, @Nullable FunctionDescriptor callee) { + if (receiverType != null && callee != null) { + boolean namespaceType = receiverType instanceof NamespaceType; + JetType calleeReceiverType = callee.getReceiverType(); + boolean nullableReceiver = !namespaceType && receiverType.isNullable(); + boolean calleeForbidsNullableReceiver = calleeReceiverType == null || !calleeReceiverType.isNullable(); - private JetType getBlockReturnedTypeWithWritableScope(@NotNull WritableScope scope, @NotNull List block, DataFlowInfo dataFlowInfo, JetType extectedReturnType) { - if (block.isEmpty()) { - return JetStandardClasses.getUnitType(); - } - - TypeInferrerVisitorWithWritableScope blockLevelVisitor = new TypeInferrerVisitorWithWritableScope(scope, true, dataFlowInfo, NO_EXPECTED_TYPE, extectedReturnType); - - JetType result = null; - for (JetElement statement : block) { - trace.recordStatement(statement); - JetExpression statementExpression = (JetExpression) statement; - result = blockLevelVisitor.getType(statementExpression); - DataFlowInfo newDataFlowInfo = blockLevelVisitor.getResultingDataFlowInfo(); - if (newDataFlowInfo == null) { - newDataFlowInfo = dataFlowInfo; - } -// WritableScope newScope = blockLevelVisitor.getResultScope(); -// if (newScope == null) { -// newScope = scope; -// } - if (newDataFlowInfo != dataFlowInfo) {// || newScope != scope) { - blockLevelVisitor = new TypeInferrerVisitorWithWritableScope(scope, true, newDataFlowInfo, NO_EXPECTED_TYPE, extectedReturnType); - } - else { - blockLevelVisitor.resetResult(); // TODO : maybe it's better to recreate the visitors with the same scope? - } - } - return result; - } - - @Nullable - private JetType resolveCall( - @NotNull JetScope scope, - @NotNull OverloadDomain overloadDomain, - @NotNull JetCall call) { - // 1) ends with a name -> (scope, name) to look up - // 2) ends with something else -> just check types - - final List jetTypeArguments = call.getTypeArguments(); - - for (JetTypeProjection typeArgument : jetTypeArguments) { - if (typeArgument.getProjectionKind() != JetProjectionKind.NONE) { - trace.getErrorHandler().genericError(typeArgument.getNode(), "Projections are not allowed on type parameters for methods"); // TODO : better positioning - } - } - - List typeArguments = new ArrayList(); - for (JetTypeProjection projection : jetTypeArguments) { - // TODO : check that there's no projection - JetTypeReference typeReference = projection.getTypeReference(); - if (typeReference != null) { - typeArguments.add(typeResolver.resolveType(scope, typeReference)); - } - } - - return resolveCallWithTypeArguments(scope, overloadDomain, call, typeArguments); - } - - private JetType resolveCallWithTypeArguments(JetScope scope, OverloadDomain overloadDomain, JetCall call, List typeArguments) { - final List valueArguments = call.getValueArguments(); - - boolean someNamed = false; - for (JetArgument argument : valueArguments) { - if (argument.isNamed()) { - someNamed = true; - break; - } - } - - final List functionLiteralArguments = call.getFunctionLiteralArguments(); - - // TODO : must be a check - assert functionLiteralArguments.size() <= 1; - - if (someNamed) { - // TODO : check that all are named - throw new UnsupportedOperationException(); // TODO - -// result = overloadDomain.getFunctionDescriptorForNamedArguments(typeArguments, valueArguments, functionLiteralArgument); - } else { - List positionedValueArguments = new ArrayList(); - for (JetArgument argument : valueArguments) { - JetExpression argumentExpression = argument.getArgumentExpression(); - if (argumentExpression != null) { - positionedValueArguments.add(argumentExpression); + IElementType operationSign = operationTokenNode.getElementType(); + if (nullableReceiver && calleeForbidsNullableReceiver && operationSign == JetTokens.DOT) { + trace.getErrorHandler().genericError(operationTokenNode, "Only safe calls (?.) are allowed on a nullable receiver of type " + receiverType); + } + else if ((!nullableReceiver || !calleeForbidsNullableReceiver) && operationSign == JetTokens.SAFE_ACCESS) { + if (namespaceType) { + trace.getErrorHandler().genericError(operationTokenNode, "Safe calls are not allowed on namespaces"); + } + else { + trace.getErrorHandler().genericWarning(operationTokenNode, "Unnecessary safe call on a non-null receiver of type " + receiverType); + } } } - - positionedValueArguments.addAll(functionLiteralArguments); - - List valueArgumentTypes = new ArrayList(); - for (JetExpression valueArgument : positionedValueArguments) { - valueArgumentTypes.add(safeGetType(scope, valueArgument, false, NO_EXPECTED_TYPE)); // TODO - } - - OverloadResolutionResult resolutionResult = overloadDomain.getFunctionDescriptorForPositionedArguments(typeArguments, valueArgumentTypes); - if (resolutionResult.isSuccess()) { - final FunctionDescriptor functionDescriptor = resolutionResult.getFunctionDescriptor(); - - checkGenericBoundsInAFunctionCall(call.getTypeArguments(), typeArguments, functionDescriptor); - return functionDescriptor.getUnsubstitutedReturnType(); - } } - return null; - } - private void checkGenericBoundsInAFunctionCall(List jetTypeArguments, List typeArguments, FunctionDescriptor functionDescriptor) { - Map context = Maps.newHashMap(); + private OverloadDomain wrapForTracing( + @NotNull final OverloadDomain overloadDomain, + @NotNull final JetReferenceExpression referenceExpression, + @Nullable final PsiElement argumentList, + final boolean reportErrors) { + return new OverloadDomain() { + @NotNull + @Override + public OverloadResolutionResult getFunctionDescriptorForNamedArguments(@NotNull List typeArguments, @NotNull Map valueArgumentTypes, @Nullable JetType functionLiteralArgumentType) { + OverloadResolutionResult resolutionResult = overloadDomain.getFunctionDescriptorForNamedArguments(typeArguments, valueArgumentTypes, functionLiteralArgumentType); + report(resolutionResult); + return resolutionResult; + } - List typeParameters = functionDescriptor.getOriginal().getTypeParameters(); - for (int i = 0, typeParametersSize = typeParameters.size(); i < typeParametersSize; i++) { - TypeParameterDescriptor typeParameter = typeParameters.get(i); - JetType typeArgument = typeArguments.get(i); - context.put(typeParameter.getTypeConstructor(), new TypeProjection(typeArgument)); - } - TypeSubstitutor substitutor = TypeSubstitutor.create(context); - for (int i = 0, typeParametersSize = typeParameters.size(); i < typeParametersSize; i++) { - TypeParameterDescriptor typeParameterDescriptor = typeParameters.get(i); - JetType typeArgument = typeArguments.get(i); - JetTypeReference typeReference = jetTypeArguments.get(i).getTypeReference(); - assert typeReference != null; - classDescriptorResolver.checkBounds(typeReference, typeArgument, typeParameterDescriptor, substitutor); - } - } + @NotNull + @Override + public OverloadResolutionResult getFunctionDescriptorForPositionedArguments(@NotNull List typeArguments, @NotNull List positionedValueArgumentTypes) { + OverloadResolutionResult resolutionResult = overloadDomain.getFunctionDescriptorForPositionedArguments(typeArguments, positionedValueArgumentTypes); + report(resolutionResult); + return resolutionResult; + } - @Nullable - public JetType checkTypeInitializerCall(JetScope scope, @NotNull JetTypeReference typeReference, @NotNull JetCall call) { - JetTypeElement typeElement = typeReference.getTypeElement(); - if (typeElement instanceof JetUserType) { - JetUserType userType = (JetUserType) typeElement; - // TODO : to infer constructor parameters, one will need to - // 1) resolve a _class_ from the typeReference - // 2) rely on the overload domain of constructors of this class to infer type arguments - // For now we assume that the type arguments are provided, and thus the typeReference can be - // resolved into a valid type - JetType receiverType = typeResolver.resolveType(scope, typeReference); - DeclarationDescriptor declarationDescriptor = receiverType.getConstructor().getDeclarationDescriptor(); - if (declarationDescriptor instanceof ClassDescriptor) { - ClassDescriptor classDescriptor = (ClassDescriptor) declarationDescriptor; - - for (JetTypeProjection typeProjection : userType.getTypeArguments()) { - switch (typeProjection.getProjectionKind()) { - case IN: - case OUT: - case STAR: - // TODO : Bug in the editor - trace.getErrorHandler().genericError(typeProjection.getProjectionNode(), "Projections are not allowed in constructor type arguments"); - break; - case NONE: - break; + private void report(OverloadResolutionResult resolutionResult) { + if (resolutionResult.isSuccess() || resolutionResult.singleFunction()) { + trace.recordReferenceResolution(referenceExpression, resolutionResult.getFunctionDescriptor()); + } + if (reportErrors) { + switch (resolutionResult.getResultCode()) { + case NAME_NOT_FOUND: + trace.getErrorHandler().unresolvedReference(referenceExpression); + break; + case SINGLE_FUNCTION_ARGUMENT_MISMATCH: + if (argumentList != null) { + // TODO : More helpful message. NOTE: there's a separate handling for this for constructors + trace.getErrorHandler().genericError(argumentList.getNode(), "Arguments do not match " + DescriptorRenderer.TEXT.render(resolutionResult.getFunctionDescriptor())); + } + else { + trace.getErrorHandler().unresolvedReference(referenceExpression); + } + break; + case AMBIGUITY: + if (argumentList != null) { + // TODO : More helpful message. NOTE: there's a separate handling for this for constructors + trace.getErrorHandler().genericError(argumentList.getNode(), "Overload ambiguity [TODO : more helpful message]"); + } + else { + trace.getErrorHandler().unresolvedReference(referenceExpression); + } + break; + default: + // Not a success + } } } - JetSimpleNameExpression referenceExpression = userType.getReferenceExpression(); - if (referenceExpression != null) { - return checkClassConstructorCall(scope, referenceExpression, classDescriptor, receiverType, call); + @Override + public boolean isEmpty() { + return overloadDomain.isEmpty(); + } + }; + } + + public void checkFunctionReturnType(@NotNull JetScope outerScope, @NotNull JetDeclarationWithBody function, @NotNull FunctionDescriptor functionDescriptor) { + final JetType expectedReturnType = functionDescriptor.getUnsubstitutedReturnType(); + JetScope functionInnerScope = FunctionDescriptorUtil.getFunctionInnerScope(outerScope, functionDescriptor, trace); + checkFunctionReturnType(functionInnerScope, function, functionDescriptor, expectedReturnType); + // Map typeMap = collectReturnedExpressionsWithTypes(outerScope, function, functionDescriptor, expectedReturnType); + // if (typeMap.isEmpty()) { + // return; // The function returns Nothing + // } + // for (Map.Entry entry : typeMap.entrySet()) { + // JetType actualType = entry.getValue(); + // JetElement element = entry.getKey(); + // JetTypeChecker typeChecker = semanticServices.getTypeChecker(); + // if (!typeChecker.isSubtypeOf(actualType, expectedReturnType)) { + // if (typeChecker.isConvertibleBySpecialConversion(actualType, expectedReturnType)) { + // if (expectedReturnType.getConstructor().equals(JetStandardClasses.getUnitType().getConstructor()) + // && element.getParent() instanceof JetReturnExpression) { + // context.trace.getErrorHandler().genericError(element.getNode(), "This function must return a value of type Unit"); + // } + // } + // else { + // if (element == function) { + // JetExpression bodyExpression = function.getBodyExpression(); + // assert bodyExpression != null; + // context.trace.getErrorHandler().genericError(bodyExpression.getNode(), "This function must return a value of type " + expectedReturnType); + // } + // else if (element instanceof JetExpression) { + // JetExpression expression = (JetExpression) element; + // context.trace.getErrorHandler().typeMismatch(expression, expectedReturnType, actualType); + // } + // else { + // context.trace.getErrorHandler().genericError(element.getNode(), "This function must return a value of type " + expectedReturnType); + // } + // } + // } + // } + } + + public void checkFunctionReturnType(JetScope functionInnerScope, JetDeclarationWithBody function, FunctionDescriptor functionDescriptor, @Nullable final JetType expectedReturnType) { + JetExpression bodyExpression = function.getBodyExpression(); + assert bodyExpression != null; + new TypeInferrerVisitor(new TypeInferenceContext(trace, functionInnerScope, function.hasBlockBody(), DataFlowInfo.getEmpty(), NO_EXPECTED_TYPE, expectedReturnType)).getType(bodyExpression); + + 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); + + // 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"); + } + + final boolean blockBody = function.hasBlockBody(); + 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 + + if (!JetStandardClasses.isUnit(expectedReturnType) && returnedExpressions.isEmpty() && !nothingReturned) { + trace.getErrorHandler().genericError(bodyExpression.getNode(), "This function must return a value of type " + expectedReturnType); + } + + for (JetExpression returnedExpression : returnedExpressions) { + returnedExpression.accept(new JetVisitor() { + @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 '{...}'"); + } + } + + @Override + public void visitExpression(JetExpression expression) { + if (blockBody && !JetStandardClasses.isUnit(expectedReturnType) && !rootUnreachableElements.contains(expression)) { + trace.getErrorHandler().genericError(expression.getNode(), "A 'return' expression required in a function with a block body ('{...}')"); + } + } + }); + } + } + + @NotNull + public JetType inferFunctionReturnType(@NotNull JetScope outerScope, JetDeclarationWithBody function, FunctionDescriptor functionDescriptor) { + Map typeMap = collectReturnedExpressionsWithTypes(trace, outerScope, function, functionDescriptor); + Collection types = typeMap.values(); + return types.isEmpty() + ? JetStandardClasses.getNothingType() + : semanticServices.getTypeChecker().commonSupertype(types); + } + + private Map collectReturnedExpressionsWithTypes( + @NotNull BindingTrace trace, + JetScope outerScope, + JetDeclarationWithBody function, + FunctionDescriptor functionDescriptor) { + JetExpression bodyExpression = function.getBodyExpression(); + assert bodyExpression != null; + JetScope functionInnerScope = FunctionDescriptorUtil.getFunctionInnerScope(outerScope, functionDescriptor, trace); + new TypeInferrerVisitor(new TypeInferenceContext(trace, functionInnerScope, function.hasBlockBody(), DataFlowInfo.getEmpty(), NO_EXPECTED_TYPE, FORBIDDEN)).getType(bodyExpression); + Collection returnedExpressions = new ArrayList(); + Collection elementsReturningUnit = new ArrayList(); + flowInformationProvider.collectReturnedInformation(function.asElement(), returnedExpressions, elementsReturningUnit); + Map typeMap = new HashMap(); + for (JetExpression returnedExpression : returnedExpressions) { + JetType cachedType = trace.getBindingContext().getExpressionType(returnedExpression); + trace.removeStatementRecord(returnedExpression); + if (cachedType != null) { + typeMap.put(returnedExpression, cachedType); } } - else { - trace.getErrorHandler().genericError(((JetElement) call).getNode(), "Calling a constructor is only supported for ordinary classes"); // TODO : review the message + for (JetElement jetElement : elementsReturningUnit) { + typeMap.put(jetElement, JetStandardClasses.getUnitType()); + } + return typeMap; + } + + private JetType getBlockReturnedTypeWithWritableScope(@NotNull WritableScope scope, @NotNull List block, DataFlowInfo dataFlowInfo, JetType extectedReturnType) { + if (block.isEmpty()) { + return JetStandardClasses.getUnitType(); + } + + TypeInferrerVisitorWithWritableScope blockLevelVisitor = new TypeInferrerVisitorWithWritableScope(scope, new TypeInferenceContext(trace, scope, true, dataFlowInfo, NO_EXPECTED_TYPE, extectedReturnType)); + + JetType result = null; + for (JetElement statement : block) { + trace.recordStatement(statement); + JetExpression statementExpression = (JetExpression) statement; + result = blockLevelVisitor.getType(statementExpression); + DataFlowInfo newDataFlowInfo = blockLevelVisitor.getResultingDataFlowInfo(); + if (newDataFlowInfo == null) { + newDataFlowInfo = dataFlowInfo; + } + // WritableScope newScope = blockLevelVisitor.getResultScope(); + // if (newScope == null) { + // newScope = scope; + // } + if (newDataFlowInfo != dataFlowInfo) {// || newScope != scope) { + blockLevelVisitor = new TypeInferrerVisitorWithWritableScope(scope, new TypeInferenceContext(trace, scope, true, newDataFlowInfo, NO_EXPECTED_TYPE, extectedReturnType)); + } + else { + blockLevelVisitor.resetResult(); // TODO : maybe it's better to recreate the visitors with the same scope? + } + } + return result; + } + + @Nullable + private JetType resolveCall( + @NotNull JetScope scope, + @NotNull OverloadDomain overloadDomain, + @NotNull JetCall call) { + // 1) ends with a name -> (scope, name) to look up + // 2) ends with something else -> just check types + + final List jetTypeArguments = call.getTypeArguments(); + + for (JetTypeProjection typeArgument : jetTypeArguments) { + if (typeArgument.getProjectionKind() != JetProjectionKind.NONE) { + trace.getErrorHandler().genericError(typeArgument.getNode(), "Projections are not allowed on type parameters for methods"); // TODO : better positioning + } + } + + List typeArguments = new ArrayList(); + for (JetTypeProjection projection : jetTypeArguments) { + // TODO : check that there's no projection + JetTypeReference typeReference = projection.getTypeReference(); + if (typeReference != null) { + typeArguments.add(new TypeResolver(semanticServices, trace, true).resolveType(scope, typeReference)); + } + } + + return resolveCallWithTypeArguments(scope, overloadDomain, call, typeArguments); + } + + private JetType resolveCallWithTypeArguments(JetScope scope, OverloadDomain overloadDomain, JetCall call, List typeArguments) { + final List valueArguments = call.getValueArguments(); + + boolean someNamed = false; + for (JetArgument argument : valueArguments) { + if (argument.isNamed()) { + someNamed = true; + break; + } + } + + final List functionLiteralArguments = call.getFunctionLiteralArguments(); + + // TODO : must be a check + assert functionLiteralArguments.size() <= 1; + + if (someNamed) { + // TODO : check that all are named + throw new UnsupportedOperationException(); // TODO + + // result = overloadDomain.getFunctionDescriptorForNamedArguments(typeArguments, valueArguments, functionLiteralArgument); + } else { + List positionedValueArguments = new ArrayList(); + for (JetArgument argument : valueArguments) { + JetExpression argumentExpression = argument.getArgumentExpression(); + if (argumentExpression != null) { + positionedValueArguments.add(argumentExpression); + } + } + + positionedValueArguments.addAll(functionLiteralArguments); + + List valueArgumentTypes = new ArrayList(); + for (JetExpression valueArgument : positionedValueArguments) { + valueArgumentTypes.add(safeGetType(scope, valueArgument, false, NO_EXPECTED_TYPE)); // TODO + } + + OverloadResolutionResult resolutionResult = overloadDomain.getFunctionDescriptorForPositionedArguments(typeArguments, valueArgumentTypes); + if (resolutionResult.isSuccess()) { + final FunctionDescriptor functionDescriptor = resolutionResult.getFunctionDescriptor(); + + checkGenericBoundsInAFunctionCall(call.getTypeArguments(), typeArguments, functionDescriptor); + return functionDescriptor.getUnsubstitutedReturnType(); + } } return null; } - else { - if (typeElement != null) { - trace.getErrorHandler().genericError(typeElement.getNode(), "Calling a constructor is only supported for ordinary classes"); // TODO : Better message + + private void checkGenericBoundsInAFunctionCall(List jetTypeArguments, List typeArguments, FunctionDescriptor functionDescriptor) { + Map context = Maps.newHashMap(); + + List typeParameters = functionDescriptor.getOriginal().getTypeParameters(); + for (int i = 0, typeParametersSize = typeParameters.size(); i < typeParametersSize; i++) { + TypeParameterDescriptor typeParameter = typeParameters.get(i); + JetType typeArgument = typeArguments.get(i); + context.put(typeParameter.getTypeConstructor(), new TypeProjection(typeArgument)); + } + TypeSubstitutor substitutor = TypeSubstitutor.create(context); + for (int i = 0, typeParametersSize = typeParameters.size(); i < typeParametersSize; i++) { + TypeParameterDescriptor typeParameterDescriptor = typeParameters.get(i); + JetType typeArgument = typeArguments.get(i); + JetTypeReference typeReference = jetTypeArguments.get(i).getTypeReference(); + assert typeReference != null; + classDescriptorResolver.checkBounds(typeReference, typeArgument, typeParameterDescriptor, substitutor); } } - return null; - } - @Nullable - public JetType checkClassConstructorCall( - @NotNull JetScope scope, - @NotNull JetReferenceExpression referenceExpression, - @NotNull ClassDescriptor classDescriptor, - @NotNull JetType receiverType, - @NotNull JetCall call) { - // When one writes 'new Array(...)' this does not make much sense, and an instance - // of 'Array' must be created anyway. - // Thus, we should either prohibit projections in type arguments in such contexts, - // or treat them as an automatic upcast to the desired type, i.e. for the user not - // to be forced to write - // val a : Array = new Array(...) - // NOTE: Array may be a bad example here, some classes may have substantial functionality - // not involving their type parameters - // - // The code below upcasts the type automatically + @Nullable + public JetType checkTypeInitializerCall(JetScope scope, @NotNull JetTypeReference typeReference, @NotNull JetCall call) { + JetTypeElement typeElement = typeReference.getTypeElement(); + if (typeElement instanceof JetUserType) { + JetUserType userType = (JetUserType) typeElement; + // TODO : to infer constructor parameters, one will need to + // 1) resolve a _class_ from the typeReference + // 2) rely on the overload domain of constructors of this class to infer type arguments + // For now we assume that the type arguments are provided, and thus the typeReference can be + // resolved into a valid type + JetType receiverType = typeResolver.resolveType(scope, typeReference); + DeclarationDescriptor declarationDescriptor = receiverType.getConstructor().getDeclarationDescriptor(); + if (declarationDescriptor instanceof ClassDescriptor) { + ClassDescriptor classDescriptor = (ClassDescriptor) declarationDescriptor; + for (JetTypeProjection typeProjection : userType.getTypeArguments()) { + switch (typeProjection.getProjectionKind()) { + case IN: + case OUT: + case STAR: + // TODO : Bug in the editor + trace.getErrorHandler().genericError(typeProjection.getProjectionNode(), "Projections are not allowed in constructor type arguments"); + break; + case NONE: + break; + } + } - FunctionGroup constructors = classDescriptor.getConstructors(); - OverloadDomain constructorsOverloadDomain = semanticServices.getOverloadResolver().getOverloadDomain(null, constructors); - - JetType constructorReturnedType; - if (call instanceof JetDelegatorToThisCall) { - List typeArguments = receiverType.getArguments(); - - List projectionsStripped = Lists.newArrayList(); - for (TypeProjection typeArgument : typeArguments) { - projectionsStripped.add(typeArgument.getType()); - } - - constructorReturnedType = resolveCallWithTypeArguments( - scope, - wrapForTracing(constructorsOverloadDomain, referenceExpression, call.getValueArgumentList(), false), - call, projectionsStripped); - } - else { - constructorReturnedType = resolveCall( - scope, - wrapForTracing(constructorsOverloadDomain, referenceExpression, call.getValueArgumentList(), false), - call); - } - if (constructorReturnedType == null && !ErrorUtils.isErrorType(receiverType)) { - DeclarationDescriptor declarationDescriptor = receiverType.getConstructor().getDeclarationDescriptor(); - assert declarationDescriptor != null; - trace.recordReferenceResolution(referenceExpression, declarationDescriptor); - // TODO : more helpful message - JetArgumentList argumentList = call.getValueArgumentList(); - final String errorMessage = "Cannot find a constructor overload for class " + classDescriptor.getName() + " with these arguments"; - if (argumentList != null) { - trace.getErrorHandler().genericError(argumentList.getNode(), errorMessage); + JetSimpleNameExpression referenceExpression = userType.getReferenceExpression(); + if (referenceExpression != null) { + return checkClassConstructorCall(scope, referenceExpression, classDescriptor, receiverType, call); + } + } + else { + trace.getErrorHandler().genericError(((JetElement) call).getNode(), "Calling a constructor is only supported for ordinary classes"); // TODO : review the message + } + return null; } else { - trace.getErrorHandler().genericError(call.asElement().getNode(), errorMessage); + if (typeElement != null) { + trace.getErrorHandler().genericError(typeElement.getNode(), "Calling a constructor is only supported for ordinary classes"); // TODO : Better message + } } - constructorReturnedType = receiverType; + return null; } - // If no upcast needed: - return constructorReturnedType; - // Automatic upcast: -// result = receiverType; + @Nullable + public JetType checkClassConstructorCall( + @NotNull JetScope scope, + @NotNull JetReferenceExpression referenceExpression, + @NotNull ClassDescriptor classDescriptor, + @NotNull JetType receiverType, + @NotNull JetCall call) { + // When one writes 'new Array(...)' this does not make much sense, and an instance + // of 'Array' must be created anyway. + // Thus, we should either prohibit projections in type arguments in such contexts, + // or treat them as an automatic upcast to the desired type, i.e. for the user not + // to be forced to write + // val a : Array = new Array(...) + // NOTE: Array may be a bad example here, some classes may have substantial functionality + // not involving their type parameters + // + // The code below upcasts the type automatically + + + FunctionGroup constructors = classDescriptor.getConstructors(); + OverloadDomain constructorsOverloadDomain = semanticServices.getOverloadResolver().getOverloadDomain(null, constructors); + + JetType constructorReturnedType; + if (call instanceof JetDelegatorToThisCall) { + List typeArguments = receiverType.getArguments(); + + List projectionsStripped = Lists.newArrayList(); + for (TypeProjection typeArgument : typeArguments) { + projectionsStripped.add(typeArgument.getType()); + } + + constructorReturnedType = resolveCallWithTypeArguments( + scope, + wrapForTracing(constructorsOverloadDomain, referenceExpression, call.getValueArgumentList(), false), + call, projectionsStripped); + } + else { + constructorReturnedType = resolveCall( + scope, + wrapForTracing(constructorsOverloadDomain, referenceExpression, call.getValueArgumentList(), false), + call); + } + if (constructorReturnedType == null && !ErrorUtils.isErrorType(receiverType)) { + DeclarationDescriptor declarationDescriptor = receiverType.getConstructor().getDeclarationDescriptor(); + assert declarationDescriptor != null; + trace.recordReferenceResolution(referenceExpression, declarationDescriptor); + // TODO : more helpful message + JetArgumentList argumentList = call.getValueArgumentList(); + final String errorMessage = "Cannot find a constructor overload for class " + classDescriptor.getName() + " with these arguments"; + if (argumentList != null) { + trace.getErrorHandler().genericError(argumentList.getNode(), errorMessage); + } + else { + trace.getErrorHandler().genericError(call.asElement().getNode(), errorMessage); + } + constructorReturnedType = receiverType; + } + // If no upcast needed: + return constructorReturnedType; + + // Automatic upcast: + // result = receiverType; + } } - //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - private class TypeInferrerVisitor extends JetVisitor { + private class TypeInferenceContext { + public final BindingTrace trace; + public final TypeResolver typeResolver; + public final ClassDescriptorResolver classDescriptorResolver; -// protected final BindingTrace trace; + public final JetScope scope; + public final boolean preferBlock; + public final DataFlowInfo dataFlowInfo; - protected final JetScope scope; - private final boolean preferBlock; - protected final DataFlowInfo dataFlowInfo; + public final JetType expectedType; + public final JetType expectedReturnType; - protected JetType result; - protected DataFlowInfo resultDataFlowInfo; - - protected final JetType expectedType; - protected final JetType expectedReturnType; - - - private TypeInferrerVisitor(@NotNull JetScope scope, boolean preferBlock, @NotNull DataFlowInfo dataFlowInfo, @NotNull JetType expectedType, @Nullable JetType expectedReturnType) { + private TypeInferenceContext( + @NotNull BindingTrace trace, + @NotNull JetScope scope, + boolean preferBlock, + @NotNull DataFlowInfo dataFlowInfo, + @NotNull JetType expectedType, + @Nullable JetType expectedReturnType) { + this.trace = trace; + this.typeResolver = new TypeResolver(semanticServices, trace, true); + this.classDescriptorResolver = semanticServices.getClassDescriptorResolver(trace); this.scope = scope; this.preferBlock = preferBlock; this.dataFlowInfo = dataFlowInfo; @@ -725,6 +730,30 @@ public class JetTypeInferrer { this.expectedReturnType = expectedReturnType; } + public TypeInferenceContext replaceDataFlowInfo(DataFlowInfo newDataFlowInfo) { + return new TypeInferenceContext(trace, scope, preferBlock, newDataFlowInfo, expectedType, expectedReturnType); + } + } + + private class TypeInferrerVisitor extends JetVisitor { + + protected final TypeInferenceContext context; + protected final Services services; + + protected JetType result; + protected DataFlowInfo resultDataFlowInfo; + + + private TypeInferrerVisitor(@NotNull TypeInferenceContext context) { + this.context = context; + this.services = new Services(context.trace); + } + + @NotNull + public TypeInferrerVisitor createNew(@NotNull TypeInferenceContext context) { + return new TypeInferrerVisitor(context); + } + @Nullable public DataFlowInfo getResultingDataFlowInfo() { return resultDataFlowInfo; @@ -732,60 +761,59 @@ public class JetTypeInferrer { @Nullable public JetType getType(@NotNull JetScope scope, @NotNull JetExpression expression, boolean preferBlock) { - return getTypeWithNewDataFlowInfo(scope, expression, preferBlock, dataFlowInfo); + return getTypeWithNewContext(expression, new TypeInferenceContext(context.trace, scope, preferBlock, context.dataFlowInfo, context.expectedType, context.expectedReturnType)); + } + + private JetType getTypeWithNewDataFlowInfo(JetScope scope, JetExpression expression, boolean preferBlock, DataFlowInfo newDataFlowInfo) { + return getTypeWithNewContext(expression, new TypeInferenceContext(context.trace, scope, preferBlock, newDataFlowInfo, context.expectedType, context.expectedReturnType)); } @Nullable - public JetType getTypeWithNewDataFlowInfo(@NotNull final JetScope scope, @NotNull JetExpression expression, final boolean preferBlock, @NotNull DataFlowInfo dataFlowInfo) { + public JetType getTypeWithNewContext(@NotNull JetExpression expression, @NotNull TypeInferenceContext newContext) { TypeInferrerVisitor visitor; - if (this.scope == scope && this.preferBlock == preferBlock && result == null && dataFlowInfo == this.dataFlowInfo) { + if (newContext.equals(context)) { visitor = this; } else { - visitor = createNew(scope, preferBlock, dataFlowInfo, expectedType, expectedReturnType); + visitor = createNew(newContext); } JetType type = visitor.getType(expression); visitor.result = null; return type; } - @NotNull - public TypeInferrerVisitor createNew(@NotNull JetScope scope, boolean preferBlock, @NotNull DataFlowInfo dataFlowInfo, @NotNull JetType expectedType, @Nullable JetType expectedReturnType) { - return new TypeInferrerVisitor(scope, preferBlock, dataFlowInfo, expectedType, expectedReturnType); - } - @Nullable public final JetType getType(@NotNull JetExpression expression) { assert result == null; - if (trace.isProcessed(expression)) { - return trace.getBindingContext().getExpressionType(expression); + if (context.trace.isProcessed(expression)) { + return context.trace.getBindingContext().getExpressionType(expression); } try { expression.accept(this); // Some recursive definitions (object expressions) must put their types in the cache manually: - if (trace.isProcessed(expression)) { - return trace.getBindingContext().getExpressionType(expression); + if (context.trace.isProcessed(expression)) { + return context.trace.getBindingContext().getExpressionType(expression); } if (result instanceof DeferredType) { result = ((DeferredType) result).getActualType(); } if (result != null) { - trace.recordExpressionType(expression, result); + context.trace.recordExpressionType(expression, result); if (JetStandardClasses.isNothing(result) && !result.isNullable()) { markDominatedExpressionsAsUnreachable(expression); } } } catch (ReenteringLazyValueComputationException e) { - trace.getErrorHandler().genericError(expression.getNode(), "Type inference has run into a recursive problem"); // TODO : message + context.trace.getErrorHandler().genericError(expression.getNode(), "Type inference has run into a recursive problem"); // TODO : message result = null; } - if (!trace.isProcessed(expression)) { - trace.recordResolutionScope(expression, scope); + if (!context.trace.isProcessed(expression)) { + context.trace.recordResolutionScope(expression, context.scope); } - trace.markAsProcessed(expression); + context.trace.markAsProcessed(expression); return result; } @@ -802,11 +830,22 @@ public class JetTypeInferrer { flowInformationProvider.collectDominatedExpressions(expression, dominated); Set rootExpressions = JetPsiUtil.findRootExpressions(dominated); for (JetElement rootExpression : rootExpressions) { - trace.getErrorHandler().genericError(rootExpression.getNode(), + context.trace.getErrorHandler().genericError(rootExpression.getNode(), "This code is unreachable, because '" + expression.getText() + "' never terminates normally"); } } + @Nullable + private JetType getBlockReturnedType(@NotNull JetScope outerScope, @NotNull List block, DataFlowInfo dataFlowInfo, JetType extectedReturnType) { + if (block.isEmpty()) { + return JetStandardClasses.getUnitType(); + } + + DeclarationDescriptor containingDescriptor = outerScope.getContainingDeclaration(); + WritableScope scope = new WritableScopeImpl(outerScope, containingDescriptor, context.trace.getErrorHandler()).setDebugName("getBlockReturnedType"); + return services.getBlockReturnedTypeWithWritableScope(scope, block, dataFlowInfo, extectedReturnType); + } + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @Override @@ -816,45 +855,45 @@ public class JetTypeInferrer { String referencedName = expression.getReferencedName(); if (expression.getReferencedNameElementType() == JetTokens.FIELD_IDENTIFIER && referencedName != null) { - PropertyDescriptor property = scope.getPropertyByFieldReference(referencedName); + PropertyDescriptor property = context.scope.getPropertyByFieldReference(referencedName); if (property == null) { - trace.getErrorHandler().unresolvedReference(expression); + context.trace.getErrorHandler().unresolvedReference(expression); } else { - trace.recordReferenceResolution(expression, property); + context.trace.recordReferenceResolution(expression, property); result = property.getOutType(); } } else { assert expression.getReferencedNameElementType() == JetTokens.IDENTIFIER; if (referencedName != null) { - VariableDescriptor variable = scope.getVariable(referencedName); + VariableDescriptor variable = context.scope.getVariable(referencedName); if (variable != null) { - trace.recordReferenceResolution(expression, variable); + context.trace.recordReferenceResolution(expression, variable); result = variable.getOutType(); if (result == null) { - trace.getErrorHandler().genericError(expression.getNode(), "This variable is not readable in this context"); + context.trace.getErrorHandler().genericError(expression.getNode(), "This variable is not readable in this context"); } return; } else { - ClassifierDescriptor classifier = scope.getClassifier(referencedName); + ClassifierDescriptor classifier = context.scope.getClassifier(referencedName); if (classifier != null) { JetType classObjectType = classifier.getClassObjectType(); if (classObjectType != null && (isNamespacePosition() || classifier.isClassObjectAValue())) { result = classObjectType; } else { - trace.getErrorHandler().genericError(expression.getNode(), "Classifier " + classifier.getName() + " does not have a class object"); + context.trace.getErrorHandler().genericError(expression.getNode(), "Classifier " + classifier.getName() + " does not have a class object"); } - trace.recordReferenceResolution(expression, classifier); + context.trace.recordReferenceResolution(expression, classifier); return; } else if (furtherNameLookup(expression, referencedName)) { return; } } - trace.getErrorHandler().unresolvedReference(expression); + context.trace.getErrorHandler().unresolvedReference(expression); } } } @@ -866,7 +905,7 @@ public class JetTypeInferrer { protected boolean furtherNameLookup(@NotNull JetSimpleNameExpression expression, @NotNull String referencedName) { NamespaceType namespaceType = lookupNamespaceType(expression, referencedName); if (namespaceType != null) { - trace.getErrorHandler().genericError(expression.getNode(), "Expression expected, but a namespace name found"); + context.trace.getErrorHandler().genericError(expression.getNode(), "Expression expected, but a namespace name found"); return true; } return false; @@ -874,17 +913,17 @@ public class JetTypeInferrer { @Nullable protected NamespaceType lookupNamespaceType(@NotNull JetSimpleNameExpression expression, @NotNull String referencedName) { - NamespaceDescriptor namespace = scope.getNamespace(referencedName); + NamespaceDescriptor namespace = context.scope.getNamespace(referencedName); if (namespace == null) { return null; } - trace.recordReferenceResolution(expression, namespace); + context.trace.recordReferenceResolution(expression, namespace); return namespace.getNamespaceType(); } @Override public void visitObjectLiteralExpression(final JetObjectLiteralExpression expression) { - TopDownAnalyzer topDownAnalyzer = new TopDownAnalyzer(semanticServices, new BindingTraceAdapter(trace) { + TopDownAnalyzer topDownAnalyzer = new TopDownAnalyzer(semanticServices, new BindingTraceAdapter(context.trace) { @Override public void recordDeclarationResolution(@NotNull PsiElement declaration, @NotNull final DeclarationDescriptor descriptor) { if (declaration == expression.getObjectDeclaration()) { @@ -895,7 +934,7 @@ public class JetTypeInferrer { } }); result = defaultType; - if (!trace.isProcessed(expression)) { + if (!context.trace.isProcessed(expression)) { recordExpressionType(expression, defaultType); markAsProcessed(expression); } @@ -903,27 +942,27 @@ public class JetTypeInferrer { super.recordDeclarationResolution(declaration, descriptor); } }); - topDownAnalyzer.processObject(scope, scope.getContainingDeclaration(), expression.getObjectDeclaration()); + topDownAnalyzer.processObject(context.scope, context.scope.getContainingDeclaration(), expression.getObjectDeclaration()); } @Override public void visitFunctionLiteralExpression(JetFunctionLiteralExpression expression) { - if (preferBlock && !expression.hasParameterSpecification()) { - trace.recordBlock(expression); - result = getBlockReturnedType(scope, expression.getBody(), dataFlowInfo, expectedReturnType); + if (context.preferBlock && !expression.hasParameterSpecification()) { + context.trace.recordBlock(expression); + result = getBlockReturnedType(context.scope, expression.getBody(), context.dataFlowInfo, context.expectedReturnType); return; } JetTypeReference receiverTypeRef = expression.getReceiverTypeRef(); final JetType receiverType; if (receiverTypeRef != null) { - receiverType = typeResolver.resolveType(scope, receiverTypeRef); + receiverType = context.typeResolver.resolveType(context.scope, receiverTypeRef); } else { - receiverType = scope.getThisType(); + receiverType = context.scope.getThisType(); } FunctionDescriptorImpl functionDescriptor = new FunctionDescriptorImpl( - scope.getContainingDeclaration(), Collections.emptyList(), ""); + context.scope.getContainingDeclaration(), Collections.emptyList(), ""); List parameterTypes = new ArrayList(); List valueParameterDescriptors = Lists.newArrayList(); @@ -934,32 +973,32 @@ public class JetTypeInferrer { JetType type; if (typeReference != null) { - type = typeResolver.resolveType(scope, typeReference); + type = context.typeResolver.resolveType(context.scope, typeReference); } else { - trace.getErrorHandler().genericError(parameter.getNode(), "Type inference for parameters is not implemented yet"); + context.trace.getErrorHandler().genericError(parameter.getNode(), "Type inference for parameters is not implemented yet"); type = ErrorUtils.createErrorType("Not inferred"); } - ValueParameterDescriptor valueParameterDescriptor = classDescriptorResolver.resolveValueParameterDescriptor(functionDescriptor, parameter, i, type); + ValueParameterDescriptor valueParameterDescriptor = context.classDescriptorResolver.resolveValueParameterDescriptor(functionDescriptor, parameter, i, type); parameterTypes.add(valueParameterDescriptor.getOutType()); valueParameterDescriptors.add(valueParameterDescriptor); } JetType effectiveReceiverType = receiverTypeRef == null ? null : receiverType; functionDescriptor.initialize(effectiveReceiverType, Collections.emptyList(), valueParameterDescriptors, null); - trace.recordDeclarationResolution(expression, functionDescriptor); + context.trace.recordDeclarationResolution(expression, functionDescriptor); JetTypeReference returnTypeRef = expression.getReturnTypeRef(); JetType returnType; if (returnTypeRef != null) { - returnType = typeResolver.resolveType(scope, returnTypeRef); + returnType = context.typeResolver.resolveType(context.scope, returnTypeRef); } else { - WritableScope writableScope = new WritableScopeImpl(scope, functionDescriptor, trace.getErrorHandler()).setDebugName("Inner scope of a function literal"); + WritableScope writableScope = new WritableScopeImpl(context.scope, functionDescriptor, context.trace.getErrorHandler()).setDebugName("Inner scope of a function literal"); for (VariableDescriptor variableDescriptor : valueParameterDescriptors) { writableScope.addVariableDescriptor(variableDescriptor); } writableScope.setThisType(receiverType); - returnType = getBlockReturnedType(writableScope, expression.getBody(), dataFlowInfo, expectedReturnType); + returnType = getBlockReturnedType(writableScope, expression.getBody(), context.dataFlowInfo, context.expectedReturnType); } JetType safeReturnType = returnType == null ? ErrorUtils.createErrorType("") : returnType; functionDescriptor.setReturnType(safeReturnType); @@ -972,7 +1011,7 @@ public class JetTypeInferrer { public void visitParenthesizedExpression(JetParenthesizedExpression expression) { JetExpression inner = expression.getExpression(); if (inner != null) { - result = getType(scope, inner, false); + result = getType(context.scope, inner, false); } } @@ -983,7 +1022,7 @@ public class JetTypeInferrer { if (elementType == JetNodeTypes.INTEGER_CONSTANT) { Object value = expression.getValue(); if (value == null) { - trace.getErrorHandler().genericError(expression.getNode(), "Number is of range for Long"); + context.trace.getErrorHandler().genericError(expression.getNode(), "Number is of range for Long"); } else if (value instanceof Long) { result = standardLibrary.getLongType(); @@ -1028,7 +1067,7 @@ public class JetTypeInferrer { public void visitThrowExpression(JetThrowExpression expression) { JetExpression thrownExpression = expression.getThrownExpression(); if (thrownExpression != null) { - JetType type = getType(scope, thrownExpression, false); + JetType type = getType(context.scope, thrownExpression, false); // TODO : check that it inherits Throwable } result = JetStandardClasses.getNothingType(); @@ -1036,25 +1075,25 @@ public class JetTypeInferrer { @Override public void visitReturnExpression(JetReturnExpression expression) { - if (expectedReturnType == FORBIDDEN) { - trace.getErrorHandler().genericError(expression.getNode(), "'return' is not allowed here"); + if (context.expectedReturnType == FORBIDDEN) { + context.trace.getErrorHandler().genericError(expression.getNode(), "'return' is not allowed here"); return; } JetExpression returnedExpression = expression.getReturnedExpression(); JetType returnedType = JetStandardClasses.getUnitType(); if (returnedExpression != null) { - returnedType = getType(scope, returnedExpression, false); + returnedType = getType(context.scope, returnedExpression, false); } else { - if (expectedReturnType != null && !JetStandardClasses.isUnit(expectedReturnType)) { - trace.getErrorHandler().genericError(expression.getNode(), "This function must return a value of type " + expectedReturnType); + if (context.expectedReturnType != null && !JetStandardClasses.isUnit(context.expectedReturnType)) { + context.trace.getErrorHandler().genericError(expression.getNode(), "This function must return a value of type " + context.expectedReturnType); } } - if (expectedReturnType != null && returnedType != null) { - if (!semanticServices.getTypeChecker().isSubtypeOf(returnedType, expectedReturnType)) { - trace.getErrorHandler().typeMismatch(returnedExpression == null ? expression : returnedExpression, expectedReturnType, returnedType); + if (context.expectedReturnType != null && returnedType != null) { + if (!semanticServices.getTypeChecker().isSubtypeOf(returnedType, context.expectedReturnType)) { + context.trace.getErrorHandler().typeMismatch(returnedExpression == null ? expression : returnedExpression, context.expectedReturnType, returnedType); } } @@ -1073,20 +1112,20 @@ public class JetTypeInferrer { @Override public void visitTypeofExpression(JetTypeofExpression expression) { - JetType type = safeGetType(scope, expression.getBaseExpression(), false, NO_EXPECTED_TYPE); // TODO + JetType type = services.safeGetType(context.scope, expression.getBaseExpression(), false, NO_EXPECTED_TYPE); // TODO result = semanticServices.getStandardLibrary().getTypeInfoType(type); } @Override public void visitBinaryWithTypeRHSExpression(JetBinaryExpressionWithTypeRHS expression) { IElementType operationType = expression.getOperationSign().getReferencedNameElementType(); - JetType actualType = getType(scope, expression.getLeft(), false); + JetType actualType = getType(context.scope, expression.getLeft(), false); JetTypeReference right = expression.getRight(); if (right != null) { - JetType targetType = typeResolver.resolveType(scope, right); + JetType targetType = context.typeResolver.resolveType(context.scope, right); if (operationType == JetTokens.COLON) { if (actualType != null && !semanticServices.getTypeChecker().isSubtypeOf(actualType, targetType)) { - trace.getErrorHandler().typeMismatch(expression.getLeft(), targetType, actualType); + context.trace.getErrorHandler().typeMismatch(expression.getLeft(), targetType, actualType); } result = targetType; } @@ -1099,7 +1138,7 @@ public class JetTypeInferrer { result = TypeUtils.makeNullable(targetType); } else { - trace.getErrorHandler().genericError(expression.getOperationSign().getNode(), "Unsupported binary operation"); + context.trace.getErrorHandler().genericError(expression.getOperationSign().getNode(), "Unsupported binary operation"); } } } @@ -1110,16 +1149,16 @@ public class JetTypeInferrer { JetTypeChecker typeChecker = semanticServices.getTypeChecker(); if (!typeChecker.isSubtypeOf(targetType, actualType)) { if (typeChecker.isSubtypeOf(actualType, targetType)) { - trace.getErrorHandler().genericWarning(expression.getOperationSign().getNode(), "No cast needed, use ':' instead"); + context.trace.getErrorHandler().genericWarning(expression.getOperationSign().getNode(), "No cast needed, use ':' instead"); } else { // See JET-58 Make 'as never succeeds' a warning, or even never check for Java (external) types - trace.getErrorHandler().genericWarning(expression.getOperationSign().getNode(), "This cast can never succeed"); + context.trace.getErrorHandler().genericWarning(expression.getOperationSign().getNode(), "This cast can never succeed"); } } else { if (typeChecker.isSubtypeOf(actualType, targetType)) { - trace.getErrorHandler().genericWarning(expression.getOperationSign().getNode(), "No cast needed"); + context.trace.getErrorHandler().genericWarning(expression.getOperationSign().getNode(), "No cast needed"); } } } @@ -1129,7 +1168,7 @@ public class JetTypeInferrer { List entries = expression.getEntries(); List types = new ArrayList(); for (JetExpression entry : entries) { - types.add(safeGetType(scope, entry, false, NO_EXPECTED_TYPE)); // TODO + types.add(services.safeGetType(context.scope, entry, false, NO_EXPECTED_TYPE)); // TODO } // TODO : labels result = JetStandardClasses.getTupleType(types); @@ -1140,7 +1179,7 @@ public class JetTypeInferrer { JetType thisType = null; String labelName = expression.getLabelName(); if (labelName != null) { - Collection declarationsByLabel = scope.getDeclarationsByLabel(labelName); + Collection declarationsByLabel = context.scope.getDeclarationsByLabel(labelName); int size = declarationsByLabel.size(); final JetSimpleNameExpression targetLabel = expression.getTargetLabel(); assert targetLabel != null; @@ -1157,48 +1196,48 @@ public class JetTypeInferrer { else { throw new UnsupportedOperationException(); // TODO } - trace.recordReferenceResolution(targetLabel, declarationDescriptor); - trace.recordReferenceResolution(expression.getThisReference(), declarationDescriptor); + context.trace.recordReferenceResolution(targetLabel, declarationDescriptor); + context.trace.recordReferenceResolution(expression.getThisReference(), declarationDescriptor); } else if (size == 0) { // This uses the info written by the control flow processor - PsiElement psiElement = trace.getBindingContext().resolveToDeclarationPsiElement(targetLabel); + PsiElement psiElement = context.trace.getBindingContext().resolveToDeclarationPsiElement(targetLabel); if (psiElement instanceof JetFunctionLiteralExpression) { - DeclarationDescriptor declarationDescriptor = trace.getBindingContext().getDeclarationDescriptor(psiElement); + DeclarationDescriptor declarationDescriptor = context.trace.getBindingContext().getDeclarationDescriptor(psiElement); if (declarationDescriptor instanceof FunctionDescriptor) { thisType = ((FunctionDescriptor) declarationDescriptor).getReceiverType(); if (thisType == null) { thisType = JetStandardClasses.getNothingType(); } else { - trace.recordReferenceResolution(targetLabel, declarationDescriptor); - trace.recordReferenceResolution(expression.getThisReference(), declarationDescriptor); + context.trace.recordReferenceResolution(targetLabel, declarationDescriptor); + context.trace.recordReferenceResolution(expression.getThisReference(), declarationDescriptor); } } else { - trace.getErrorHandler().unresolvedReference(targetLabel); + context.trace.getErrorHandler().unresolvedReference(targetLabel); } } else { - trace.getErrorHandler().unresolvedReference(targetLabel); + context.trace.getErrorHandler().unresolvedReference(targetLabel); } } else { - trace.getErrorHandler().genericError(targetLabel.getNode(), "Ambiguous label"); + context.trace.getErrorHandler().genericError(targetLabel.getNode(), "Ambiguous label"); } } else { - thisType = scope.getThisType(); + thisType = context.scope.getThisType(); - DeclarationDescriptor declarationDescriptorForUnqualifiedThis = scope.getDeclarationDescriptorForUnqualifiedThis(); + DeclarationDescriptor declarationDescriptorForUnqualifiedThis = context.scope.getDeclarationDescriptorForUnqualifiedThis(); if (declarationDescriptorForUnqualifiedThis != null) { - trace.recordReferenceResolution(expression.getThisReference(), declarationDescriptorForUnqualifiedThis); + context.trace.recordReferenceResolution(expression.getThisReference(), declarationDescriptorForUnqualifiedThis); } } if (thisType != null) { if (JetStandardClasses.isNothing(thisType)) { - trace.getErrorHandler().genericError(expression.getNode(), "'this' is not defined in this context"); + context.trace.getErrorHandler().genericError(expression.getNode(), "'this' is not defined in this context"); } else { JetTypeReference superTypeQualifier = expression.getSuperTypeQualifier(); @@ -1208,7 +1247,7 @@ public class JetTypeInferrer { if (superTypeElement instanceof JetUserType) { JetUserType typeElement = (JetUserType) superTypeElement; - ClassifierDescriptor classifierCandidate = typeResolver.resolveClass(scope, typeElement); + ClassifierDescriptor classifierCandidate = context.typeResolver.resolveClass(context.scope, typeElement); if (classifierCandidate instanceof ClassDescriptor) { ClassDescriptor superclass = (ClassDescriptor) classifierCandidate; @@ -1221,7 +1260,7 @@ public class JetTypeInferrer { } } if (result == null) { - trace.getErrorHandler().genericError(superTypeElement.getNode(), "Not a superclass"); + context.trace.getErrorHandler().genericError(superTypeElement.getNode(), "Not a superclass"); } } } @@ -1229,7 +1268,7 @@ public class JetTypeInferrer { result = thisType; } if (result != null) { - trace.recordExpressionType(expression.getThisReference(), result); + context.trace.recordExpressionType(expression.getThisReference(), result); } } } @@ -1237,7 +1276,7 @@ public class JetTypeInferrer { @Override public void visitBlockExpression(JetBlockExpression expression) { - result = getBlockReturnedType(scope, expression.getStatements(), dataFlowInfo, expectedReturnType); + result = getBlockReturnedType(context.scope, expression.getStatements(), context.dataFlowInfo, context.expectedReturnType); } @Override @@ -1245,7 +1284,7 @@ public class JetTypeInferrer { // TODO :change scope according to the bound value in the when header final JetExpression subjectExpression = expression.getSubjectExpression(); - final JetType subjectType = subjectExpression != null ? safeGetType(scope, subjectExpression, false, NO_EXPECTED_TYPE) : ErrorUtils.createErrorType("Unknown type"); + final JetType subjectType = subjectExpression != null ? services.safeGetType(context.scope, subjectExpression, false, NO_EXPECTED_TYPE) : ErrorUtils.createErrorType("Unknown type"); final VariableDescriptor variableDescriptor = subjectExpression != null ? getVariableDescriptorFromSimpleName(subjectExpression) : null; // TODO : exhaustive patterns @@ -1254,7 +1293,7 @@ public class JetTypeInferrer { for (JetWhenEntry whenEntry : expression.getEntries()) { JetWhenCondition condition = whenEntry.getCondition(); WritableScope scopeToExtend = newWritableScopeImpl().setDebugName("Scope extended in when entry"); - DataFlowInfo newDataFlowInfo = dataFlowInfo; + DataFlowInfo newDataFlowInfo = context.dataFlowInfo; if (condition != null) { newDataFlowInfo = checkWhenCondition(subjectExpression, subjectType, condition, scopeToExtend, variableDescriptor); } @@ -1272,21 +1311,21 @@ public class JetTypeInferrer { result = semanticServices.getTypeChecker().commonSupertype(expressionTypes); } else if (expression.getEntries().isEmpty()) { - trace.getErrorHandler().genericError(expression.getNode(), "Entries required for when-expression"); // TODO : Scope, and maybe this should not an error + context.trace.getErrorHandler().genericError(expression.getNode(), "Entries required for when-expression"); // TODO : Scope, and maybe this should not an error } } private DataFlowInfo checkWhenCondition(@Nullable final JetExpression subjectExpression, final JetType subjectType, JetWhenCondition condition, final WritableScope scopeToExtend, final VariableDescriptor... subjectVariables) { - final DataFlowInfo[] newDataFlowInfo = new DataFlowInfo[]{dataFlowInfo}; + final DataFlowInfo[] newDataFlowInfo = new DataFlowInfo[]{context.dataFlowInfo}; condition.accept(new JetVisitor() { @Override public void visitWhenConditionWithExpression(JetWhenConditionWithExpression condition) { JetExpression conditionExpression = condition.getExpression(); if (conditionExpression != null) { - JetType type = getType(scope, conditionExpression, false); + JetType type = getType(context.scope, conditionExpression, false); if (type != null && subjectType != null) { if (TypeUtils.intersect(semanticServices.getTypeChecker(), Sets.newHashSet(subjectType, type)) == null) { - trace.getErrorHandler().genericError(conditionExpression.getNode(), "This condition can never hold"); + context.trace.getErrorHandler().genericError(conditionExpression.getNode(), "This condition can never hold"); } } } @@ -1295,11 +1334,11 @@ public class JetTypeInferrer { @Override public void visitWhenConditionCall(JetWhenConditionCall condition) { JetExpression callSuffixExpression = condition.getCallSuffixExpression(); - JetScope compositeScope = new ScopeWithReceiver(scope, subjectType, semanticServices.getTypeChecker()); + JetScope compositeScope = new ScopeWithReceiver(context.scope, subjectType, semanticServices.getTypeChecker()); if (callSuffixExpression != null) { JetType selectorReturnType = getType(compositeScope, callSuffixExpression, false); ensureBooleanResultWithCustomSubject(callSuffixExpression, selectorReturnType, "This expression"); - checkNullSafety(subjectType, condition.getOperationTokenNode(), getCalleeFunctionDescriptor(callSuffixExpression)); + services.checkNullSafety(subjectType, condition.getOperationTokenNode(), getCalleeFunctionDescriptor(callSuffixExpression)); } } @@ -1322,22 +1361,22 @@ public class JetTypeInferrer { @Override public void visitJetElement(JetElement element) { - trace.getErrorHandler().genericError(element.getNode(), "Unsupported [JetTypeInferrer] : " + element); + context.trace.getErrorHandler().genericError(element.getNode(), "Unsupported [JetTypeInferrer] : " + element); } }); return newDataFlowInfo[0]; } private DataFlowInfo checkPatternType(@NotNull JetPattern pattern, @NotNull final JetType subjectType, @NotNull final WritableScope scopeToExtend, @NotNull final VariableDescriptor... subjectVariables) { - final DataFlowInfo[] result = new DataFlowInfo[] {dataFlowInfo}; + final DataFlowInfo[] result = new DataFlowInfo[] {context.dataFlowInfo}; pattern.accept(new JetVisitor() { @Override public void visitTypePattern(JetTypePattern typePattern) { JetTypeReference typeReference = typePattern.getTypeReference(); if (typeReference != null) { - JetType type = typeResolver.resolveType(scope, typeReference); + JetType type = context.typeResolver.resolveType(context.scope, typeReference); checkTypeCompatibility(type, subjectType, typePattern); - result[0] = dataFlowInfo.isInstanceOf(subjectVariables, type); + result[0] = context.dataFlowInfo.isInstanceOf(subjectVariables, type); } } @@ -1347,7 +1386,7 @@ public class JetTypeInferrer { TypeConstructor typeConstructor = subjectType.getConstructor(); if (!JetStandardClasses.getTuple(entries.size()).getTypeConstructor().equals(typeConstructor) || typeConstructor.getParameters().size() != entries.size()) { - trace.getErrorHandler().genericError(pattern.getNode(), "Type mismatch: subject is of type " + subjectType + " but the pattern is of type Tuple" + entries.size()); // TODO : message + context.trace.getErrorHandler().genericError(pattern.getNode(), "Type mismatch: subject is of type " + subjectType + " but the pattern is of type Tuple" + entries.size()); // TODO : message } else { for (int i = 0, entriesSize = entries.size(); i < entriesSize; i++) { @@ -1357,7 +1396,7 @@ public class JetTypeInferrer { // TODO : is a name always allowed, ie for tuple patterns, not decomposer arg lists? ASTNode nameLabelNode = entry.getNameLabelNode(); if (nameLabelNode != null) { - trace.getErrorHandler().genericError(nameLabelNode, "Unsupported [JetTypeInferrer]"); + context.trace.getErrorHandler().genericError(nameLabelNode, "Unsupported [JetTypeInferrer]"); } JetPattern entryPattern = entry.getPattern(); @@ -1390,12 +1429,12 @@ public class JetTypeInferrer { public void visitBindingPattern(JetBindingPattern pattern) { JetProperty variableDeclaration = pattern.getVariableDeclaration(); JetTypeReference propertyTypeRef = variableDeclaration.getPropertyTypeRef(); - JetType type = propertyTypeRef == null ? subjectType : typeResolver.resolveType(scope, propertyTypeRef); - VariableDescriptor variableDescriptor = classDescriptorResolver.resolveLocalVariableDescriptorWithType(scope.getContainingDeclaration(), variableDeclaration, type); + JetType type = propertyTypeRef == null ? subjectType : context.typeResolver.resolveType(context.scope, propertyTypeRef); + VariableDescriptor variableDescriptor = context.classDescriptorResolver.resolveLocalVariableDescriptorWithType(context.scope.getContainingDeclaration(), variableDeclaration, type); scopeToExtend.addVariableDescriptor(variableDescriptor); if (propertyTypeRef != null) { if (!semanticServices.getTypeChecker().isSubtypeOf(subjectType, type)) { - trace.getErrorHandler().genericError(propertyTypeRef.getNode(), type + " must be a supertype of " + subjectType + ". Use 'is' to match against " + type); + context.trace.getErrorHandler().genericError(propertyTypeRef.getNode(), type + " must be a supertype of " + subjectType + ". Use 'is' to match against " + type); } } @@ -1415,13 +1454,13 @@ public class JetTypeInferrer { return; } if (TypeUtils.intersect(semanticServices.getTypeChecker(), Sets.newHashSet(type, subjectType)) == null) { - trace.getErrorHandler().genericError(reportErrorOn.getNode(), "Incompatible types: " + type + " and " + subjectType); // TODO : message + context.trace.getErrorHandler().genericError(reportErrorOn.getNode(), "Incompatible types: " + type + " and " + subjectType); // TODO : message } } @Override public void visitJetElement(JetElement element) { - trace.getErrorHandler().genericError(element.getNode(), "Unsupported [JetTypeInferrer]"); + context.trace.getErrorHandler().genericError(element.getNode(), "Unsupported [JetTypeInferrer]"); } }); return result[0]; @@ -1437,7 +1476,7 @@ public class JetTypeInferrer { JetParameter catchParameter = catchClause.getCatchParameter(); JetExpression catchBody = catchClause.getCatchBody(); if (catchParameter != null) { - VariableDescriptor variableDescriptor = classDescriptorResolver.resolveLocalVariableDescriptor(scope.getContainingDeclaration(), scope, catchParameter); + VariableDescriptor variableDescriptor = context.classDescriptorResolver.resolveLocalVariableDescriptor(context.scope.getContainingDeclaration(), context.scope, catchParameter); if (catchBody != null) { WritableScope catchScope = newWritableScopeImpl().setDebugName("Catch scope"); catchScope.addVariableDescriptor(variableDescriptor); @@ -1450,12 +1489,12 @@ public class JetTypeInferrer { } if (finallyBlock != null) { types.clear(); // Do not need the list for the check, but need the code above to typecheck catch bodies - JetType type = getType(scope, finallyBlock.getFinalExpression(), true); + JetType type = getType(context.scope, finallyBlock.getFinalExpression(), true); if (type != null) { types.add(type); } } - JetType type = getType(scope, tryBlock, true); + JetType type = getType(context.scope, tryBlock, true); if (type != null) { types.add(type); } @@ -1470,7 +1509,7 @@ public class JetTypeInferrer { @Override public void visitIfExpression(JetIfExpression expression) { JetExpression condition = expression.getCondition(); - checkCondition(scope, condition); + checkCondition(context.scope, condition); JetExpression elseBranch = expression.getElse(); JetExpression thenBranch = expression.getThen(); @@ -1490,7 +1529,7 @@ public class JetTypeInferrer { } } else if (thenBranch == null) { - JetType type = getTypeWithNewDataFlowInfo(scope, elseBranch, true, elseInfo); + JetType type = getTypeWithNewDataFlowInfo(context.scope, elseBranch, true, elseInfo); if (type != null && JetStandardClasses.isNothing(type)) { resultDataFlowInfo = thenInfo; // resultScope = thenScope; @@ -1499,7 +1538,7 @@ public class JetTypeInferrer { } else { JetType thenType = getTypeWithNewDataFlowInfo(thenScope, thenBranch, true, thenInfo); - JetType elseType = getTypeWithNewDataFlowInfo(scope, elseBranch, true, elseInfo); + JetType elseType = getTypeWithNewDataFlowInfo(context.scope, elseBranch, true, elseInfo); if (thenType == null) { result = elseType; @@ -1526,8 +1565,8 @@ public class JetTypeInferrer { } private DataFlowInfo extractDataFlowInfoFromCondition(@Nullable JetExpression condition, final boolean conditionValue, @Nullable final WritableScope scopeToExtend) { - if (condition == null) return dataFlowInfo; - final DataFlowInfo[] result = new DataFlowInfo[] {dataFlowInfo}; + if (condition == null) return context.dataFlowInfo; + final DataFlowInfo[] result = new DataFlowInfo[] {context.dataFlowInfo}; condition.accept(new JetVisitor() { @Override public void visitIsExpression(JetIsExpression expression) { @@ -1592,7 +1631,7 @@ public class JetTypeInferrer { // TODO : validate that DF makes sense for this variable: local, val, internal w/backing field, etc // Comparison to a non-null expression - JetType rhsType = trace.getBindingContext().getExpressionType(right); + JetType rhsType = context.trace.getBindingContext().getExpressionType(right); if (rhsType != null && !rhsType.isNullable()) { extendDataFlowWithNullComparison(operationToken, variableDescriptor, !conditionValue); return; @@ -1600,7 +1639,7 @@ public class JetTypeInferrer { VariableDescriptor rightVariable = getVariableDescriptorFromSimpleName(right); if (rightVariable != null) { - JetType lhsType = trace.getBindingContext().getExpressionType(left); + JetType lhsType = context.trace.getBindingContext().getExpressionType(left); if (lhsType != null && !lhsType.isNullable()) { extendDataFlowWithNullComparison(operationToken, rightVariable, !conditionValue); return; @@ -1622,10 +1661,10 @@ public class JetTypeInferrer { private void extendDataFlowWithNullComparison(IElementType operationToken, VariableDescriptor variableDescriptor, boolean equalsToNull) { if (operationToken == JetTokens.EQEQ || operationToken == JetTokens.EQEQEQ) { - result[0] = dataFlowInfo.equalsToNull(variableDescriptor, !equalsToNull); + result[0] = context.dataFlowInfo.equalsToNull(variableDescriptor, !equalsToNull); } else if (operationToken == JetTokens.EXCLEQ || operationToken == JetTokens.EXCLEQEQEQ) { - result[0] = dataFlowInfo.equalsToNull(variableDescriptor, equalsToNull); + result[0] = context.dataFlowInfo.equalsToNull(variableDescriptor, equalsToNull); } } @@ -1656,7 +1695,7 @@ public class JetTypeInferrer { JetType conditionType = getType(scope, condition, false); if (conditionType != null && !isBoolean(conditionType)) { - trace.getErrorHandler().genericError(condition.getNode(), "Condition must be of type Boolean, but was of type " + conditionType); + context.trace.getErrorHandler().genericError(condition.getNode(), "Condition must be of type Boolean, but was of type " + conditionType); } } } @@ -1664,11 +1703,11 @@ public class JetTypeInferrer { @Override public void visitWhileExpression(JetWhileExpression expression) { JetExpression condition = expression.getCondition(); - checkCondition(scope, condition); + checkCondition(context.scope, condition); JetExpression body = expression.getBody(); if (body != null) { WritableScopeImpl scopeToExtend = newWritableScopeImpl().setDebugName("Scope extended in while's condition"); - DataFlowInfo conditionInfo = condition == null ? dataFlowInfo : extractDataFlowInfoFromCondition(condition, true, scopeToExtend); + DataFlowInfo conditionInfo = condition == null ? context.dataFlowInfo : extractDataFlowInfoFromCondition(condition, true, scopeToExtend); getTypeWithNewDataFlowInfo(scopeToExtend, body, true, conditionInfo); } if (!flowInformationProvider.isBreakable(expression)) { @@ -1681,22 +1720,22 @@ public class JetTypeInferrer { @Override public void visitDoWhileExpression(JetDoWhileExpression expression) { JetExpression body = expression.getBody(); - JetScope conditionScope = scope; + JetScope conditionScope = context.scope; if (body instanceof JetFunctionLiteralExpression) { JetFunctionLiteralExpression function = (JetFunctionLiteralExpression) body; if (!function.hasParameterSpecification()) { WritableScope writableScope = newWritableScopeImpl().setDebugName("do..while body scope"); conditionScope = writableScope; - getBlockReturnedTypeWithWritableScope(writableScope, function.getBody(), dataFlowInfo, expectedReturnType); - trace.recordBlock(function); + services.getBlockReturnedTypeWithWritableScope(writableScope, function.getBody(), context.dataFlowInfo, context.expectedReturnType); + context.trace.recordBlock(function); } else { - getType(scope, body, true); + getType(context.scope, body, true); } } else if (body != null) { WritableScope writableScope = newWritableScopeImpl().setDebugName("do..while body scope"); conditionScope = writableScope; - getBlockReturnedTypeWithWritableScope(writableScope, Collections.singletonList(body), dataFlowInfo, expectedReturnType); + services.getBlockReturnedTypeWithWritableScope(writableScope, Collections.singletonList(body), context.dataFlowInfo, context.expectedReturnType); } JetExpression condition = expression.getCondition(); checkCondition(conditionScope, condition); @@ -1708,11 +1747,11 @@ public class JetTypeInferrer { } protected WritableScopeImpl newWritableScopeImpl() { - return newWritableScopeImpl(scope); + return newWritableScopeImpl(context.scope); } protected WritableScopeImpl newWritableScopeImpl(JetScope scopeToExtend) { - return new WritableScopeImpl(scopeToExtend, scopeToExtend.getContainingDeclaration(), trace.getErrorHandler()); + return new WritableScopeImpl(scopeToExtend, scopeToExtend.getContainingDeclaration(), context.trace.getErrorHandler()); } @Override @@ -1721,7 +1760,7 @@ public class JetTypeInferrer { JetExpression loopRange = expression.getLoopRange(); JetType loopRangeType = null; if (loopRange != null) { - loopRangeType = getType(scope, loopRange, false); + loopRangeType = getType(context.scope, loopRange, false); } JetType expectedParameterType = null; if (loopRangeType != null) { @@ -1734,19 +1773,19 @@ public class JetTypeInferrer { JetTypeReference typeReference = loopParameter.getTypeReference(); VariableDescriptor variableDescriptor; if (typeReference != null) { - variableDescriptor = classDescriptorResolver.resolveLocalVariableDescriptor(scope.getContainingDeclaration(), scope, loopParameter); + variableDescriptor = context.classDescriptorResolver.resolveLocalVariableDescriptor(context.scope.getContainingDeclaration(), context.scope, loopParameter); JetType actualParameterType = variableDescriptor.getOutType(); if (expectedParameterType != null && actualParameterType != null && !semanticServices.getTypeChecker().isSubtypeOf(expectedParameterType, actualParameterType)) { - trace.getErrorHandler().genericError(typeReference.getNode(), "The loop iterates over values of type " + expectedParameterType + " but the parameter is declared to be " + actualParameterType); + context.trace.getErrorHandler().genericError(typeReference.getNode(), "The loop iterates over values of type " + expectedParameterType + " but the parameter is declared to be " + actualParameterType); } } else { if (expectedParameterType == null) { expectedParameterType = ErrorUtils.createErrorType("Error"); } - variableDescriptor = classDescriptorResolver.resolveLocalVariableDescriptor(scope.getContainingDeclaration(), loopParameter, expectedParameterType); + variableDescriptor = context.classDescriptorResolver.resolveLocalVariableDescriptor(context.scope.getContainingDeclaration(), loopParameter, expectedParameterType); } loopScope.addVariableDescriptor(variableDescriptor); } @@ -1761,24 +1800,24 @@ public class JetTypeInferrer { @Nullable private JetType checkIterableConvention(@NotNull JetType type, @NotNull ASTNode reportErrorsOn) { - OverloadResolutionResult iteratorResolutionResult = resolveNoParametersFunction(type, scope, "iterator"); + OverloadResolutionResult iteratorResolutionResult = services.resolveNoParametersFunction(type, context.scope, "iterator"); if (iteratorResolutionResult.isSuccess()) { JetType iteratorType = iteratorResolutionResult.getFunctionDescriptor().getUnsubstitutedReturnType(); boolean hasNextFunctionSupported = checkHasNextFunctionSupport(reportErrorsOn, iteratorType); boolean hasNextPropertySupported = checkHasNextPropertySupport(reportErrorsOn, iteratorType); if (hasNextFunctionSupported && hasNextPropertySupported && !ErrorUtils.isErrorType(iteratorType)) { // TODO : overload resolution rules impose priorities here??? - trace.getErrorHandler().genericError(reportErrorsOn, "An ambiguity between 'iterator().hasNext()' function and 'iterator().hasNext()' property"); + context.trace.getErrorHandler().genericError(reportErrorsOn, "An ambiguity between 'iterator().hasNext()' function and 'iterator().hasNext()' property"); } else if (!hasNextFunctionSupported && !hasNextPropertySupported) { - trace.getErrorHandler().genericError(reportErrorsOn, "Loop range must have an 'iterator().hasNext()' function or an 'iterator().hasNext' property"); + context.trace.getErrorHandler().genericError(reportErrorsOn, "Loop range must have an 'iterator().hasNext()' function or an 'iterator().hasNext' property"); } - OverloadResolutionResult nextResolutionResult = resolveNoParametersFunction(iteratorType, scope, "next"); + OverloadResolutionResult nextResolutionResult = services.resolveNoParametersFunction(iteratorType, context.scope, "next"); if (nextResolutionResult.isAmbiguity()) { - trace.getErrorHandler().genericError(reportErrorsOn, "Method 'iterator().next()' is ambiguous for this expression"); + context.trace.getErrorHandler().genericError(reportErrorsOn, "Method 'iterator().next()' is ambiguous for this expression"); } else if (nextResolutionResult.isNothing()) { - trace.getErrorHandler().genericError(reportErrorsOn, "Loop range must have an 'iterator().next()' method"); + context.trace.getErrorHandler().genericError(reportErrorsOn, "Loop range must have an 'iterator().next()' method"); } else { return nextResolutionResult.getFunctionDescriptor().getUnsubstitutedReturnType(); } @@ -1788,21 +1827,21 @@ public class JetTypeInferrer { if (iteratorResolutionResult.isAmbiguity()) { errorMessage = "Method 'iterator()' is ambiguous for this expression"; } - trace.getErrorHandler().genericError(reportErrorsOn, errorMessage); + context.trace.getErrorHandler().genericError(reportErrorsOn, errorMessage); } return null; } private boolean checkHasNextFunctionSupport(@NotNull ASTNode reportErrorsOn, @NotNull JetType iteratorType) { - OverloadResolutionResult hasNextResolutionResult = resolveNoParametersFunction(iteratorType, scope, "hasNext"); + OverloadResolutionResult hasNextResolutionResult = services.resolveNoParametersFunction(iteratorType, context.scope, "hasNext"); if (hasNextResolutionResult.isAmbiguity()) { - trace.getErrorHandler().genericError(reportErrorsOn, "Method 'iterator().hasNext()' is ambiguous for this expression"); + context.trace.getErrorHandler().genericError(reportErrorsOn, "Method 'iterator().hasNext()' is ambiguous for this expression"); } else if (hasNextResolutionResult.isNothing()) { return false; } else { JetType hasNextReturnType = hasNextResolutionResult.getFunctionDescriptor().getUnsubstitutedReturnType(); if (!isBoolean(hasNextReturnType)) { - trace.getErrorHandler().genericError(reportErrorsOn, "The 'iterator().hasNext()' method of the loop range must return Boolean, but returns " + hasNextReturnType); + context.trace.getErrorHandler().genericError(reportErrorsOn, "The 'iterator().hasNext()' method of the loop range must return Boolean, but returns " + hasNextReturnType); } } return true; @@ -1817,10 +1856,10 @@ public class JetTypeInferrer { JetType hasNextReturnType = hasNextProperty.getOutType(); if (hasNextReturnType == null) { // TODO : accessibility - trace.getErrorHandler().genericError(reportErrorsOn, "The 'iterator().hasNext' property of the loop range must be readable"); + context.trace.getErrorHandler().genericError(reportErrorsOn, "The 'iterator().hasNext' property of the loop range must be readable"); } else if (!isBoolean(hasNextReturnType)) { - trace.getErrorHandler().genericError(reportErrorsOn, "The 'iterator().hasNext' property of the loop range must return Boolean, but returns " + hasNextReturnType); + context.trace.getErrorHandler().genericError(reportErrorsOn, "The 'iterator().hasNext' property of the loop range must return Boolean, but returns " + hasNextReturnType); } } return true; @@ -1841,7 +1880,7 @@ public class JetTypeInferrer { // boolean allNamed = true; // for (ValueArgumentPsi valueArgument : valueArguments) { // if (valueArgument.isNamed()) { -// trace.getErrorHandler().genericError(valueArgument.asElement().getNode(), "Named arguments are not supported"); +// context.trace.getErrorHandler().genericError(valueArgument.asElement().getNode(), "Named arguments are not supported"); // someNamed = true; // } // else { @@ -1855,7 +1894,7 @@ public class JetTypeInferrer { // // TODO function literals outside parentheses // } // -// ErrorHandlerWithRegions errorHandler = trace.getErrorHandler(); +// ErrorHandlerWithRegions errorHandler = context.trace.getErrorHandler(); // // // 1. resolve 'receiver' in 'scope' with expected type 'NO_EXPECTED_TYPE' // errorHandler.openRegion(); @@ -1911,7 +1950,7 @@ public class JetTypeInferrer { @Override public void visitHashQualifiedExpression(JetHashQualifiedExpression expression) { - trace.getErrorHandler().genericError(expression.getOperationTokenNode(), "Unsupported"); + context.trace.getErrorHandler().genericError(expression.getOperationTokenNode(), "Unsupported"); } @Override @@ -1919,9 +1958,9 @@ public class JetTypeInferrer { // TODO : functions as values JetExpression selectorExpression = expression.getSelectorExpression(); JetExpression receiverExpression = expression.getReceiverExpression(); - JetType receiverType = new TypeInferrerVisitorWithNamespaces(scope, false, dataFlowInfo, NO_EXPECTED_TYPE, null).getType(receiverExpression); + JetType receiverType = new TypeInferrerVisitorWithNamespaces(new TypeInferenceContext(context.trace, context.scope, false, context.dataFlowInfo, NO_EXPECTED_TYPE, null)).getType(receiverExpression); if (receiverType != null) { - ErrorHandlerWithRegions errorHandler = trace.getErrorHandler(); + ErrorHandlerWithRegions errorHandler = context.trace.getErrorHandler(); errorHandler.openRegion(); JetType selectorReturnType = getSelectorReturnType(receiverType, selectorExpression); @@ -1933,14 +1972,14 @@ public class JetTypeInferrer { VariableDescriptor variableDescriptor = getVariableDescriptorFromSimpleName(receiverExpression); if (variableDescriptor != null) { - List possibleTypes = Lists.newArrayList(dataFlowInfo.getPossibleTypes(variableDescriptor)); + List possibleTypes = Lists.newArrayList(context.dataFlowInfo.getPossibleTypes(variableDescriptor)); Collections.reverse(possibleTypes); for (JetType possibleType : possibleTypes) { errorHandler.openRegion(); selectorReturnType = getSelectorReturnType(possibleType, selectorExpression); if (selectorReturnType != null) { regionToCommit = errorHandler.closeAndReturnCurrentRegion(); - trace.recordAutoCast(receiverExpression, possibleType); + context.trace.recordAutoCast(receiverExpression, possibleType); break; } else { @@ -1963,14 +2002,14 @@ public class JetTypeInferrer { result = selectorReturnType; } if (selectorExpression != null && result != null) { - trace.recordExpressionType(selectorExpression, result); + context.trace.recordExpressionType(selectorExpression, result); } if (selectorReturnType != null) { // TODO : extensions to 'Any?' if (selectorExpression != null) { receiverType = enrichOutType(receiverExpression, receiverType); - checkNullSafety(receiverType, expression.getOperationTokenNode(), getCalleeFunctionDescriptor(selectorExpression)); + services.checkNullSafety(receiverType, expression.getOperationTokenNode(), getCalleeFunctionDescriptor(selectorExpression)); } } } @@ -1979,7 +2018,7 @@ public class JetTypeInferrer { private JetType enrichOutType(JetExpression receiverExpression, JetType receiverType) { VariableDescriptor variableDescriptor = getVariableDescriptorFromSimpleName(receiverExpression); if (variableDescriptor != null) { - return dataFlowInfo.getOutType(variableDescriptor); + return context.dataFlowInfo.getOutType(variableDescriptor); } return receiverType; } @@ -1995,7 +2034,7 @@ public class JetTypeInferrer { VariableDescriptor variableDescriptor = null; if (receiverExpression instanceof JetSimpleNameExpression) { JetSimpleNameExpression nameExpression = (JetSimpleNameExpression) receiverExpression; - DeclarationDescriptor declarationDescriptor = trace.getBindingContext().resolveReferenceExpression(nameExpression); + DeclarationDescriptor declarationDescriptor = context.trace.getBindingContext().resolveReferenceExpression(nameExpression); if (declarationDescriptor instanceof VariableDescriptor) { variableDescriptor = (VariableDescriptor) declarationDescriptor; } @@ -2017,7 +2056,7 @@ public class JetTypeInferrer { @Override public void visitReferenceExpression(JetReferenceExpression referenceExpression) { - DeclarationDescriptor declarationDescriptor = trace.getBindingContext().resolveReferenceExpression(referenceExpression); + DeclarationDescriptor declarationDescriptor = context.trace.getBindingContext().resolveReferenceExpression(referenceExpression); if (declarationDescriptor instanceof FunctionDescriptor) { result[0] = (FunctionDescriptor) declarationDescriptor; } @@ -2040,7 +2079,7 @@ public class JetTypeInferrer { @Override public void visitJetElement(JetElement element) { - trace.getErrorHandler().genericError(element.getNode(), "Unsupported [getCalleeFunctionDescriptor]: " + element); + context.trace.getErrorHandler().genericError(element.getNode(), "Unsupported [getCalleeFunctionDescriptor]: " + element); } }); if (result[0] == null) { @@ -2054,8 +2093,8 @@ public class JetTypeInferrer { if (calleeExpression == null) { return null; } - OverloadDomain overloadDomain = getOverloadDomain(receiverType, scope, calleeExpression, callExpression.getValueArgumentList()); - return resolveCall(scope, overloadDomain, callExpression); + OverloadDomain overloadDomain = services.getOverloadDomain(receiverType, context.scope, calleeExpression, callExpression.getValueArgumentList()); + return services.resolveCall(context.scope, overloadDomain, callExpression); } private JetType getSelectorReturnType(JetType receiverType, JetExpression selectorExpression) { @@ -2063,12 +2102,12 @@ public class JetTypeInferrer { return getCallExpressionType(receiverType, (JetCallExpression) selectorExpression); } else if (selectorExpression instanceof JetSimpleNameExpression) { - JetScope compositeScope = new ScopeWithReceiver(scope, receiverType, semanticServices.getTypeChecker()); + JetScope compositeScope = new ScopeWithReceiver(context.scope, receiverType, semanticServices.getTypeChecker()); return getType(compositeScope, selectorExpression, false); } else if (selectorExpression != null) { // TODO : not a simple name -> resolve in scope, expect property type or a function type - trace.getErrorHandler().genericError(selectorExpression.getNode(), "Unsupported selector element type: " + selectorExpression); + context.trace.getErrorHandler().genericError(selectorExpression.getNode(), "Unsupported selector element type: " + selectorExpression); } return null; } @@ -2080,7 +2119,7 @@ public class JetTypeInferrer { @Override public void visitIsExpression(JetIsExpression expression) { - JetType knownType = getType(scope, expression.getLeftHandSide(), false); + JetType knownType = getType(context.scope, expression.getLeftHandSide(), false); JetPattern pattern = expression.getPattern(); if (pattern != null && knownType != null) { WritableScopeImpl scopeToExtend = newWritableScopeImpl().setDebugName("Scope extended in 'is'"); @@ -2106,12 +2145,12 @@ public class JetTypeInferrer { IElementType operationType = operationSign.getReferencedNameElementType(); String name = unaryOperationNames.get(operationType); if (name == null) { - trace.getErrorHandler().genericError(operationSign.getNode(), "Unknown unary operation"); + context.trace.getErrorHandler().genericError(operationSign.getNode(), "Unknown unary operation"); } else { - JetType receiverType = getType(scope, baseExpression, false); + JetType receiverType = getType(context.scope, baseExpression, false); if (receiverType != null) { - FunctionDescriptor functionDescriptor = lookupFunction(scope, expression.getOperationSign(), name, receiverType, Collections.emptyList(), true); + FunctionDescriptor functionDescriptor = services.lookupFunction(context.scope, expression.getOperationSign(), name, receiverType, Collections.emptyList(), true); if (functionDescriptor != null) { JetType returnType = functionDescriptor.getUnsubstitutedReturnType(); if (operationType == JetTokens.PLUSPLUS || operationType == JetTokens.MINUSMINUS) { @@ -2120,7 +2159,7 @@ public class JetTypeInferrer { } else { if (!semanticServices.getTypeChecker().isSubtypeOf(returnType, receiverType)) { - trace.getErrorHandler().genericError(operationSign.getNode(), name + " must return " + receiverType + " but returns " + returnType); + context.trace.getErrorHandler().genericError(operationSign.getNode(), name + " must return " + receiverType + " but returns " + returnType); } // TODO : Maybe returnType? result = receiverType; @@ -2144,11 +2183,11 @@ public class JetTypeInferrer { if (operationType == JetTokens.IDENTIFIER) { String referencedName = operationSign.getReferencedName(); if (referencedName != null) { - result = getTypeForBinaryCall(expression, referencedName, scope, true); + result = getTypeForBinaryCall(expression, referencedName, context.scope, true); } } else if (binaryOperationNames.containsKey(operationType)) { - result = getTypeForBinaryCall(expression, binaryOperationNames.get(operationType), scope, true); + result = getTypeForBinaryCall(expression, binaryOperationNames.get(operationType), context.scope, true); } else if (operationType == JetTokens.EQ) { visitAssignment(expression); @@ -2157,7 +2196,7 @@ public class JetTypeInferrer { visitAssignmentOperation(expression); } else if (comparisonOperations.contains(operationType)) { - JetType compareToReturnType = getTypeForBinaryCall(expression, "compareTo", scope, true); + JetType compareToReturnType = getTypeForBinaryCall(expression, "compareTo", context.scope, true); if (compareToReturnType != null) { TypeConstructor constructor = compareToReturnType.getConstructor(); JetStandardLibrary standardLibrary = semanticServices.getStandardLibrary(); @@ -2165,19 +2204,19 @@ public class JetTypeInferrer { if (constructor.equals(intTypeConstructor)) { result = standardLibrary.getBooleanType(); } else { - trace.getErrorHandler().genericError(operationSign.getNode(), "compareTo must return Int, but returns " + compareToReturnType); + context.trace.getErrorHandler().genericError(operationSign.getNode(), "compareTo must return Int, but returns " + compareToReturnType); } } } else if (equalsOperations.contains(operationType)) { String name = "equals"; if (right != null) { - JetType leftType = getType(scope, left, false); + JetType leftType = getType(context.scope, left, false); if (leftType != null) { - JetType rightType = getType(scope, right, false); + JetType rightType = getType(context.scope, right, false); if (rightType != null) { - FunctionDescriptor equals = lookupFunction( - scope, operationSign, "equals", + FunctionDescriptor equals = services.lookupFunction( + context.scope, operationSign, "equals", leftType, Collections.singletonList(JetStandardClasses.getNullableAnyType()), false); if (equals != null) { if (ensureBooleanResult(operationSign, name, equals.getUnsubstitutedReturnType())) { @@ -2185,7 +2224,7 @@ public class JetTypeInferrer { } } else { - trace.getErrorHandler().genericError(operationSign.getNode(), "No method 'equals(Any?) : Boolean' available"); + context.trace.getErrorHandler().genericError(operationSign.getNode(), "No method 'equals(Any?) : Boolean' available"); } } } @@ -2207,25 +2246,25 @@ public class JetTypeInferrer { result = semanticServices.getStandardLibrary().getBooleanType(); } else if (operationType == JetTokens.ANDAND || operationType == JetTokens.OROR) { - JetType leftType = getType(scope, left, false); + JetType leftType = getType(context.scope, left, false); WritableScopeImpl leftScope = newWritableScopeImpl().setDebugName("Left scope of && or ||"); DataFlowInfo flowInfoLeft = extractDataFlowInfoFromCondition(left, operationType == JetTokens.ANDAND, leftScope); // TODO: This gets computed twice: here and in extractDataFlowInfoFromCondition() for the whole condition WritableScopeImpl rightScope = operationType == JetTokens.ANDAND ? leftScope : newWritableScopeImpl().setDebugName("Right scope of && or ||"); JetType rightType = right == null ? null : getTypeWithNewDataFlowInfo(rightScope, right, false, flowInfoLeft); if (leftType != null && !isBoolean(leftType)) { - trace.getErrorHandler().typeMismatch(left, semanticServices.getStandardLibrary().getBooleanType(), leftType); + context.trace.getErrorHandler().typeMismatch(left, semanticServices.getStandardLibrary().getBooleanType(), leftType); } if (rightType != null && !isBoolean(rightType)) { - trace.getErrorHandler().typeMismatch(right, semanticServices.getStandardLibrary().getBooleanType(), rightType); + context.trace.getErrorHandler().typeMismatch(right, semanticServices.getStandardLibrary().getBooleanType(), rightType); } result = semanticServices.getStandardLibrary().getBooleanType(); } else if (operationType == JetTokens.ELVIS) { - JetType leftType = getType(scope, left, false); - JetType rightType = right == null ? null : getType(scope, right, false); + JetType leftType = getType(context.scope, left, false); + JetType rightType = right == null ? null : getType(context.scope, right, false); if (leftType != null) { if (!leftType.isNullable()) { - trace.getErrorHandler().genericWarning(left.getNode(), "Elvis operator (?:) is always returns the left operand of non-nullable type " + leftType); + context.trace.getErrorHandler().genericWarning(left.getNode(), "Elvis operator (?:) is always returns the left operand of non-nullable type " + leftType); } if (rightType != null) { result = TypeUtils.makeNullableAsSpecified(semanticServices.getTypeChecker().commonSupertype(leftType, rightType), rightType.isNullable()); @@ -2233,13 +2272,13 @@ public class JetTypeInferrer { } } else { - trace.getErrorHandler().genericError(operationSign.getNode(), "Unknown operation"); + context.trace.getErrorHandler().genericError(operationSign.getNode(), "Unknown operation"); } } private void checkInExpression(JetSimpleNameExpression operationSign, JetExpression left, JetExpression right) { String name = "contains"; - JetType containsType = getTypeForBinaryCall(scope, right, operationSign, left, name, true); + JetType containsType = getTypeForBinaryCall(context.scope, right, operationSign, left, name, true); ensureBooleanResult(operationSign, name, containsType); } @@ -2249,14 +2288,14 @@ public class JetTypeInferrer { JetExpression right = expression.getRight(); // TODO : duplicated effort for == and != - JetType leftType = getType(scope, left, false); + JetType leftType = getType(context.scope, left, false); if (leftType != null && right != null) { - JetType rightType = getType(scope, right, false); + JetType rightType = getType(context.scope, right, false); if (rightType != null) { JetType intersect = TypeUtils.intersect(semanticServices.getTypeChecker(), new HashSet(Arrays.asList(leftType, rightType))); if (intersect == null) { - trace.getErrorHandler().genericError(expression.getNode(), "Operator " + operationSign.getReferencedName() + " cannot be applied to " + leftType + " and " + rightType); + context.trace.getErrorHandler().genericError(expression.getNode(), "Operator " + operationSign.getReferencedName() + " cannot be applied to " + leftType + " and " + rightType); } } } @@ -2271,7 +2310,7 @@ public class JetTypeInferrer { } private void assignmentIsNotAnExpressionError(JetBinaryExpression expression) { - trace.getErrorHandler().genericError(expression.getNode(), "Assignments are not expressions, and only expressions are allowed in this context"); + context.trace.getErrorHandler().genericError(expression.getNode(), "Assignments are not expressions, and only expressions are allowed in this context"); } private boolean ensureBooleanResult(JetExpression operationSign, String name, JetType resultType) { @@ -2282,7 +2321,7 @@ public class JetTypeInferrer { if (resultType != null) { // TODO : Relax? if (!isBoolean(resultType)) { - trace.getErrorHandler().genericError(operationSign.getNode(), subjectName + " must return Boolean but returns " + resultType); + context.trace.getErrorHandler().genericError(operationSign.getNode(), subjectName + " must return Boolean but returns " + resultType); return false; } } @@ -2296,7 +2335,7 @@ public class JetTypeInferrer { @Nullable protected List getTypes(JetScope scope, List indexExpressions) { List argumentTypes = new ArrayList(); - TypeInferrerVisitor typeInferrerVisitor = new TypeInferrerVisitor(scope, false, dataFlowInfo, NO_EXPECTED_TYPE, null); + TypeInferrerVisitor typeInferrerVisitor = new TypeInferrerVisitor(new TypeInferenceContext(context.trace, scope, false, context.dataFlowInfo, NO_EXPECTED_TYPE, null)); for (JetExpression indexExpression : indexExpressions) { JetType type = typeInferrerVisitor.getType(indexExpression); if (type == null) { @@ -2311,13 +2350,13 @@ public class JetTypeInferrer { @Override public void visitArrayAccessExpression(JetArrayAccessExpression expression) { JetExpression arrayExpression = expression.getArrayExpression(); - JetType receiverType = getType(scope, arrayExpression, false); + JetType receiverType = getType(context.scope, arrayExpression, false); List indexExpressions = expression.getIndexExpressions(); - List argumentTypes = getTypes(scope, indexExpressions); + List argumentTypes = getTypes(context.scope, indexExpressions); if (argumentTypes == null) return; if (receiverType != null) { - FunctionDescriptor functionDescriptor = lookupFunction(scope, expression, "get", receiverType, argumentTypes, true); + FunctionDescriptor functionDescriptor = services.lookupFunction(context.scope, expression, "get", receiverType, argumentTypes, true); if (functionDescriptor != null) { // checkNullSafety(receiverType, expression.getIndexExpressions().get(0).getNode(), functionDescriptor); result = functionDescriptor.getUnsubstitutedReturnType(); @@ -2353,11 +2392,11 @@ public class JetTypeInferrer { if (leftType == null || rightType == null) { return null; } - FunctionDescriptor functionDescriptor = lookupFunction(scope, operationSign, name, leftType, Collections.singletonList(rightType), reportUnresolved); + FunctionDescriptor functionDescriptor = services.lookupFunction(scope, operationSign, name, leftType, Collections.singletonList(rightType), reportUnresolved); if (functionDescriptor != null) { if (leftType.isNullable()) { // TODO : better error message for '1 + nullableVar' case - trace.getErrorHandler().genericError(operationSign.getNode(), + context.trace.getErrorHandler().genericError(operationSign.getNode(), "Infix call corresponds to a dot-qualified call '" + left.getText() + "." + name + "(" + right.getText() + ")'" + " which is not allowed on a nullable receiver '" + right.getText() + "'." + @@ -2371,24 +2410,30 @@ public class JetTypeInferrer { @Override public void visitDeclaration(JetDeclaration dcl) { - trace.getErrorHandler().genericError(dcl.getNode(), "Declarations are not allowed in this position"); + context.trace.getErrorHandler().genericError(dcl.getNode(), "Declarations are not allowed in this position"); } @Override public void visitRootNamespaceExpression(JetRootNamespaceExpression expression) { - trace.getErrorHandler().genericError(expression.getNode(), "'namespace' is not an expression"); + context.trace.getErrorHandler().genericError(expression.getNode(), "'namespace' is not an expression"); result = null; } @Override public void visitJetElement(JetElement element) { - trace.getErrorHandler().genericError(element.getNode(), "[JetTypeInferrer] Unsupported element: " + element + " " + element.getClass().getCanonicalName()); + context.trace.getErrorHandler().genericError(element.getNode(), "[JetTypeInferrer] Unsupported element: " + element + " " + element.getClass().getCanonicalName()); } } private class TypeInferrerVisitorWithNamespaces extends TypeInferrerVisitor { - private TypeInferrerVisitorWithNamespaces(@NotNull JetScope scope, boolean preferBlock, @NotNull DataFlowInfo dataFlowInfo, @NotNull JetType expectedType, @Nullable JetType expectedReturnType) { - super(scope, preferBlock, dataFlowInfo, expectedType, expectedReturnType); + private TypeInferrerVisitorWithNamespaces(@NotNull TypeInferenceContext context) { + super(context); + } + + @NotNull + @Override + public TypeInferrerVisitor createNew(@NotNull TypeInferenceContext context) { + return new TypeInferrerVisitorWithNamespaces(context); } @Override @@ -2396,12 +2441,6 @@ public class JetTypeInferrer { return true; } - @NotNull - @Override - public TypeInferrerVisitor createNew(@NotNull JetScope scope, boolean preferBlock, @NotNull DataFlowInfo dataFlowInfo, @NotNull JetType expectedType, @Nullable JetType expectedReturnType) { - return new TypeInferrerVisitorWithNamespaces(scope, preferBlock, dataFlowInfo, expectedType, expectedReturnType); - } - @Override public void visitRootNamespaceExpression(JetRootNamespaceExpression expression) { result = JetModuleUtil.getRootNamespaceType(expression); @@ -2418,18 +2457,25 @@ public class JetTypeInferrer { private class TypeInferrerVisitorWithWritableScope extends TypeInferrerVisitor { private final WritableScope scope; - public TypeInferrerVisitorWithWritableScope(@NotNull WritableScope scope, boolean preferBlock, DataFlowInfo dataFlowInfo, @NotNull JetType expectedType, @Nullable JetType expectedReturnType) { - super(scope, preferBlock, dataFlowInfo, expectedType, expectedReturnType); + public TypeInferrerVisitorWithWritableScope(@NotNull WritableScope scope, @NotNull TypeInferenceContext context) { + super(context); this.scope = scope; } + @NotNull + @Override + public TypeInferrerVisitor createNew(@NotNull TypeInferenceContext context) { + WritableScopeImpl writableScope = newWritableScopeImpl(context.scope).setDebugName("Block scope"); + return new TypeInferrerVisitorWithWritableScope(writableScope, context); + } + @Override public void visitObjectDeclaration(JetObjectDeclaration declaration) { - TopDownAnalyzer topDownAnalyzer = new TopDownAnalyzer(semanticServices, trace); + TopDownAnalyzer topDownAnalyzer = new TopDownAnalyzer(semanticServices, context.trace); topDownAnalyzer.processObject(scope, scope.getContainingDeclaration(), declaration); - ClassDescriptor classDescriptor = trace.getBindingContext().getClassDescriptor(declaration); + ClassDescriptor classDescriptor = context.trace.getBindingContext().getClassDescriptor(declaration); if (classDescriptor != null) { - PropertyDescriptor propertyDescriptor = classDescriptorResolver.resolveObjectDeclarationAsPropertyDescriptor(scope.getContainingDeclaration(), declaration, classDescriptor); + PropertyDescriptor propertyDescriptor = context.classDescriptorResolver.resolveObjectDeclarationAsPropertyDescriptor(scope.getContainingDeclaration(), declaration, classDescriptor); scope.addVariableDescriptor(propertyDescriptor); } } @@ -2439,20 +2485,20 @@ public class JetTypeInferrer { JetTypeReference receiverTypeRef = property.getReceiverTypeRef(); if (receiverTypeRef != null) { - trace.getErrorHandler().genericError(receiverTypeRef.getNode(), "Local receiver-properties are not allowed"); + context.trace.getErrorHandler().genericError(receiverTypeRef.getNode(), "Local receiver-properties are not allowed"); } JetPropertyAccessor getter = property.getGetter(); if (getter != null) { - trace.getErrorHandler().genericError(getter.getNode(), "Local variables are not allowed to have getters"); + context.trace.getErrorHandler().genericError(getter.getNode(), "Local variables are not allowed to have getters"); } JetPropertyAccessor setter = property.getSetter(); if (setter != null) { - trace.getErrorHandler().genericError(setter.getNode(), "Local variables are not allowed to have setters"); + context.trace.getErrorHandler().genericError(setter.getNode(), "Local variables are not allowed to have setters"); } - VariableDescriptor propertyDescriptor = classDescriptorResolver.resolveLocalVariableDescriptor(scope.getContainingDeclaration(), scope, property); + VariableDescriptor propertyDescriptor = context.classDescriptorResolver.resolveLocalVariableDescriptor(scope.getContainingDeclaration(), scope, property); JetExpression initializer = property.getInitializer(); if (property.getPropertyTypeRef() != null && initializer != null) { JetType initializerType = getType(scope, initializer, false); @@ -2460,7 +2506,7 @@ public class JetTypeInferrer { if (outType != null && initializerType != null && !semanticServices.getTypeChecker().isConvertibleTo(initializerType, outType)) { - trace.getErrorHandler().typeMismatch(initializer, outType, initializerType); + context.trace.getErrorHandler().typeMismatch(initializer, outType, initializerType); } } @@ -2469,7 +2515,7 @@ public class JetTypeInferrer { @Override public void visitFunction(JetFunction function) { - scope.addFunctionDescriptor(classDescriptorResolver.resolveFunctionDescriptor(scope.getContainingDeclaration(), scope, function)); + scope.addFunctionDescriptor(context.classDescriptorResolver.resolveFunctionDescriptor(scope.getContainingDeclaration(), scope, function)); } @Override @@ -2521,7 +2567,7 @@ public class JetTypeInferrer { if (rightType != null && leftType != null && !semanticServices.getTypeChecker().isConvertibleTo(rightType, leftType)) { - trace.getErrorHandler().typeMismatch(right, leftType, rightType); + context.trace.getErrorHandler().typeMismatch(right, leftType, rightType); } } } @@ -2539,8 +2585,8 @@ public class JetTypeInferrer { if (receiverType == null) return; // TODO : nasty hack: effort is duplicated - lookupFunction(scope, arrayAccessExpression, "set", receiverType, argumentTypes, true); - FunctionDescriptor functionDescriptor = lookupFunction(scope, operationSign, "set", receiverType, argumentTypes, true); + services.lookupFunction(scope, arrayAccessExpression, "set", receiverType, argumentTypes, true); + FunctionDescriptor functionDescriptor = services.lookupFunction(scope, operationSign, "set", receiverType, argumentTypes, true); if (functionDescriptor != null) { result = functionDescriptor.getUnsubstitutedReturnType(); } @@ -2548,14 +2594,7 @@ public class JetTypeInferrer { @Override public void visitJetElement(JetElement element) { - trace.getErrorHandler().genericError(element.getNode(), "Unsupported element in a block: " + element + " " + element.getClass().getCanonicalName()); - } - - @NotNull - @Override - public TypeInferrerVisitor createNew(@NotNull JetScope scope, boolean preferBlock, @NotNull DataFlowInfo dataFlowInfo, @NotNull JetType expectedType, @Nullable JetType expectedReturnType) { - WritableScopeImpl writableScope = newWritableScopeImpl(scope).setDebugName("Block scope"); - return new TypeInferrerVisitorWithWritableScope(writableScope, preferBlock, dataFlowInfo, expectedType, expectedReturnType); + context.trace.getErrorHandler().genericError(element.getNode(), "Unsupported element in a block: " + element + " " + element.getClass().getCanonicalName()); } } } diff --git a/idea/tests/org/jetbrains/jet/types/JetTypeCheckerTest.java b/idea/tests/org/jetbrains/jet/types/JetTypeCheckerTest.java index 4228276fffd..f416987f2b8 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 = JetChangeUtil.createExpression(project, expression); - JetType type = semanticServices.getTypeInferrer(JetTestUtils.DUMMY, JetFlowInformationProvider.NONE).getType(classDefinitions.BASIC_SCOPE, jetExpression, false, JetTypeInferrer.NO_EXPECTED_TYPE); + JetType type = semanticServices.getTypeInferrerServices(JetTestUtils.DUMMY, JetFlowInformationProvider.NONE).getType(classDefinitions.BASIC_SCOPE, jetExpression, false, JetTypeInferrer.NO_EXPECTED_TYPE); assertTrue(type + " != " + expectedType, type.equals(expectedType)); } private void assertErrorType(String expression) { Project project = getProject(); JetExpression jetExpression = JetChangeUtil.createExpression(project, expression); - JetType type = semanticServices.getTypeInferrer(JetTestUtils.DUMMY, JetFlowInformationProvider.NONE).safeGetType(classDefinitions.BASIC_SCOPE, jetExpression, false, JetTypeInferrer.NO_EXPECTED_TYPE); + JetType type = semanticServices.getTypeInferrerServices(JetTestUtils.DUMMY, JetFlowInformationProvider.NONE).safeGetType(classDefinitions.BASIC_SCOPE, jetExpression, false, 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 = JetChangeUtil.createExpression(project, expression); - JetType type = semanticServices.getTypeInferrer(JetTestUtils.DUMMY, JetFlowInformationProvider.NONE).getType(scope, jetExpression, false, JetTypeInferrer.NO_EXPECTED_TYPE); + JetType type = semanticServices.getTypeInferrerServices(JetTestUtils.DUMMY, JetFlowInformationProvider.NONE).getType(scope, jetExpression, false, JetTypeInferrer.NO_EXPECTED_TYPE); JetType expectedType = expectedTypeStr == null ? null : makeType(expectedTypeStr); assertEquals(expectedType, type); }