All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.jetbrains.jet.lang.resolve.calls.CandidateResolver Maven / Gradle / Ivy

/*
 * Copyright 2010-2013 JetBrains s.r.o.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.jetbrains.jet.lang.resolve.calls;

import com.google.common.base.Function;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.intellij.openapi.progress.ProgressIndicatorProvider;
import com.intellij.psi.PsiElement;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.lang.descriptors.*;
import org.jetbrains.jet.lang.psi.*;
import org.jetbrains.jet.lang.resolve.*;
import org.jetbrains.jet.lang.resolve.calls.autocasts.AutoCastUtils;
import org.jetbrains.jet.lang.resolve.calls.autocasts.DataFlowInfo;
import org.jetbrains.jet.lang.resolve.calls.autocasts.DataFlowValue;
import org.jetbrains.jet.lang.resolve.calls.autocasts.DataFlowValueFactory;
import org.jetbrains.jet.lang.resolve.calls.context.*;
import org.jetbrains.jet.lang.resolve.calls.inference.*;
import org.jetbrains.jet.lang.resolve.calls.model.*;
import org.jetbrains.jet.lang.resolve.calls.results.OverloadResolutionResultsImpl;
import org.jetbrains.jet.lang.resolve.calls.results.ResolutionDebugInfo;
import org.jetbrains.jet.lang.resolve.calls.results.ResolutionStatus;
import org.jetbrains.jet.lang.resolve.calls.tasks.ResolutionTask;
import org.jetbrains.jet.lang.resolve.calls.tasks.TaskPrioritizer;
import org.jetbrains.jet.lang.resolve.calls.tasks.TracingStrategy;
import org.jetbrains.jet.lang.resolve.scopes.receivers.ExpressionReceiver;
import org.jetbrains.jet.lang.resolve.scopes.receivers.ReceiverValue;
import org.jetbrains.jet.lang.types.*;
import org.jetbrains.jet.lang.types.checker.JetTypeChecker;
import org.jetbrains.jet.lang.types.expressions.DataFlowUtils;
import org.jetbrains.jet.lang.types.expressions.ExpressionTypingUtils;
import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns;

import javax.inject.Inject;
import java.util.*;

import static org.jetbrains.jet.lang.diagnostics.Errors.PROJECTION_ON_NON_CLASS_TYPE_ARGUMENT;
import static org.jetbrains.jet.lang.diagnostics.Errors.SUPER_IS_NOT_AN_EXPRESSION;
import static org.jetbrains.jet.lang.resolve.calls.CallResolverUtil.ResolveArgumentsMode.RESOLVE_FUNCTION_ARGUMENTS;
import static org.jetbrains.jet.lang.resolve.calls.CallResolverUtil.ResolveArgumentsMode.SHAPE_FUNCTION_ARGUMENTS;
import static org.jetbrains.jet.lang.resolve.calls.CallTransformer.CallForImplicitInvoke;
import static org.jetbrains.jet.lang.resolve.calls.context.ContextDependency.INDEPENDENT;
import static org.jetbrains.jet.lang.resolve.calls.results.ResolutionStatus.*;
import static org.jetbrains.jet.lang.types.TypeUtils.*;

public class CandidateResolver {
    @NotNull
    private ArgumentTypeResolver argumentTypeResolver;

    @Inject
    public void setArgumentTypeResolver(@NotNull ArgumentTypeResolver argumentTypeResolver) {
        this.argumentTypeResolver = argumentTypeResolver;
    }

    public  void performResolutionForCandidateCall(
            @NotNull CallCandidateResolutionContext context,
            @NotNull ResolutionTask task) {

        ProgressIndicatorProvider.checkCanceled();

        MutableResolvedCall candidateCall = context.candidateCall;
        D candidate = candidateCall.getCandidateDescriptor();

        candidateCall.addStatus(checkReceiverTypeError(context));

        if (ErrorUtils.isError(candidate)) {
            candidateCall.addStatus(SUCCESS);
            markAllArgumentsAsUnmapped(context);
            return;
        }

        if (!checkOuterClassMemberIsAccessible(context)) {
            candidateCall.addStatus(OTHER_ERROR);
            markAllArgumentsAsUnmapped(context);
            return;
        }


        DeclarationDescriptorWithVisibility invisibleMember =
                Visibilities.findInvisibleMember(candidate, context.scope.getContainingDeclaration());
        if (invisibleMember != null) {
            candidateCall.addStatus(OTHER_ERROR);
            context.tracing.invisibleMember(context.trace, invisibleMember);
        }

        if (task.checkArguments == CheckValueArgumentsMode.ENABLED) {
            Set unmappedArguments = Sets.newLinkedHashSet();
            ValueArgumentsToParametersMapper.Status argumentMappingStatus = ValueArgumentsToParametersMapper.mapValueArgumentsToParameters(
                    context.call, context.tracing, candidateCall, unmappedArguments);
            if (!argumentMappingStatus.isSuccess()) {
                candidateCall.addUnmappedArguments(unmappedArguments);
                //For the expressions like '42.(f)()' where f: () -> Unit we'd like to generate an error 'no receiver admitted',
                //not to throw away the candidate.
                if (argumentMappingStatus == ValueArgumentsToParametersMapper.Status.STRONG_ERROR
                            && !CallResolverUtil.isInvokeCallOnExpressionWithBothReceivers(context.call)) {
                    candidateCall.addStatus(RECEIVER_PRESENCE_ERROR);
                    return;
                }
                else {
                    candidateCall.addStatus(OTHER_ERROR);
                }
            }
        }

        List jetTypeArguments = context.call.getTypeArguments();
        if (jetTypeArguments.isEmpty()) {
            if (!candidate.getTypeParameters().isEmpty()) {
                ResolutionStatus status = inferTypeArguments(context);
                candidateCall.addStatus(status);
            }
            else {
                candidateCall.addStatus(checkAllValueArguments(context, SHAPE_FUNCTION_ARGUMENTS).status);
            }
        }
        else {
            // Explicit type arguments passed

            List typeArguments = new ArrayList();
            for (JetTypeProjection projection : jetTypeArguments) {
                if (projection.getProjectionKind() != JetProjectionKind.NONE) {
                    context.trace.report(PROJECTION_ON_NON_CLASS_TYPE_ARGUMENT.on(projection));
                }
                typeArguments.add(argumentTypeResolver.resolveTypeRefWithDefault(
                        projection.getTypeReference(), context.scope, context.trace, ErrorUtils.createErrorType("Star projection in a call")));
            }
            int expectedTypeArgumentCount = candidate.getTypeParameters().size();
            if (expectedTypeArgumentCount == jetTypeArguments.size()) {

                checkGenericBoundsInAFunctionCall(jetTypeArguments, typeArguments, candidate, context.trace);

                Map
                        substitutionContext = FunctionDescriptorUtil
                        .createSubstitutionContext((FunctionDescriptor) candidate, typeArguments);
                TypeSubstitutor substitutor = TypeSubstitutor.create(substitutionContext);
                candidateCall.setResultingSubstitutor(substitutor);

                candidateCall.addStatus(checkAllValueArguments(context, SHAPE_FUNCTION_ARGUMENTS).status);
            }
            else {
                candidateCall.addStatus(OTHER_ERROR);
                context.tracing.wrongNumberOfTypeArguments(context.trace, expectedTypeArgumentCount);
            }
        }

        task.performAdvancedChecks(candidate, context.trace, context.tracing);

        // 'super' cannot be passed as an argument, for receiver arguments expression typer does not track this
        // See TaskPrioritizer for more
        JetSuperExpression superExpression = TaskPrioritizer.getReceiverSuper(candidateCall.getReceiverArgument());
        if (superExpression != null) {
            context.trace.report(SUPER_IS_NOT_AN_EXPRESSION.on(superExpression, superExpression.getText()));
            candidateCall.addStatus(OTHER_ERROR);
        }
    }

    private static void markAllArgumentsAsUnmapped(CallCandidateResolutionContext context) {
        if (context.checkArguments == CheckValueArgumentsMode.ENABLED) {
            context.candidateCall.addUnmappedArguments(context.call.getValueArguments());
        }
    }

    private static boolean checkOuterClassMemberIsAccessible(@NotNull CallCandidateResolutionContext context) {
        // In "[email protected]()" the error will be reported on "this@Outer" instead
        if (context.call.getExplicitReceiver().exists() || context.call.getThisObject().exists()) return true;

        ClassDescriptor candidateThis = getDeclaringClass(context.candidateCall.getCandidateDescriptor());
        if (candidateThis == null || candidateThis.getKind().isSingleton()) return true;

        return DescriptorResolver.checkHasOuterClassInstance(context.scope, context.trace, context.call.getCallElement(), candidateThis);
    }

    @Nullable
    private static ClassDescriptor getDeclaringClass(@NotNull CallableDescriptor candidate) {
        ReceiverParameterDescriptor expectedThis = candidate.getExpectedThisObject();
        if (expectedThis == null) return null;
        DeclarationDescriptor descriptor = expectedThis.getContainingDeclaration();
        return descriptor instanceof ClassDescriptor ? (ClassDescriptor) descriptor : null;
    }

    public  void completeTypeInferenceDependentOnFunctionLiteralsForCall(
            CallCandidateResolutionContext context
    ) {
        MutableResolvedCall resolvedCall = context.candidateCall;
        ConstraintSystem constraintSystem = resolvedCall.getConstraintSystem();
        if (!resolvedCall.hasIncompleteTypeParameters() || constraintSystem == null) return;

        // constraints for function literals
        // Value parameters
        for (Map.Entry entry : resolvedCall.getValueArguments().entrySet()) {
            ResolvedValueArgument resolvedValueArgument = entry.getValue();
            ValueParameterDescriptor valueParameterDescriptor = entry.getKey();

            for (ValueArgument valueArgument : resolvedValueArgument.getArguments()) {
                addConstraintForFunctionLiteral(valueArgument, valueParameterDescriptor, constraintSystem, context);
            }
        }
        resolvedCall.setResultingSubstitutor(constraintSystem.getResultingSubstitutor());
    }

    @Nullable
    public  JetType completeTypeInferenceDependentOnExpectedTypeForCall(
            @NotNull CallCandidateResolutionContext context,
            boolean isInnerCall
    ) {
        MutableResolvedCall resolvedCall = context.candidateCall;
        if (resolvedCall.isCompleted()) {
            return resolvedCall.getResultingDescriptor().getReturnType();
        }
        if (!resolvedCall.hasIncompleteTypeParameters()) {
            completeNestedCallsInference(context);
            checkValueArgumentTypes(context);
            resolvedCall.markCallAsCompleted();
            return resolvedCall.getResultingDescriptor().getReturnType();
        }

        assert resolvedCall.getConstraintSystem() != null;

        JetType unsubstitutedReturnType = resolvedCall.getCandidateDescriptor().getReturnType();
        if (unsubstitutedReturnType != null) {
            resolvedCall.getConstraintSystem().addSupertypeConstraint(
                    context.expectedType, unsubstitutedReturnType, ConstraintPosition.EXPECTED_TYPE_POSITION);
        }

        updateSystemWithConstraintSystemCompleter(context, resolvedCall);

        updateSystemIfExpectedTypeIsUnit(context, resolvedCall);

        ((ConstraintSystemImpl)resolvedCall.getConstraintSystem()).processDeclaredBoundConstraints();

        JetType returnType;
        if (!resolvedCall.getConstraintSystem().getStatus().isSuccessful()) {
            returnType = reportInferenceError(context);
        }
        else {
            resolvedCall.setResultingSubstitutor(resolvedCall.getConstraintSystem().getResultingSubstitutor());

            completeNestedCallsInference(context);
            // Here we type check the arguments with inferred types expected
            checkAllValueArguments(context, context.trace, RESOLVE_FUNCTION_ARGUMENTS);

            resolvedCall.setHasIncompleteTypeParameters(false);
            ResolutionStatus status = resolvedCall.getStatus();
            if (status == ResolutionStatus.UNKNOWN_STATUS || status == ResolutionStatus.INCOMPLETE_TYPE_INFERENCE) {
                resolvedCall.setStatusToSuccess();
            }
            returnType = resolvedCall.getResultingDescriptor().getReturnType();
            if (isInnerCall) {
                PsiElement callElement = context.call.getCallElement();
                if (callElement instanceof JetCallExpression) {
                    DataFlowUtils.checkType(returnType, (JetCallExpression) callElement, context, context.dataFlowInfo);
                }
            }
        }
        resolvedCall.markCallAsCompleted();
        return returnType;
    }

    private  void completeTypeInferenceForAllCandidatesForArgument(
            @NotNull CallCandidateResolutionContext context,
            @Nullable JetExpression argumentExpression
    ) {
        // All candidates for inner calls are not needed, so there is no need to complete them
        if (context.collectAllCandidates) return;

        if (argumentExpression == null) return;

        CallKey callKey = CallKey.create(argumentExpression);
        OverloadResolutionResultsImpl resolutionResults = context.resolutionResultsCache.getResolutionResults(callKey);
        if (resolutionResults == null) return;

        completeTypeInferenceForAllCandidates(context.toBasic(), resolutionResults);
    }

    public  void completeTypeInferenceForAllCandidates(
            @NotNull BasicCallResolutionContext context,
            @NotNull OverloadResolutionResultsImpl results
    ) {
        Collection> candidates;
        if (context.collectAllCandidates) {
            candidates = results.getAllCandidates();
            assert candidates != null : "Should be guaranteed by collectAllCandidates == true";
        }
        else {
            candidates = results.getResultingCalls();
        }
        for (ResolvedCall resolvedCall : candidates) {
            MutableResolvedCall mutableResolvedCall = (MutableResolvedCall) resolvedCall;
            if (mutableResolvedCall.isCompleted()) continue;

            TemporaryBindingTrace temporaryBindingTrace = TemporaryBindingTrace.create(
                    context.trace, "Trace to complete a candidate that is not a resulting call");

            CallCandidateResolutionContext callCandidateResolutionContext = CallCandidateResolutionContext.createForCallBeingAnalyzed(
                    mutableResolvedCall, context.replaceBindingTrace(temporaryBindingTrace), TracingStrategy.EMPTY);

            completeTypeInferenceDependentOnExpectedTypeForCall(callCandidateResolutionContext, false);
        }
    }

    private static  void updateSystemWithConstraintSystemCompleter(
            @NotNull CallCandidateResolutionContext context,
            @NotNull MutableResolvedCall resolvedCall
    ) {
        ConstraintSystem constraintSystem = resolvedCall.getConstraintSystem();
        assert constraintSystem != null;
        ConstraintSystemCompleter constraintSystemCompleter = context.trace.get(
                BindingContext.CONSTRAINT_SYSTEM_COMPLETER, context.call.getCalleeExpression());
        if (constraintSystemCompleter == null) return;

        ConstraintSystem copy = constraintSystem.copy();

        constraintSystemCompleter.completeConstraintSystem(copy, resolvedCall);

        //todo improve error reporting with errors in constraints from completer
        if (!copy.getStatus().hasOnlyErrorsFromPosition(ConstraintPosition.FROM_COMPLETER)) {
            resolvedCall.setConstraintSystem(copy);
        }
    }

    private static  void updateSystemIfExpectedTypeIsUnit(
            @NotNull CallCandidateResolutionContext context,
            @NotNull MutableResolvedCall resolvedCall
    ) {
        ConstraintSystem constraintSystem = resolvedCall.getConstraintSystem();
        assert constraintSystem != null;
        JetType returnType = resolvedCall.getCandidateDescriptor().getReturnType();
        if (returnType == null) return;

        if (!constraintSystem.getStatus().isSuccessful() && context.expectedType == TypeUtils.UNIT_EXPECTED_TYPE) {
            ConstraintSystemImpl copy = (ConstraintSystemImpl) constraintSystem.copy();

            copy.addSupertypeConstraint(KotlinBuiltIns.getInstance().getUnitType(), returnType, ConstraintPosition.EXPECTED_TYPE_POSITION);
            if (copy.getStatus().isSuccessful()) {
                resolvedCall.setConstraintSystem(copy);
            }
        }
    }

    private  JetType reportInferenceError(
            @NotNull CallCandidateResolutionContext context
    ) {
        MutableResolvedCall resolvedCall = context.candidateCall;
        ConstraintSystem constraintSystem = resolvedCall.getConstraintSystem();
        assert constraintSystem != null;

        resolvedCall.setResultingSubstitutor(constraintSystem.getResultingSubstitutor());
        completeNestedCallsInference(context);
        List argumentTypes = checkValueArgumentTypes(
                context, resolvedCall, context.trace, RESOLVE_FUNCTION_ARGUMENTS).argumentTypes;
        JetType receiverType = resolvedCall.getReceiverArgument().exists() ? resolvedCall.getReceiverArgument().getType() : null;
        InferenceErrorData errorData = InferenceErrorData
                .create(resolvedCall.getCandidateDescriptor(), constraintSystem, argumentTypes, receiverType, context.expectedType);

        context.tracing.typeInferenceFailed(context.trace, errorData);
        resolvedCall.addStatus(ResolutionStatus.OTHER_ERROR);
        if (!resolvedCall.hasInferredReturnType()) return null;
        return resolvedCall.getResultingDescriptor().getReturnType();
    }

    public  void completeNestedCallsInference(
            @NotNull CallCandidateResolutionContext context
    ) {
        if (CallResolverUtil.isInvokeCallOnVariable(context.call)) return;
        MutableResolvedCall resolvedCall = context.candidateCall;
        for (Map.Entry entry : resolvedCall.getValueArguments().entrySet()) {
            ValueParameterDescriptor parameterDescriptor = entry.getKey();
            ResolvedValueArgument resolvedArgument = entry.getValue();

            for (ValueArgument argument : resolvedArgument.getArguments()) {
                completeInferenceForArgument(argument, parameterDescriptor, context);
            }
        }
        completeUnmappedArguments(context, context.candidateCall.getUnmappedArguments());
        recordReferenceForInvokeFunction(context);
    }

    private  void completeInferenceForArgument(
            @NotNull ValueArgument argument,
            @NotNull ValueParameterDescriptor parameterDescriptor,
            @NotNull CallCandidateResolutionContext context
    ) {
        JetExpression expression = argument.getArgumentExpression();
        if (expression == null) return;

        JetType expectedType = getEffectiveExpectedType(parameterDescriptor, argument);
        context = context.replaceExpectedType(expectedType);

        JetExpression keyExpression = getDeferredComputationKeyExpression(expression);
        CallCandidateResolutionContext storedContextForArgument = context.resolutionResultsCache.getDeferredComputation(keyExpression);

        PsiElement parent = expression.getParent();
        if (parent instanceof JetWhenExpression && expression == ((JetWhenExpression) parent).getSubjectExpression()
            || (expression instanceof JetFunctionLiteralExpression)) {
            return;
        }
        if (storedContextForArgument == null) {
            JetType type = ArgumentTypeResolver.updateResultArgumentTypeIfNotDenotable(context, expression);
            checkResultArgumentType(type, argument, context);
            completeTypeInferenceForAllCandidatesForArgument(context, keyExpression);
            return;
        }

        CallCandidateResolutionContext contextForArgument = storedContextForArgument
                .replaceContextDependency(INDEPENDENT).replaceBindingTrace(context.trace).replaceExpectedType(expectedType);
        JetType type = completeTypeInferenceDependentOnExpectedTypeForCall(contextForArgument, true);
        JetType recordedType = context.trace.get(BindingContext.EXPRESSION_TYPE, expression);
        if (recordedType != null && !recordedType.getConstructor().isDenotable()) {
            type = ArgumentTypeResolver.updateResultArgumentTypeIfNotDenotable(context, expression);
        }

        JetType result = BindingContextUtils.updateRecordedType(
                type, expression, context.trace, isFairSafeCallExpression(expression, context.trace));

        completeTypeInferenceForAllCandidatesForArgument(context, keyExpression);

        DataFlowUtils.checkType(result, expression, contextForArgument);
    }

    public void completeNestedCallsForNotResolvedInvocation(@NotNull CallResolutionContext context) {
        completeNestedCallsForNotResolvedInvocation(context, context.call.getValueArguments());
    }

    public void completeUnmappedArguments(@NotNull CallResolutionContext context, @NotNull Collection unmappedArguments) {
        completeNestedCallsForNotResolvedInvocation(context, unmappedArguments);
    }

    private void completeNestedCallsForNotResolvedInvocation(@NotNull CallResolutionContext context, @NotNull Collection arguments) {
        if (CallResolverUtil.isInvokeCallOnVariable(context.call)) return;
        if (context.checkArguments == CheckValueArgumentsMode.DISABLED) return;

        for (ValueArgument argument : arguments) {
            JetExpression expression = argument.getArgumentExpression();

            JetExpression keyExpression = getDeferredComputationKeyExpression(expression);

            CallCandidateResolutionContext storedContextForArgument =
                    context.resolutionResultsCache.getDeferredComputation(keyExpression);
            if (storedContextForArgument == null) continue;
            if (storedContextForArgument.candidateCall.isCompleted()) continue;

            CallCandidateResolutionContext newContext =
                    storedContextForArgument.replaceBindingTrace(context.trace).replaceContextDependency(INDEPENDENT);
            completeTypeInferenceDependentOnExpectedTypeForCall(newContext, true);
        }
    }

    @Nullable
    private JetExpression getDeferredComputationKeyExpression(@Nullable JetExpression expression) {
        if (expression == null) return null;
        return expression.accept(new JetVisitor() {
            @Nullable
            private JetExpression visitInnerExpression(@Nullable JetElement expression) {
                if (expression == null) return null;
                return expression.accept(this, null);
            }

            @Override
            public JetExpression visitQualifiedExpression(@NotNull JetQualifiedExpression expression, Void data) {
                return visitInnerExpression(expression.getSelectorExpression());
            }

            @Override
            public JetExpression visitExpression(@NotNull JetExpression expression, Void data) {
                return expression;
            }

            @Override
            public JetExpression visitParenthesizedExpression(@NotNull JetParenthesizedExpression expression, Void data) {
                return visitInnerExpression(expression.getExpression());
            }

            @Override
            public JetExpression visitUnaryExpression(@NotNull JetUnaryExpression expression, Void data) {
                return ExpressionTypingUtils.isUnaryExpressionDependentOnExpectedType(expression) ? expression : null;
            }

            @Override
            public JetExpression visitLabeledExpression(@NotNull JetLabeledExpression expression, Void data) {
                return visitInnerExpression(expression.getBaseExpression());
            }

            @Override
            public JetExpression visitBlockExpression(@NotNull JetBlockExpression expression, Void data) {
                JetElement lastStatement = JetPsiUtil.getLastStatementInABlock(expression);
                if (lastStatement != null) {
                    return visitInnerExpression(lastStatement);
                }
                return expression;
            }

            @Override
            public JetExpression visitBinaryExpression(@NotNull JetBinaryExpression expression, Void data) {
                return ExpressionTypingUtils.isBinaryExpressionDependentOnExpectedType(expression) ? expression : null;
            }
        }, null);
    }

    private static boolean isFairSafeCallExpression(@NotNull JetExpression expression, @NotNull BindingTrace trace) {
        // We are interested in type of the last call:
        // 'a.b?.foo()' is safe call, but 'a?.b.foo()' is not.
        // Since receiver is 'a.b' and selector is 'foo()',
        // we can only check if an expression is safe call.
        if (!(expression instanceof JetSafeQualifiedExpression)) return false;

        JetSafeQualifiedExpression safeQualifiedExpression = (JetSafeQualifiedExpression) expression;
        //If a receiver type is not null, then this safe expression is useless, and we don't need to make the result type nullable.
        JetType type = trace.get(BindingContext.EXPRESSION_TYPE, safeQualifiedExpression.getReceiverExpression());
        return type != null && type.isNullable();
    }

    private static  void checkResultArgumentType(
            @Nullable JetType type,
            @NotNull ValueArgument argument,
            @NotNull CallCandidateResolutionContext context
    ) {
        JetExpression expression = argument.getArgumentExpression();
        if (expression == null) return;

        DataFlowInfo dataFlowInfoForValueArgument = context.candidateCall.getDataFlowInfoForArguments().getInfo(argument);
        ResolutionContext newContext = context.replaceExpectedType(context.expectedType).replaceDataFlowInfo(
                dataFlowInfoForValueArgument);
        DataFlowUtils.checkType(type, expression, newContext);
    }

    private static  void recordReferenceForInvokeFunction(CallCandidateResolutionContext context) {
        PsiElement callElement = context.call.getCallElement();
        if (!(callElement instanceof JetCallExpression)) return;

        JetCallExpression callExpression = (JetCallExpression) callElement;
        if (BindingContextUtils.isCallExpressionWithValidReference(callExpression, context.trace.getBindingContext())) {
            CallableDescriptor resultingDescriptor = context.candidateCall.getResultingDescriptor();
            context.trace.record(BindingContext.EXPRESSION_TYPE, callExpression, resultingDescriptor.getReturnType());
            context.trace.record(BindingContext.REFERENCE_TARGET, callExpression, context.candidateCall.getCandidateDescriptor());
        }
    }

    private  void addConstraintForFunctionLiteral(
            @NotNull ValueArgument valueArgument,
            @NotNull ValueParameterDescriptor valueParameterDescriptor,
            @NotNull ConstraintSystem constraintSystem,
            @NotNull CallCandidateResolutionContext context
    ) {
        JetExpression argumentExpression = valueArgument.getArgumentExpression();
        if (argumentExpression == null) return;
        if (!ArgumentTypeResolver.isFunctionLiteralArgument(argumentExpression)) return;

        JetFunctionLiteralExpression functionLiteralExpression = ArgumentTypeResolver.getFunctionLiteralArgument(argumentExpression);

        JetType effectiveExpectedType = getEffectiveExpectedType(valueParameterDescriptor, valueArgument);
        JetType expectedType = constraintSystem.getCurrentSubstitutor().substitute(effectiveExpectedType, Variance.INVARIANT);
        if (expectedType == null || expectedType == DONT_CARE) {
            expectedType = argumentTypeResolver.getShapeTypeOfFunctionLiteral(functionLiteralExpression, context.scope, context.trace, false);
        }
        if (expectedType == null || !KotlinBuiltIns.getInstance().isFunctionOrExtensionFunctionType(expectedType)
                || CallResolverUtil.hasUnknownFunctionParameter(expectedType)) {
            return;
        }
        MutableDataFlowInfoForArguments dataFlowInfoForArguments = context.candidateCall.getDataFlowInfoForArguments();
        DataFlowInfo dataFlowInfoForArgument = dataFlowInfoForArguments.getInfo(valueArgument);

        //todo analyze function literal body once in 'dependent' mode, then complete it with respect to expected type
        boolean hasExpectedReturnType = !CallResolverUtil.hasUnknownReturnType(expectedType);
        if (hasExpectedReturnType) {
            TemporaryTraceAndCache temporaryToResolveFunctionLiteral = TemporaryTraceAndCache.create(
                    context, "trace to resolve function literal with expected return type", argumentExpression);

            JetElement statementExpression = JetPsiUtil.getLastStatementInABlock(functionLiteralExpression.getBodyExpression());
            if (statementExpression == null) return;
            boolean[] mismatch = new boolean[1];
            ObservableBindingTrace errorInterceptingTrace = ExpressionTypingUtils.makeTraceInterceptingTypeMismatch(
                    temporaryToResolveFunctionLiteral.trace, statementExpression, mismatch);
            CallCandidateResolutionContext newContext = context
                    .replaceBindingTrace(errorInterceptingTrace).replaceExpectedType(expectedType)
                    .replaceDataFlowInfo(dataFlowInfoForArgument).replaceResolutionResultsCache(temporaryToResolveFunctionLiteral.cache)
                    .replaceContextDependency(INDEPENDENT);
            JetType type = argumentTypeResolver.getFunctionLiteralTypeInfo(
                    argumentExpression, functionLiteralExpression, newContext, RESOLVE_FUNCTION_ARGUMENTS).getType();
            if (!mismatch[0]) {
                constraintSystem.addSubtypeConstraint(
                        type, effectiveExpectedType, ConstraintPosition.getValueParameterPosition(valueParameterDescriptor.getIndex()));
                temporaryToResolveFunctionLiteral.commit();
                return;
            }
        }
        JetType expectedTypeWithoutReturnType = hasExpectedReturnType ? CallResolverUtil.replaceReturnTypeByUnknown(expectedType) : expectedType;
        CallCandidateResolutionContext newContext = context
                .replaceExpectedType(expectedTypeWithoutReturnType).replaceDataFlowInfo(dataFlowInfoForArgument)
                .replaceContextDependency(INDEPENDENT);
        JetType type = argumentTypeResolver.getFunctionLiteralTypeInfo(argumentExpression, functionLiteralExpression, newContext,
                                                                       RESOLVE_FUNCTION_ARGUMENTS).getType();
        constraintSystem.addSubtypeConstraint(
                type, effectiveExpectedType, ConstraintPosition.getValueParameterPosition(valueParameterDescriptor.getIndex()));
    }

    private  ResolutionStatus inferTypeArguments(CallCandidateResolutionContext context) {
        MutableResolvedCall candidateCall = context.candidateCall;
        final D candidate = candidateCall.getCandidateDescriptor();

        context.trace.get(ResolutionDebugInfo.RESOLUTION_DEBUG_INFO, context.call.getCallElement());

        ConstraintSystemImpl constraintSystem = new ConstraintSystemImpl();

        // If the call is recursive, e.g.
        //   fun foo(t : T) : T = foo(t)
        // we can't use same descriptor objects for T's as actual type values and same T's as unknowns,
        // because constraints become trivial (T :< T), and inference fails
        //
        // Thus, we replace the parameters of our descriptor with fresh objects (perform alpha-conversion)
        CallableDescriptor candidateWithFreshVariables = FunctionDescriptorUtil.alphaConvertTypeParameters(candidate);

        Map typeVariables = Maps.newLinkedHashMap();
        for (TypeParameterDescriptor typeParameterDescriptor : candidateWithFreshVariables.getTypeParameters()) {
            typeVariables.put(typeParameterDescriptor, Variance.INVARIANT); // TODO: variance of the occurrences
        }
        constraintSystem.registerTypeVariables(typeVariables);

        TypeSubstitutor substituteDontCare =
                makeConstantSubstitutor(candidateWithFreshVariables.getTypeParameters(), DONT_CARE);

        // Value parameters
        for (Map.Entry entry : candidateCall.getValueArguments().entrySet()) {
            ResolvedValueArgument resolvedValueArgument = entry.getValue();
            ValueParameterDescriptor valueParameterDescriptor = candidateWithFreshVariables.getValueParameters().get(entry.getKey().getIndex());


            for (ValueArgument valueArgument : resolvedValueArgument.getArguments()) {
                // TODO : more attempts, with different expected types

                // Here we type check expecting an error type (DONT_CARE, substitution with substituteDontCare)
                // and throw the results away
                // We'll type check the arguments later, with the inferred types expected
                boolean[] isErrorType = new boolean[1];
                addConstraintForValueArgument(valueArgument, valueParameterDescriptor, substituteDontCare, constraintSystem,
                                              context, isErrorType, SHAPE_FUNCTION_ARGUMENTS);
                if (isErrorType[0]) {
                    candidateCall.argumentHasNoType();
                }
            }
        }

        // Receiver
        // Error is already reported if something is missing
        ReceiverValue receiverArgument = candidateCall.getReceiverArgument();
        ReceiverParameterDescriptor receiverParameter = candidateWithFreshVariables.getReceiverParameter();
        if (receiverArgument.exists() && receiverParameter != null) {
            JetType receiverType =
                    context.candidateCall.isSafeCall()
                    ? TypeUtils.makeNotNullable(receiverArgument.getType())
                    : receiverArgument.getType();
            if (receiverArgument instanceof ExpressionReceiver) {
                receiverType = updateResultTypeForSmartCasts(receiverType, ((ExpressionReceiver) receiverArgument).getExpression(),
                                                             context.dataFlowInfo, context.trace);
            }
            constraintSystem.addSubtypeConstraint(receiverType, receiverParameter.getType(), ConstraintPosition.RECEIVER_POSITION);
        }

        // Restore type variables before alpha-conversion
        ConstraintSystem constraintSystemWithRightTypeParameters = constraintSystem.substituteTypeVariables(
                new Function() {
                    @Override
                    public TypeParameterDescriptor apply(@Nullable TypeParameterDescriptor typeParameterDescriptor) {
                        assert typeParameterDescriptor != null;
                        return candidate.getTypeParameters().get(typeParameterDescriptor.getIndex());
                    }
                });
        candidateCall.setConstraintSystem(constraintSystemWithRightTypeParameters);


        // Solution
        boolean hasContradiction = constraintSystem.getStatus().hasContradiction();
        candidateCall.setHasIncompleteTypeParameters(true);
        if (!hasContradiction) {
            return INCOMPLETE_TYPE_INFERENCE;
        }
        ValueArgumentsCheckingResult checkingResult = checkAllValueArguments(context, SHAPE_FUNCTION_ARGUMENTS);
        ResolutionStatus argumentsStatus = checkingResult.status;
        return OTHER_ERROR.combine(argumentsStatus);
    }

    private void addConstraintForValueArgument(
            @NotNull ValueArgument valueArgument,
            @NotNull ValueParameterDescriptor valueParameterDescriptor,
            @NotNull TypeSubstitutor substitutor,
            @NotNull ConstraintSystem constraintSystem,
            @NotNull CallCandidateResolutionContext context,
            @Nullable boolean[] isErrorType,
            @NotNull CallResolverUtil.ResolveArgumentsMode resolveFunctionArgumentBodies) {

        JetType effectiveExpectedType = getEffectiveExpectedType(valueParameterDescriptor, valueArgument);
        JetExpression argumentExpression = valueArgument.getArgumentExpression();

        JetType expectedType = substitutor.substitute(effectiveExpectedType, Variance.INVARIANT);
        DataFlowInfo dataFlowInfoForArgument = context.candidateCall.getDataFlowInfoForArguments().getInfo(valueArgument);
        CallResolutionContext newContext = context.replaceExpectedType(expectedType).replaceDataFlowInfo(dataFlowInfoForArgument);

        JetTypeInfo typeInfoForCall = argumentTypeResolver.getArgumentTypeInfo(
                argumentExpression, newContext, resolveFunctionArgumentBodies);
        context.candidateCall.getDataFlowInfoForArguments().updateInfo(valueArgument, typeInfoForCall.getDataFlowInfo());

        JetType type = updateResultTypeForSmartCasts(typeInfoForCall.getType(), argumentExpression, dataFlowInfoForArgument, context.trace);
        constraintSystem.addSubtypeConstraint(type, effectiveExpectedType, ConstraintPosition.getValueParameterPosition(
                valueParameterDescriptor.getIndex()));
        if (isErrorType != null) {
            isErrorType[0] = type == null || type.isError();
        }
    }

    @Nullable
    private static JetType updateResultTypeForSmartCasts(
            @Nullable JetType type,
            @Nullable JetExpression argumentExpression,
            @NotNull DataFlowInfo dataFlowInfoForArgument,
            @NotNull BindingTrace trace
    ) {
        if (argumentExpression == null || type == null) return type;

        DataFlowValue dataFlowValue = DataFlowValueFactory.createDataFlowValue(
                argumentExpression, type, trace.getBindingContext());
        if (!dataFlowValue.isStableIdentifier()) return type;

        Set possibleTypes = dataFlowInfoForArgument.getPossibleTypes(dataFlowValue);
        if (possibleTypes.isEmpty()) return type;

        return TypeUtils.intersect(JetTypeChecker.INSTANCE, possibleTypes);
    }

    private  ValueArgumentsCheckingResult checkAllValueArguments(
            @NotNull CallCandidateResolutionContext context,
            @NotNull CallResolverUtil.ResolveArgumentsMode resolveFunctionArgumentBodies) {
        return checkAllValueArguments(context, context.candidateCall.getTrace(), resolveFunctionArgumentBodies);
    }

    private  ValueArgumentsCheckingResult checkAllValueArguments(
            @NotNull CallCandidateResolutionContext context,
            @NotNull BindingTrace trace,
            @NotNull CallResolverUtil.ResolveArgumentsMode resolveFunctionArgumentBodies
    ) {
        ValueArgumentsCheckingResult checkingResult = checkValueArgumentTypes(
                context, context.candidateCall, trace, resolveFunctionArgumentBodies);
        ResolutionStatus resultStatus = checkingResult.status;
        resultStatus = resultStatus.combine(checkReceivers(context, trace));

        return new ValueArgumentsCheckingResult(resultStatus, checkingResult.argumentTypes);
    }

    private static  ResolutionStatus checkReceivers(
            @NotNull CallCandidateResolutionContext context,
            @NotNull BindingTrace trace
    ) {
        ResolutionStatus resultStatus = SUCCESS;
        ResolvedCall candidateCall = context.candidateCall;

        resultStatus = resultStatus.combine(checkReceiverTypeError(context));

        // Comment about a very special case.
        // Call 'b.foo(1)' where class 'Foo' has an extension member 'fun B.invoke(Int)' should be checked two times for safe call (in 'checkReceiver'), because
        // both 'b' (receiver) and 'foo' (this object) might be nullable. In the first case we mark dot, in the second 'foo'.
        // Class 'CallForImplicitInvoke' helps up to recognise this case, and parameter 'implicitInvokeCheck' helps us to distinguish whether we check receiver or this object.

        resultStatus = resultStatus.combine(checkReceiver(
                context, candidateCall, trace,
                candidateCall.getResultingDescriptor().getReceiverParameter(),
                candidateCall.getReceiverArgument(), candidateCall.getExplicitReceiverKind().isReceiver(), false));

        resultStatus = resultStatus.combine(checkReceiver(
                context, candidateCall, trace,
                candidateCall.getResultingDescriptor().getExpectedThisObject(), candidateCall.getThisObject(),
                candidateCall.getExplicitReceiverKind().isThisObject(),
                // for the invocation 'foo(1)' where foo is a variable of function type we should mark 'foo' if there is unsafe call error
                context.call instanceof CallForImplicitInvoke));
        return resultStatus;
    }

    public  ValueArgumentsCheckingResult checkValueArgumentTypes(
            @NotNull CallCandidateResolutionContext context
    ) {
        return checkValueArgumentTypes(context, context.candidateCall, context.trace, RESOLVE_FUNCTION_ARGUMENTS);
    }

    private > ValueArgumentsCheckingResult checkValueArgumentTypes(
            @NotNull CallResolutionContext context,
            @NotNull MutableResolvedCall candidateCall,
            @NotNull BindingTrace trace,
            @NotNull CallResolverUtil.ResolveArgumentsMode resolveFunctionArgumentBodies) {
        ResolutionStatus resultStatus = SUCCESS;
        List argumentTypes = Lists.newArrayList();
        MutableDataFlowInfoForArguments infoForArguments = candidateCall.getDataFlowInfoForArguments();
        for (Map.Entry entry : candidateCall.getValueArguments().entrySet()) {
            ValueParameterDescriptor parameterDescriptor = entry.getKey();
            ResolvedValueArgument resolvedArgument = entry.getValue();


            for (ValueArgument argument : resolvedArgument.getArguments()) {
                JetExpression expression = argument.getArgumentExpression();
                if (expression == null) continue;

                JetType expectedType = getEffectiveExpectedType(parameterDescriptor, argument);
                if (TypeUtils.dependsOnTypeParameters(expectedType, candidateCall.getCandidateDescriptor().getTypeParameters())) {
                    expectedType = NO_EXPECTED_TYPE;
                }

                CallResolutionContext newContext = context.replaceDataFlowInfo(infoForArguments.getInfo(argument))
                        .replaceBindingTrace(trace).replaceExpectedType(expectedType);
                JetTypeInfo typeInfoForCall = argumentTypeResolver.getArgumentTypeInfo(
                        expression, newContext, resolveFunctionArgumentBodies);
                JetType type = typeInfoForCall.getType();
                infoForArguments.updateInfo(argument, typeInfoForCall.getDataFlowInfo());

                boolean hasTypeMismatch = false;
                if (type == null || (type.isError() && type != PLACEHOLDER_FUNCTION_TYPE)) {
                    candidateCall.argumentHasNoType();
                    argumentTypes.add(type);
                    hasTypeMismatch = true;
                }
                else {
                    JetType resultingType;
                    if (noExpectedType(expectedType) || ArgumentTypeResolver.isSubtypeOfForArgumentType(type, expectedType)) {
                        resultingType = type;
                    }
                    else {
                        resultingType = autocastValueArgumentTypeIfPossible(expression, expectedType, type, newContext);
                        if (resultingType == null) {
                            resultingType = type;
                            resultStatus = OTHER_ERROR;
                            hasTypeMismatch = true;
                        }
                    }

                    argumentTypes.add(resultingType);
                }
                candidateCall.recordArgumentMatch(argument, parameterDescriptor, hasTypeMismatch);
            }
        }
        return new ValueArgumentsCheckingResult(resultStatus, argumentTypes);
    }

    @Nullable
    private static JetType autocastValueArgumentTypeIfPossible(
            @NotNull JetExpression expression,
            @NotNull JetType expectedType,
            @NotNull JetType actualType,
            @NotNull ResolutionContext context
    ) {
        ExpressionReceiver receiverToCast = new ExpressionReceiver(JetPsiUtil.safeDeparenthesize(expression, false), actualType);
        List variants =
                AutoCastUtils.getAutoCastVariantsExcludingReceiver(context.trace.getBindingContext(), context.dataFlowInfo, receiverToCast);
        for (JetType possibleType : variants) {
            if (JetTypeChecker.INSTANCE.isSubtypeOf(possibleType, expectedType)) {
                return possibleType;
            }
        }
        return null;
    }

    private static  ResolutionStatus checkReceiverTypeError(
            @NotNull CallCandidateResolutionContext context
    ) {
        MutableResolvedCall candidateCall = context.candidateCall;
        D candidateDescriptor = candidateCall.getCandidateDescriptor();

        ReceiverParameterDescriptor receiverDescriptor = candidateDescriptor.getReceiverParameter();
        ReceiverParameterDescriptor expectedThisObjectDescriptor = candidateDescriptor.getExpectedThisObject();
        ResolutionStatus status = SUCCESS;
        // For the expressions like '42.(f)()' where f: String.() -> Unit we'd like to generate a type mismatch error on '1',
        // not to throw away the candidate, so the following check is skipped.
        if (!CallResolverUtil.isInvokeCallOnExpressionWithBothReceivers(context.call)) {
            status = status.combine(checkReceiverTypeError(context, receiverDescriptor, candidateCall.getReceiverArgument()));
        }
        status = status.combine(checkReceiverTypeError(context, expectedThisObjectDescriptor, candidateCall.getThisObject()));
        return status;
    }

    private static  ResolutionStatus checkReceiverTypeError(
            @NotNull CallCandidateResolutionContext context,
            @Nullable ReceiverParameterDescriptor receiverParameterDescriptor,
            @NotNull ReceiverValue receiverArgument
    ) {
        if (receiverParameterDescriptor == null || !receiverArgument.exists()) return SUCCESS;

        D candidateDescriptor = context.candidateCall.getCandidateDescriptor();

        JetType erasedReceiverType = CallResolverUtil.getErasedReceiverType(receiverParameterDescriptor, candidateDescriptor);

        boolean isSubtypeByAutoCast = AutoCastUtils.isSubTypeByAutoCastIgnoringNullability(receiverArgument, erasedReceiverType, context);
        if (!isSubtypeByAutoCast) {
            return RECEIVER_TYPE_ERROR;
        }

        return SUCCESS;
    }

    private static  ResolutionStatus checkReceiver(
            @NotNull CallCandidateResolutionContext context,
            @NotNull ResolvedCall candidateCall,
            @NotNull BindingTrace trace,
            @Nullable ReceiverParameterDescriptor receiverParameter,
            @NotNull ReceiverValue receiverArgument,
            boolean isExplicitReceiver,
            boolean implicitInvokeCheck
    ) {
        if (receiverParameter == null || !receiverArgument.exists()) return SUCCESS;
        D candidateDescriptor = candidateCall.getCandidateDescriptor();
        if (TypeUtils.dependsOnTypeParameters(receiverParameter.getType(), candidateDescriptor.getTypeParameters())) return SUCCESS;

        boolean safeAccess = isExplicitReceiver && !implicitInvokeCheck && candidateCall.isSafeCall();
        boolean isSubtypeByAutoCast = AutoCastUtils.isSubTypeByAutoCastIgnoringNullability(
                receiverArgument, receiverParameter.getType(), context);
        if (!isSubtypeByAutoCast) {
            context.tracing.wrongReceiverType(trace, receiverParameter, receiverArgument);
            return OTHER_ERROR;
        }
        AutoCastUtils.recordAutoCastIfNecessary(receiverArgument, receiverParameter.getType(), context, safeAccess);

        JetType receiverArgumentType = receiverArgument.getType();

        BindingContext bindingContext = trace.getBindingContext();
        if (!safeAccess && !receiverParameter.getType().isNullable() && receiverArgumentType.isNullable()) {
            if (!AutoCastUtils.isNotNull(receiverArgument, bindingContext, context.dataFlowInfo)) {

                context.tracing.unsafeCall(trace, receiverArgumentType, implicitInvokeCheck);
                return UNSAFE_CALL_ERROR;
            }
        }
        DataFlowValue receiverValue = DataFlowValueFactory.createDataFlowValue(receiverArgument, bindingContext);
        if (safeAccess && !context.dataFlowInfo.getNullability(receiverValue).canBeNull()) {
            context.tracing.unnecessarySafeCall(trace, receiverArgumentType);
        }
        return SUCCESS;
    }

    private static class ValueArgumentsCheckingResult {

        public final List argumentTypes;
        public final ResolutionStatus status;

        private ValueArgumentsCheckingResult(@NotNull ResolutionStatus status, @NotNull List argumentTypes) {
            this.status = status;
            this.argumentTypes = argumentTypes;
        }
    }

    @NotNull
    private static JetType getEffectiveExpectedType(ValueParameterDescriptor parameterDescriptor, ValueArgument argument) {
        if (argument.getSpreadElement() != null) {
            if (parameterDescriptor.getVarargElementType() == null) {
                // Spread argument passed to a non-vararg parameter, an error is already reported by ValueArgumentsToParametersMapper
                return DONT_CARE;
            }
            else {
                return parameterDescriptor.getType();
            }
        }
        else {
            JetType varargElementType = parameterDescriptor.getVarargElementType();
            if (varargElementType != null) {
                return varargElementType;
            }

            return parameterDescriptor.getType();
        }
    }

    private static void checkGenericBoundsInAFunctionCall(
            @NotNull List jetTypeArguments,
            @NotNull List typeArguments,
            @NotNull CallableDescriptor functionDescriptor,
            @NotNull BindingTrace trace) {
        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 TypeProjectionImpl(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();
            if (typeReference != null) {
                DescriptorResolver.checkBounds(typeReference, typeArgument, typeParameterDescriptor, substitutor, trace);
            }
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy