//===--- CSDiagnostics.h - Constraint Diagnostics -------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2018 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
//
// This file provides necessary abstractions for constraint system diagnostics.
//
//===----------------------------------------------------------------------===//
#ifndef SWIFT_SEMA_CSDIAGNOSTICS_H
#define SWIFT_SEMA_CSDIAGNOSTICS_H

#include "Constraint.h"
#include "ConstraintSystem.h"
#include "OverloadChoice.h"
#include "swift/AST/ASTContext.h"
#include "swift/AST/Decl.h"
#include "swift/AST/DiagnosticEngine.h"
#include "swift/AST/Expr.h"
#include "swift/AST/Identifier.h"
#include "swift/AST/Types.h"
#include "swift/Basic/SourceLoc.h"
#include "llvm/ADT/ArrayRef.h"
#include <tuple>

namespace swift {
namespace constraints {

/// Base class for all of the possible diagnostics,
/// provides most basic information such as location of
/// the problem, parent expression and some utility methods.
class FailureDiagnostic {
  Expr *E;
  ConstraintSystem &CS;
  ConstraintLocator *Locator;

  /// The original anchor before any simplification.
  Expr *RawAnchor;
  /// Simplified anchor associated with the given locator.
  Expr *Anchor;
  /// Indicates whether locator could be simplified
  /// down to anchor expression.
  bool HasComplexLocator;

public:
  FailureDiagnostic(Expr *expr, ConstraintSystem &cs,
                    ConstraintLocator *locator)
      : E(expr), CS(cs), Locator(locator), RawAnchor(locator->getAnchor()) {
    std::tie(Anchor, HasComplexLocator) = computeAnchor();
  }

  virtual ~FailureDiagnostic();

  /// Try to diagnose a problem given affected expression,
  /// failure location, types and declarations deduced by
  /// constraint system, and other auxiliary information.
  ///
  /// \param asNote In ambiguity cases it's beneficial to
  /// produce diagnostic as a note instead of an error if possible.
  ///
  /// \returns true If the problem has been successfully diagnosed
  /// and diagnostic message emitted, false otherwise.
  bool diagnose(bool asNote = false);

  /// Try to produce an error diagnostic for the problem at hand.
  virtual bool diagnoseAsError() = 0;

  /// Instead of producing an error diagnostic, attempt to
  /// produce a "note" to complement some other diagnostic
  /// e.g. ambiguity error.
  virtual bool diagnoseAsNote();

  ConstraintSystem &getConstraintSystem() const {
    return CS;
  }

  Expr *getParentExpr() const { return E; }

  Expr *getRawAnchor() const { return RawAnchor; }

  Expr *getAnchor() const { return Anchor; }

  ConstraintLocator *getLocator() const { return Locator; }

  Type getType(Expr *expr) const;

  /// Resolve type variables present in the raw type, if any.
  Type resolveType(Type rawType, bool reconstituteSugar = false) const {
    auto resolvedType = CS.simplifyType(rawType);
    return reconstituteSugar
               ? resolvedType->reconstituteSugar(/*recursive*/ true)
               : resolvedType;
  }

  template <typename... ArgTypes>
  InFlightDiagnostic emitDiagnostic(ArgTypes &&... Args) const;

protected:
  TypeChecker &getTypeChecker() const { return CS.TC; }

  DeclContext *getDC() const { return CS.DC; }

  ASTContext &getASTContext() const { return CS.getASTContext(); }

  Optional<std::pair<Type, ConversionRestrictionKind>>
  getRestrictionForType(Type type) const {
    for (auto &restriction : CS.ConstraintRestrictions) {
      if (std::get<0>(restriction)->isEqual(type))
        return std::pair<Type, ConversionRestrictionKind>(
            std::get<1>(restriction), std::get<2>(restriction));
    }
    return None;
  }

  ValueDecl *getResolvedMemberRef(UnresolvedDotExpr *member) {
    auto locator = CS.getConstraintLocator(member, ConstraintLocator::Member);
    return CS.findResolvedMemberRef(locator);
  }

  Optional<SelectedOverload>
  getOverloadChoiceIfAvailable(ConstraintLocator *locator) const {
    if (auto *overload = getResolvedOverload(locator))
      return Optional<SelectedOverload>(
           {overload->Choice, overload->OpenedFullType, overload->ImpliedType});
    return None;
  }

