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