//===--- Bridging/LangOptsBridging.cpp ------------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2022-2025 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
//
//===----------------------------------------------------------------------===//

#include "swift/AST/ASTBridging.h"

#include "swift/Basic/LangOptions.h"

using namespace swift;

bool BridgedLangOptions_hasFeature(BridgedLangOptions cLangOpts,
                                   BridgedFeature feature) {
  return cLangOpts.unbridged().hasFeature((Feature)feature);
}


unsigned BridgedLangOptions::getTargetPointerBitWidth() const {
  return unbridged().Target.isArch64Bit()   ? 64
         : unbridged().Target.isArch32Bit() ? 32
         : unbridged().Target.isArch16Bit() ? 16
                                            : 0;
}

BridgedEndianness BridgedLangOptions::getTargetEndianness() const {
  return unbridged().Target.isLittleEndian() ? EndianLittle : EndianBig;
}

bool BridgedLangOptions::getAttachCommentsToDecls() const {
  return unbridged().AttachCommentsToDecls;
}

/// Convert an array of numbers into a form we can use in Swift.
namespace {
template <typename Arr>
SwiftInt convertArray(const Arr &array, SwiftInt **cElements) {
  SwiftInt numElements = array.size();
  *cElements = (SwiftInt *)malloc(sizeof(SwiftInt) * numElements);
  for (SwiftInt i = 0; i != numElements; ++i)
    (*cElements)[i] = array[i];
  return numElements;
}
} // namespace

void deallocateIntBuffer(SwiftInt *_Nullable cComponents) { free(cComponents); }

SwiftInt
BridgedLangOptions_getLanguageVersion(BridgedLangOptions cLangOpts,
                                      SwiftInt **cComponents) {
  auto theVersion = cLangOpts.unbridged().EffectiveLanguageVersion;
  return convertArray(theVersion, cComponents);
}

SwiftInt
BridgedLangOptions_getCompilerVersion(BridgedLangOptions cLangOpts,
                                      SwiftInt **cComponents) {
  auto theVersion = version::Version::getCurrentLanguageVersion();
  return convertArray(theVersion, cComponents);
}

SwiftInt BridgedLangOptions_getTargetAtomicBitWidths(
    BridgedLangOptions cLangOpts, SwiftInt *_Nullable *_Nonnull cElements) {
  return convertArray(cLangOpts.unbridged().getAtomicBitWidthValues(),
                      cElements);
}

namespace {

/// Describe behaviors that should prevent an attribute from being shown.
///
/// This is DeclAttrBehaviors, but with irrelevent values set to zero.
enum DeclAttrBehaviorsNotShown : uint64_t {
  /// Whether this attribute is only valid when concurrency is enabled.
  ConcurrencyOnly = 0,

  /// True if multiple instances of this attribute are allowed on a single
  /// declaration.
  AllowMultipleAttributes = 0,

  /// True if this is a decl modifier - i.e., that it should not be spelled
  /// with an @.
  DeclModifier = 1ull << 2,

  /// True if this is a long attribute that should be printed on its own line.
  ///
  /// Currently has no effect on DeclModifier attributes.
  LongAttribute = 0,

  /// True if this shouldn't be serialized.
  NotSerialized = 0,

  /// True if this attribute is only valid when parsing a .sil file.
  SILOnly = 1ull << 5,

  /// The attribute should be reported by parser as unknown.
  RejectByParser = 1ull << 6,

  /// Whether client code cannot use the attribute. Hides it in code completion.
  UserInaccessible = 1ull << 7,

  /// Whether adding this attribute can break API
  APIBreakingToAdd = 0,

  /// Whether removing this attribute can break API
  APIBreakingToRemove = 0,

  /// Whether adding this attribute can break ABI
  ABIBreakingToAdd = 0,

  /// Whether removing this attribute can break ABI
  ABIBreakingToRemove = 0,

  /// The opposite of APIBreakingToAdd
  APIStableToAdd = 0,

  /// The opposite of APIBreakingToRemove
  APIStableToRemove = 0,

  /// The opposite of ABIBreakingToAdd
  ABIStableToAdd = 0,

  /// The opposite of ABIBreakingToRemove
  ABIStableToRemove = 0,

  /// Attribute should not be used in an \c \@abi attribute. Use for
  /// attributes which cannot affect mangled names, even indirectly, and
  /// which either don't affect ABI or where ABI-only declarations get their
  /// behavior from their API counterpart.
  ForbiddenInABIAttr = 0,

  /// Attribute can be used without restrictions in an \c \@abi attribute.
  /// Use for attributes which affect mangled names but otherwise don't alter
  /// the ABI, or ones where the \c ABIDeclChecker manually implements
  /// special checking logic (e.g. because several different attributes
  /// contribute to the same aspect of ABI in some complicated way).
  UnconstrainedInABIAttr = 0,

  /// Attribute can be used in an \c \@abi attribute, but must match
  /// equivalent on API decl. Use for attributes which affect both mangled
  /// names and other parts of the ABI such that the declaration can only be
  /// valid if they match.
  EquivalentInABIAttr = 0,

