//===--- SwiftDeclSynthesizer.h - Synthesize helper Swift decls -*- C++ -*-===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2022 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
//
//===----------------------------------------------------------------------===//

#ifndef SWIFT_SWIFT_DECL_SYNTHESIZER_H
#define SWIFT_SWIFT_DECL_SYNTHESIZER_H

#include "ImporterImpl.h"
#include "swift/ClangImporter/ClangImporter.h"

namespace swift {

class CallExpr;

enum class MakeStructRawValuedFlags {
  /// whether to also create an unlabeled init
  MakeUnlabeledValueInit = 0x01,

  /// whether the raw value should be a let
  IsLet = 0x02,

  /// whether to mark the rawValue as implicit
  IsImplicit = 0x04,
};
using MakeStructRawValuedOptions = OptionSet<MakeStructRawValuedFlags>;

inline MakeStructRawValuedOptions getDefaultMakeStructRawValuedOptions() {
  MakeStructRawValuedOptions opts;
  opts -= MakeStructRawValuedFlags::MakeUnlabeledValueInit; // default off
  opts |= MakeStructRawValuedFlags::IsLet;                  // default on
  opts |= MakeStructRawValuedFlags::IsImplicit;             // default on
  return opts;
}

inline AccessLevel getOverridableAccessLevel(const DeclContext *dc) {
  return (dc->getSelfClassDecl() ? AccessLevel::Open : AccessLevel::Public);
}

enum class ReferenceReturnTypeBehaviorForBaseMethodSynthesis {
  KeepReference,
  RemoveReference,
  RemoveReferenceIfPointer,
};

enum class ForwardingMethodKind { Base, Virtual };

class SwiftDeclSynthesizer {
private:
  ClangImporter::Implementation &ImporterImpl;

public:
  explicit SwiftDeclSynthesizer(ClangImporter::Implementation &Impl)
      : ImporterImpl(Impl) {}
  explicit SwiftDeclSynthesizer(ClangImporter *importer)
      : ImporterImpl(importer->Impl) {}

  /// Create a typedpattern(namedpattern(decl))
  static Pattern *createTypedNamedPattern(VarDecl *decl);

  /// Create a var member for this struct, along with its pattern binding, and
  /// add it as a member.
  static std::pair<VarDecl *, PatternBindingDecl *>
  createVarWithPattern(DeclContext *dc, Identifier name, Type ty,
                       VarDecl::Introducer introducer, bool isImplicit,
                       AccessLevel access, AccessLevel setterAccess);

  /// Create a reinterpretCast from the `exprType`, to the `givenType`.
  static Expr *synthesizeReturnReinterpretCast(ASTContext &ctx, Type givenType,
                                               Type exprType, Expr *baseExpr);

  /// Create a new named constant with the given value.
  ///
  /// \param name The name of the constant.
  /// \param dc The declaration context into which the name will be introduced.
  /// \param type The type of the named constant.
  /// \param value The value of the named constant.
  /// \param convertKind How to convert the constant to the given type.
  /// \param isStatic Whether the constant should be a static member of \p dc.
  /// \param access What access level should be given to the constant.
  ValueDecl *createConstant(Identifier name, DeclContext *dc, Type type,
                            const clang::APValue &value,
                            ConstantConvertKind convertKind, bool isStatic,
                            ClangNode ClangN, AccessLevel access);

  /// Create a new named constant with the given value.
  ///
  /// \param name The name of the constant.
  /// \param dc The declaration context into which the name will be introduced.
  /// \param type The type of the named constant.
  /// \param value The value of the named constant.
  /// \param convertKind How to convert the constant to the given type.
  /// \param isStatic Whether the constant should be a static member of \p dc.
  /// \param access What access level should be given to the constant.
  ValueDecl *createConstant(Identifier name, DeclContext *dc, Type type,
                            StringRef value, ConstantConvertKind convertKind,
                            bool isStatic, ClangNode ClangN,
                            AccessLevel access);

  /// Create a new named constant using the given expression.
  ///
  /// \param name The name of the constant.
  /// \param dc The declaration context into which the name will be introduced.
  /// \param type The type of the named constant.
  /// \param valueExpr An expression to use as the value of the constant.
  /// \param convertKind How to convert the constant to the given type.
  /// \param isStatic Whether the constant should be a static member of \p dc.
  /// \param access What access level should be given to the constant.
  ValueDecl *createConstant(Identifier name, DeclContext *dc, Type type,
                            Expr *valueExpr, ConstantConvertKind convertKind,
                            bool isStatic, ClangNode ClangN,
                            AccessLevel access);

  /// Create a default constructor that initializes a struct to zero.
  ConstructorDecl *createDefaultConstructor(NominalTypeDecl *structDecl);

  /// Create a constructor that initializes a struct from its members.
  ConstructorDecl *createValueConstructor(NominalTypeDecl *structDecl,
                                          ArrayRef<VarDecl *> members,
                                          bool wantCtorParamNames,
                                          bool wantBody);

