//===--- GenProto.h - Swift IR generation for prototypes --------*- C++ -*-===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2017 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 the private interface to the protocol-emission code.
//
//===----------------------------------------------------------------------===//

#ifndef SWIFT_IRGEN_GENPROTO_H
#define SWIFT_IRGEN_GENPROTO_H

#include "swift/SIL/SILFunction.h"

#include "Fulfillment.h"
#include "GenericRequirement.h"

namespace llvm {
  class Type;
}

namespace swift {
  class AssociatedConformance;
  class AssociatedType;
  class CanType;
  class FuncDecl;
  enum class MetadataState : size_t;
  class ProtocolConformanceRef;
  struct SILDeclRef;
  class SILType;
  class SILFunction;

namespace irgen {
  class Address;
  class DynamicMetadataRequest;
  class EntryPointArgumentEmission;
  class Explosion;
  class FunctionPointer;
  class IRGenFunction;
  class IRGenModule;
  class MetadataPath;
  class MetadataResponse;
  class NativeCCEntryPointArgumentEmission;
  class ProtocolInfo;
  class TypeInfo;

  /// Set an LLVM value name for the given type metadata.
  void setTypeMetadataName(IRGenModule &IGM, llvm::Value *value, CanType type);

  /// Set an LLVM value name for the given protocol witness table.
  void setProtocolWitnessTableName(IRGenModule &IGM, llvm::Value *value,
                                   CanType type, ProtocolDecl *protocol);

  /// Extract the method pointer from the given witness table
  /// as a function value.
  FunctionPointer emitWitnessMethodValue(IRGenFunction &IGF,
                                         llvm::Value *wtable,
                                         SILDeclRef member);

  /// Extract the method pointer from an archetype's witness table
  /// as a function value.
  FunctionPointer emitWitnessMethodValue(IRGenFunction &IGF, CanType baseTy,
                                         llvm::Value **baseMetadataCache,
                                         SILDeclRef member,
                                         ProtocolConformanceRef conformance);

  /// Compute the index into a witness table for a resilient protocol given
  /// a reference to a descriptor of one of the requirements in that witness
  /// table.
  llvm::Value *computeResilientWitnessTableIndex(
                                            IRGenFunction &IGF,
                                            ProtocolDecl *proto,
                                            llvm::Constant *reqtDescriptor);

  /// Given a type T and an associated type X of some protocol P to
  /// which T conforms, return the type metadata for T.X.
  ///
  /// \param parentMetadata - the type metadata for T
  /// \param wtable - the witness table witnessing the conformance of T to P
  /// \param associatedType - the declaration of X; a member of P
  MetadataResponse emitAssociatedTypeMetadataRef(IRGenFunction &IGF,
                                                 llvm::Value *parentMetadata,
                                                 llvm::Value *wtable,
                                                 AssociatedType associatedType,
                                                 DynamicMetadataRequest request);

  // Return the offset one should do on a witness table pointer to retrieve the
  // `index`th piece of private data.
  inline int privateWitnessTableIndexToTableOffset(unsigned index) {
    return -1 - (int)index;
  }

  /// Add the witness parameters necessary for calling a function with
  /// the given generics clause.
  void expandPolymorphicSignature(IRGenModule &IGM,
                                  CanSILFunctionType type,
                                  SmallVectorImpl<llvm::Type*> &types);

  /// Return the number of trailing arguments necessary for calling a
  /// witness method.
  inline unsigned getTrailingWitnessSignatureLength(IRGenModule &IGM,
                                                    CanSILFunctionType type) {
    return 2;
  }

  /// Add the trailing arguments necessary for calling a witness method.
  void expandTrailingWitnessSignature(IRGenModule &IGM,
                                      CanSILFunctionType type,
                                      SmallVectorImpl<llvm::Type*> &types);

  struct WitnessMetadata {
    llvm::Value *SelfMetadata = nullptr;
    llvm::Value *SelfWitnessTable = nullptr;
  };

  /// Collect any required metadata for a witness method from the end
  /// of the given parameter list.
  void
  collectTrailingWitnessMetadata(IRGenFunction &IGF, SILFunction &fn,
                                 NativeCCEntryPointArgumentEmission &params,
                                 WitnessMetadata &metadata);

  using GetParameterFn = llvm::function_ref<llvm::Value*(unsigned)>;

  /// In the prelude of a generic function, perform the bindings for a
  /// generics clause.
  ///
  /// \param witnessMetadata - can be omitted if the function is
  ///   definitely not a witness method
  void emitPolymorphicParameters(IRGenFunction &IGF, SILFunction &Fn,
                                 EntryPointArgumentEmission &emission,
                                 WitnessMetadata *witnessMetadata,
                                 const GetParameterFn &getParameter);

