//===-- ComponentPath.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
//
//===----------------------------------------------------------------------===//

#ifndef FORTRAN_LOWER_COMPONENTPATH_H
#define FORTRAN_LOWER_COMPONENTPATH_H

#include "flang/Lower/IterationSpace.h"
#include "llvm/ADT/SmallVector.h"
#include <optional>

namespace fir {
class ArrayLoadOp;
}
namespace Fortran::evaluate {
class ArrayRef;
}

namespace Fortran::lower {

namespace details {
class ImplicitSubscripts {};
} // namespace details

using PathComponent =
    std::variant<const evaluate::ArrayRef *, const evaluate::Component *,
                 const evaluate::ComplexPart *, details::ImplicitSubscripts>;

/// Collection of components.
///
/// This class is used both to collect front-end post-order functional Expr
/// trees and their translations to Values to be used in a pre-order list of
/// arguments.
class ComponentPath {
public:
  using ExtendRefFunc = std::function<mlir::Value(const mlir::Value &)>;

  ComponentPath(bool isImplicit) { setPC(isImplicit); }
  ComponentPath(bool isImplicit, const evaluate::Substring *ss)
      : substring(ss) {
    setPC(isImplicit);
  }
  ComponentPath() = delete;

  bool isSlice() const { return !trips.empty() || hasComponents(); }
  bool hasComponents() const { return !suffixComponents.empty(); }
  void clear();

  bool hasExtendCoorRef() const { return extendCoorRef.has_value(); }
  ExtendRefFunc getExtendCoorRef() const;
  void resetExtendCoorRef() { extendCoorRef = std::nullopt; }
  void resetPC();

  llvm::SmallVector<PathComponent> reversePath;
  const evaluate::Substring *substring = nullptr;
  bool applied = false;

  llvm::SmallVector<mlir::Value> prefixComponents;
  llvm::SmallVector<mlir::Value> trips;
  llvm::SmallVector<mlir::Value> suffixComponents;
  std::function<IterationSpace(const IterationSpace &)> pc;

  /// In the case where a path of components involves members that are POINTER
  /// or ALLOCATABLE, a dereference is required in FIR for semantic correctness.
  /// This optional continuation allows the generation of those dereferences.
  /// These accesses are always on Fortran entities of record types, which are
  /// implicitly in-memory objects.
  std::optional<ExtendRefFunc> extendCoorRef;

private:
  void setPC(bool isImplicit);
};

/// Examine each subscript expression of \p x and return true if and only if any
/// of the subscripts is a vector or has a rank greater than 0.
bool isRankedArrayAccess(const Fortran::evaluate::ArrayRef &x);

} // namespace Fortran::lower

#endif // FORTRAN_LOWER_COMPONENTPATH_H