  /// Create a rawValue-ed constructor that bridges to its underlying storage.
  ConstructorDecl *createRawValueBridgingConstructor(StructDecl *structDecl,
                                                     VarDecl *computedRawValue,
                                                     VarDecl *storedRawValue,
                                                     bool wantLabel,
                                                     bool wantBody);

  /// Make a struct declaration into a raw-value-backed struct, with
  /// bridged computed rawValue property which differs from stored backing
  ///
  /// \param structDecl the struct to make a raw value for
  /// \param storedUnderlyingType the type of the stored raw value
  /// \param bridgedType the type of the 'rawValue' computed property bridge
  /// \param synthesizedProtocolAttrs synthesized protocol attributes to add
  ///
  /// This will perform most of the work involved in making a new Swift struct
  /// be backed by a stored raw value and computed raw value of bridged type.
  /// This will populated derived protocols and synthesized protocols, add the
  /// new variable and pattern bindings, and create the inits parameterized
  /// over a bridged type that will cast to the stored type, as appropriate.
  void makeStructRawValuedWithBridge(
      StructDecl *structDecl, Type storedUnderlyingType, Type bridgedType,
      ArrayRef<KnownProtocolKind> synthesizedProtocolAttrs,
      bool makeUnlabeledValueInit = false);

  /// Make a struct declaration into a raw-value-backed struct
  ///
  /// \param structDecl the struct to make a raw value for
  /// \param underlyingType the type of the raw value
  /// \param synthesizedProtocolAttrs synthesized protocol attributes to add
  /// \param setterAccess the access level of the raw value's setter
  ///
  /// This will perform most of the work involved in making a new Swift struct
  /// be backed by a raw value. This will populated derived protocols and
  /// synthesized protocols, add the new variable and pattern bindings, and
  /// create the inits parameterized over a raw value
  ///
  void makeStructRawValued(StructDecl *structDecl, Type underlyingType,
                           ArrayRef<KnownProtocolKind> synthesizedProtocolAttrs,
                           MakeStructRawValuedOptions options =
                               getDefaultMakeStructRawValuedOptions(),
                           AccessLevel setterAccess = AccessLevel::Private);

  /// Build the union field getter and setter.
  ///
  /// \code
  /// struct SomeImportedUnion {
  ///   var myField: Int {
  ///     get {
  ///       return Builtin.reinterpretCast(self)
  ///     }
  ///     set(newValue) {
  ///       Builtin.initialize(Builtin.addressof(self), newValue))
  ///     }
  ///   }
  /// }
  /// \endcode
  ///
  /// \returns a pair of the getter and setter function decls.
  std::pair<AccessorDecl *, AccessorDecl *>
  makeUnionFieldAccessors(NominalTypeDecl *importedUnionDecl,
                          VarDecl *importedFieldDecl);

  /// Build the bitfield getter and setter using Clang.
  ///
  /// \code
  /// static inline int get(RecordType self) {
  ///   return self.field;
  /// }
  /// static inline void set(int newValue, RecordType *self) {
  ///   self->field = newValue;
  /// }
  /// \endcode
  ///
  /// \returns a pair of the getter and setter function decls.
  std::pair<FuncDecl *, FuncDecl *> makeBitFieldAccessors(
      clang::RecordDecl *structDecl, NominalTypeDecl *importedStructDecl,
      clang::FieldDecl *fieldDecl, VarDecl *importedFieldDecl);

  /// Build the indirect field getter and setter.
  ///
  /// \code
  /// struct SomeImportedIndirectField {
  ///   struct __Unnamed_struct___Anonymous_field_1 {
  ///     var myField : Int
  ///   }
  ///   var __Anonymous_field_1 : __Unnamed_struct___Anonymous_field_1
  ///   var myField : Int {
  ///     get {
  ///       __Anonymous_field_1.myField
  ///     }
  ///     set(newValue) {
  ///       __Anonymous_field_1.myField = newValue
  ///     }
  ///   }
  /// }
  /// \endcode
  ///
  /// \returns a pair of getter and setter function decls.
  std::pair<AccessorDecl *, AccessorDecl *>
  makeIndirectFieldAccessors(const clang::IndirectFieldDecl *indirectField,
                             ArrayRef<VarDecl *> members,
                             NominalTypeDecl *importedStructDecl,
                             VarDecl *importedFieldDecl);

  /// Build the init(rawValue:) initializer for an imported NS_ENUM.
  ///
  /// \code
  /// enum NSSomeEnum: RawType {
  ///   init?(rawValue: RawType) {
  ///     self = Builtin.reinterpretCast(rawValue)
  ///   }
  /// }
  /// \endcode
  ///
  /// Unlike a standard init(rawValue:) enum initializer, this does a
  /// reinterpret cast in order to preserve unknown or future cases from C.
  ConstructorDecl *makeEnumRawValueConstructor(EnumDecl *enumDecl);

  /// Build the rawValue getter for an imported NS_ENUM.
  ///
  /// \code
  /// enum NSSomeEnum: RawType {
  ///   var rawValue: RawType {
  ///     return Builtin.reinterpretCast(self)
  ///   }
  /// }
  /// \endcode
  ///
  /// Unlike a standard init(rawValue:) enum initializer, this does a
  /// reinterpret cast in order to preserve unknown or future cases from C.
  void makeEnumRawValueGetter(EnumDecl *enumDecl, VarDecl *rawValueDecl);