  void emitPolymorphicParametersFromArray(IRGenFunction &IGF,
                                          NominalTypeDecl *typeDecl,
                                          Address array,
                                          MetadataState metadataState);

  /// When calling a polymorphic call, pass the arguments for the
  /// generics clause.
  void emitPolymorphicArguments(IRGenFunction &IGF,
                                CanSILFunctionType origType,
                                SubstitutionMap subs,
                                WitnessMetadata *witnessMetadata,
                                Explosion &args);

  /// Bind the polymorphic parameter inside of a partial apply forwarding thunk.
  void bindPolymorphicParameter(IRGenFunction &IGF,
                                CanSILFunctionType &OrigFnType,
                                CanSILFunctionType &SubstFnType,
                                Explosion &nativeParam, unsigned paramIndex);

  /// Load a reference to the protocol descriptor for the given protocol.
  ///
  /// For Swift protocols, this is a constant reference to the protocol
  /// descriptor symbol.
  /// For ObjC protocols, descriptors are uniqued at runtime by the ObjC
  /// runtime. We need to load the unique reference from a global variable fixed up at
  /// startup.
  llvm::Value *emitProtocolDescriptorRef(IRGenFunction &IGF,
                                         ProtocolDecl *protocol);

  /// Emit a witness table reference.
  llvm::Value *emitWitnessTableRef(IRGenFunction &IGF,
                                   CanType srcType,
                                   llvm::Value **srcMetadataCache,
                                   ProtocolConformanceRef conformance);

  llvm::Value *emitWitnessTableRef(IRGenFunction &IGF,
                                   CanType srcType,
                                   ProtocolConformanceRef conformance);

  class MetadataSource {
  public:
    enum class Kind {
      /// Metadata is derived from a source class pointer.
      ClassPointer,

      /// Metadata is derived from a type metadata pointer.
      Metadata,

      /// Metadata is derived from the origin type parameter.
      GenericLValueMetadata,

      /// Metadata is obtained directly from the from a Self metadata
      /// parameter passed via the WitnessMethod convention.
      SelfMetadata,

      /// Metadata is derived from the Self witness table parameter
      /// passed via the WitnessMethod convention.
      SelfWitnessTable,

      /// Metadata is obtained directly from the FixedType indicated. Used with
      /// Objective-C generics, where the actual argument is erased at runtime
      /// and its existential bound is used instead.
      ErasedTypeMetadata,
    };

    static bool requiresSourceIndex(Kind kind) {
      return (kind == Kind::ClassPointer ||
              kind == Kind::Metadata ||
              kind == Kind::GenericLValueMetadata);
    }

    static bool requiresFixedType(Kind kind) {
      return (kind == Kind::ErasedTypeMetadata);
    }

    enum : unsigned { InvalidSourceIndex = ~0U };

  private:
    /// The kind of source this is.
    Kind TheKind;

    /// For ClassPointer, Metadata, and GenericLValueMetadata, the source index;
    /// for ErasedTypeMetadata, the type; for others, Index should be set to
    /// InvalidSourceIndex.
    union {
      unsigned Index;
      CanType FixedType;
    };

  public:
    CanType Type;

    MetadataSource(Kind kind, CanType type)
      : TheKind(kind), Index(InvalidSourceIndex), Type(type)
    {
      assert(!requiresSourceIndex(kind) && !requiresFixedType(kind));
    }


    MetadataSource(Kind kind, CanType type, unsigned index)
      : TheKind(kind), Index(index), Type(type) {
      assert(requiresSourceIndex(kind));
      assert(index != InvalidSourceIndex);
    }

    MetadataSource(Kind kind, CanType type, CanType fixedType)
      : TheKind(kind), FixedType(fixedType), Type(type) {
      assert(requiresFixedType(kind));
    }

    Kind getKind() const { return TheKind; }

    unsigned getParamIndex() const {
      assert(requiresSourceIndex(getKind()));
      return Index;
    }

    CanType getFixedType() const {
      assert(requiresFixedType(getKind()));
      return FixedType;
    }
  };

  using GenericParamFulfillmentCallback =
    llvm::function_ref<void(CanType genericParamType,
                            const MetadataSource &source,
                            const MetadataPath &path)>;

  void enumerateGenericParamFulfillments(IRGenModule &IGM,
    CanSILFunctionType fnType,
    GenericParamFulfillmentCallback callback);
} // end namespace irgen
} // end namespace swift

#endif
