//===--- NameLookupRequests.h - Name Lookup Requests ------------*- C++ -*-===//
//
// 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 defines name-lookup requests.
//
//===----------------------------------------------------------------------===//
#ifndef SWIFT_NAME_LOOKUP_REQUESTS_H
#define SWIFT_NAME_LOOKUP_REQUESTS_H

#include "swift/AST/SimpleRequest.h"
#include "swift/AST/ASTTypeIDs.h"
#include "swift/AST/EvaluatorDependencies.h"
#include "swift/AST/FileUnit.h"
#include "swift/AST/Identifier.h"
#include "swift/AST/NameLookup.h"
#include "swift/AST/TypeOrExtensionDecl.h"
#include "swift/Basic/Statistic.h"
#include "llvm/ADT/Hashing.h"
#include "llvm/ADT/PointerIntPair.h"
#include "llvm/ADT/TinyPtrVector.h"

namespace swift {

class ClassDecl;
class DeclContext;
class DeclName;
class DeclNameRef;
class DestructorDecl;
class GenericContext;
class GenericParamList;
class LookupResult;
enum class NLKind;
class PotentialMacroExpansions;
class SourceLoc;
class TypeAliasDecl;
class TypeDecl;
enum class UnqualifiedLookupFlags;
namespace ast_scope {
class ASTScopeImpl;
class ScopeCreator;
} // namespace ast_scope
namespace namelookup {
enum class ResolutionKind;
} // namespace namelookup

/// Display a nominal type or extension thereof.
void simple_display(
    llvm::raw_ostream &out,
    const llvm::PointerUnion<const TypeDecl *, const ExtensionDecl *> &value);

/// Describes a set of type declarations that are "direct" referenced by
/// a particular type in the AST.
using DirectlyReferencedTypeDecls = std::pair<llvm::TinyPtrVector<TypeDecl *>,
                                              InvertibleProtocolSet>;

/// Request the set of declarations directly referenced by the an "inherited"
/// type of a type or extension declaration.
///
/// This request retrieves the set of declarations that are directly referenced
/// by a particular type in the "inherited" clause of a type or extension
/// declaration. For example:
///
/// \code
///   protocol P { }
///   protocol Q { }
///   typealias Alias = P & Q
///   class C { }
///
///   class D: C, Alias { }
/// \endcode
///
/// The inherited declaration of \c D at index 0 is the class declaration C.
/// The inherited declaration of \c D at index 1 is the typealias Alias.
class InheritedDeclsReferencedRequest
    : public SimpleRequest<
          InheritedDeclsReferencedRequest,
          DirectlyReferencedTypeDecls(
              llvm::PointerUnion<const TypeDecl *, const ExtensionDecl *>,
              unsigned),
          RequestFlags::Uncached> // FIXME: Cache these
{
public:
  using SimpleRequest::SimpleRequest;

private:
  friend SimpleRequest;

  // Evaluation.
  DirectlyReferencedTypeDecls
  evaluate(Evaluator &evaluator,
           llvm::PointerUnion<const TypeDecl *, const ExtensionDecl *> decl,
           unsigned index) const;

public:
  // Caching
  bool isCached() const { return true; }

  // Source location information.
  SourceLoc getNearestLoc() const;
};

/// Request the set of declarations directly referenced by the underlying
/// type of a typealias.
///
/// This request retrieves the set of type declarations that directly referenced
/// by the underlying type of a typealias. For example:
///
/// \code
///   protocol P { }
///   protocol Q { }
///   class C { }
///   typealias PQ = P & Q
///   typealias Alias = C & PQ
/// \endcode
///
/// The set of declarations referenced by the underlying type of \c PQ
/// contains both \c P and \c Q.
/// The set of declarations referenced by the underlying type of \c Alias
/// contains \c C and \c PQ. Clients can choose to look further into \c PQ
/// using another instance of this request.
class UnderlyingTypeDeclsReferencedRequest :
  public SimpleRequest<UnderlyingTypeDeclsReferencedRequest,
                       DirectlyReferencedTypeDecls(TypeAliasDecl *),
                       RequestFlags::Uncached> // FIXME: Cache these
{
public:
  using SimpleRequest::SimpleRequest;

private:
  friend SimpleRequest;

  // Evaluation.
  DirectlyReferencedTypeDecls evaluate(
      Evaluator &evaluator,
      TypeAliasDecl *typealias) const;

public:
  // Caching
  bool isCached() const { return true; }
};

/// Request the superclass declaration for the given class.
class SuperclassDeclRequest :
    public SimpleRequest<SuperclassDeclRequest,
                         ClassDecl *(NominalTypeDecl *),
                         RequestFlags::SeparatelyCached> {
public:
  using SimpleRequest::SimpleRequest;

private:
  friend SimpleRequest;

  // Evaluation.
  ClassDecl *
  evaluate(Evaluator &evaluator, NominalTypeDecl *subject) const;

public:
  // Cycle handling
  void diagnoseCycle(DiagnosticEngine &diags) const;
  void noteCycleStep(DiagnosticEngine &diags) const;

  // Caching
  bool isCached() const { return true; }
  std::optional<ClassDecl *> getCachedResult() const;
  void cacheResult(ClassDecl *value) const;
};

class InheritedProtocolsRequest
    : public SimpleRequest<
          InheritedProtocolsRequest, ArrayRef<ProtocolDecl *>(ProtocolDecl *),
          RequestFlags::SeparatelyCached | RequestFlags::DependencySink> {
public:
  using SimpleRequest::SimpleRequest;

private:
  friend SimpleRequest;

  // Evaluation.
  ArrayRef<ProtocolDecl *>
  evaluate(Evaluator &evaluator, ProtocolDecl *PD) const;

public:
  // Caching
  bool isCached() const { return true; }
  std::optional<ArrayRef<ProtocolDecl *>> getCachedResult() const;
  void cacheResult(ArrayRef<ProtocolDecl *> value) const;

public:
  // Incremental dependencies
  void writeDependencySink(evaluator::DependencyCollector &tracker,
                           ArrayRef<ProtocolDecl *> result) const;
};

class AllInheritedProtocolsRequest
    : public SimpleRequest<
          AllInheritedProtocolsRequest, ArrayRef<ProtocolDecl *>(ProtocolDecl *),
          RequestFlags::SeparatelyCached> {
public:
  using SimpleRequest::SimpleRequest;

private:
  friend SimpleRequest;

  // Evaluation.
  ArrayRef<ProtocolDecl *>
  evaluate(Evaluator &evaluator, ProtocolDecl *PD) const;

public:
  // Caching
  bool isCached() const { return true; }
  std::optional<ArrayRef<ProtocolDecl *>> getCachedResult() const;
  void cacheResult(ArrayRef<ProtocolDecl *> value) const;
};

class ProtocolRequirementsRequest
    : public SimpleRequest<ProtocolRequirementsRequest,
                           ArrayRef<ValueDecl *>(ProtocolDecl *),
                           RequestFlags::SeparatelyCached> {
public:
  using SimpleRequest::SimpleRequest;

private:
  friend SimpleRequest;

  // Evaluation.
  ArrayRef<ValueDecl *> evaluate(Evaluator &, ProtocolDecl *) const;

public:
  // Caching
  bool isCached() const { return true; }
  std::optional<ArrayRef<ValueDecl *>> getCachedResult() const;
  void cacheResult(ArrayRef<ValueDecl *> value) const;
};

/// Computes the set of protocols that are reparenting a given protocol.
class ReparentingProtocolsRequest
    : public SimpleRequest<
          ReparentingProtocolsRequest,
          ArrayRef<std::tuple<ProtocolDecl *, ExtensionDecl *, unsigned>>(
              ProtocolDecl *),
          RequestFlags::Cached> {
public:
  using SimpleRequest::SimpleRequest;

  /// Indicates a reparenting by a protocol, defined at a specific extension,
  /// with an index into the extension's InheritedTypes establishing it.
  using Result = std::tuple<ProtocolDecl *, ExtensionDecl *, unsigned>;

private:
  friend SimpleRequest;

  // Evaluation.
  ArrayRef<Result> evaluate(Evaluator &, ProtocolDecl *) const;

public:
  bool isCached() const { return true; }
};

/// Requests whether or not this class has designated initializers that are
/// not public or @usableFromInline.
class HasMissingDesignatedInitializersRequest :
    public SimpleRequest<HasMissingDesignatedInitializersRequest,
                         bool(ClassDecl *),
                         RequestFlags::SeparatelyCached> {
public:
  using SimpleRequest::SimpleRequest;

private:
  friend SimpleRequest;

  // Evaluation.
  bool
  evaluate(Evaluator &evaluator, ClassDecl *subject) const;

public:
  // Caching
  bool isCached() const { return true; }
  std::optional<bool> getCachedResult() const;
  void cacheResult(bool) const;
};

/// Request the nominal declaration extended by a given extension declaration.
class ExtendedNominalRequest
    : public SimpleRequest<
          ExtendedNominalRequest, NominalTypeDecl *(const ExtensionDecl *),
          RequestFlags::SeparatelyCached | RequestFlags::DependencySink> {
public:
  using SimpleRequest::SimpleRequest;

private:
  friend SimpleRequest;

  // Evaluation.
  NominalTypeDecl * evaluate(Evaluator &evaluator, const ExtensionDecl *ext) const;

public:
  // Separate caching.
  bool isCached() const { return true; }
  std::optional<NominalTypeDecl *> getCachedResult() const;
  void cacheResult(NominalTypeDecl *value) const;

public:
  // Incremental dependencies
  void writeDependencySink(evaluator::DependencyCollector &tracker,
                           NominalTypeDecl *result) const;
};

struct SelfBounds {
  llvm::TinyPtrVector<NominalTypeDecl *> decls;
  InvertibleProtocolSet inverses;
  bool anyObject = false;
};

/// Request the nominal types that occur as the right-hand side of "Self: Foo"
/// constraints in the "where" clause of a protocol extension.
class SelfBoundsFromWhereClauseRequest
    : public SimpleRequest<SelfBoundsFromWhereClauseRequest,
                           SelfBounds(llvm::PointerUnion<
                                      const TypeDecl *, const ExtensionDecl *>),
                           RequestFlags::Uncached> {
public:
  using SimpleRequest::SimpleRequest;

private:
  friend SimpleRequest;

  // Evaluation.
  SelfBounds
  evaluate(Evaluator &evaluator,
           llvm::PointerUnion<const TypeDecl *, const ExtensionDecl *>) const;
};

/// Request the nominal types that occur as the right-hand side of "Self: Foo"
/// constraints in the generic signature of a protocol extension.
class SelfBoundsFromGenericSignatureRequest
    : public SimpleRequest<SelfBoundsFromGenericSignatureRequest,
                           SelfBounds(const ExtensionDecl *),
                           RequestFlags::Uncached> {
public:
  using SimpleRequest::SimpleRequest;

private:
  friend SimpleRequest;

  // Evaluation.
  SelfBounds evaluate(Evaluator &evaluator, const ExtensionDecl *extDecl) const;
};

/// Request all type aliases and nominal types that appear in the "where"
/// clause of an extension.
class TypeDeclsFromWhereClauseRequest :
    public SimpleRequest<TypeDeclsFromWhereClauseRequest,
                         DirectlyReferencedTypeDecls(ExtensionDecl *),
                         RequestFlags::Uncached> {
public:
  using SimpleRequest::SimpleRequest;

private:
  friend SimpleRequest;

  // Evaluation.
  DirectlyReferencedTypeDecls evaluate(Evaluator &evaluator,
                                       ExtensionDecl *ext) const;
};

/// Request the nominal type declaration to which the given custom
/// attribute refers.
class CustomAttrNominalRequest
    : public SimpleRequest<CustomAttrNominalRequest,
                           NominalTypeDecl *(CustomAttr *),
                           RequestFlags::Cached> {
public:
  using SimpleRequest::SimpleRequest;

private:
  friend SimpleRequest;

  // Evaluation.
  NominalTypeDecl *evaluate(Evaluator &evaluator, CustomAttr *attr) const;

public:
  // Caching
  bool isCached() const { return true; }
};

/// Finds or synthesizes a destructor for the given class.
class GetDestructorRequest
    : public SimpleRequest<GetDestructorRequest, DestructorDecl *(ClassDecl *),
                           RequestFlags::SeparatelyCached> {
public:
  using SimpleRequest::SimpleRequest;

private:
  friend SimpleRequest;

  // Evaluation.
  DestructorDecl *
  evaluate(Evaluator &evaluator, ClassDecl *classDecl) const;

public:
  // Caching
  bool isCached() const { return true; }
  std::optional<DestructorDecl *> getCachedResult() const;
  void cacheResult(DestructorDecl *value) const;
};

class GenericParamListRequest :
    public SimpleRequest<GenericParamListRequest,
                         GenericParamList *(GenericContext *),
                         RequestFlags::SeparatelyCached> {
public:
  using SimpleRequest::SimpleRequest;
  
private:
  friend SimpleRequest;
  
  // Evaluation.
  GenericParamList *
  evaluate(Evaluator &evaluator, GenericContext *value) const;
  
public:
  // Separate caching.
  bool isCached() const { return true; }
  std::optional<GenericParamList *> getCachedResult() const;
  void cacheResult(GenericParamList *value) const;
};

/// The input type for an unqualified lookup request.
class UnqualifiedLookupDescriptor {
  using LookupOptions = OptionSet<UnqualifiedLookupFlags>;

public:
  DeclNameRef Name;
  DeclContext *DC;
  SourceLoc Loc;
  LookupOptions Options;

