001    /*
002     * Copyright 2010-2013 JetBrains s.r.o.
003     *
004     * Licensed under the Apache License, Version 2.0 (the "License");
005     * you may not use this file except in compliance with the License.
006     * You may obtain a copy of the License at
007     *
008     * http://www.apache.org/licenses/LICENSE-2.0
009     *
010     * Unless required by applicable law or agreed to in writing, software
011     * distributed under the License is distributed on an "AS IS" BASIS,
012     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013     * See the License for the specific language governing permissions and
014     * limitations under the License.
015     */
016    
017    package org.jetbrains.jet.lang.types.expressions;
018    
019    import com.google.common.collect.Lists;
020    import com.intellij.openapi.project.Project;
021    import com.intellij.openapi.util.Pair;
022    import com.intellij.psi.PsiElement;
023    import com.intellij.psi.tree.IElementType;
024    import com.intellij.psi.tree.TokenSet;
025    import com.intellij.psi.util.PsiTreeUtil;
026    import org.jetbrains.annotations.NotNull;
027    import org.jetbrains.annotations.Nullable;
028    import org.jetbrains.jet.JetNodeTypes;
029    import org.jetbrains.jet.lang.descriptors.*;
030    import org.jetbrains.jet.lang.diagnostics.AbstractDiagnosticFactory;
031    import org.jetbrains.jet.lang.diagnostics.Diagnostic;
032    import org.jetbrains.jet.lang.diagnostics.Errors;
033    import org.jetbrains.jet.lang.psi.*;
034    import org.jetbrains.jet.lang.resolve.*;
035    import org.jetbrains.jet.lang.resolve.calls.autocasts.DataFlowInfo;
036    import org.jetbrains.jet.lang.resolve.calls.context.ExpressionPosition;
037    import org.jetbrains.jet.lang.resolve.calls.inference.ConstraintPosition;
038    import org.jetbrains.jet.lang.resolve.calls.inference.ConstraintSystem;
039    import org.jetbrains.jet.lang.resolve.calls.inference.ConstraintSystemImpl;
040    import org.jetbrains.jet.lang.resolve.calls.inference.ConstraintsUtil;
041    import org.jetbrains.jet.lang.resolve.calls.model.ResolvedCall;
042    import org.jetbrains.jet.lang.resolve.calls.results.OverloadResolutionResults;
043    import org.jetbrains.jet.lang.resolve.calls.util.CallMaker;
044    import org.jetbrains.jet.lang.resolve.name.FqName;
045    import org.jetbrains.jet.lang.resolve.name.Name;
046    import org.jetbrains.jet.lang.resolve.scopes.JetScope;
047    import org.jetbrains.jet.lang.resolve.scopes.WritableScope;
048    import org.jetbrains.jet.lang.resolve.scopes.WritableScopeImpl;
049    import org.jetbrains.jet.lang.resolve.scopes.receivers.ExpressionReceiver;
050    import org.jetbrains.jet.lang.resolve.scopes.receivers.ReceiverValue;
051    import org.jetbrains.jet.lang.types.*;
052    import org.jetbrains.jet.lang.types.checker.JetTypeChecker;
053    import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns;
054    import org.jetbrains.jet.lexer.JetTokens;
055    import org.jetbrains.jet.util.slicedmap.WritableSlice;
056    
057    import java.util.*;
058    
059    import static org.jetbrains.jet.lang.diagnostics.Errors.*;
060    import static org.jetbrains.jet.lang.resolve.BindingContext.*;
061    import static org.jetbrains.jet.lang.types.TypeUtils.noExpectedType;
062    
063    public class ExpressionTypingUtils {
064    
065        private ExpressionTypingUtils() {
066        }
067    
068        public static final JetType CANT_INFER_LAMBDA_PARAM_TYPE = ErrorUtils.createErrorType("Cannot be inferred");
069    
070        @Nullable
071        protected static ExpressionReceiver getExpressionReceiver(@NotNull JetExpression expression, @Nullable JetType type) {
072            if (type == null) return null;
073            return new ExpressionReceiver(expression, type);
074        }
075    
076        @Nullable
077        protected static ExpressionReceiver getExpressionReceiver(@NotNull ExpressionTypingFacade facade, @NotNull JetExpression expression, ExpressionTypingContext context) {
078            return getExpressionReceiver(expression, facade.getTypeInfo(expression, context).getType());
079        }
080    
081        @NotNull
082        protected static ExpressionReceiver safeGetExpressionReceiver(@NotNull ExpressionTypingFacade facade, @NotNull JetExpression expression, ExpressionTypingContext context) {
083            JetType type = facade.safeGetTypeInfo(expression, context).getType();
084            assert type != null : "safeGetTypeInfo should return @NotNull type";
085            return new ExpressionReceiver(expression, type);
086        }
087    
088        @NotNull
089        public static WritableScopeImpl newWritableScopeImpl(ExpressionTypingContext context, @NotNull String scopeDebugName) {
090            WritableScopeImpl scope = new WritableScopeImpl(
091                    context.scope, context.scope.getContainingDeclaration(), new TraceBasedRedeclarationHandler(context.trace), scopeDebugName);
092            scope.changeLockLevel(WritableScope.LockLevel.BOTH);
093            return scope;
094        }
095    
096        public static boolean isBoolean(@NotNull JetType type) {
097            return JetTypeChecker.INSTANCE.isSubtypeOf(type, KotlinBuiltIns.getInstance().getBooleanType());
098        }
099    
100        public static boolean ensureBooleanResult(JetExpression operationSign, Name name, JetType resultType, ExpressionTypingContext context) {
101            return ensureBooleanResultWithCustomSubject(operationSign, resultType, "'" + name + "'", context);
102        }
103    
104        public static boolean ensureBooleanResultWithCustomSubject(JetExpression operationSign, JetType resultType, String subjectName, ExpressionTypingContext context) {
105            if (resultType != null) {
106                // TODO : Relax?
107                if (!isBoolean(resultType)) {
108                    context.trace.report(RESULT_TYPE_MISMATCH.on(operationSign, subjectName, KotlinBuiltIns.getInstance().getBooleanType(), resultType));
109                    return false;
110                }
111            }
112            return true;
113        }
114    
115        @NotNull
116        public static JetType getDefaultType(IElementType constantType) {
117            if (constantType == JetNodeTypes.INTEGER_CONSTANT) {
118                return KotlinBuiltIns.getInstance().getIntType();
119            }
120            else if (constantType == JetNodeTypes.FLOAT_CONSTANT) {
121                return KotlinBuiltIns.getInstance().getDoubleType();
122            }
123            else if (constantType == JetNodeTypes.BOOLEAN_CONSTANT) {
124                return KotlinBuiltIns.getInstance().getBooleanType();
125            }
126            else if (constantType == JetNodeTypes.CHARACTER_CONSTANT) {
127                return KotlinBuiltIns.getInstance().getCharType();
128            }
129            else if (constantType == JetNodeTypes.NULL) {
130                return KotlinBuiltIns.getInstance().getNullableNothingType();
131            }
132            else {
133                throw new IllegalArgumentException("Unsupported constant type: " + constantType);
134            }
135        }
136    
137        public static boolean isTypeFlexible(@Nullable JetExpression expression) {
138            if (expression == null) return false;
139    
140            return TokenSet.create(
141                    JetNodeTypes.INTEGER_CONSTANT,
142                    JetNodeTypes.FLOAT_CONSTANT
143            ).contains(expression.getNode().getElementType());
144        }
145    
146        private static boolean isCapturedInInline(
147                @NotNull BindingContext context,
148                @NotNull DeclarationDescriptor scopeContainer,
149                @NotNull DeclarationDescriptor variableParent
150        ) {
151            PsiElement scopeDeclaration = BindingContextUtils.descriptorToDeclaration(context, scopeContainer);
152            if (!(scopeDeclaration instanceof JetFunctionLiteral)) {
153                return false;
154            }
155    
156            PsiElement parent = scopeDeclaration.getParent();
157            assert parent instanceof JetFunctionLiteralExpression : "parent of JetFunctionLiteral is " + parent;
158            JetCallExpression callExpression = getCallExpression((JetFunctionLiteralExpression) parent);
159            if (callExpression == null) {
160                return false;
161            }
162    
163            ResolvedCall<? extends CallableDescriptor> call = context.get(BindingContext.RESOLVED_CALL, callExpression.getCalleeExpression());
164            if (call == null) {
165                return false;
166            }
167    
168            CallableDescriptor callable = call.getResultingDescriptor();
169            if (callable instanceof SimpleFunctionDescriptor && ((SimpleFunctionDescriptor) callable).isInline()) {
170                DeclarationDescriptor scopeContainerParent = scopeContainer.getContainingDeclaration();
171                assert scopeContainerParent != null : "parent is null for " + scopeContainer;
172                return scopeContainerParent == variableParent || isCapturedInInline(context, scopeContainerParent, variableParent);
173            }
174            else {
175                return false;
176            }
177        }
178    
179        @Nullable
180        private static JetCallExpression getCallExpression(@NotNull JetFunctionLiteralExpression functionLiteralExpression) {
181            PsiElement parent = functionLiteralExpression.getParent();
182            if (parent instanceof JetValueArgument) {
183                // foo({ ... })    or     foo(f = { ... })
184    
185                PsiElement valueArgumentList = parent.getParent();
186                assert valueArgumentList instanceof JetValueArgumentList : "parent of value argument is " + valueArgumentList;
187    
188                if (valueArgumentList.getParent() instanceof JetCallExpression) { // may be argument list of annotation
189                    return (JetCallExpression) valueArgumentList.getParent();
190                }
191            }
192            else if (parent instanceof JetCallExpression) {
193                // foo { ... }
194    
195                return  (JetCallExpression) parent;
196            }
197            return null;
198        }
199        public static void checkCapturingInClosure(JetSimpleNameExpression expression, BindingTrace trace, JetScope scope) {
200            VariableDescriptor variable = BindingContextUtils.extractVariableDescriptorIfAny(trace.getBindingContext(), expression, true);
201            if (variable != null) {
202                DeclarationDescriptor variableParent = variable.getContainingDeclaration();
203                DeclarationDescriptor scopeContainer = scope.getContainingDeclaration();
204                if (scopeContainer != variableParent && variableParent instanceof CallableDescriptor) {
205                    if (trace.get(CAPTURED_IN_CLOSURE, variable) != CaptureKind.NOT_INLINE) {
206                        boolean inline = isCapturedInInline(trace.getBindingContext(), scopeContainer, variableParent);
207                        trace.record(CAPTURED_IN_CLOSURE, variable, inline ? CaptureKind.INLINE_ONLY : CaptureKind.NOT_INLINE);
208                    }
209                }
210            }
211        }
212    
213        public static boolean isVariableIterable(@NotNull ExpressionTypingServices expressionTypingServices,
214                @NotNull Project project, @NotNull VariableDescriptor variableDescriptor, @NotNull JetScope scope) {
215            JetExpression expression = JetPsiFactory.createExpression(project, "fake");
216            ExpressionReceiver expressionReceiver = new ExpressionReceiver(expression, variableDescriptor.getType());
217            ExpressionTypingContext context = ExpressionTypingContext.newContext(
218                    expressionTypingServices,
219                    new BindingTraceContext(),
220                    scope,
221                    DataFlowInfo.EMPTY,
222                    TypeUtils.NO_EXPECTED_TYPE,
223                    ExpressionPosition.FREE
224            );
225            return ControlStructureTypingVisitor.checkIterableConvention(expressionReceiver, context) != null;
226        }
227    
228        /**
229         * Check that function or property with the given qualified name can be resolved in given scope and called on given receiver
230         *
231         * @param callableFQN
232         * @param project
233         * @param scope
234         * @return
235         */
236        public static List<CallableDescriptor> canFindSuitableCall(
237                @NotNull FqName callableFQN,
238                @NotNull Project project,
239                @NotNull JetExpression receiverExpression,
240                @NotNull JetType receiverType,
241                @NotNull JetScope scope,
242                @NotNull ModuleDescriptor module
243        ) {
244            JetImportDirective importDirective = JetPsiFactory.createImportDirective(project, callableFQN.asString());
245    
246            Collection<? extends DeclarationDescriptor> declarationDescriptors = new QualifiedExpressionResolver()
247                    .analyseImportReference(importDirective, scope, new BindingTraceContext(), module);
248    
249            List<CallableDescriptor> callableExtensionDescriptors = new ArrayList<CallableDescriptor>();
250            ReceiverValue receiverValue = new ExpressionReceiver(receiverExpression, receiverType);
251    
252            for (DeclarationDescriptor declarationDescriptor : declarationDescriptors) {
253                if (declarationDescriptor instanceof CallableDescriptor) {
254                    CallableDescriptor callableDescriptor = (CallableDescriptor) declarationDescriptor;
255    
256                    if (checkIsExtensionCallable(receiverValue, callableDescriptor)) {
257                        callableExtensionDescriptors.add(callableDescriptor);
258                    }
259                }
260            }
261    
262            return callableExtensionDescriptors;
263        }
264    
265        /*
266        * Checks if receiver declaration could be resolved to call expected receiver.
267        */
268        public static boolean checkIsExtensionCallable (
269                @NotNull ReceiverValue receiverArgument,
270                @NotNull CallableDescriptor callableDescriptor
271        ) {
272            JetType type = receiverArgument.getType();
273    
274            if (type instanceof NamespaceType) {
275                // This fake class ruins standard algorithms
276                return false;
277            }
278    
279            if (checkReceiverResolution(receiverArgument, type, callableDescriptor)) return true;
280            if (type.isNullable()) {
281                JetType notNullableType = TypeUtils.makeNotNullable(type);
282                if (checkReceiverResolution(receiverArgument, notNullableType, callableDescriptor)) return true;
283            }
284            return false;
285        }
286    
287        private static boolean checkReceiverResolution (
288                @NotNull ReceiverValue receiverArgument,
289                @NotNull JetType receiverType,
290                @NotNull CallableDescriptor callableDescriptor
291        ) {
292            ReceiverParameterDescriptor receiverParameter = callableDescriptor.getReceiverParameter();
293    
294            if (!receiverArgument.exists() && receiverParameter == null) {
295                // Both receivers do not exist
296                return true;
297            }
298    
299            if (!(receiverArgument.exists() && receiverParameter != null)) {
300                return false;
301            }
302    
303            Set<Name> typeNamesInReceiver = collectUsedTypeNames(receiverParameter.getType());
304    
305            ConstraintSystem constraintSystem = new ConstraintSystemImpl();
306            for (TypeParameterDescriptor typeParameterDescriptor : callableDescriptor.getTypeParameters()) {
307                if (typeNamesInReceiver.contains(typeParameterDescriptor.getName())) {
308                    constraintSystem.registerTypeVariable(typeParameterDescriptor, Variance.INVARIANT);
309                }
310            }
311    
312            constraintSystem.addSubtypeConstraint(receiverType, receiverParameter.getType(), ConstraintPosition.RECEIVER_POSITION);
313            return constraintSystem.isSuccessful() && ConstraintsUtil.checkBoundsAreSatisfied(constraintSystem, true);
314        }
315    
316        private static Set<Name> collectUsedTypeNames(@NotNull JetType jetType) {
317            Set<Name> typeNames = new HashSet<Name>();
318    
319            ClassifierDescriptor descriptor = jetType.getConstructor().getDeclarationDescriptor();
320            if (descriptor != null) {
321                typeNames.add(descriptor.getName());
322            }
323    
324            for (TypeProjection argument : jetType.getArguments()) {
325                typeNames.addAll(collectUsedTypeNames(argument.getType()));
326            }
327    
328            return typeNames;
329        }
330    
331        @NotNull
332        public static OverloadResolutionResults<FunctionDescriptor> resolveFakeCall(
333                @NotNull ExpressionTypingContext context,
334                @NotNull ReceiverValue receiver,
335                @NotNull Name name,
336                @NotNull JetType... argumentTypes
337        ) {
338            TemporaryBindingTrace traceWithFakeArgumentInfo = TemporaryBindingTrace.create(context.trace, "trace to store fake argument for",
339                                                                                           name);
340            List<JetExpression> fakeArguments = Lists.newArrayList();
341            for (JetType type : argumentTypes) {
342                fakeArguments.add(createFakeExpressionOfType(context.expressionTypingServices.getProject(), traceWithFakeArgumentInfo,
343                                                             "fakeArgument" + fakeArguments.size(), type));
344            }
345            return makeAndResolveFakeCall(receiver, context.replaceBindingTrace(traceWithFakeArgumentInfo), fakeArguments, name).getSecond();
346        }
347    
348        public static JetExpression createFakeExpressionOfType(
349                @NotNull Project project,
350                @NotNull BindingTrace trace,
351                @NotNull String argumentName,
352                @NotNull JetType argumentType
353        ) {
354            JetExpression fakeExpression = JetPsiFactory.createExpression(project, argumentName);
355            trace.record(EXPRESSION_TYPE, fakeExpression, argumentType);
356            trace.record(PROCESSED, fakeExpression);
357            return fakeExpression;
358        }
359    
360        @NotNull
361        public static OverloadResolutionResults<FunctionDescriptor> resolveFakeCall(
362                @NotNull ExpressionTypingContext context,
363                @NotNull ReceiverValue receiver,
364                @NotNull Name name
365        ) {
366            return makeAndResolveFakeCall(receiver, context, Collections.<JetExpression>emptyList(), name).getSecond();
367        }
368    
369        @NotNull
370        public static Pair<Call, OverloadResolutionResults<FunctionDescriptor>> makeAndResolveFakeCall(
371                @NotNull ReceiverValue receiver,
372                @NotNull ExpressionTypingContext context,
373                @NotNull List<JetExpression> valueArguments,
374                @NotNull Name name
375        ) {
376            final JetReferenceExpression fake = JetPsiFactory.createSimpleName(context.expressionTypingServices.getProject(), "fake");
377            TemporaryBindingTrace fakeTrace = TemporaryBindingTrace.create(context.trace, "trace to resolve fake call for", name);
378            Call call = CallMaker.makeCallWithExpressions(fake, receiver, null, fake, valueArguments);
379            OverloadResolutionResults<FunctionDescriptor> results =
380                    context.replaceBindingTrace(fakeTrace).resolveCallWithGivenName(call, fake, name);
381            if (results.isSuccess()) {
382                fakeTrace.commit(new TraceEntryFilter() {
383                    @Override
384                    public boolean accept(@NotNull WritableSlice<?, ?> slice, Object key) {
385                        // excluding all entries related to fake expression
386                        return key != fake;
387                    }
388                }, false);
389            }
390            return Pair.create(call, results);
391        }
392    
393        public static void defineLocalVariablesFromMultiDeclaration(
394                @NotNull WritableScope writableScope,
395                @NotNull JetMultiDeclaration multiDeclaration,
396                @NotNull ReceiverValue receiver,
397                @NotNull JetExpression reportErrorsOn,
398                @NotNull ExpressionTypingContext context
399        ) {
400            int componentIndex = 1;
401            for (JetMultiDeclarationEntry entry : multiDeclaration.getEntries()) {
402                Name componentName = Name.identifier(DescriptorResolver.COMPONENT_FUNCTION_NAME_PREFIX + componentIndex);
403                componentIndex++;
404    
405                JetType expectedType = getExpectedTypeForComponent(context, entry);
406                OverloadResolutionResults<FunctionDescriptor> results =
407                        resolveFakeCall(context.replaceExpectedType(expectedType), receiver, componentName);
408    
409                JetType componentType = null;
410                if (results.isSuccess()) {
411                    context.trace.record(COMPONENT_RESOLVED_CALL, entry, results.getResultingCall());
412                    componentType = results.getResultingDescriptor().getReturnType();
413                    if (componentType != null && !noExpectedType(expectedType)
414                           && !JetTypeChecker.INSTANCE.isSubtypeOf(componentType, expectedType)) {
415    
416                        context.trace.report(
417                                COMPONENT_FUNCTION_RETURN_TYPE_MISMATCH.on(reportErrorsOn, componentName, componentType, expectedType));
418                    }
419                }
420                else if (results.isAmbiguity()) {
421                    context.trace.report(COMPONENT_FUNCTION_AMBIGUITY.on(reportErrorsOn, componentName, results.getResultingCalls()));
422                }
423                else {
424                    context.trace.report(COMPONENT_FUNCTION_MISSING.on(reportErrorsOn, componentName, receiver.getType()));
425                }
426                if (componentType == null) {
427                    componentType = ErrorUtils.createErrorType(componentName + "() return type");
428                }
429                VariableDescriptor variableDescriptor = context.expressionTypingServices.getDescriptorResolver().
430                    resolveLocalVariableDescriptorWithType(writableScope, entry, componentType, context.trace);
431    
432                VariableDescriptor olderVariable = writableScope.getLocalVariable(variableDescriptor.getName());
433                checkVariableShadowing(context, variableDescriptor, olderVariable);
434    
435                writableScope.addVariableDescriptor(variableDescriptor);
436            }
437        }
438    
439        public static void checkVariableShadowing(@NotNull ExpressionTypingContext context, @NotNull VariableDescriptor variableDescriptor, VariableDescriptor oldDescriptor) {
440            if (oldDescriptor != null && DescriptorUtils.isLocal(variableDescriptor.getContainingDeclaration(), oldDescriptor)) {
441                PsiElement declaration = BindingContextUtils.descriptorToDeclaration(context.trace.getBindingContext(), variableDescriptor);
442                if (declaration != null) {
443                    context.trace.report(Errors.NAME_SHADOWING.on(declaration, variableDescriptor.getName().asString()));
444                }
445            }
446        }
447    
448        @NotNull
449        private static JetType getExpectedTypeForComponent(ExpressionTypingContext context, JetMultiDeclarationEntry entry) {
450            JetTypeReference entryTypeRef = entry.getTypeRef();
451            if (entryTypeRef != null) {
452                return context.expressionTypingServices.getTypeResolver().resolveType(context.scope, entryTypeRef, context.trace, true);
453            }
454            else {
455                return TypeUtils.NO_EXPECTED_TYPE;
456            }
457        }
458    
459        public static ObservableBindingTrace makeTraceInterceptingTypeMismatch(@NotNull BindingTrace trace, @NotNull final JetElement expressionToWatch, @NotNull final boolean[] mismatchFound) {
460            return new ObservableBindingTrace(trace) {
461    
462                @Override
463                public void report(@NotNull Diagnostic diagnostic) {
464                    AbstractDiagnosticFactory factory = diagnostic.getFactory();
465                    if ((factory == TYPE_MISMATCH || factory == ERROR_COMPILE_TIME_VALUE)
466                            && diagnostic.getPsiElement() == expressionToWatch) {
467                        mismatchFound[0] = true;
468                    }
469                    if (TYPE_INFERENCE_ERRORS.contains(factory) &&
470                        PsiTreeUtil.isAncestor(expressionToWatch, diagnostic.getPsiElement(), false)) {
471                        mismatchFound[0] = true;
472                    }
473                    super.report(diagnostic);
474                }
475            };
476        }
477    
478        @NotNull
479        public static JetTypeInfo getTypeInfoOrNullType(
480                @Nullable JetExpression expression,
481                @NotNull ExpressionTypingContext context,
482                @NotNull ExpressionTypingInternals facade
483        ) {
484            return expression != null
485                   ? facade.getTypeInfo(expression, context)
486                   : JetTypeInfo.create(null, context.dataFlowInfo);
487        }
488    
489        @SuppressWarnings("SuspiciousMethodCalls")
490        public static boolean isBinaryExpressionDependentOnExpectedType(@NotNull JetBinaryExpression expression) {
491            IElementType operationType = expression.getOperationReference().getReferencedNameElementType();
492            return (operationType == JetTokens.IDENTIFIER || OperatorConventions.BINARY_OPERATION_NAMES.containsKey(operationType)
493                    || operationType == JetTokens.ELVIS);
494        }
495    }