  /// Retrieve overload choice resolved for given locator
  /// by the constraint solver.
  ResolvedOverloadSetListItem *
  getResolvedOverload(ConstraintLocator *locator) const {
    auto resolvedOverload = CS.getResolvedOverloadSets();
    while (resolvedOverload) {
      if (resolvedOverload->Locator == locator)
        return resolvedOverload;
      resolvedOverload = resolvedOverload->Previous;
    }
    return nullptr;
  }

  /// \returns true is locator hasn't been simplified down to expression.
  bool hasComplexLocator() const { return HasComplexLocator; }

  /// \returns A parent expression if sub-expression is contained anywhere
  /// in the root expression or `nullptr` otherwise.
  Expr *findParentExpr(Expr *subExpr) const;

  /// \returns An argument expression if given anchor is a call, member
  /// reference or subscript, nullptr otherwise.
  Expr *getArgumentExprFor(Expr *anchor) const;

private:
  /// Compute anchor expression associated with current diagnostic.
  std::pair<Expr *, bool> computeAnchor() const;
};

/// Base class for all of the diagnostics related to generic requirement
/// failures, provides common information like failed requirement,
/// declaration where such requirement comes from, etc.
class RequirementFailure : public FailureDiagnostic {
protected:
  using PathEltKind = ConstraintLocator::PathElementKind;
  using DiagOnDecl = Diag<DescriptiveDeclKind, DeclName, Type, Type>;
  using DiagInReference = Diag<DescriptiveDeclKind, DeclName, Type, Type, Type>;
  using DiagAsNote = Diag<Type, Type, Type, Type, StringRef>;

  /// If this failure associated with one of the conditional requirements,
  /// this field would represent conformance where requirement comes from.
  const ProtocolConformance *Conformance = nullptr;

  /// The source of the requirement, if available. One exception
  /// is failure associated with conditional requirement where
  /// underlying conformance is specialized.
  const GenericSignature *Signature;

  const ValueDecl *AffectedDecl;
  /// If possible, find application expression associated
  /// with current generic requirement failure, that helps
  /// to diagnose failures related to arguments.
  const ApplyExpr *Apply = nullptr;

public:
  RequirementFailure(ConstraintSystem &cs, Expr *expr, RequirementKind kind,
                     ConstraintLocator *locator)
      : FailureDiagnostic(expr, cs, locator),
        Conformance(getConformanceForConditionalReq(locator)),
        Signature(getSignature(locator)), AffectedDecl(getDeclRef()) {
    assert(locator);
    assert(isConditional() || Signature);
    assert(AffectedDecl);

    auto path = locator->getPath();
    assert(!path.empty());

    auto &last = path.back();
    assert(last.isTypeParameterRequirement() ||
           last.isConditionalRequirement());
    assert(static_cast<RequirementKind>(last.getValue2()) == kind);

    // It's possible sometimes not to have no base expression.
    if (!expr)
      return;

    if (auto *parentExpr = findParentExpr(getRawAnchor()))
      Apply = dyn_cast<ApplyExpr>(parentExpr);
  }

  unsigned getRequirementIndex() const {
    auto path = getLocator()->getPath();
    assert(!path.empty());

    auto &requirementLoc = path.back();
    assert(requirementLoc.isTypeParameterRequirement() ||
           requirementLoc.isConditionalRequirement());
    return requirementLoc.getValue();
  }

  /// The generic base type where failing requirement comes from.
  Type getOwnerType() const;

  /// Generic context associated with the failure.
  const GenericContext *getGenericContext() const;

  /// Generic requirement associated with the failure.
  const Requirement &getRequirement() const;

  virtual Type getLHS() const = 0;
  virtual Type getRHS() const = 0;

  bool diagnoseAsError() override;
  bool diagnoseAsNote() override;

protected:
  /// Determine whether this is a conditional requirement failure.
  bool isConditional() const { return bool(Conformance); }

  /// Check whether this requirement comes from the contextual type
  /// that root expression is coerced/converted into.
  bool isFromContextualType() const;

  /// Retrieve declaration contextual where current
  /// requirement has been introduced.
  const DeclContext *getRequirementDC() const;

  virtual DiagOnDecl getDiagnosticOnDecl() const = 0;
  virtual DiagInReference getDiagnosticInRereference() const = 0;
  virtual DiagAsNote getDiagnosticAsNote() const = 0;

