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.google.common.collect.Maps;
021    import com.intellij.openapi.project.Project;
022    import com.intellij.openapi.util.Pair;
023    import com.intellij.psi.PsiElement;
024    import com.intellij.psi.tree.IElementType;
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.Diagnostic;
031    import org.jetbrains.jet.lang.diagnostics.DiagnosticFactory;
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.CallResolver;
036    import org.jetbrains.jet.lang.resolve.calls.autocasts.AutoCastUtils;
037    import org.jetbrains.jet.lang.resolve.calls.autocasts.DataFlowInfo;
038    import org.jetbrains.jet.lang.resolve.calls.callUtil.CallUtilPackage;
039    import org.jetbrains.jet.lang.resolve.calls.inference.ConstraintPosition;
040    import org.jetbrains.jet.lang.resolve.calls.inference.ConstraintSystem;
041    import org.jetbrains.jet.lang.resolve.calls.inference.ConstraintSystemImpl;
042    import org.jetbrains.jet.lang.resolve.calls.inference.ConstraintsUtil;
043    import org.jetbrains.jet.lang.resolve.calls.model.ResolvedCall;
044    import org.jetbrains.jet.lang.resolve.calls.results.OverloadResolutionResults;
045    import org.jetbrains.jet.lang.resolve.calls.util.CallMaker;
046    import org.jetbrains.jet.lang.resolve.dataClassUtils.DataClassUtilsPackage;
047    import org.jetbrains.jet.lang.resolve.name.Name;
048    import org.jetbrains.jet.lang.resolve.scopes.JetScope;
049    import org.jetbrains.jet.lang.resolve.scopes.WritableScope;
050    import org.jetbrains.jet.lang.resolve.scopes.WritableScopeImpl;
051    import org.jetbrains.jet.lang.resolve.scopes.receivers.ExpressionReceiver;
052    import org.jetbrains.jet.lang.resolve.scopes.receivers.ReceiverValue;
053    import org.jetbrains.jet.lang.types.*;
054    import org.jetbrains.jet.lang.types.checker.JetTypeChecker;
055    import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns;
056    import org.jetbrains.jet.lexer.JetTokens;
057    import org.jetbrains.jet.util.slicedmap.WritableSlice;
058    
059    import java.util.*;
060    
061    import static org.jetbrains.jet.lang.diagnostics.Errors.*;
062    import static org.jetbrains.jet.lang.psi.PsiPackage.JetPsiFactory;
063    import static org.jetbrains.jet.lang.resolve.BindingContext.*;
064    import static org.jetbrains.jet.lang.types.TypeUtils.noExpectedType;
065    
066    public class ExpressionTypingUtils {
067    
068        private final ExpressionTypingServices expressionTypingServices;
069        private final CallResolver callResolver;
070    
071        public ExpressionTypingUtils(@NotNull ExpressionTypingServices expressionTypingServices, @NotNull CallResolver resolver) {
072            this.expressionTypingServices = expressionTypingServices;
073            callResolver = resolver;
074        }
075    
076        @Nullable
077        protected static ExpressionReceiver getExpressionReceiver(@NotNull JetExpression expression, @Nullable JetType type) {
078            if (type == null) return null;
079            return new ExpressionReceiver(expression, type);
080        }
081    
082        @Nullable
083        protected static ExpressionReceiver getExpressionReceiver(@NotNull ExpressionTypingFacade facade, @NotNull JetExpression expression, ExpressionTypingContext context) {
084            return getExpressionReceiver(expression, facade.getTypeInfo(expression, context).getType());
085        }
086    
087        @NotNull
088        protected static ExpressionReceiver safeGetExpressionReceiver(@NotNull ExpressionTypingFacade facade, @NotNull JetExpression expression, ExpressionTypingContext context) {
089            JetType type = safeGetType(facade.safeGetTypeInfo(expression, context));
090            return new ExpressionReceiver(expression, type);
091        }
092    
093        @NotNull
094        public static JetType safeGetType(@NotNull JetTypeInfo typeInfo) {
095            JetType type = typeInfo.getType();
096            assert type != null : "safeGetType should be invoked on safe JetTypeInfo; safeGetTypeInfo should return @NotNull type";
097            return type;
098        }
099    
100        @NotNull
101        public static WritableScopeImpl newWritableScopeImpl(ExpressionTypingContext context, @NotNull String scopeDebugName) {
102            WritableScopeImpl scope = new WritableScopeImpl(
103                    context.scope, context.scope.getContainingDeclaration(), new TraceBasedRedeclarationHandler(context.trace), scopeDebugName);
104            scope.changeLockLevel(WritableScope.LockLevel.BOTH);
105            return scope;
106        }
107    
108        public static boolean isBoolean(@NotNull JetType type) {
109            return JetTypeChecker.DEFAULT.isSubtypeOf(type, KotlinBuiltIns.getInstance().getBooleanType());
110        }
111    
112        public static boolean ensureBooleanResult(JetExpression operationSign, Name name, JetType resultType, ExpressionTypingContext context) {
113            return ensureBooleanResultWithCustomSubject(operationSign, resultType, "'" + name + "'", context);
114        }
115    
116        public static boolean ensureBooleanResultWithCustomSubject(JetExpression operationSign, JetType resultType, String subjectName, ExpressionTypingContext context) {
117            if (resultType != null) {
118                // TODO : Relax?
119                if (!isBoolean(resultType)) {
120                    context.trace.report(RESULT_TYPE_MISMATCH.on(operationSign, subjectName, KotlinBuiltIns.getInstance().getBooleanType(), resultType));
121                    return false;
122                }
123            }
124            return true;
125        }
126    
127        @NotNull
128        public static JetType getDefaultType(IElementType constantType) {
129            if (constantType == JetNodeTypes.INTEGER_CONSTANT) {
130                return KotlinBuiltIns.getInstance().getIntType();
131            }
132            else if (constantType == JetNodeTypes.FLOAT_CONSTANT) {
133                return KotlinBuiltIns.getInstance().getDoubleType();
134            }
135            else if (constantType == JetNodeTypes.BOOLEAN_CONSTANT) {
136                return KotlinBuiltIns.getInstance().getBooleanType();
137            }
138            else if (constantType == JetNodeTypes.CHARACTER_CONSTANT) {
139                return KotlinBuiltIns.getInstance().getCharType();
140            }
141            else if (constantType == JetNodeTypes.NULL) {
142                return KotlinBuiltIns.getInstance().getNullableNothingType();
143            }
144            else {
145                throw new IllegalArgumentException("Unsupported constant type: " + constantType);
146            }
147        }
148    
149        private static boolean isCapturedInInline(
150                @NotNull BindingContext context,
151                @NotNull DeclarationDescriptor scopeContainer,
152                @NotNull DeclarationDescriptor variableParent
153        ) {
154            PsiElement scopeDeclaration = DescriptorToSourceUtils.descriptorToDeclaration(scopeContainer);
155            if (!(scopeDeclaration instanceof JetFunctionLiteral)) {
156                return false;
157            }
158    
159            PsiElement parent = scopeDeclaration.getParent();
160            assert parent instanceof JetFunctionLiteralExpression : "parent of JetFunctionLiteral is " + parent;
161            ResolvedCall<?> resolvedCall = CallUtilPackage.getParentResolvedCall((JetFunctionLiteralExpression) parent, context, true);
162            if (resolvedCall == null) {
163                return false;
164            }
165    
166            CallableDescriptor callable = resolvedCall.getResultingDescriptor();
167            if (callable instanceof SimpleFunctionDescriptor && ((SimpleFunctionDescriptor) callable).getInlineStrategy().isInline()) {
168                DeclarationDescriptor scopeContainerParent = scopeContainer.getContainingDeclaration();
169                assert scopeContainerParent != null : "parent is null for " + scopeContainer;
170                return scopeContainerParent == variableParent || isCapturedInInline(context, scopeContainerParent, variableParent);
171            }
172            else {
173                return false;
174            }
175        }
176    
177        public static void checkCapturingInClosure(JetSimpleNameExpression expression, BindingTrace trace, JetScope scope) {
178            VariableDescriptor variable = BindingContextUtils.extractVariableDescriptorIfAny(trace.getBindingContext(), expression, true);
179            if (variable != null) {
180                DeclarationDescriptor variableParent = variable.getContainingDeclaration();
181                DeclarationDescriptor scopeContainer = scope.getContainingDeclaration();
182                if (scopeContainer != variableParent && variableParent instanceof CallableDescriptor) {
183                    if (trace.get(CAPTURED_IN_CLOSURE, variable) != CaptureKind.NOT_INLINE) {
184                        boolean inline = isCapturedInInline(trace.getBindingContext(), scopeContainer, variableParent);
185                        trace.record(CAPTURED_IN_CLOSURE, variable, inline ? CaptureKind.INLINE_ONLY : CaptureKind.NOT_INLINE);
186                    }
187                }
188            }
189        }
190    
191        /*
192        * Checks if receiver declaration could be resolved to call expected receiver.
193        */
194        public static boolean checkIsExtensionCallable (
195                @NotNull ReceiverValue receiverArgument,
196                @NotNull CallableDescriptor callableDescriptor,
197                boolean isInfixCall,
198                @NotNull BindingContext bindingContext,
199                @NotNull DataFlowInfo dataFlowInfo
200        ) {
201            if (isInfixCall
202                  && (!(callableDescriptor instanceof SimpleFunctionDescriptor) || callableDescriptor.getValueParameters().size() != 1)) {
203                return false;
204            }
205    
206            List<JetType> types = AutoCastUtils.getAutoCastVariants(receiverArgument, bindingContext, dataFlowInfo);
207    
208            for (JetType type : types) {
209                if (checkReceiverResolution(receiverArgument, type, callableDescriptor)) return true;
210                if (type.isNullable()) {
211                    JetType notNullableType = TypeUtils.makeNotNullable(type);
212                    if (checkReceiverResolution(receiverArgument, notNullableType, callableDescriptor)) return true;
213                }
214            }
215    
216            return false;
217        }
218    
219        private static boolean checkReceiverResolution (
220                @NotNull ReceiverValue receiverArgument,
221                @NotNull JetType receiverType,
222                @NotNull CallableDescriptor callableDescriptor
223        ) {
224            ReceiverParameterDescriptor receiverParameter = callableDescriptor.getReceiverParameter();
225    
226            if (!receiverArgument.exists() && receiverParameter == null) {
227                // Both receivers do not exist
228                return true;
229            }
230    
231            if (!(receiverArgument.exists() && receiverParameter != null)) {
232                return false;
233            }
234    
235            Set<Name> typeNamesInReceiver = collectUsedTypeNames(receiverParameter.getType());
236    
237            ConstraintSystem constraintSystem = new ConstraintSystemImpl();
238            Map<TypeParameterDescriptor, Variance> typeVariables = Maps.newLinkedHashMap();
239            for (TypeParameterDescriptor typeParameterDescriptor : callableDescriptor.getTypeParameters()) {
240                if (typeNamesInReceiver.contains(typeParameterDescriptor.getName())) {
241                    typeVariables.put(typeParameterDescriptor, Variance.INVARIANT);
242                }
243            }
244            constraintSystem.registerTypeVariables(typeVariables);
245    
246            constraintSystem.addSubtypeConstraint(receiverType, receiverParameter.getType(), ConstraintPosition.RECEIVER_POSITION);
247            return constraintSystem.getStatus().isSuccessful() && ConstraintsUtil.checkBoundsAreSatisfied(constraintSystem, true);
248        }
249    
250        private static Set<Name> collectUsedTypeNames(@NotNull JetType jetType) {
251            Set<Name> typeNames = new HashSet<Name>();
252    
253            ClassifierDescriptor descriptor = jetType.getConstructor().getDeclarationDescriptor();
254            if (descriptor != null) {
255                typeNames.add(descriptor.getName());
256            }
257    
258            for (TypeProjection argument : jetType.getArguments()) {
259                typeNames.addAll(collectUsedTypeNames(argument.getType()));
260            }
261    
262            return typeNames;
263        }
264    
265        @NotNull
266        public OverloadResolutionResults<FunctionDescriptor> resolveFakeCall(
267                @NotNull ExpressionTypingContext context,
268                @NotNull ReceiverValue receiver,
269                @NotNull Name name,
270                @NotNull JetType... argumentTypes
271        ) {
272            TemporaryBindingTrace traceWithFakeArgumentInfo = TemporaryBindingTrace.create(context.trace, "trace to store fake argument for",
273                                                                                           name);
274            List<JetExpression> fakeArguments = Lists.newArrayList();
275            for (JetType type : argumentTypes) {
276                fakeArguments.add(createFakeExpressionOfType(expressionTypingServices.getProject(), traceWithFakeArgumentInfo,
277                                                             "fakeArgument" + fakeArguments.size(), type));
278            }
279            return makeAndResolveFakeCall(receiver, context.replaceBindingTrace(traceWithFakeArgumentInfo), fakeArguments, name).getSecond();
280        }
281    
282        public static JetExpression createFakeExpressionOfType(
283                @NotNull Project project,
284                @NotNull BindingTrace trace,
285                @NotNull String argumentName,
286                @NotNull JetType argumentType
287        ) {
288            JetExpression fakeExpression = JetPsiFactory(project).createExpression(argumentName);
289            trace.record(EXPRESSION_TYPE, fakeExpression, argumentType);
290            trace.record(PROCESSED, fakeExpression);
291            return fakeExpression;
292        }
293    
294        @NotNull
295        public OverloadResolutionResults<FunctionDescriptor> resolveFakeCall(
296                @NotNull ExpressionTypingContext context,
297                @NotNull ReceiverValue receiver,
298                @NotNull Name name
299        ) {
300            return resolveFakeCall(receiver, context, Collections.<JetExpression>emptyList(), name);
301        }
302    
303        @NotNull
304        public OverloadResolutionResults<FunctionDescriptor> resolveFakeCall(
305                @NotNull ReceiverValue receiver,
306                @NotNull ExpressionTypingContext context,
307                @NotNull List<JetExpression> valueArguments,
308                @NotNull Name name
309        ) {
310            return makeAndResolveFakeCall(receiver, context, valueArguments, name).getSecond();
311        }
312    
313        @NotNull
314        public Pair<Call, OverloadResolutionResults<FunctionDescriptor>> makeAndResolveFakeCall(
315                @NotNull ReceiverValue receiver,
316                @NotNull ExpressionTypingContext context,
317                @NotNull List<JetExpression> valueArguments,
318                @NotNull Name name
319        ) {
320            final JetReferenceExpression fake = JetPsiFactory(expressionTypingServices.getProject()).createSimpleName("fake");
321            TemporaryBindingTrace fakeTrace = TemporaryBindingTrace.create(context.trace, "trace to resolve fake call for", name);
322            Call call = CallMaker.makeCallWithExpressions(fake, receiver, null, fake, valueArguments);
323            OverloadResolutionResults<FunctionDescriptor> results =
324                    callResolver.resolveCallWithGivenName(context.replaceBindingTrace(fakeTrace), call, fake, name);
325            if (results.isSuccess()) {
326                fakeTrace.commit(new TraceEntryFilter() {
327                    @Override
328                    public boolean accept(@Nullable WritableSlice<?, ?> slice, Object key) {
329                        // excluding all entries related to fake expression
330                        return key != fake;
331                    }
332                }, true);
333            }
334            return Pair.create(call, results);
335        }
336    
337        public void defineLocalVariablesFromMultiDeclaration(
338                @NotNull WritableScope writableScope,
339                @NotNull JetMultiDeclaration multiDeclaration,
340                @NotNull ReceiverValue receiver,
341                @NotNull JetExpression reportErrorsOn,
342                @NotNull ExpressionTypingContext context
343        ) {
344            int componentIndex = 1;
345            for (JetMultiDeclarationEntry entry : multiDeclaration.getEntries()) {
346                Name componentName = DataClassUtilsPackage.createComponentName(componentIndex);
347                componentIndex++;
348    
349                JetType expectedType = getExpectedTypeForComponent(context, entry);
350                OverloadResolutionResults<FunctionDescriptor> results =
351                        resolveFakeCall(context.replaceExpectedType(expectedType), receiver, componentName);
352    
353                JetType componentType = null;
354                if (results.isSuccess()) {
355                    context.trace.record(COMPONENT_RESOLVED_CALL, entry, results.getResultingCall());
356                    componentType = results.getResultingDescriptor().getReturnType();
357                    if (componentType != null && !noExpectedType(expectedType)
358                           && !JetTypeChecker.DEFAULT.isSubtypeOf(componentType, expectedType)) {
359    
360                        context.trace.report(
361                                COMPONENT_FUNCTION_RETURN_TYPE_MISMATCH.on(reportErrorsOn, componentName, componentType, expectedType));
362                    }
363                }
364                else if (results.isAmbiguity()) {
365                    context.trace.report(COMPONENT_FUNCTION_AMBIGUITY.on(reportErrorsOn, componentName, results.getResultingCalls()));
366                }
367                else {
368                    context.trace.report(COMPONENT_FUNCTION_MISSING.on(reportErrorsOn, componentName, receiver.getType()));
369                }
370                if (componentType == null) {
371                    componentType = ErrorUtils.createErrorType(componentName + "() return type");
372                }
373                VariableDescriptor variableDescriptor = expressionTypingServices.getDescriptorResolver().
374                    resolveLocalVariableDescriptorWithType(writableScope, entry, componentType, context.trace);
375    
376                VariableDescriptor olderVariable = writableScope.getLocalVariable(variableDescriptor.getName());
377                checkVariableShadowing(context, variableDescriptor, olderVariable);
378    
379                writableScope.addVariableDescriptor(variableDescriptor);
380            }
381        }
382    
383        public static void checkVariableShadowing(@NotNull ExpressionTypingContext context, @NotNull VariableDescriptor variableDescriptor, VariableDescriptor oldDescriptor) {
384            if (oldDescriptor != null && isLocal(variableDescriptor.getContainingDeclaration(), oldDescriptor)) {
385                PsiElement declaration = DescriptorToSourceUtils.descriptorToDeclaration(variableDescriptor);
386                if (declaration != null) {
387                    context.trace.report(Errors.NAME_SHADOWING.on(declaration, variableDescriptor.getName().asString()));
388                }
389            }
390        }
391    
392        @NotNull
393        private JetType getExpectedTypeForComponent(ExpressionTypingContext context, JetMultiDeclarationEntry entry) {
394            JetTypeReference entryTypeRef = entry.getTypeRef();
395            if (entryTypeRef != null) {
396                return expressionTypingServices.getTypeResolver().resolveType(context.scope, entryTypeRef, context.trace, true);
397            }
398            else {
399                return TypeUtils.NO_EXPECTED_TYPE;
400            }
401        }
402    
403        public static ObservableBindingTrace makeTraceInterceptingTypeMismatch(@NotNull BindingTrace trace, @NotNull final JetElement expressionToWatch, @NotNull final boolean[] mismatchFound) {
404            return new ObservableBindingTrace(trace) {
405    
406                @Override
407                public void report(@NotNull Diagnostic diagnostic) {
408                    DiagnosticFactory<?> factory = diagnostic.getFactory();
409                    if ((factory == TYPE_MISMATCH || factory == CONSTANT_EXPECTED_TYPE_MISMATCH || factory == NULL_FOR_NONNULL_TYPE)
410                            && diagnostic.getPsiElement() == expressionToWatch) {
411                        mismatchFound[0] = true;
412                    }
413                    if (TYPE_INFERENCE_ERRORS.contains(factory) &&
414                        PsiTreeUtil.isAncestor(expressionToWatch, diagnostic.getPsiElement(), false)) {
415                        mismatchFound[0] = true;
416                    }
417                    super.report(diagnostic);
418                }
419            };
420        }
421    
422        @NotNull
423        public static JetTypeInfo getTypeInfoOrNullType(
424                @Nullable JetExpression expression,
425                @NotNull ExpressionTypingContext context,
426                @NotNull ExpressionTypingInternals facade
427        ) {
428            return expression != null
429                   ? facade.getTypeInfo(expression, context)
430                   : JetTypeInfo.create(null, context.dataFlowInfo);
431        }
432    
433        @SuppressWarnings("SuspiciousMethodCalls")
434        public static boolean isBinaryExpressionDependentOnExpectedType(@NotNull JetBinaryExpression expression) {
435            IElementType operationType = expression.getOperationReference().getReferencedNameElementType();
436            return (operationType == JetTokens.IDENTIFIER || OperatorConventions.BINARY_OPERATION_NAMES.containsKey(operationType)
437                    || operationType == JetTokens.ELVIS);
438        }
439    
440        public static boolean isUnaryExpressionDependentOnExpectedType(@NotNull JetUnaryExpression expression) {
441            return expression.getOperationReference().getReferencedNameElementType() == JetTokens.EXCLEXCL;
442        }
443    
444        @NotNull
445        public static List<JetType> getValueParametersTypes(@NotNull List<ValueParameterDescriptor> valueParameters) {
446            List<JetType> parameterTypes = new ArrayList<JetType>(valueParameters.size());
447            for (ValueParameterDescriptor parameter : valueParameters) {
448                parameterTypes.add(parameter.getType());
449            }
450            return parameterTypes;
451        }
452    
453        /**
454         * The primary case for local extensions is the following:
455         *
456         * I had a locally declared extension function or a local variable of function type called foo
457         * And I called it on my x
458         * Now, someone added function foo() to the class of x
459         * My code should not change
460         *
461         * thus
462         *
463         * local extension prevail over members (and members prevail over all non-local extensions)
464         */
465        public static boolean isLocal(DeclarationDescriptor containerOfTheCurrentLocality, DeclarationDescriptor candidate) {
466            if (candidate instanceof ValueParameterDescriptor) {
467                return true;
468            }
469            DeclarationDescriptor parent = candidate.getContainingDeclaration();
470            if (!(parent instanceof FunctionDescriptor)) {
471                return false;
472            }
473            FunctionDescriptor functionDescriptor = (FunctionDescriptor) parent;
474            DeclarationDescriptor current = containerOfTheCurrentLocality;
475            while (current != null) {
476                if (current == functionDescriptor) {
477                    return true;
478                }
479                current = current.getContainingDeclaration();
480            }
481            return false;
482        }
483    
484        public static boolean dependsOnExpectedType(@Nullable JetExpression expression) {
485            JetExpression expr = JetPsiUtil.deparenthesize(expression, false);
486            if (expr == null) return false;
487    
488            if (expr instanceof JetBinaryExpressionWithTypeRHS) {
489                return false;
490            }
491            if (expr instanceof JetBinaryExpression) {
492                return isBinaryExpressionDependentOnExpectedType((JetBinaryExpression) expr);
493            }
494            if (expr instanceof JetUnaryExpression) {
495                return isUnaryExpressionDependentOnExpectedType((JetUnaryExpression) expr);
496            }
497            return true;
498        }
499    }