  UnqualifiedLookupDescriptor(DeclNameRef name, DeclContext *dc,
                              SourceLoc loc = SourceLoc(),
                              LookupOptions options = {})
      : Name(name), DC(dc), Loc(loc), Options(options) { }

  friend llvm::hash_code hash_value(const UnqualifiedLookupDescriptor &desc) {
    return llvm::hash_combine(desc.Name, desc.DC, desc.Loc,
                              desc.Options.toRaw());
  }

  friend bool operator==(const UnqualifiedLookupDescriptor &lhs,
                         const UnqualifiedLookupDescriptor &rhs) {
    return lhs.Name == rhs.Name && lhs.DC == rhs.DC && lhs.Loc == rhs.Loc &&
           lhs.Options.toRaw() == rhs.Options.toRaw();
  }

  friend bool operator!=(const UnqualifiedLookupDescriptor &lhs,
                         const UnqualifiedLookupDescriptor &rhs) {
    return !(lhs == rhs);
  }
};

void simple_display(llvm::raw_ostream &out,
                    const UnqualifiedLookupDescriptor &desc);

SourceLoc extractNearestSourceLoc(const UnqualifiedLookupDescriptor &desc);

/// Performs unqualified lookup for a DeclName from a given context.
class UnqualifiedLookupRequest
    : public SimpleRequest<UnqualifiedLookupRequest,
                           LookupResult(UnqualifiedLookupDescriptor),
                           RequestFlags::Uncached |
                               RequestFlags::DependencySink> {
public:
  UnqualifiedLookupRequest(UnqualifiedLookupDescriptor);

private:
  friend SimpleRequest;

  // Evaluation.
  LookupResult evaluate(Evaluator &evaluator,
                        UnqualifiedLookupDescriptor desc) const;

public:
  // Incremental dependencies
  void writeDependencySink(evaluator::DependencyCollector &tracker,
                           const LookupResult &res) const;
};

using QualifiedLookupResult = SmallVector<ValueDecl *, 4>;

/// Performs a lookup into a given module and its imports.
class LookupInModuleRequest
    : public SimpleRequest<
          LookupInModuleRequest,
          QualifiedLookupResult(const DeclContext *, DeclName, bool, NLKind,
                                namelookup::ResolutionKind, const DeclContext *,
                                NLOptions),
          RequestFlags::Uncached | RequestFlags::DependencySink> {
public:
  LookupInModuleRequest(
      const DeclContext *, DeclName, bool, NLKind,
      namelookup::ResolutionKind, const DeclContext *,
      SourceLoc, NLOptions);

private:
  friend SimpleRequest;

  /// Performs a lookup into the given module and its imports.
  ///
  /// If 'moduleOrFile' is a ModuleDecl, we search the module and its
  /// public imports. If 'moduleOrFile' is a SourceFile, we search the
  /// file's parent module, the module's public imports, and the source
  /// file's private imports.
  ///
  /// \param evaluator The request evaluator.
  /// \param moduleOrFile The module or file unit to search, including imports.
  /// \param name The name to look up.
  /// \param hasModuleSelector Whether \p name was originally qualified by a
  ///        module selector. This information is threaded through to underlying
  ///        lookup calls; the callee is responsible for actually applying the
  ///        module selector.
  /// \param lookupKind Whether this lookup is qualified or unqualified.
  /// \param resolutionKind What sort of decl is expected.
  /// \param moduleScopeContext The top-level context from which the lookup is
  ///        being performed, for checking access. This must be either a
  ///        FileUnit or a Module.
  /// \param options Lookup options to apply.
  QualifiedLookupResult
  evaluate(Evaluator &evaluator, const DeclContext *moduleOrFile, DeclName name,
           bool hasModuleSelector, NLKind lookupKind,
           namelookup::ResolutionKind resolutionKind,
           const DeclContext *moduleScopeContext, NLOptions options) const;

public:
  // Incremental dependencies
  void writeDependencySink(evaluator::DependencyCollector &tracker,
                           const QualifiedLookupResult &l) const;
};

/// Perform \c AnyObject lookup for a given member.
class AnyObjectLookupRequest
    : public SimpleRequest<AnyObjectLookupRequest,
                           QualifiedLookupResult(const DeclContext *,
                                                 DeclNameRef, NLOptions),
                           RequestFlags::Uncached | RequestFlags::DependencySink> {
public:
  using SimpleRequest::SimpleRequest;

private:
  friend SimpleRequest;

  QualifiedLookupResult evaluate(Evaluator &evaluator,
                                 const DeclContext *dc,
                                 DeclNameRef name,
                                 NLOptions options) const;

public:
  // Incremental dependencies
  void writeDependencySink(evaluator::DependencyCollector &tracker,
                           const QualifiedLookupResult &l) const;
};

class ModuleQualifiedLookupRequest
    : public SimpleRequest<ModuleQualifiedLookupRequest,
                           QualifiedLookupResult(const DeclContext *,
                                                 ModuleDecl *, DeclNameRef,
                                                 NLOptions),
                           RequestFlags::Uncached |
                              RequestFlags::DependencySink> {
public:
  ModuleQualifiedLookupRequest(const DeclContext *,
                               ModuleDecl *, DeclNameRef,
                               SourceLoc, NLOptions);

private:
  friend SimpleRequest;

  // Evaluation.
  QualifiedLookupResult evaluate(Evaluator &evaluator,
                                 const DeclContext *DC,
                                 ModuleDecl *mod, DeclNameRef name,
                                 NLOptions opts) const;

public:
  // Incremental dependencies
  void writeDependencySink(evaluator::DependencyCollector &tracker,
                           const QualifiedLookupResult &lookupResult) const;
};

class QualifiedLookupRequest
    : public SimpleRequest<QualifiedLookupRequest,
                           QualifiedLookupResult(const DeclContext *,
                                                 SmallVector<NominalTypeDecl *, 4>,
                                                 DeclNameRef, NLOptions),
                           RequestFlags::Uncached> {
public:
  QualifiedLookupRequest(const DeclContext *,
                         SmallVector<NominalTypeDecl *, 4>,
                         DeclNameRef, SourceLoc, NLOptions);

private:
  friend SimpleRequest;

  // Evaluation.
  QualifiedLookupResult
  evaluate(Evaluator &evaluator, const DeclContext *DC,
           SmallVector<NominalTypeDecl *, 4> decls,
           DeclNameRef name,
           NLOptions opts) const;
};

/// The input type for a direct lookup request.
class DirectLookupDescriptor final {
  using LookupOptions = OptionSet<NominalTypeDecl::LookupDirectFlags>;

public:
  NominalTypeDecl *DC;
  DeclName Name;
  LookupOptions Options;