  /// Determine whether it would be possible to diagnose
  /// current requirement failure.
  bool canDiagnoseFailure() const {
    // If this is a conditional requirement failure,
    // we have a lot more information compared to
    // type requirement case, because we know that
    // underlying conformance requirement matched.
    if (isConditional())
      return true;

    auto *anchor = getAnchor();
    // In the situations like this:
    //
    // ```swift
    // enum E<T: P> { case foo(T) }
    // let _: E = .foo(...)
    // ```
    //
    // `E` is going to be opened twice. First, when
    // it's used as a contextual type, and when `E.foo`
    // is found and its function type is opened.
    // We still want to record both fixes but should
    // avoid diagnosing the same problem multiple times.
    if (isa<UnresolvedMemberExpr>(anchor)) {
      auto path = getLocator()->getPath();
      if (path.front().getKind() != ConstraintLocator::UnresolvedMember)
        return false;
    }

    // For static/initializer calls there is going to be
    // a separate fix, attached to the argument, which is
    // much easier to diagnose.
    // For operator calls we can't currently produce a good
    // diagnostic, so instead let's refer to expression diagnostics.
    return !(Apply && (isOperator(Apply) || isa<TypeExpr>(anchor)));
  }

  static bool isOperator(const ApplyExpr *apply) {
    return isa<PrefixUnaryExpr>(apply) || isa<PostfixUnaryExpr>(apply) ||
           isa<BinaryExpr>(apply);
  }

private:
  /// Retrieve declaration associated with failing generic requirement.
  ValueDecl *getDeclRef() const;

  /// Retrieve generic signature where this parameter originates from.
  GenericSignature *getSignature(ConstraintLocator *locator);

  void emitRequirementNote(const Decl *anchor, Type lhs, Type rhs) const;

  /// Determine whether given declaration represents a static
  /// or instance property/method, excluding operators.
  static bool isStaticOrInstanceMember(const ValueDecl *decl);

  /// If this is a failure in conditional requirement, retrieve
  /// conformance information.
  ProtocolConformance *
  getConformanceForConditionalReq(ConstraintLocator *locator);
};

/// Diagnostics for failed conformance checks originating from
/// generic requirements e.g.
/// ```swift
///   struct S {}
///   func foo<T: Hashable>(_ t: T) {}
///   foo(S())
/// ```
class MissingConformanceFailure final : public RequirementFailure {
  Type NonConformingType;
  ProtocolDecl *Protocol;

public:
  MissingConformanceFailure(Expr *expr, ConstraintSystem &cs,
                            ConstraintLocator *locator,
                            std::pair<Type, ProtocolDecl *> conformance)
      : RequirementFailure(cs, expr, RequirementKind::Conformance, locator),
        NonConformingType(conformance.first), Protocol(conformance.second) {}

  bool diagnoseAsError() override;

private:
  /// The type which was expected, by one of the generic requirements,
  /// to conform to associated protocol.
  Type getLHS() const override { return NonConformingType; }

  /// The protocol generic requirement expected associated type to conform to.
  Type getRHS() const override { return Protocol->getDeclaredType(); }

protected:
  DiagOnDecl getDiagnosticOnDecl() const override {
    return diag::type_does_not_conform_decl_owner;
  }

  DiagInReference getDiagnosticInRereference() const override {
    return diag::type_does_not_conform_in_decl_ref;
  }

  DiagAsNote getDiagnosticAsNote() const override {
    return diag::candidate_types_conformance_requirement;
  }
};

/// Diagnose failures related to same-type generic requirements, e.g.
/// ```swift
/// protocol P {
///   associatedtype T
/// }
///
/// struct S : P {
///   typealias T = String
/// }
///
/// func foo<U: P>(_ t: [U]) where U.T == Int {}
/// foo([S()])
/// ```
///
/// `S.T` is not the same type as `Int`, which is required by `foo`.
class SameTypeRequirementFailure final : public RequirementFailure {
  Type LHS, RHS;

public:
  SameTypeRequirementFailure(Expr *expr, ConstraintSystem &cs, Type lhs,
                             Type rhs, ConstraintLocator *locator)
      : RequirementFailure(cs, expr, RequirementKind::SameType, locator),
        LHS(lhs), RHS(rhs) {}

  Type getLHS() const override { return LHS; }
  Type getRHS() const override { return RHS; }

protected:
  DiagOnDecl getDiagnosticOnDecl() const override {
    return diag::types_not_equal_decl;
  }

