//===----- Linkage.h - Linkage calculation-related utilities ----*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// This file provides AST-internal utilities for linkage and visibility
// calculation.
//
//===----------------------------------------------------------------------===//

#ifndef LLVM_CLANG_LIB_AST_LINKAGE_H
#define LLVM_CLANG_LIB_AST_LINKAGE_H

#include "clang/AST/ASTFwd.h"
#include "clang/AST/Decl.h"
#include "clang/AST/DeclCXX.h"
#include "clang/AST/Type.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/PointerIntPair.h"
#include <optional>

namespace clang {
/// Kinds of LV computation.  The linkage side of the computation is
/// always the same, but different things can change how visibility is
/// computed.
struct LVComputationKind {
  /// The kind of entity whose visibility is ultimately being computed;
  /// visibility computations for types and non-types follow different rules.
  LLVM_PREFERRED_TYPE(bool)
  unsigned ExplicitKind : 1;
  /// Whether explicit visibility attributes should be ignored. When set,
  /// visibility may only be restricted by the visibility of template arguments.
  LLVM_PREFERRED_TYPE(bool)
  unsigned IgnoreExplicitVisibility : 1;
  /// Whether all visibility should be ignored. When set, we're only interested
  /// in computing linkage.
  LLVM_PREFERRED_TYPE(bool)
  unsigned IgnoreAllVisibility : 1;

  static constexpr int NumLVComputationKindBits = 3;

  explicit LVComputationKind(NamedDecl::ExplicitVisibilityKind EK)
      : ExplicitKind(EK), IgnoreExplicitVisibility(false),
        IgnoreAllVisibility(false) {}

  NamedDecl::ExplicitVisibilityKind getExplicitVisibilityKind() const {
    return static_cast<NamedDecl::ExplicitVisibilityKind>(ExplicitKind);
  }

  bool isTypeVisibility() const {
    return getExplicitVisibilityKind() == NamedDecl::VisibilityForType;
  }
  bool isValueVisibility() const {
    return getExplicitVisibilityKind() == NamedDecl::VisibilityForValue;
  }

  /// Do an LV computation when we only care about the linkage.
  static LVComputationKind forLinkageOnly() {
    LVComputationKind Result(NamedDecl::VisibilityForValue);
    Result.IgnoreExplicitVisibility = true;
    Result.IgnoreAllVisibility = true;
    return Result;
  }

  unsigned toBits() {
    unsigned Bits = 0;
    Bits = (Bits << 1) | ExplicitKind;
    Bits = (Bits << 1) | IgnoreExplicitVisibility;
    Bits = (Bits << 1) | IgnoreAllVisibility;
    return Bits;
  }
};

class LinkageComputer {
  // We have a cache for repeated linkage/visibility computations. This saves us
  // from exponential behavior in heavily templated code, such as:
  //
  // template <typename T, typename V> struct {};
  // using A = int;
  // using B = Foo<A, A>;
  // using C = Foo<B, B>;
  // using D = Foo<C, C>;
  //
  // The integer represents an LVComputationKind.
  using QueryType =
      llvm::PointerIntPair<const NamedDecl *,
                           LVComputationKind::NumLVComputationKindBits>;
  llvm::SmallDenseMap<QueryType, LinkageInfo, 8> CachedLinkageInfo;

  static QueryType makeCacheKey(const NamedDecl *ND, LVComputationKind Kind) {
    return QueryType(ND, Kind.toBits());
  }

  std::optional<LinkageInfo> lookup(const NamedDecl *ND,
                                    LVComputationKind Kind) const {
    auto Iter = CachedLinkageInfo.find(makeCacheKey(ND, Kind));
    if (Iter == CachedLinkageInfo.end())
      return std::nullopt;
    return Iter->second;
  }

  void cache(const NamedDecl *ND, LVComputationKind Kind, LinkageInfo Info) {
    CachedLinkageInfo[makeCacheKey(ND, Kind)] = Info;
  }

  LinkageInfo getLVForTemplateArgumentList(ArrayRef<TemplateArgument> Args,
                                           LVComputationKind computation);

  LinkageInfo getLVForTemplateArgumentList(const TemplateArgumentList &TArgs,
                                           LVComputationKind computation);

  void mergeTemplateLV(LinkageInfo &LV, const FunctionDecl *fn,
                       const FunctionTemplateSpecializationInfo *specInfo,
                       LVComputationKind computation);

  void mergeTemplateLV(LinkageInfo &LV,
                       const ClassTemplateSpecializationDecl *spec,
                       LVComputationKind computation);

  void mergeTemplateLV(LinkageInfo &LV,
                       const VarTemplateSpecializationDecl *spec,
                       LVComputationKind computation);

  LinkageInfo getLVForNamespaceScopeDecl(const NamedDecl *D,
                                         LVComputationKind computation,
                                         bool IgnoreVarTypeLinkage);

  LinkageInfo getLVForClassMember(const NamedDecl *D,
                                  LVComputationKind computation,
                                  bool IgnoreVarTypeLinkage);

  LinkageInfo getLVForClosure(const DeclContext *DC, Decl *ContextDecl,
                              LVComputationKind computation);

  LinkageInfo getLVForLocalDecl(const NamedDecl *D,
                                LVComputationKind computation);

  LinkageInfo getLVForType(const Type &T, LVComputationKind computation);

  LinkageInfo getLVForTemplateParameterList(const TemplateParameterList *Params,
                                            LVComputationKind computation);

  LinkageInfo getLVForValue(const APValue &V, LVComputationKind computation);

public:
  LinkageInfo computeLVForDecl(const NamedDecl *D,
                               LVComputationKind computation,
                               bool IgnoreVarTypeLinkage = false);

  LinkageInfo getLVForDecl(const NamedDecl *D, LVComputationKind computation);

  LinkageInfo computeTypeLinkageInfo(const Type *T);
  LinkageInfo computeTypeLinkageInfo(QualType T) {
    return computeTypeLinkageInfo(T.getTypePtr());
  }

  LinkageInfo getDeclLinkageAndVisibility(const NamedDecl *D);

  LinkageInfo getTypeLinkageAndVisibility(const Type *T);
  LinkageInfo getTypeLinkageAndVisibility(QualType T) {
    return getTypeLinkageAndVisibility(T.getTypePtr());
  }
};
} // namespace clang

#endif