  DirectLookupDescriptor(NominalTypeDecl *dc, DeclName name,
                         LookupOptions options = {})
      : DC(dc), Name(name), Options(options) {}

  friend llvm::hash_code hash_value(const DirectLookupDescriptor &desc) {
    return llvm::hash_combine(desc.Name, desc.DC, desc.Options.toRaw());
  }

  friend bool operator==(const DirectLookupDescriptor &lhs,
                         const DirectLookupDescriptor &rhs) {
    return lhs.Name == rhs.Name && lhs.DC == rhs.DC &&
           lhs.Options.toRaw() == rhs.Options.toRaw();
  }

  friend bool operator!=(const DirectLookupDescriptor &lhs,
                         const DirectLookupDescriptor &rhs) {
    return !(lhs == rhs);
  }
};

void simple_display(llvm::raw_ostream &out, const DirectLookupDescriptor &desc);

SourceLoc extractNearestSourceLoc(const DirectLookupDescriptor &desc);

class DirectLookupRequest
    : public SimpleRequest<DirectLookupRequest,
                           TinyPtrVector<ValueDecl *>(DirectLookupDescriptor),
                           RequestFlags::Uncached|RequestFlags::DependencySink> {
public:
  DirectLookupRequest(DirectLookupDescriptor, SourceLoc);

private:
  friend SimpleRequest;

  // Evaluation.
  TinyPtrVector<ValueDecl *>
  evaluate(Evaluator &evaluator, DirectLookupDescriptor desc) const;

public:
  // Incremental dependencies
  void writeDependencySink(evaluator::DependencyCollector &tracker,
                           const TinyPtrVector<ValueDecl *> &result) const;
};

class OperatorLookupDescriptor final {
public:
  using Storage = llvm::PointerUnion<FileUnit *, ModuleDecl *>;
  Storage fileOrModule;
  Identifier name;

private:
  OperatorLookupDescriptor(Storage fileOrModule, Identifier name)
      : fileOrModule(fileOrModule), name(name) {}

public:
  /// Retrieves the files to perform lookup in.
  ArrayRef<FileUnit *> getFiles() const;

