//===--- GenericEnvironment.h - Generic Environment AST ---------*- 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 defines the GenericEnvironment class.
//
//===----------------------------------------------------------------------===//

#ifndef SWIFT_AST_GENERIC_ENVIRONMENT_H
#define SWIFT_AST_GENERIC_ENVIRONMENT_H

#include "swift/AST/SubstitutionMap.h"
#include "swift/AST/GenericParamKey.h"
#include "swift/AST/GenericParamList.h"
#include "swift/AST/GenericSignature.h"
#include "swift/Basic/Compiler.h"
#include "swift/Basic/Debug.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/TrailingObjects.h"
#include <utility>

namespace swift {

class ArchetypeType;
class ASTContext;
class GenericTypeParamType;
class OpaqueTypeDecl;
class ElementArchetypeType;
class ExistentialArchetypeType;
class PackArchetypeType;
class PackExpansionType;
class SILModule;
class SILType;
template <class> class CanTypeWrapper;

/// Query function suitable for use as a \c TypeSubstitutionFn that queries
/// the mapping of interface types to archetypes.
class QueryInterfaceTypeSubstitutions {
  const GenericEnvironment *self;
  
public:
  QueryInterfaceTypeSubstitutions(const GenericEnvironment *self)
  : self(self) { }
  
  Type operator()(SubstitutableType *type) const;
};

/// Extra data in a generic environment for an opaque type.
struct OpaqueEnvironmentData {
  OpaqueTypeDecl *decl;
};

/// Extra data in a generic environment for an opened existential.
struct ExistentialEnvironmentData {
  Type existential;
  uint64_t id;
};

/// Extra data in a generic environment for an opened pack element.
struct ElementEnvironmentData {
  uint64_t id;
  CanGenericTypeParamType shapeClass;
};

/// Describes the mapping between archetypes and interface types for the
/// generic parameters of a DeclContext.
///
/// The most frequently used method here is mapTypeIntoEnvironment(), which
/// maps an interface type to a type written in terms of the generic
/// environment's archetypes; to go in the other direction, use
/// TypeBase::mapTypeOutOfEnvironment().
///
class alignas(1 << DeclAlignInBits) GenericEnvironment final
    : private llvm::TrailingObjects<
        GenericEnvironment,
        SubstitutionMap,
        OpaqueEnvironmentData,
        ExistentialEnvironmentData,
        ElementEnvironmentData,
        Type> {
public:
  enum class Kind: uint8_t {
    /// A normal generic environment, determined only by its generic
    /// signature.
    Primary,
    /// A generic environment describing an opaque type archetype.
    Opaque,
    /// A generic environment describing an opened existential archetype.
    Existential,
    /// A generic environment describing an opened element type of a
    /// pack archetype inside a pack expansion expression.
    Element,
  };

  class NestedTypeStorage;

private:
  GenericSignature sig;
  NestedTypeStorage *nestedTypeStorage = nullptr;
  Kind kind;
  bool canonical;

  friend TrailingObjects;
  friend OpaqueTypeArchetypeType;

  size_t numTrailingObjects(OverloadToken<SubstitutionMap>) const;
  size_t numTrailingObjects(OverloadToken<OpaqueEnvironmentData>) const;
  size_t numTrailingObjects(OverloadToken<ExistentialEnvironmentData>) const;
  size_t numTrailingObjects(OverloadToken<ElementEnvironmentData>) const;
  size_t numTrailingObjects(OverloadToken<Type>) const;

  /// Retrieve the array containing the context types associated with the
  /// generic parameters, stored in parallel with the generic parameters of the
  /// generic signature.
  MutableArrayRef<Type> getContextTypes();

  /// Retrieve the array containing the context types associated with the
  /// generic parameters, stored in parallel with the generic parameters of the
  /// generic signature.
  ArrayRef<Type> getContextTypes() const;

  /// Retrieve the array of opened pack parameters for this opened-element
  /// environment.  This is parallel to the array of element parameters,
  /// i.e. the innermost generic context.
  MutableArrayRef<Type> getOpenedPackParams();
  ArrayRef<Type> getOpenedPackParams() const;

  /// Get the nested type storage, allocating it if required.
  NestedTypeStorage &getOrCreateNestedTypeStorage();

  /// Private constructor for primary environments.
  explicit GenericEnvironment(GenericSignature signature);

  /// Private constructor for opaque type environments.
  explicit GenericEnvironment(
      GenericSignature signature, OpaqueTypeDecl *opaque, SubstitutionMap subs);

  /// Private constructor for opened existential environments.
  explicit GenericEnvironment(
      GenericSignature signature, Type existential, SubstitutionMap subs,
      uint64_t id);

  /// Private constructor for opened element environments.
  explicit GenericEnvironment(GenericSignature signature,
                              uint64_t id,
                              CanGenericTypeParamType shapeClass,
                              SubstitutionMap outerSubs);

  friend ArchetypeType;
  friend QueryInterfaceTypeSubstitutions;

  /// Add a mapping of a type parameter to a contextual type, usually
  /// an archetype.
  void addMapping(CanType depType, Type contextType);

  /// Retrieve the mapping for the given type parameter, if present.
  ///
  /// This is only useful when lazily populating a generic environment.
  Type getMappingIfPresent(CanType depType) const;

public:
  GenericSignature getGenericSignature() const {
    return sig;
  }

  Kind getKind() const { return kind; }

  /// Returns if the archetypes from this environment are canonical types.
  bool isCanonical() const { return canonical; }

  ArrayRef<GenericTypeParamType *> getGenericParams() const;

  /// Retrieve the existential type for an opened existential environment.
  Type getOpenedExistentialType() const;

  /// Retrieve the ID for an opened existential environment.
  uint64_t getOpenedExistentialID() const;

  /// Retrieve the opaque type declaration for a generic environment describing
  /// opaque types.
  OpaqueTypeDecl *getOpaqueTypeDecl() const;

  /// Retrieve the outer substitutions for an opaque or local environment.
  ///
  /// Applying a substitution map to a local or opaque archetype outputs
  /// an archetype with the same interface type, and composed outer
  /// substitutions.
  SubstitutionMap getOuterSubstitutions() const;

  /// Retrieve the shape equivalence class for an opened element environment.
  /// This is always a pack parameter.
  CanGenericTypeParamType getOpenedElementShapeClass() const;

  /// Retrieve the ID for an opened element environment.
  uint64_t getOpenedElementID() const;

  /// Return the number of opened pack parameters.
  unsigned getNumOpenedPackParams() const;

  void forEachPackElementArchetype(
          llvm::function_ref<void(ElementArchetypeType*)> function) const;

  void forEachPackElementGenericTypeParam(
      llvm::function_ref<void(GenericTypeParamType *)> function) const;

  using PackElementBindingCallback =
    llvm::function_ref<void(ElementArchetypeType *elementType,
                            PackType *packSubstitution)>;

  /// Given that this is an opened element environment, iterate the
  /// opened pack element bindings: the pack archetype that's been opened
  /// (which may not be meaningful in the surrounding context), the element
  /// archetype that it has been opened as, and the pack type whose elements
  /// are opened.
  void forEachPackElementBinding(PackElementBindingCallback function) const;

  /// Create a new, primary generic environment.
  static GenericEnvironment *forPrimary(GenericSignature signature);

  /// Create a new generic environment for an opaque type with the given set of
  /// outer substitutions.
  static GenericEnvironment *forOpaqueType(
      OpaqueTypeDecl *opaque, SubstitutionMap subs);

  /// Create a new generic environment for an opened existential.
  ///
  /// \param existential The subject existential type
  /// \param id The unique identifier for this opened existential
  static GenericEnvironment *
  forOpenedExistential(Type existential, uint64_t id);

  /// Create a new generic environment for an opened existential.
  ///
  /// \param signature The opened existential signature
  /// \param existential The generalized existential type
  /// \param outerSubs The substitution map containing archetypes from the
  /// outer generic context
  /// \param id The unique identifier for this opened existential
  static GenericEnvironment *
  forOpenedExistential(GenericSignature signature,
                       Type existential, SubstitutionMap outerSubs,
                       uint64_t id);

  /// Create a new generic environment for an opened element.
  ///
  /// \param signature The opened element signature, which is the same as the
  /// signature of the context whose element type is being opened, but with
  /// the pack parameter bit erased from one or more generic parameters
  /// \param id The unique identifier for this opened element
  /// \param shapeClass The shape equivalence class for the originating packs
  /// \param outerSubs The substitution map containing archetypes from the
  /// outer generic context
  static GenericEnvironment *
  forOpenedElement(GenericSignature signature,
                   uint64_t id, CanGenericTypeParamType shapeClass,
                   SubstitutionMap outerSubs);

  /// Make vanilla new/delete illegal.
  void *operator new(size_t Bytes) = delete;
  void operator delete(void *Data) = delete;

  /// Only allow placement new.
  void *operator new(size_t Bytes, void *Mem) {
    assert(Mem); 
    return Mem; 
  }

  /// For an opaque or pack element archetype environment, apply the
  /// substitutions.
  Type maybeApplyOuterContextSubstitutions(Type type) const;

  /// Map an interface type to a contextual type.
  static Type mapTypeIntoEnvironment(GenericEnvironment *genericEnv,
                                 Type type);

  /// Map an interface type to a contextual type.
  Type mapTypeIntoEnvironment(Type type) const;

  /// Map a generic parameter type to a contextual type.
  Type mapTypeIntoEnvironment(GenericTypeParamType *type) const;

  /// Map a type parameter type to a contextual type.
  Type getOrCreateArchetypeFromInterfaceType(Type depType);

  /// Map an interface type containing parameter packs to a contextual
  /// type in the opened element generic context.
  Type mapPackTypeIntoElementContext(Type type) const;

  /// Map a contextual type containing parameter packs to a contextual
  /// type in the opened element generic context.
  ///
  /// This operation only makes sense if the generic environment that the
  /// pack archetypes are contextual in matches the generic signature
  /// of this environment.  That will be true for opened element
  /// environments coming straight out of the type checker, such as
  /// the one in a PackExpansionExpr, or opened element environments
  /// created directly from the current environment.  It is not
  /// reliable for opened element environments in arbitrary SIL functions.
  Type mapContextualPackTypeIntoElementContext(Type type) const;

  /// Map a contextual type containing parameter packs to a contextual
  /// type in the opened element generic context.
  ///
  /// This operation only makes sense if the generic environment that the
  /// pack archetypes are contextual in matches the generic signature
  /// of this environment.  That will be true for opened element
  /// environments coming straight out of the type checker, such as
  /// the one in a PackExpansionExpr, or opened element environments
  /// created directly from the current environment.  It is not
  /// reliable for opened element environments in arbitrary SIL functions.
  CanType mapContextualPackTypeIntoElementContext(CanType type) const;

  /// Map a type containing pack element type parameters to a contextual
  /// type in the pack generic context.
  Type mapElementTypeIntoPackContext(Type type) const;

  /// Map the given SIL interface type to a contextual type.
  ///
  /// This operation will also reabstract dependent types according to the
  /// abstraction level of their associated type requirements.
  SILType mapTypeIntoEnvironment(SILModule &M, SILType type) const;

  /// Returns a substitution map that sends every generic parameter to its
  /// corresponding archetype in this generic environment.
  SubstitutionMap getForwardingSubstitutionMap() const;

  void dump(raw_ostream &os) const;

  SWIFT_DEBUG_DUMP;
};

/// A pair of an opened-element generic signature and an opened-element
/// generic environment.
struct OpenedElementContext {
  /// The opened-element environment for this expansion.
  GenericEnvironment *environment;

  /// The opened-element signature for this expansion.
  CanGenericSignature signature;

  /// Create a fresh opened element context from a contextual pack
  /// expansion type.  This is useful when writing code that needs to
  /// break down the components of a pack expansion.
  static OpenedElementContext
  createForContextualExpansion(ASTContext &ctx,
                   CanTypeWrapper<PackExpansionType> expansionType);
};
  
} // end namespace swift

#endif // SWIFT_AST_GENERIC_ENVIRONMENT_H

