//===--- Initialization.h - Buffer initialization. --------------*- C++ -*-===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2015 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See http://swift.org/LICENSE.txt for license information
// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
//
// A storage structure for representing a buffer or collection of buffers to
// be initialized.
//
//===----------------------------------------------------------------------===//

#ifndef SWIFT_LOWERING_INITIALIZATION_H
#define SWIFT_LOWERING_INITIALIZATION_H

#include "RValue.h"
#include <memory>

namespace swift {
namespace Lowering {
  class ManagedValue;
  class SILGenFunction;

class Initialization;
using InitializationPtr = std::unique_ptr<Initialization>;
  
/// Initialization - Abstract base class for initialization buffers. An
/// initialization represents an uninitialized buffer or a tuple of
/// uninitialized buffers that must be initialized with the result of an
/// expression, such as in a var declaration or return statement. An
/// initialization may also have partial cleanups that should be disabled and
/// replaced when the buffer is initialized; for instance, a freshly allocated
/// box with an uninitialized value must be deallocated with `dealloc_ref`, but
/// once the box is fully initialized, both the box and the contained value can
/// be cleaned up together with `release`.
///
/// FIXME: provide a reset() operation to support multiple
/// initialization paths.
class Initialization {
public:
  Initialization() {}
  virtual ~Initialization() {}

  /// Return true if this initialization is a simple address in memory.
  virtual bool isSingleBuffer() const {
    return false;
  }

   /// If this initialization represents a single contiguous buffer, return the
  /// SILValue of that buffer's address. If not, returns an invalid SILValue.
  virtual SILValue getAddressOrNull() const = 0;
  
  /// Returns the address of the single contiguous buffer represented by this
  /// initialization. Once the address has been stored to,
  /// finishInitialization must be called.
  SILValue getAddress() const {
    SILValue address = getAddressOrNull();
    assert(address && "initialization does not represent a single buffer");
    return address;
  }
  
  
  /// If this initialization has an address we can directly emit into, return
  /// it.  Otherwise, return a null SILValue.
  virtual SILValue getAddressForInPlaceInitialization() const {
    return SILValue();
  }
  
  /// Return true if we can get the addresses of elements with the
  /// 'getSubInitializationsForTuple' method.  Subclasses can override this to
  /// enable this behavior.
  virtual bool canSplitIntoSubelementAddresses() const {
    return false;
  }

  /// If this initialization represents an aggregation of sub-initializations,
  /// return the sub-initializations. If it represents a single
  /// initialization of tuple type, explode it into initializations for each
  /// individual tuple element. In either case, once all the sub-initializations
  /// have been initialized and finalized with finishInitialization,
  /// finishInitialization must then be called on this aggregate initialization.
  ///
  /// \param buf - If new Initializations need to be created, their ownership
  /// is given to this vector.
  /// \param Loc The location with which the single initialization should be
  ///        associated.
  virtual MutableArrayRef<InitializationPtr>
  getSubInitializationsForTuple(SILGenFunction &gen, CanType type,
                                SmallVectorImpl<InitializationPtr> &buf,
                                SILLocation Loc) {
    llvm_unreachable("Must implement if canSplitIntoSubelementAddresses"
                     "returns true");
  }
  
  /// Perform post-initialization bookkeeping for this initialization.
  virtual void finishInitialization(SILGenFunction &gen) {}

  
  /// When emitting an exploded RValue into an initialization, this method is
  /// called once per scalar value in the explosion.
  ///
  /// If this is an *copy* of the rvalue into this initialization then isInit is
  /// false.  If it is an *initialization* of the memory in the initialization,
  /// then isInit is true.
  virtual void copyOrInitValueInto(ManagedValue explodedElement, bool isInit,
                                   SILLocation loc, SILGenFunction &gen) = 0;

private:
  Initialization(const Initialization &) = delete;
  Initialization(Initialization &&) = delete;
  
  virtual void _anchor();
};

/// Abstract base class for single-buffer initializations.  These are
/// initializations that have an addressable memory object to be stored into.
class SingleBufferInitialization : public Initialization {
public:
  SingleBufferInitialization() {}
  
  bool isSingleBuffer() const override {
    return true;
  }

  // SingleBufferInitializations always have an address.
  SILValue getAddressForInPlaceInitialization() const override {
    return getAddress();
  }
  
  bool canSplitIntoSubelementAddresses() const override {
    return true;
  }
  
  MutableArrayRef<InitializationPtr>
  getSubInitializationsForTuple(SILGenFunction &gen, CanType type,
                                SmallVectorImpl<InitializationPtr> &buf,
                                SILLocation Loc) override;


  void copyOrInitValueInto(ManagedValue explodedElement, bool isInit,
                           SILLocation loc, SILGenFunction &gen) override {
    copyOrInitValueIntoSingleBuffer(explodedElement, isInit, getAddress(),
                                    loc, gen);
  }
  
  /// Emit the exploded element into a buffer at the specified address.
  static void copyOrInitValueIntoSingleBuffer(ManagedValue explodedElement,
                                              bool isInit,
                                              SILValue BufferAddress,
                                              SILLocation loc,
                                              SILGenFunction &gen);
};
  
/// This is an initialization for a specific address in memory.
class KnownAddressInitialization : public SingleBufferInitialization {
  /// The physical address of the global.
  SILValue address;
  
  virtual void anchor() const;
public:
  KnownAddressInitialization(SILValue address) : address(address) {}
  
  SILValue getAddressOrNull() const override {
    return address;
  }
};

/// Abstract base class for single-buffer initializations.
class TemporaryInitialization : public SingleBufferInitialization {
  SILValue Addr;
  CleanupHandle Cleanup;
public:
  TemporaryInitialization(SILValue addr, CleanupHandle cleanup)
    : Addr(addr), Cleanup(cleanup) {}

  void finishInitialization(SILGenFunction &gen) override;

  SILValue getAddressOrNull() const override {
    return Addr;
  }

  SILValue getAddress() const {
    return Addr;
  }

  /// Returns the cleanup corresponding to the value of the temporary.
  CleanupHandle getInitializedCleanup() const { return Cleanup; }

  ManagedValue getManagedAddress() const  {
    return ManagedValue(getAddress(), getInitializedCleanup());
  }
};

/// An initialization which accumulates several other initializations
/// into a tuple.
class TupleInitialization : public Initialization {
public:
  /// The sub-Initializations aggregated by this tuple initialization.
  /// The TupleInitialization object takes ownership of Initializations pushed
  /// here.
  SmallVector<InitializationPtr, 4> SubInitializations;
    
  TupleInitialization() {}
    
  SILValue getAddressOrNull() const override {
    if (SubInitializations.size() == 1)
      return SubInitializations[0]->getAddressOrNull();
    else
      return SILValue();
  }
    
  bool canSplitIntoSubelementAddresses() const override {
    return true;
  }
    
  MutableArrayRef<InitializationPtr>
  getSubInitializationsForTuple(SILGenFunction &gen, CanType type,
                                SmallVectorImpl<InitializationPtr> &buf,
                                SILLocation Loc) override {
    return SubInitializations;
  }
    
  void finishInitialization(SILGenFunction &gen) override {
    for (auto &sub : SubInitializations)
      sub->finishInitialization(gen);
  }
    
  void copyOrInitValueInto(ManagedValue valueMV, bool isInit, SILLocation loc,
                           SILGenFunction &SGF) override;
};

} // end namespace Lowering
} // end namespace swift

#endif
