@Nullable
  private static JetType computeUnsafeReturnType(
      @NotNull JetFunctionLiteralExpression expression,
      @NotNull ExpressionTypingContext context,
      @NotNull SimpleFunctionDescriptorImpl functionDescriptor,
      @Nullable JetType expectedReturnType) {
    JetFunctionLiteral functionLiteral = expression.getFunctionLiteral();
    JetBlockExpression bodyExpression = functionLiteral.getBodyExpression();
    assert bodyExpression != null;

    JetScope functionInnerScope =
        FunctionDescriptorUtil.getFunctionInnerScope(
            context.scope, functionDescriptor, context.trace);
    JetTypeReference returnTypeRef = functionLiteral.getReturnTypeRef();
    JetType declaredReturnType = null;
    if (returnTypeRef != null) {
      declaredReturnType =
          context
              .expressionTypingServices
              .getTypeResolver()
              .resolveType(context.scope, returnTypeRef, context.trace, true);
      // This is needed for ControlStructureTypingVisitor#visitReturnExpression() to properly
      // type-check returned expressions
      functionDescriptor.setReturnType(declaredReturnType);
      if (expectedReturnType != null) {
        if (!JetTypeChecker.INSTANCE.isSubtypeOf(declaredReturnType, expectedReturnType)) {
          context.trace.report(EXPECTED_RETURN_TYPE_MISMATCH.on(returnTypeRef, expectedReturnType));
        }
      }
    }

    // Type-check the body
    ExpressionTypingContext newContext =
        context
            .replaceScope(functionInnerScope)
            .replaceExpectedType(
                declaredReturnType != null
                    ? declaredReturnType
                    : (expectedReturnType != null ? expectedReturnType : NO_EXPECTED_TYPE));

    JetType typeOfBodyExpression =
        context
            .expressionTypingServices
            .getBlockReturnedType(bodyExpression, COERCION_TO_UNIT, newContext)
            .getType();

    List<JetType> returnedExpressionTypes =
        Lists.newArrayList(
            getTypesOfLocallyReturnedExpressions(
                functionLiteral, context.trace, collectReturns(bodyExpression)));
    ContainerUtil.addIfNotNull(returnedExpressionTypes, typeOfBodyExpression);

    if (declaredReturnType != null) return declaredReturnType;
    if (returnedExpressionTypes.isEmpty()) return null;
    return CommonSupertypes.commonSupertype(returnedExpressionTypes);
  }
  @Override
  public JetTypeInfo visitFunctionLiteralExpression(
      @NotNull JetFunctionLiteralExpression expression, ExpressionTypingContext context) {
    JetBlockExpression bodyExpression = expression.getFunctionLiteral().getBodyExpression();
    if (bodyExpression == null) return null;

    Name callerName = getCallerName(expression);
    if (callerName != null) {
      context.labelResolver.enterLabeledElement(new LabelName(callerName.asString()), expression);
    }

    JetType expectedType = context.expectedType;
    boolean functionTypeExpected =
        !noExpectedType(expectedType)
            && KotlinBuiltIns.getInstance().isFunctionOrExtensionFunctionType(expectedType);

    AnonymousFunctionDescriptor functionDescriptor =
        createFunctionDescriptor(expression, context, functionTypeExpected);
    JetType safeReturnType =
        computeReturnType(expression, context, functionDescriptor, functionTypeExpected);
    functionDescriptor.setReturnType(safeReturnType);

    JetType receiver =
        DescriptorUtils.getReceiverParameterType(functionDescriptor.getReceiverParameter());
    List<JetType> valueParametersTypes =
        DescriptorUtils.getValueParametersTypes(functionDescriptor.getValueParameters());
    JetType resultType =
        KotlinBuiltIns.getInstance()
            .getFunctionType(
                Collections.<AnnotationDescriptor>emptyList(),
                receiver,
                valueParametersTypes,
                safeReturnType);
    if (!noExpectedType(expectedType)
        && KotlinBuiltIns.getInstance().isFunctionOrExtensionFunctionType(expectedType)) {
      // all checks were done before
      return JetTypeInfo.create(resultType, context.dataFlowInfo);
    }

    if (callerName != null) {
      context.labelResolver.exitLabeledElement(expression);
    }

    return DataFlowUtils.checkType(resultType, expression, context, context.dataFlowInfo);
  }
  @NotNull
  private static AnonymousFunctionDescriptor createFunctionDescriptor(
      @NotNull JetFunctionLiteralExpression expression,
      @NotNull ExpressionTypingContext context,
      boolean functionTypeExpected) {
    JetFunctionLiteral functionLiteral = expression.getFunctionLiteral();
    JetTypeReference receiverTypeRef = functionLiteral.getReceiverTypeRef();
    AnonymousFunctionDescriptor functionDescriptor =
        new AnonymousFunctionDescriptor(
            context.scope.getContainingDeclaration(),
            Collections.<AnnotationDescriptor>emptyList(),
            CallableMemberDescriptor.Kind.DECLARATION);

    List<ValueParameterDescriptor> valueParameterDescriptors =
        createValueParameterDescriptors(
            context, functionLiteral,
            functionDescriptor, functionTypeExpected);

    JetType effectiveReceiverType;
    if (receiverTypeRef == null) {
      if (functionTypeExpected) {
        effectiveReceiverType = KotlinBuiltIns.getInstance().getReceiverType(context.expectedType);
      } else {
        effectiveReceiverType = null;
      }
    } else {
      effectiveReceiverType =
          context
              .expressionTypingServices
              .getTypeResolver()
              .resolveType(context.scope, receiverTypeRef, context.trace, true);
    }
    functionDescriptor.initialize(
        effectiveReceiverType,
        ReceiverParameterDescriptor.NO_RECEIVER_PARAMETER,
        Collections.<TypeParameterDescriptorImpl>emptyList(),
        valueParameterDescriptors,
        /*unsubstitutedReturnType = */ null,
        Modality.FINAL,
        Visibilities.LOCAL);
    BindingContextUtils.recordFunctionDeclarationToDescriptor(
        context.trace, functionLiteral, functionDescriptor);
    return functionDescriptor;
  }
  @NotNull
  private static JetType computeReturnType(
      @NotNull JetFunctionLiteralExpression expression,
      @NotNull ExpressionTypingContext context,
      @NotNull SimpleFunctionDescriptorImpl functionDescriptor,
      boolean functionTypeExpected) {
    JetType expectedReturnType =
        functionTypeExpected
            ? KotlinBuiltIns.getInstance().getReturnTypeFromFunctionType(context.expectedType)
            : null;
    JetType returnType =
        computeUnsafeReturnType(expression, context, functionDescriptor, expectedReturnType);

    if (!expression.getFunctionLiteral().hasDeclaredReturnType() && functionTypeExpected) {
      if (KotlinBuiltIns.getInstance().isUnit(expectedReturnType)) {
        return KotlinBuiltIns.getInstance().getUnitType();
      }
    }
    return returnType == null ? CANT_INFER_LAMBDA_PARAM_TYPE : returnType;
  }
  @Nullable
  private static JetCallExpression getContainingCallExpression(
      JetFunctionLiteralExpression expression) {
    PsiElement parent = expression.getParent();
    if (parent instanceof JetCallExpression) {
      // f {}
      return (JetCallExpression) parent;
    }

    if (parent instanceof JetValueArgument) {
      // f ({}) or f(p = {})
      JetValueArgument argument = (JetValueArgument) parent;
      PsiElement argList = argument.getParent();
      if (argList == null) return null;
      PsiElement call = argList.getParent();
      if (call instanceof JetCallExpression) {
        return (JetCallExpression) call;
      }
    }
    return null;
  }