  DiagInReference getDiagnosticInRereference() const override {
    return diag::types_not_equal_in_decl_ref;
  }

  DiagAsNote getDiagnosticAsNote() const override {
    return diag::candidate_types_equal_requirement;
  }
};

/// Diagnose failures related to superclass generic requirements, e.g.
/// ```swift
/// class A {
/// }
///
/// class B {
/// }
///
/// func foo<T>(_ t: [T]) where T: A {}
/// foo([B()])
/// ```
///
/// `A` is not the superclass of `B`, which is required by `foo<T>`.
class SuperclassRequirementFailure final : public RequirementFailure {
  Type LHS, RHS;

public:
  SuperclassRequirementFailure(Expr *expr, ConstraintSystem &cs, Type lhs,
                               Type rhs, ConstraintLocator *locator)
      : RequirementFailure(cs, expr, RequirementKind::Superclass, locator),
        LHS(lhs), RHS(rhs) {}

  Type getLHS() const override { return LHS; }
  Type getRHS() const override { return RHS; }

protected:
  DiagOnDecl getDiagnosticOnDecl() const override {
    return diag::types_not_inherited_decl;
  }

  DiagInReference getDiagnosticInRereference() const override {
    return diag::types_not_inherited_in_decl_ref;
  }

  DiagAsNote getDiagnosticAsNote() const override {
    return diag::candidate_types_inheritance_requirement;
  }
};

/// Diagnose errors associated with missing, extraneous
/// or incorrect labels supplied by arguments, e.g.
/// ```swift
///   func foo(q: String, _ a: Int) {}
///   foo("ultimate quesiton", a: 42)
/// ```
/// Call to `foo` is going to be diagnosed as missing `q:`
/// and having extraneous `a:` labels, with appropriate fix-its added.
class LabelingFailure final : public FailureDiagnostic {
  ArrayRef<Identifier> CorrectLabels;

public:
  LabelingFailure(Expr *root, ConstraintSystem &cs, ConstraintLocator *locator,
                  ArrayRef<Identifier> labels)
      : FailureDiagnostic(root, cs, locator), CorrectLabels(labels) {}

  bool diagnoseAsError() override;
};

/// Diagnose errors related to converting function type which
/// isn't explicitly '@escaping' to some other type.
class NoEscapeFuncToTypeConversionFailure final : public FailureDiagnostic {
  Type ConvertTo;

public:
  NoEscapeFuncToTypeConversionFailure(Expr *expr, ConstraintSystem &cs,
                                      ConstraintLocator *locator,
                                      Type toType = Type())
      : FailureDiagnostic(expr, cs, locator), ConvertTo(toType) {}

  bool diagnoseAsError() override;
};

class MissingForcedDowncastFailure final : public FailureDiagnostic {
public:
  MissingForcedDowncastFailure(Expr *expr, ConstraintSystem &cs,
                               ConstraintLocator *locator)
      : FailureDiagnostic(expr, cs, locator) {}

  bool diagnoseAsError() override;
};

/// Diagnose failures related to passing value of some type
/// to `inout` parameter, without explicitly specifying `&`.
class MissingAddressOfFailure final : public FailureDiagnostic {
public:
  MissingAddressOfFailure(Expr *expr, ConstraintSystem &cs,
                          ConstraintLocator *locator)
      : FailureDiagnostic(expr, cs, locator) {}

  bool diagnoseAsError() override;
};

/// Diagnose failures related attempt to implicitly convert types which
/// do not support such implicit converstion.
/// "as" or "as!" has to be specified explicitly in cases like that.
class MissingExplicitConversionFailure final : public FailureDiagnostic {
  Type ConvertingTo;

public:
  MissingExplicitConversionFailure(Expr *expr, ConstraintSystem &cs,
                                   ConstraintLocator *locator, Type toType)
      : FailureDiagnostic(expr, cs, locator), ConvertingTo(toType) {}

  bool diagnoseAsError() override;

private:
  bool exprNeedsParensBeforeAddingAs(Expr *expr) {
    auto *DC = getDC();
    auto &TC = getTypeChecker();

    auto asPG = TC.lookupPrecedenceGroup(
        DC, DC->getASTContext().Id_CastingPrecedence, SourceLoc());
    if (!asPG)
      return true;
    return exprNeedsParensInsideFollowingOperator(TC, DC, expr, asPG);
  }