  /// If this is for a module lookup, returns the module. Otherwise returns
  /// \c nullptr.
  ModuleDecl *getModule() const {
    return fileOrModule.dyn_cast<ModuleDecl *>();
  }

  /// Retrieve the file or module for the lookup, as a DeclContext.
  DeclContext *getDC() const {
    if (auto *module = getModule())
      return module;
    return cast<FileUnit *>(fileOrModule);
  }

  friend llvm::hash_code hash_value(const OperatorLookupDescriptor &desc) {
    return llvm::hash_combine(desc.fileOrModule, desc.name);
  }

  friend bool operator==(const OperatorLookupDescriptor &lhs,
                         const OperatorLookupDescriptor &rhs) {
    return lhs.fileOrModule == rhs.fileOrModule && lhs.name == rhs.name;
  }

  friend bool operator!=(const OperatorLookupDescriptor &lhs,
                         const OperatorLookupDescriptor &rhs) {
    return !(lhs == rhs);
  }

  static OperatorLookupDescriptor forFile(FileUnit *file, Identifier name) {
    return OperatorLookupDescriptor(file, name);
  }

  static OperatorLookupDescriptor forModule(ModuleDecl *mod, Identifier name) {
    return OperatorLookupDescriptor(mod, name);
  }

  static OperatorLookupDescriptor forDC(const DeclContext *DC, Identifier name);
};

void simple_display(llvm::raw_ostream &out,
                    const OperatorLookupDescriptor &desc);

SourceLoc extractNearestSourceLoc(const OperatorLookupDescriptor &desc);

/// Looks up an operator in a given file or module without looking through
/// imports.
class DirectOperatorLookupRequest
    : public SimpleRequest<DirectOperatorLookupRequest,
                           TinyPtrVector<OperatorDecl *>(
                               OperatorLookupDescriptor, OperatorFixity),
                           RequestFlags::Uncached |
                               RequestFlags::DependencySink> {
public:
  using SimpleRequest::SimpleRequest;

private:
  friend SimpleRequest;

  TinyPtrVector<OperatorDecl *>
  evaluate(Evaluator &evaluator, OperatorLookupDescriptor descriptor,
           OperatorFixity fixity) const;

public:
  // Incremental dependencies.
  void writeDependencySink(evaluator::DependencyCollector &tracker,
                           const TinyPtrVector<OperatorDecl *> &ops) const;
};

/// Looks up an precedencegroup in a given file or module without looking
/// through imports.
class DirectPrecedenceGroupLookupRequest
    : public SimpleRequest<DirectPrecedenceGroupLookupRequest,
                           TinyPtrVector<PrecedenceGroupDecl *>(
                               OperatorLookupDescriptor),
                           RequestFlags::Uncached |
                               RequestFlags::DependencySink> {
public:
  using SimpleRequest::SimpleRequest;

private:
  friend SimpleRequest;

  TinyPtrVector<PrecedenceGroupDecl *>
  evaluate(Evaluator &evaluator, OperatorLookupDescriptor descriptor) const;

public:
  // Incremental dependencies.
  void writeDependencySink(evaluator::DependencyCollector &tracker,
                           const TinyPtrVector<PrecedenceGroupDecl *> &groups) const;
};

class LookupConformanceDescriptor final {
public:
  Type Ty;
  ProtocolDecl *PD;