  /// Build the rawValue getter for a struct type.
  ///
  /// \code
  /// struct SomeType: RawRepresentable {
  ///   private var _rawValue: ObjCType
  ///   var rawValue: SwiftType {
  ///     return _rawValue as SwiftType
  ///   }
  /// }
  /// \endcode
  AccessorDecl *makeStructRawValueGetter(StructDecl *structDecl,
                                         VarDecl *computedVar,
                                         VarDecl *storedVar);

  /// Build a declaration for an Objective-C subscript getter.
  AccessorDecl *buildSubscriptGetterDecl(SubscriptDecl *subscript,
                                         const FuncDecl *getter, Type elementTy,
                                         DeclContext *dc, ParamDecl *index);

  /// Build a declaration for an Objective-C subscript setter.
  AccessorDecl *buildSubscriptSetterDecl(SubscriptDecl *subscript,
                                         const FuncDecl *setter,
                                         Type elementInterfaceTy,
                                         DeclContext *dc, ParamDecl *index);

  /// Given either the getter, the setter, or both getter & setter
  /// for a subscript operation, create the Swift subscript declaration.
  ///
  /// \param getter function returning `UnsafePointer<T>`
  /// \param setter function returning `UnsafeMutablePointer<T>`
  /// \return subscript declaration
  SubscriptDecl *makeSubscript(FuncDecl *getter, FuncDecl *setter);

  /// Given an imported C++ dereference operator (`operator*()`), create a
  /// `pointee` computed property.
  ///
  /// \param getter function returning `UnsafePointer<T>`
  /// \param setter function returning `UnsafeMutablePointer<T>`
  /// \return computed property declaration
  VarDecl *makeDereferencedPointeeProperty(FuncDecl *getter, FuncDecl *setter);

  /// Given a C++ pre-increment operator (`operator++()`). create a non-mutating
  /// function `successor() -> Self`.
  FuncDecl *makeSuccessorFunc(FuncDecl *incrementFunc);

  FuncDecl *makeOperator(FuncDecl *operatorMethod,
                         clang::OverloadedOperatorKind opKind);
  
  // Synthesize a C++ method that invokes the method from the base
  // class. This lets Clang take care of the cast from the derived class
  // to the base class during the invocation of the method.
  clang::CXXMethodDecl *synthesizeCXXForwardingMethod(
      const clang::CXXRecordDecl *derivedClass,
      const clang::CXXRecordDecl *baseClass, const clang::CXXMethodDecl *method,
      ForwardingMethodKind forwardingMethodKind,
      ReferenceReturnTypeBehaviorForBaseMethodSynthesis
          referenceReturnTypeBehavior =
              ReferenceReturnTypeBehaviorForBaseMethodSynthesis::KeepReference,
      bool forceConstQualifier = false);

  /// Given an overload of a C++ virtual method on a reference type, create a
  /// method that dispatches the call dynamically.
  FuncDecl *makeVirtualMethod(const clang::CXXMethodDecl *clangMethodDecl,
                              StringRef swiftName);

  FuncDecl *makeInstanceToStaticOperatorCallMethod(
      const clang::CXXMethodDecl *clangMethodDecl);

  VarDecl *makeComputedPropertyFromCXXMethods(FuncDecl *getter,
                                              FuncDecl *setter);

  CallExpr *makeDefaultArgument(const clang::ParmVarDecl *param,
                                const swift::Type &swiftParamTy,
                                SourceLoc paramLoc);

  /// Synthesize a static factory method for a C++ foreign reference type,
  /// returning a `CXXMethodDecl*` or `nullptr` if the required constructor or
  /// allocation function is not found.
  llvm::SmallVector<clang::CXXMethodDecl *, 4>
  synthesizeStaticFactoryForCXXForeignRef(
      const clang::CXXRecordDecl *cxxRecordDecl);

  /// Look for an explicitly-provided "destroy" operation. If one exists
  /// and the type has been imported as noncopyable, add an explicit `deinit`
  /// that calls that destroy operation.
  void addExplicitDeinitIfRequired(
      NominalTypeDecl *nominal, const clang::RecordDecl *clangType);

  /// Synthesize a Swift function that calls the Clang runtime predicate
  /// function for the availability domain represented by `var`.
  FuncDecl *makeAvailabilityDomainPredicate(const clang::VarDecl *var);

  bool isCGFloat(Type type);

  bool isObjCBool(Type type);

private:
  Type getConstantLiteralType(Type type, ConstantConvertKind convertKind);

  /// Find an explicitly-provided "destroy" operation specified for the
  /// given Clang type and return it.
  FuncDecl *findExplicitDestroy(
      NominalTypeDecl *nominal, const clang::RecordDecl *clangType);

};

} // namespace swift

#endif // SWIFT_SWIFT_DECL_SYNTHESIZER_H