  bool exprNeedsParensAfterAddingAs(Expr *expr, Expr *rootExpr) {
    auto *DC = getDC();
    auto &TC = getTypeChecker();

    auto asPG = TC.lookupPrecedenceGroup(
        DC, DC->getASTContext().Id_CastingPrecedence, SourceLoc());
    if (!asPG)
      return true;

    return exprNeedsParensOutsideFollowingOperator(TC, DC, expr, rootExpr,
                                                   asPG);
  }
};

/// Diagnose failures related to attempting member access on optional base
/// type without optional chaining or force-unwrapping it first.
class MemberAccessOnOptionalBaseFailure final : public FailureDiagnostic {
  DeclName Member;
  bool ResultTypeIsOptional;

public:
  MemberAccessOnOptionalBaseFailure(Expr *expr, ConstraintSystem &cs,
                                    ConstraintLocator *locator,
                                    DeclName memberName, bool resultOptional)
      : FailureDiagnostic(expr, cs, locator), Member(memberName),
        ResultTypeIsOptional(resultOptional) {}

  bool diagnoseAsError() override;
};

/// Diagnose failures related to use of the unwrapped optional types,
/// which require some type of force-unwrap e.g. "!" or "try!".
class MissingOptionalUnwrapFailure final : public FailureDiagnostic {
  Type BaseType;
  Type UnwrappedType;

public:
  MissingOptionalUnwrapFailure(Expr *expr, ConstraintSystem &cs, Type baseType,
                               Type unwrappedType, ConstraintLocator *locator)
      : FailureDiagnostic(expr, cs, locator), BaseType(baseType),
        UnwrappedType(unwrappedType) {}

  bool diagnoseAsError() override;

private:
  Type getBaseType() const {
    return resolveType(BaseType, /*reconstituteSugar=*/true);
  }

  Type getUnwrappedType() const {
    return resolveType(UnwrappedType, /*reconstituteSugar=*/true);
  }

  /// Suggest a default value via `?? <default value>`
  void offerDefaultValueUnwrapFixIt(DeclContext *DC, Expr *expr) const;
  /// Suggest a force optional unwrap via `!`
  void offerForceUnwrapFixIt(Expr *expr) const;

  /// Determine whether given expression is an argument used in the
  /// operator invocation, and if so return corresponding parameter.
  Optional<AnyFunctionType::Param> getOperatorParameterFor(Expr *expr) const;
};

/// Diagnose errors associated with rvalues in positions
/// where an lvalue is required, such as inout arguments.
class RValueTreatedAsLValueFailure final : public FailureDiagnostic {

public:
  RValueTreatedAsLValueFailure(ConstraintSystem &cs, ConstraintLocator *locator)
      : FailureDiagnostic(nullptr, cs, locator) {}

  bool diagnoseAsError() override;
};

class TrailingClosureAmbiguityFailure final : public FailureDiagnostic {
  ArrayRef<OverloadChoice> Choices;

public:
  TrailingClosureAmbiguityFailure(Expr *root, ConstraintSystem &cs,
                                  Expr *anchor,
                                  ArrayRef<OverloadChoice> choices)
      : FailureDiagnostic(root, cs, cs.getConstraintLocator(anchor)),
        Choices(choices) {}

  bool diagnoseAsError() override { return false; }

  bool diagnoseAsNote() override;
};

/// Diagnose errors related to assignment expressions e.g.
/// trying to assign something to immutable value, or trying
/// to access mutating member on immutable base.
class AssignmentFailure final : public FailureDiagnostic {
  SourceLoc Loc;
  Diag<StringRef> DeclDiagnostic;
  Diag<Type> TypeDiagnostic;

public:
  AssignmentFailure(Expr *destExpr, ConstraintSystem &cs,
                    SourceLoc diagnosticLoc);

  AssignmentFailure(Expr *destExpr, ConstraintSystem &cs,
                    SourceLoc diagnosticLoc, Diag<StringRef> declDiag,
                    Diag<Type> typeDiag)
      : FailureDiagnostic(destExpr, cs, cs.getConstraintLocator(destExpr)),
        Loc(diagnosticLoc), DeclDiagnostic(declDiag), TypeDiagnostic(typeDiag) {
  }

  bool diagnoseAsError() override;

private:
  void fixItChangeInoutArgType(const Expr *arg, Type actualType,
                               Type neededType) const;