  /// Use for attributes which are \em only valid on declarations that cannot
  /// have an \c @abi attribute, such as \c ImportDecl .
  UnreachableInABIAttr = 0,
};

}

void BridgedLangOptions_enumerateBuildConfigurationEntries(
    BridgedLangOptions cLangOpts,
    void * _Nonnull callbackContext,
    void (* _Nonnull callback)(
        BridgedLangOptions cLangOpts, void * _Nonnull callbackContext,
        BuildConfigurationKey key, BridgedStringRef value)) {
  const LangOptions &langOpts = cLangOpts.unbridged();

  // Enumerate custom conditions.
  for (const auto &customCondition: langOpts.getCustomConditionalCompilationFlags()) {
    callback(cLangOpts, callbackContext, BCKCustomCondition,
             StringRef(customCondition));
  }

  // Enumerate features that are enabled.
#define LANGUAGE_FEATURE(FeatureName, SENumber, Description)                   \
  if (langOpts.hasFeature(Feature::FeatureName))                               \
    callback(cLangOpts, callbackContext, BCKFeature, StringRef(#FeatureName));
#include "swift/Basic/Features.def"

  // Compiler-owned COM model conditions use the same `$Name` source syntax as
  // language features, but are derived exclusively from COMModel rather than
  // Features.def or an independently-enableable feature flag.
  if (auto condition = langOpts.getCOMInteropModelConditionalCompilationFlag();
      !condition.empty()) {
    assert(condition.starts_with("$"));
    callback(cLangOpts, callbackContext, BCKFeature, condition.drop_front());
  }

  // Enumerate attributes that are available.
#define DECL_ATTR(SPELLING, CLASS, REQUIREMENTS, BEHAVIORS, CODE)              \
  if ((BEHAVIORS) == 0) \
    callback(cLangOpts, callbackContext, BCKAttribute, StringRef(#SPELLING));
#include "swift/AST/DeclAttr.def"

#define SIL_TYPE_ATTR(X, C)
#define TYPE_ATTR(SPELLING, CLASS)                                             \
  callback(cLangOpts, callbackContext, BCKAttribute, StringRef(#SPELLING));
#include "swift/AST/TypeAttr.def"

  // Deal with all of the target platform/architecture information.
  for (const auto &[kind, value] : langOpts.getPlatformConditionValues()) {
    switch (kind) {
      case PlatformConditionKind::OS:
        callback(cLangOpts, callbackContext, BCKTargetOSName, StringRef(value));
        break;

      case PlatformConditionKind::Arch:
        callback(cLangOpts, callbackContext, BCKTargetArchitecture, StringRef(value));
        break;

      case PlatformConditionKind::Runtime:
        callback(cLangOpts, callbackContext, BCKTargetRuntime, StringRef(value));
        break;

      case PlatformConditionKind::TargetEnvironment:
        callback(cLangOpts, callbackContext, BCKTargetEnvironment,
                 StringRef(value));

        // When compiling for iOS we consider "macCatalyst" to be a
        // synonym of "macabi". This enables the use of
        // #if targetEnvironment(macCatalyst) as a compilation
        // condition for macCatalyst.
        if (value == "macabi" && langOpts.Target.isiOS()) {
          callback(cLangOpts, callbackContext, BCKTargetEnvironment,
                   StringRef("macCatalyst"));
        }
        break;

      case PlatformConditionKind::PtrAuth:
        callback(cLangOpts, callbackContext, BCKTargetPointerAuthenticationScheme,
                 StringRef(value));
        break;

      case PlatformConditionKind::Endianness:
      case PlatformConditionKind::PointerBitWidth:
      case PlatformConditionKind::ObjectFileFormat:
      case PlatformConditionKind::CanImport:
      case PlatformConditionKind::HasAtomicBitWidth:
        // Handled separately.
        break;
    }
  }

  // Object file format.
  llvm::Triple triple(langOpts.Target.getTriple());
  switch (triple.getObjectFormat()) {
    case llvm::Triple::ObjectFormatType::COFF:
      callback(cLangOpts, callbackContext, BCKTargetObjectFileFormat,
               StringRef("COFF"));
      break;
    case llvm::Triple::ObjectFormatType::ELF:
      callback(cLangOpts, callbackContext, BCKTargetObjectFileFormat,
               StringRef("ELF"));
      break;
    case llvm::Triple::ObjectFormatType::MachO:
      callback(cLangOpts, callbackContext, BCKTargetObjectFileFormat,
               StringRef("MachO"));
      break;
    case llvm::Triple::ObjectFormatType::SPIRV:
      callback(cLangOpts, callbackContext, BCKTargetObjectFileFormat,
               StringRef("SPIRV"));
      break;
    case llvm::Triple::ObjectFormatType::Wasm:
      callback(cLangOpts, callbackContext, BCKTargetObjectFileFormat,
               StringRef("Wasm"));
      break;
    default:
      // Ignore others.
      break;
  }
}