  LookupConformanceDescriptor(Type Ty, ProtocolDecl *PD) : Ty(Ty), PD(PD) {}

  friend llvm::hash_code hash_value(const LookupConformanceDescriptor &desc) {
    return llvm::hash_combine(desc.Ty.getPointer(), desc.PD);
  }

  friend bool operator==(const LookupConformanceDescriptor &lhs,
                         const LookupConformanceDescriptor &rhs) {
    return lhs.Ty.getPointer() == rhs.Ty.getPointer() && lhs.PD == rhs.PD;
  }

  friend bool operator!=(const LookupConformanceDescriptor &lhs,
                         const LookupConformanceDescriptor &rhs) {
    return !(lhs == rhs);
  }
};

void simple_display(llvm::raw_ostream &out,
                    const LookupConformanceDescriptor &desc);

SourceLoc extractNearestSourceLoc(const LookupConformanceDescriptor &desc);

class LookupConformanceRequest
    : public SimpleRequest<LookupConformanceRequest,
                           ProtocolConformanceRef(LookupConformanceDescriptor),
                           RequestFlags::Uncached |
                               RequestFlags::DependencySink> {
public:
  using SimpleRequest::SimpleRequest;

private:
  friend SimpleRequest;

  // Evaluation.
  ProtocolConformanceRef evaluate(
      Evaluator &evaluator, LookupConformanceDescriptor desc) const;

public:
  // Incremental dependencies
  void writeDependencySink(evaluator::DependencyCollector &tracker,
                           ProtocolConformanceRef result) const;
};

/// Look up an 'infix operator' decl by name.
class LookupInfixOperatorRequest
    : public SimpleRequest<LookupInfixOperatorRequest,
                           TinyPtrVector<InfixOperatorDecl *>(
                               OperatorLookupDescriptor),
                           RequestFlags::Cached> {
public:
  using SimpleRequest::SimpleRequest;

private:
  friend SimpleRequest;

  TinyPtrVector<InfixOperatorDecl *>
  evaluate(Evaluator &evaluator, OperatorLookupDescriptor desc) const;

public:
  // Cached.
  bool isCached() const { return true; }
};

/// Look up an 'prefix operator' decl by name.
class LookupPrefixOperatorRequest
    : public SimpleRequest<LookupPrefixOperatorRequest,
                           PrefixOperatorDecl *(OperatorLookupDescriptor),
                           RequestFlags::Cached> {
public:
  using SimpleRequest::SimpleRequest;

private:
  friend SimpleRequest;

  PrefixOperatorDecl *evaluate(Evaluator &evaluator,
                               OperatorLookupDescriptor desc) const;

public:
  // Cached.
  bool isCached() const { return true; }
};

/// Look up an 'postfix operator' decl by name.
class LookupPostfixOperatorRequest
    : public SimpleRequest<LookupPostfixOperatorRequest,
                           PostfixOperatorDecl *(OperatorLookupDescriptor),
                           RequestFlags::Cached> {
public:
  using SimpleRequest::SimpleRequest;

private:
  friend SimpleRequest;

  PostfixOperatorDecl *evaluate(Evaluator &evaluator,
                                OperatorLookupDescriptor desc) const;

public:
  // Cached.
  bool isCached() const { return true; }
};

/// Look up a precedencegroup decl by name.
class LookupPrecedenceGroupRequest
    : public SimpleRequest<LookupPrecedenceGroupRequest,
                           TinyPtrVector<PrecedenceGroupDecl *>(
                               OperatorLookupDescriptor),
                           RequestFlags::Cached> {
public:
  using SimpleRequest::SimpleRequest;

private:
  friend SimpleRequest;

  TinyPtrVector<PrecedenceGroupDecl *>
  evaluate(Evaluator &evaluator, OperatorLookupDescriptor descriptor) const;

public:
  // Cached.
  bool isCached() const { return true; }
};

/// Computes whether this is a decl that supports being called through the
/// implementation of a \c callAsFunction method.
class IsCallAsFunctionNominalRequest
    : public SimpleRequest<IsCallAsFunctionNominalRequest,
                           bool(NominalTypeDecl *, DeclContext *),
                           RequestFlags::Cached> {
public:
  using SimpleRequest::SimpleRequest;

private:
  friend SimpleRequest;

  // Evaluation.
  bool evaluate(Evaluator &evaluator, NominalTypeDecl *decl,
                DeclContext *dc) const;

public:
  bool isCached() const { return true; }
};

/// Computes whether the specified decl or a super-class/super-protocol has the
/// @dynamicMemberLookup attribute on it.
class HasDynamicMemberLookupAttributeRequest
    : public SimpleRequest<HasDynamicMemberLookupAttributeRequest,
                           bool(NominalTypeDecl *), RequestFlags::Cached> {
public:
  using SimpleRequest::SimpleRequest;

private:
  friend SimpleRequest;

  // Evaluation.
  bool evaluate(Evaluator &evaluator, NominalTypeDecl *decl) const;

public:
  bool isCached() const { return true; }
};

/// Computes whether the specified decl or a super-class/super-protocol has the
/// @dynamicCallable attribute on it.
class HasDynamicCallableAttributeRequest
    : public SimpleRequest<HasDynamicCallableAttributeRequest,
                           bool(NominalTypeDecl *), RequestFlags::Cached> {
public:
  using SimpleRequest::SimpleRequest;

private:
  friend SimpleRequest;

  // Evaluation.
  bool evaluate(Evaluator &evaluator, NominalTypeDecl *decl) const;

public:
  bool isCached() const { return true; }
};

/// Determine the potential macro expansions for a given type or extension
/// context.
class PotentialMacroExpansionsInContextRequest
    : public SimpleRequest<
          PotentialMacroExpansionsInContextRequest,
          PotentialMacroExpansions(TypeOrExtensionDecl),
          RequestFlags::Cached> {
public:
  using SimpleRequest::SimpleRequest;

private:
  friend SimpleRequest;

  // Evaluation.
  PotentialMacroExpansions evaluate(
      Evaluator &evaluator, TypeOrExtensionDecl container) const;

public:
  bool isCached() const { return true; }
};

/// Resolves the protocol referenced by an @_implements attribute.
class ImplementsAttrProtocolRequest
    : public SimpleRequest<ImplementsAttrProtocolRequest,
                           ProtocolDecl *(const ImplementsAttr *, DeclContext *),
                           RequestFlags::Cached> {
public:
  using SimpleRequest::SimpleRequest;

private:
  friend SimpleRequest;

  // Evaluation.
  ProtocolDecl *evaluate(Evaluator &evaluator, const ImplementsAttr *attr,
                         DeclContext *dc) const;

public:
  bool isCached() const { return true; }
};

class LookupIntrinsicRequest
    : public SimpleRequest<LookupIntrinsicRequest,
                           FuncDecl *(ModuleDecl *, Identifier),
                           RequestFlags::Cached> {
public:
  using SimpleRequest::SimpleRequest;

private:
  friend SimpleRequest;

  // Evaluation.
  FuncDecl *evaluate(Evaluator &evaluator, ModuleDecl *module,
                     Identifier funcName) const;

public:
  bool isCached() const { return true; }
};

using ObjCCategoryNameMap =
  llvm::DenseMap<Identifier, llvm::TinyPtrVector<ExtensionDecl *>>;

/// Generate a map of all known extensions of the given class that have an
/// explicit category name. This request does not force clang categories that
/// haven't been imported already, but it will generate a new map if new
/// categories have been imported since the cached value was generated.
///
/// \seeAlso ClassDecl::getObjCCategoryNameMap()
class ObjCCategoryNameMapRequest
    : public SimpleRequest<ObjCCategoryNameMapRequest,
                           ObjCCategoryNameMap(ClassDecl *, ExtensionDecl *),
                           RequestFlags::Cached> {
public:
  using SimpleRequest::SimpleRequest;

  // Convenience to automatically extract `lastExtension`.
  ObjCCategoryNameMapRequest(ClassDecl *classDecl)
    : ObjCCategoryNameMapRequest(classDecl, classDecl->getLastExtension())
  {}

private:
  friend SimpleRequest;

  // Evaluation.
  ObjCCategoryNameMap evaluate(Evaluator &evaluator,
                               ClassDecl *classDecl,
                               ExtensionDecl *lastExtension) const;

public:
  bool isCached() const { return true; }
};

struct DeclABIRoleInfoResult {
  llvm::PointerIntPair<Decl *, 2, uint8_t> storage;
  DeclABIRoleInfoResult(Decl *counterpart, uint8_t roleValue)
    : storage(counterpart, roleValue) {}
};

/// Return the ABI role info (role and counterpart) of a given declaration.
class DeclABIRoleInfoRequest
    : public SimpleRequest<DeclABIRoleInfoRequest,
                           DeclABIRoleInfoResult(Decl *),
                           RequestFlags::Cached> {
public:
  using SimpleRequest::SimpleRequest;

  void recordABIOnly(Evaluator &evaluator, Decl *counterpart);

private:
  friend SimpleRequest;

  // Evaluation.
  DeclABIRoleInfoResult evaluate(Evaluator &evaluator, Decl *decl) const;

public:
  bool isCached() const { return true; }
};

#define SWIFT_TYPEID_ZONE NameLookup
#define SWIFT_TYPEID_HEADER "swift/AST/NameLookupTypeIDZone.def"
#include "swift/Basic/DefineTypeIDZone.h"
#undef SWIFT_TYPEID_ZONE
#undef SWIFT_TYPEID_HEADER

// Set up reporting of evaluated requests.
template<typename Request>
void reportEvaluatedRequest(UnifiedStatsReporter &stats,
                            const Request &request);

#define SWIFT_REQUEST(Zone, RequestType, Sig, Caching, LocOptions)             \
  template <>                                                                  \
  inline void reportEvaluatedRequest(UnifiedStatsReporter &stats,              \
                                     const RequestType &request) {             \
    ++stats.getFrontendCounters().RequestType;                                 \
  }
#include "swift/AST/NameLookupTypeIDZone.def"
#undef SWIFT_REQUEST

} // end namespace swift

#endif // SWIFT_NAME_LOOKUP_REQUESTS