  /// Given an expression that has a non-lvalue type, dig into it until
  /// we find the part of the expression that prevents the entire subexpression
  /// from being mutable.  For example, in a sequence like "x.v.v = 42" we want
  /// to complain about "x" being a let property if "v.v" are both mutable.
  ///
  /// \returns The base subexpression that looks immutable (or that can't be
  /// analyzed any further) along with a decl extracted from it if we could.
  std::pair<Expr *, ValueDecl *> resolveImmutableBase(Expr *expr) const;

  static Diag<StringRef> findDeclDiagonstic(ASTContext &ctx, Expr *destExpr);

  static bool isLoadedLValue(Expr *expr) {
    expr = expr->getSemanticsProvidingExpr();
    if (isa<LoadExpr>(expr))
      return true;
    if (auto ifExpr = dyn_cast<IfExpr>(expr))
      return isLoadedLValue(ifExpr->getThenExpr()) &&
             isLoadedLValue(ifExpr->getElseExpr());
    return false;
  }

  /// Retrive an member reference associated with given member
  /// looking through dynamic member lookup on the way.
  ValueDecl *getMemberRef(ConstraintLocator *locator) const;
};

/// Intended to diagnose any possible contextual failure
/// e.g. argument/parameter, closure result, conversions etc.
class ContextualFailure final : public FailureDiagnostic {
  Type FromType, ToType;

public:
  ContextualFailure(Expr *root, ConstraintSystem &cs, Type lhs, Type rhs,
                    ConstraintLocator *locator)
      : FailureDiagnostic(root, cs, locator), FromType(resolve(lhs)),
        ToType(resolve(rhs)) {}

  bool diagnoseAsError() override;

  // If we're trying to convert something of type "() -> T" to T,
  // then we probably meant to call the value.
  bool diagnoseMissingFunctionCall() const;

  /// Try to add a fix-it when converting between a collection and its slice
  /// type, such as String <-> Substring or (eventually) Array <-> ArraySlice
  static bool trySequenceSubsequenceFixIts(InFlightDiagnostic &diag,
                                           ConstraintSystem &CS, Type fromType,
                                           Type toType, Expr *expr);

private:
  Type resolve(Type rawType) {
    auto type = resolveType(rawType)->getWithoutSpecifierType();
    if (auto *BGT = type->getAs<BoundGenericType>()) {
      if (BGT->hasUnresolvedType())
        return BGT->getDecl()->getDeclaredInterfaceType();
    }
    return type;
  }

  /// Try to add a fix-it to convert a stored property into a computed
  /// property
  void tryComputedPropertyFixIts(Expr *expr) const;
};

/// Diagnose situations when @autoclosure argument is passed to @autoclosure
/// parameter directly without calling it first.
class AutoClosureForwardingFailure final : public FailureDiagnostic {
public:
  AutoClosureForwardingFailure(ConstraintSystem &cs, ConstraintLocator *locator)
      : FailureDiagnostic(nullptr, cs, locator) {}

  bool diagnoseAsError() override;
};

/// Diagnose situations when there was an attempt to unwrap entity
/// of non-optional type e.g.
///
/// ```swift
/// let i: Int = 0
/// _ = i!
///
/// struct A { func foo() {} }
/// func foo(_ a: A) {
///   a?.foo()
/// }
/// ```
class NonOptionalUnwrapFailure final : public FailureDiagnostic {
  Type BaseType;

public:
  NonOptionalUnwrapFailure(Expr *root, ConstraintSystem &cs, Type baseType,
                           ConstraintLocator *locator)
      : FailureDiagnostic(root, cs, locator), BaseType(baseType) {}

  bool diagnoseAsError() override;
};

class MissingCallFailure final : public FailureDiagnostic {
public:
  MissingCallFailure(Expr *root, ConstraintSystem &cs,
                     ConstraintLocator *locator)
      : FailureDiagnostic(root, cs, locator) {}

  bool diagnoseAsError() override;
};

class SubscriptMisuseFailure final : public FailureDiagnostic {
public:
  SubscriptMisuseFailure(Expr *root, ConstraintSystem &cs,
                         ConstraintLocator *locator)
      : FailureDiagnostic(root, cs, locator) {}

