//===-- Optimizer/Dialect/FIRAttr.h -- FIR attributes -----------*- 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
//
//===----------------------------------------------------------------------===//
//
// Coding style: https://mlir.llvm.org/getting_started/DeveloperGuide/
//
//===----------------------------------------------------------------------===//

#ifndef FORTRAN_OPTIMIZER_DIALECT_FIRATTR_H
#define FORTRAN_OPTIMIZER_DIALECT_FIRATTR_H

#include "mlir/IR/BuiltinAttributes.h"

namespace mlir {
class DialectAsmParser;
class DialectAsmPrinter;
} // namespace mlir

namespace fir {

class FIROpsDialect;

namespace detail {
struct RealAttributeStorage;
struct TypeAttributeStorage;
} // namespace detail

using KindTy = unsigned;

class ExactTypeAttr
    : public mlir::Attribute::AttrBase<ExactTypeAttr, mlir::Attribute,
                                       detail::TypeAttributeStorage> {
public:
  using Base::Base;
  using ValueType = mlir::Type;

  static constexpr llvm::StringLiteral name = "fir.type_is";
  static constexpr llvm::StringRef getAttrName() { return "type_is"; }
  static ExactTypeAttr get(mlir::Type value);

  mlir::Type getType() const;
};

class SubclassAttr
    : public mlir::Attribute::AttrBase<SubclassAttr, mlir::Attribute,
                                       detail::TypeAttributeStorage> {
public:
  using Base::Base;
  using ValueType = mlir::Type;

  static constexpr llvm::StringLiteral name = "fir.class_is";
  static constexpr llvm::StringRef getAttrName() { return "class_is"; }
  static SubclassAttr get(mlir::Type value);

  mlir::Type getType() const;
};

/// Attribute which can be applied to a fir.allocmem operation, specifying that
/// the allocation may not be moved to the heap by passes
class MustBeHeapAttr : public mlir::BoolAttr {
public:
  using BoolAttr::BoolAttr;

  static constexpr llvm::StringLiteral name = "fir.must_be_heap";
  static constexpr llvm::StringRef getAttrName() { return "fir.must_be_heap"; }
};

// Attributes for building SELECT CASE multiway branches

/// A closed interval (including the bound values) is an interval with both an
/// upper and lower bound as given as ssa-values.
/// A case selector of `CASE (n:m)` corresponds to any value from `n` to `m` and
/// is encoded as `#fir.interval, %n, %m`.
class ClosedIntervalAttr
    : public mlir::Attribute::AttrBase<ClosedIntervalAttr, mlir::Attribute,
                                       mlir::AttributeStorage> {
public:
  using Base::Base;

  static constexpr llvm::StringLiteral name = "fir.interval";
  static constexpr llvm::StringRef getAttrName() { return "interval"; }
  static ClosedIntervalAttr get(mlir::MLIRContext *ctxt);
};

/// An upper bound is an open interval (including the bound value) as given as
/// an ssa-value.
/// A case selector of `CASE (:m)` corresponds to any value up to and including
/// `m` and is encoded as `#fir.upper, %m`.
class UpperBoundAttr
    : public mlir::Attribute::AttrBase<UpperBoundAttr, mlir::Attribute,
                                       mlir::AttributeStorage> {
public:
  using Base::Base;

  static constexpr llvm::StringLiteral name = "fir.upper";
  static constexpr llvm::StringRef getAttrName() { return "upper"; }
  static UpperBoundAttr get(mlir::MLIRContext *ctxt);
};

/// A lower bound is an open interval (including the bound value) as given as
/// an ssa-value.
/// A case selector of `CASE (n:)` corresponds to any value down to and
/// including `n` and is encoded as `#fir.lower, %n`.
class LowerBoundAttr
    : public mlir::Attribute::AttrBase<LowerBoundAttr, mlir::Attribute,
                                       mlir::AttributeStorage> {
public:
  using Base::Base;

  static constexpr llvm::StringLiteral name = "fir.lower";
  static constexpr llvm::StringRef getAttrName() { return "lower"; }
  static LowerBoundAttr get(mlir::MLIRContext *ctxt);
};

/// A pointer interval is a closed interval as given as an ssa-value. The
/// interval contains exactly one value.
/// A case selector of `CASE (p)` corresponds to exactly the value `p` and is
/// encoded as `#fir.point, %p`.
class PointIntervalAttr
    : public mlir::Attribute::AttrBase<PointIntervalAttr, mlir::Attribute,
                                       mlir::AttributeStorage> {
public:
  using Base::Base;

  static constexpr llvm::StringLiteral name = "fir.point";
  static constexpr llvm::StringRef getAttrName() { return "point"; }
  static PointIntervalAttr get(mlir::MLIRContext *ctxt);
};

/// A real attribute is used to workaround MLIR's default parsing of a real
/// constant.
/// `#fir.real<10, 3.14>` is used to introduce a real constant of value `3.14`
/// with a kind of `10`.
class RealAttr
    : public mlir::Attribute::AttrBase<RealAttr, mlir::Attribute,
                                       detail::RealAttributeStorage> {
public:
  using Base::Base;
  using ValueType = std::pair<int, llvm::APFloat>;

  static constexpr llvm::StringLiteral name = "fir.real";
  static constexpr llvm::StringRef getAttrName() { return "real"; }
  static RealAttr get(mlir::MLIRContext *ctxt, const ValueType &key);

  KindTy getFKind() const;
  llvm::APFloat getValue() const;
};

mlir::Attribute parseFirAttribute(FIROpsDialect *dialect,
                                  mlir::DialectAsmParser &parser,
                                  mlir::Type type);

void printFirAttribute(FIROpsDialect *dialect, mlir::Attribute attr,
                       mlir::DialectAsmPrinter &p);

} // namespace fir

#include "flang/Optimizer/Dialect/FIREnumAttr.h.inc"

#define GET_ATTRDEF_CLASSES
#include "flang/Optimizer/Dialect/FIRAttr.h.inc"

#endif // FORTRAN_OPTIMIZER_DIALECT_FIRATTR_H
