//===-- include/flang/Runtime/array-constructor.h ---------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

// External APIs to create temporary storage for array constructors when their
// final extents or length parameters cannot be pre-computed.

#ifndef FORTRAN_RUNTIME_ARRAYCONSTRUCTOR_H_
#define FORTRAN_RUNTIME_ARRAYCONSTRUCTOR_H_

#include "flang/Runtime/descriptor.h"
#include "flang/Runtime/entry-names.h"
#include <cstdint>

namespace Fortran::runtime {

// Runtime data structure to hold information about the storage of
// an array constructor being constructed.
struct ArrayConstructorVector {
  ArrayConstructorVector(class Descriptor &to, SubscriptValue nextValuePosition,
      SubscriptValue actualAllocationSize, const char *sourceFile,
      int sourceLine, bool useValueLengthParameters)
      : to{to}, nextValuePosition{nextValuePosition},
        actualAllocationSize{actualAllocationSize}, sourceFile{sourceFile},
        sourceLine{sourceLine}, useValueLengthParameters_{
                                    useValueLengthParameters} {}

  bool useValueLengthParameters() const { return useValueLengthParameters_; }

  class Descriptor &to;
  SubscriptValue nextValuePosition;
  SubscriptValue actualAllocationSize;
  const char *sourceFile;
  int sourceLine;

private:
  unsigned char useValueLengthParameters_ : 1;
};

// This file defines an API to "push" an evaluated array constructor value
// "from" into some storage "to" of an array constructor. It can be seen as a
// form of std::vector::push_back() implementation for Fortran array
// constructors. In the APIs and ArrayConstructorVector struct above:
//
// - "to" is a ranked-1 descriptor whose declared type is already set to the
// array constructor derived type. It may be already allocated, even before the
// first call to this API, or it may be unallocated. "to" extent is increased
// every time a "from" is pushed past its current extent. At this end of the
// API calls, its extent is the extent of the array constructor. If "to" is
// unallocated and its extent is not null, it is assumed this is the final array
// constructor extent value, and the first allocation already "reserves" storage
// space accordingly to avoid reallocations.
//  - "from" is a scalar or array descriptor for the evaluated array
//  constructor value that must be copied into the storage of "to" at
//  "nextValuePosition".
//  - "useValueLengthParameters" must be set to true if the array constructor
//  has length parameters and no type spec. If it is true and "to" is
//  unallocated, "to" will take the length parameters of "from". If it is true
//  and "to" is an allocated character array constructor, it will be checked
//  that "from" length matches the one from "to". When it is false, the
//  character length must already be set in "to" before the first call to this
//  API and "from" character lengths are allowed to mismatch from "to".
// - "nextValuePosition" is the zero based sequence position of "from" in the
// array constructor. It is updated after this call by the number of "from"
// elements. It should be set to zero by the caller of this API before the first
// call.
// - "actualAllocationSize" is the current allocation size of "to" storage. It
// may be bigger than "to" extent for reallocation optimization purposes, but
// should never be smaller, unless this is the first call and "to" is
// unallocated. It is updated by the runtime after each successful allocation or
// reallocation. It should be set to "to" extent if "to" is allocated before the
// first call of this API, and can be left undefined otherwise.
//
// Note that this API can be used with "to" being a variable (that can be
// discontiguous). This can be done when the variable is the left hand side of
// an assignment from an array constructor as long as:
//  - none of the ac-value overlaps with the variable,
//  - this is an intrinsic assignment that is not a whole allocatable
//  assignment, *and* for a type that has no components requiring user defined
//  assignments,
//  - the variable is properly finalized before using this API if its need to,
//  - "useValueLengthParameters" should be set to false in this case, even if
//  the array constructor has no type-spec, since the variable may have a
//  different character length than the array constructor values.

extern "C" {
// API to initialize an ArrayConstructorVector before any values are pushed to
// it. Inlined code is only expected to allocate the "ArrayConstructorVector"
// class instance storage with sufficient size (using
// "2*sizeof(ArrayConstructorVector)" on the host should be safe regardless of
// the target the runtime is compiled for). This avoids the need for the runtime
// to maintain a state, or to use dynamic allocation for it. "vectorClassSize"
// is used to validate that lowering allocated enough space for it.
void RTNAME(InitArrayConstructorVector)(ArrayConstructorVector &vector,
    Descriptor &to, bool useValueLengthParameters, int vectorClassSize,
    const char *sourceFile = nullptr, int sourceLine = 0);

// Generic API to push any kind of entity into the array constructor (any
// Fortran type and any rank).
void RTNAME(PushArrayConstructorValue)(
    ArrayConstructorVector &vector, const Descriptor &from);

// API to push scalar array constructor value of:
//   - a numerical or logical type,
//   - or a derived type that has no length parameters, and no allocatable
//   component (that would require deep copies).
// It requires no descriptor for the value that is passed via its base address.
void RTNAME(PushArrayConstructorSimpleScalar)(
    ArrayConstructorVector &vector, void *from);
} // extern "C"
} // namespace Fortran::runtime
#endif // FORTRAN_RUNTIME_ARRAYCONSTRUCTOR_H_