  bool diagnoseAsError() override;
  bool diagnoseAsNote() override;
};

/// Diagnose situations when member referenced by name is missing
/// from the associated base type, e.g.
///
/// ```swift
/// struct S {}
/// func foo(_ s: S) {
///   let _: Int = s.foo(1, 2) // expected type is `(Int, Int) -> Int`
/// }
/// ```
class MissingMemberFailure final : public FailureDiagnostic {
  Type BaseType;
  DeclName Name;

public:
  MissingMemberFailure(Expr *root, ConstraintSystem &cs, Type baseType,
                       DeclName memberName, ConstraintLocator *locator)
      : FailureDiagnostic(root, cs, locator), BaseType(baseType),
        Name(memberName) {}

  bool diagnoseAsError() override;

private:
  static DeclName findCorrectEnumCaseName(Type Ty,
                                          TypoCorrectionResults &corrections,
                                          DeclName memberName);
};

/// Diagnose situations when we use an instance member on a type
/// or a type member on an instance
///
/// ```swift
/// class Bar {}
///
/// enum Foo {
///
///   static func f() {
///     g(Bar())
///   }
///
///   func g(_: Bar) {}
///
/// }
/// ```
class AllowTypeOrInstanceMemberFailure final : public FailureDiagnostic {
  Type BaseType;
  DeclName Name;

public:
  AllowTypeOrInstanceMemberFailure(Expr *root, ConstraintSystem &cs, Type baseType,
                                   DeclName memberName, ConstraintLocator *locator)
      : FailureDiagnostic(root, cs, locator), BaseType(baseType),
        Name(memberName) {}
    
  bool diagnoseAsError() override;
};
class PartialApplicationFailure final : public FailureDiagnostic {
  enum RefKind : unsigned {
    MutatingMethod,
    SuperInit,
    SelfInit,
  };

  bool CompatibilityWarning;

public:
  PartialApplicationFailure(Expr *root, bool warning, ConstraintSystem &cs,
                            ConstraintLocator *locator)
      : FailureDiagnostic(root, cs, locator), CompatibilityWarning(warning) {}

  bool diagnoseAsError() override;
};

class InvalidInitRefFailure : public FailureDiagnostic {
protected:
  Type BaseType;
  const ConstructorDecl *Init;
  SourceRange BaseRange;

  InvalidInitRefFailure(Expr *root, ConstraintSystem &cs, Type baseTy,
                        const ConstructorDecl *init, SourceRange baseRange,
                        ConstraintLocator *locator)
      : FailureDiagnostic(root, cs, locator), BaseType(baseTy), Init(init),
        BaseRange(baseRange) {}

public:
  bool diagnoseAsError() override = 0;
};

/// Diagnose an attempt to construct an object of class type with a metatype
/// value without using 'required' initializer:
///
/// ```swift
///  class C {
///    init(value: Int) {}
///  }
///
///  func make<T: C>(type: T.Type) -> T {
///    return T.init(value: 42)
///  }
/// ```
class InvalidDynamicInitOnMetatypeFailure final : public InvalidInitRefFailure {
public:
  InvalidDynamicInitOnMetatypeFailure(Expr *root, ConstraintSystem &cs,
                                      Type baseTy, const ConstructorDecl *init,
                                      SourceRange baseRange,
                                      ConstraintLocator *locator)
      : InvalidInitRefFailure(root, cs, baseTy, init, baseRange, locator) {}

  bool diagnoseAsError() override;
};

/// Diagnose an attempt to call initializer on protocol metatype:
///
/// ```swift
///  protocol P {
///    init(value: Int)
///  }
///
///  func make(type: P.Type) -> P {
///    return type.init(value: 42)
///  }
/// ```
class InitOnProtocolMetatypeFailure final : public InvalidInitRefFailure {
  bool IsStaticallyDerived;

public:
  InitOnProtocolMetatypeFailure(Expr *root, ConstraintSystem &cs, Type baseTy,
                                const ConstructorDecl *init,
                                bool isStaticallyDerived, SourceRange baseRange,
                                ConstraintLocator *locator)
      : InvalidInitRefFailure(root, cs, baseTy, init, baseRange, locator),
        IsStaticallyDerived(isStaticallyDerived) {}

  bool diagnoseAsError() override;
};

/// Diagnose an attempt to construct an instance using non-constant
/// metatype base without explictly specifying `init`:
///
/// ```swift
/// let foo = Int.self
/// foo(0) // should be `foo.init(0)`
/// ```
class ImplicitInitOnNonConstMetatypeFailure final
    : public InvalidInitRefFailure {
public:
  ImplicitInitOnNonConstMetatypeFailure(Expr *root, ConstraintSystem &cs,
                                        Type baseTy,
                                        const ConstructorDecl *init,
                                        ConstraintLocator *locator)
      : InvalidInitRefFailure(root, cs, baseTy, init, SourceRange(), locator) {}

  bool diagnoseAsError() override;
};

class MissingArgumentsFailure final : public FailureDiagnostic {
  using Param = AnyFunctionType::Param;

  FunctionType *Fn;
  unsigned NumSynthesized;

public:
  MissingArgumentsFailure(Expr *root, ConstraintSystem &cs,
                          FunctionType *funcType,
                          unsigned numSynthesized,
                          ConstraintLocator *locator)
      : FailureDiagnostic(root, cs, locator), Fn(funcType),
        NumSynthesized(numSynthesized) {}

  bool diagnoseAsError() override;

private:
  /// If missing arguments come from trailing closure,
  /// let's produce tailored diagnostics.
  bool diagnoseTrailingClosure(ClosureExpr *closure);
};

class OutOfOrderArgumentFailure final : public FailureDiagnostic {
  using ParamBinding = SmallVector<unsigned, 1>;

  unsigned ArgIdx;
  unsigned PrevArgIdx;

  SmallVector<ParamBinding, 4> Bindings;

public:
  OutOfOrderArgumentFailure(Expr *root, ConstraintSystem &cs,
                            unsigned argIdx,
                            unsigned prevArgIdx,
                            ArrayRef<ParamBinding> bindings,
                            ConstraintLocator *locator)
      : FailureDiagnostic(root, cs, locator), ArgIdx(argIdx),
        PrevArgIdx(prevArgIdx), Bindings(bindings.begin(), bindings.end()) {}

  bool diagnoseAsError() override;
};

/// Diagnose an attempt to destructure a single tuple closure parameter
/// into a multiple (possibly anonymous) arguments e.g.
///
/// ```swift
/// let _: ((Int, Int)) -> Void = { $0 + $1 }
/// ```
class ClosureParamDestructuringFailure final : public FailureDiagnostic {
  FunctionType *ContextualType;

public:
  ClosureParamDestructuringFailure(Expr *root, ConstraintSystem &cs,
                                   FunctionType *contextualType,
                                   ConstraintLocator *locator)
      : FailureDiagnostic(root, cs, locator), ContextualType(contextualType) {}

  bool diagnoseAsError() override;

private:
  Type getParameterType() const {
    const auto &param = ContextualType->getParams().front();
    return resolveType(param.getPlainType());
  }
};

/// Diagnose an attempt to reference inaccessible member e.g.
///
/// ```swift
/// struct S {
///   var foo: String
///
///   private init(_ v: String) {
///     self.foo = v
///   }
/// }
/// _ = S("ultimate question")
/// ```
class InaccessibleMemberFailure final : public FailureDiagnostic {
  ValueDecl *Member;

public:
  InaccessibleMemberFailure(Expr *root, ConstraintSystem &cs, ValueDecl *member,
                            ConstraintLocator *locator)
      : FailureDiagnostic(root, cs, locator), Member(member) {}

  bool diagnoseAsError() override;
};

/// Diagnose an attempt to reference subscript as a keypath component
/// where at least one of the index arguments doesn't conform to Hashable e.g.
///
/// ```swift
/// protocol P {}
///
/// struct S {
///   subscript<T: P>(x: Int, _ y: T) -> Bool { return true }
/// }
///
/// func foo<T: P>(_ x: Int, _ y: T) {
///   _ = \S.[x, y]
/// }
/// ```
class KeyPathSubscriptIndexHashableFailure final : public FailureDiagnostic {
  Type NonConformingType;

public:
  KeyPathSubscriptIndexHashableFailure(Expr *root, ConstraintSystem &cs,
                                       Type type, ConstraintLocator *locator)
      : FailureDiagnostic(root, cs, locator), NonConformingType(type) {
    assert(locator->isResultOfKeyPathDynamicMemberLookup() ||
           locator->isKeyPathSubscriptComponent());
  }

  bool diagnoseAsError() override;
};

} // end namespace constraints
} // end namespace swift

#endif // SWIFT_SEMA_CSDIAGNOSTICS_H
