forked from OSchip/llvm-project
[flang] Support operator== for expressions.
Original-commit: flang-compiler/f18@42013513e9 Reviewed-on: https://github.com/flang-compiler/f18/pull/251
This commit is contained in:
parent
b45b098d0f
commit
2ad9986698
|
@ -58,6 +58,8 @@ public:
|
|||
A *operator->() { return p_; }
|
||||
const A *operator->() const { return p_; }
|
||||
|
||||
bool operator==(const Indirection &that) const { return *p_ == *that.p_; }
|
||||
|
||||
template<typename... ARGS> static Indirection Make(ARGS &&... args) {
|
||||
return {new A(std::forward<ARGS>(args)...)};
|
||||
}
|
||||
|
@ -106,6 +108,8 @@ public:
|
|||
A *operator->() { return p_; }
|
||||
const A *operator->() const { return p_; }
|
||||
|
||||
bool operator==(const Indirection &that) const { return *p_ == *that.p_; }
|
||||
|
||||
template<typename... ARGS> static Indirection Make(ARGS &&... args) {
|
||||
return {new A(std::forward<ARGS>(args)...)};
|
||||
}
|
||||
|
@ -149,6 +153,11 @@ public:
|
|||
p_ = p;
|
||||
}
|
||||
|
||||
bool operator==(const OwningPointer &that) const {
|
||||
return (p_ == nullptr && that.p_ == nullptr) ||
|
||||
(p_ != nullptr && that.p_ != nullptr && *p_ == *that.p_);
|
||||
}
|
||||
|
||||
private:
|
||||
A *p_{nullptr};
|
||||
};
|
||||
|
|
|
@ -26,7 +26,15 @@
|
|||
// a packaged value of a specific type if it is present and returns a pointer
|
||||
// thereto; otherwise, it returns a null pointer. It's analogous to
|
||||
// std::get_if<>() but it accepts a reference argument and is recursive.
|
||||
// The type parameter cannot be omitted.
|
||||
// The target type parameter cannot be omitted.
|
||||
//
|
||||
// Be advised: If the target type parameter is not const-qualified, but the
|
||||
// isolated value is const-qualified, the result of Unwrap<> will be a
|
||||
// pointer to a const-qualified value.
|
||||
//
|
||||
// Further: const-qualified alternatives in instances of non-const-qualified
|
||||
// variants will not be returned from Unwrap if the target type is not
|
||||
// const-qualified.
|
||||
//
|
||||
// UnwrapCopy<>() is a variation of Unwrap<>() that returns an optional copy
|
||||
// of the value if one is present with the desired type.
|
||||
|
@ -48,8 +56,7 @@ template<typename A, typename B> auto Unwrap(B &x) -> Constify<A, B> * {
|
|||
}
|
||||
|
||||
// Prototypes of specializations, to enable mutual recursion
|
||||
template<typename A, typename B>
|
||||
auto Unwrap(B *) -> Constify<A, std::remove_pointer<B>> *;
|
||||
template<typename A, typename B> auto Unwrap(B *p) -> Constify<A, B> *;
|
||||
template<typename A, typename B>
|
||||
auto Unwrap(const std::unique_ptr<B> &) -> Constify<A, B> *;
|
||||
template<typename A, typename B>
|
||||
|
@ -69,8 +76,7 @@ template<typename A, typename B>
|
|||
auto Unwrap(const CountedReference<B> &) -> Constify<A, B> *;
|
||||
|
||||
// Implementations of specializations
|
||||
template<typename A, typename B>
|
||||
auto Unwrap(B *p) -> Constify<A, std::remove_pointer<B>> * {
|
||||
template<typename A, typename B> auto Unwrap(B *p) -> Constify<A, B> * {
|
||||
if (p != nullptr) {
|
||||
return Unwrap<A>(*p);
|
||||
} else {
|
||||
|
@ -115,12 +121,22 @@ auto Unwrap(const std::optional<B> &x) -> Constify<A, B> * {
|
|||
}
|
||||
|
||||
template<typename A, typename... Bs> A *Unwrap(std::variant<Bs...> &u) {
|
||||
return std::visit([](auto &x) { return Unwrap<A>(x); }, u);
|
||||
return std::visit(
|
||||
[](auto &x) -> A * {
|
||||
using Ty = std::decay_t<decltype(Unwrap<A>(x))>;
|
||||
if constexpr (!std::is_const_v<std::remove_pointer_t<Ty>> ||
|
||||
std::is_const_v<A>) {
|
||||
return Unwrap<A>(x);
|
||||
}
|
||||
return nullptr;
|
||||
},
|
||||
u);
|
||||
}
|
||||
|
||||
template<typename A, typename... Bs>
|
||||
auto Unwrap(const std::variant<Bs...> &u) -> std::add_const_t<A> * {
|
||||
return std::visit([](const auto &x) { return Unwrap<A>(x); }, u);
|
||||
return std::visit(
|
||||
[](const auto &x) -> std::add_const_t<A> * { return Unwrap<A>(x); }, u);
|
||||
}
|
||||
|
||||
template<typename A, typename B, bool COPY>
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
|
||||
#include "call.h"
|
||||
#include "expression.h"
|
||||
#include "../semantics/symbol.h"
|
||||
|
||||
namespace Fortran::evaluate {
|
||||
|
||||
|
@ -23,6 +24,11 @@ std::optional<DynamicType> ActualArgument::GetType() const {
|
|||
|
||||
int ActualArgument::Rank() const { return value->Rank(); }
|
||||
|
||||
bool ActualArgument::operator==(const ActualArgument &that) const {
|
||||
return keyword == that.keyword &&
|
||||
isAlternateReturn == that.isAlternateReturn && value == that.value;
|
||||
}
|
||||
|
||||
std::ostream &ActualArgument::AsFortran(std::ostream &o) const {
|
||||
if (keyword.has_value()) {
|
||||
o << keyword->ToString() << '=';
|
||||
|
@ -41,10 +47,57 @@ std::optional<int> ActualArgument::VectorSize() const {
|
|||
return std::nullopt;
|
||||
}
|
||||
|
||||
bool SpecificIntrinsic::operator==(const SpecificIntrinsic &that) const {
|
||||
return name == that.name && type == that.type && rank == that.rank &&
|
||||
attrs == that.attrs;
|
||||
}
|
||||
|
||||
std::ostream &SpecificIntrinsic::AsFortran(std::ostream &o) const {
|
||||
return o << name;
|
||||
}
|
||||
|
||||
std::optional<DynamicType> ProcedureDesignator::GetType() const {
|
||||
if (const auto *intrinsic{std::get_if<SpecificIntrinsic>(&u)}) {
|
||||
return intrinsic->type;
|
||||
}
|
||||
if (const Symbol * symbol{GetSymbol()}) {
|
||||
return GetSymbolType(symbol);
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
int ProcedureDesignator::Rank() const {
|
||||
if (const Symbol * symbol{GetSymbol()}) {
|
||||
return symbol->Rank();
|
||||
}
|
||||
if (const auto *intrinsic{std::get_if<SpecificIntrinsic>(&u)}) {
|
||||
return intrinsic->rank;
|
||||
}
|
||||
CHECK(!"ProcedureDesignator::Rank(): no case");
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool ProcedureDesignator::IsElemental() const {
|
||||
if (const Symbol * symbol{GetSymbol()}) {
|
||||
return symbol->attrs().test(semantics::Attr::ELEMENTAL);
|
||||
}
|
||||
if (const auto *intrinsic{std::get_if<SpecificIntrinsic>(&u)}) {
|
||||
return intrinsic->attrs.test(semantics::Attr::ELEMENTAL);
|
||||
}
|
||||
CHECK(!"ProcedureDesignator::IsElemental(): no case");
|
||||
return 0;
|
||||
}
|
||||
|
||||
const Symbol *ProcedureDesignator::GetSymbol() const {
|
||||
return std::visit(
|
||||
common::visitors{
|
||||
[](const Symbol *sym) { return sym; },
|
||||
[](const Component &c) { return &c.GetLastSymbol(); },
|
||||
[](const auto &) -> const Symbol * { return nullptr; },
|
||||
},
|
||||
u);
|
||||
}
|
||||
|
||||
std::ostream &ProcedureRef::AsFortran(std::ostream &o) const {
|
||||
proc_.AsFortran(o);
|
||||
char separator{'('};
|
||||
|
|
|
@ -37,6 +37,7 @@ struct ActualArgument {
|
|||
|
||||
std::optional<DynamicType> GetType() const;
|
||||
int Rank() const;
|
||||
bool operator==(const ActualArgument &) const;
|
||||
std::ostream &AsFortran(std::ostream &) const;
|
||||
std::optional<int> VectorSize() const;
|
||||
|
||||
|
@ -64,6 +65,7 @@ struct SpecificIntrinsic {
|
|||
SpecificIntrinsic(IntrinsicProcedure n, std::optional<DynamicType> &&dt,
|
||||
int r, semantics::Attrs a)
|
||||
: name{n}, type{std::move(dt)}, rank{r}, attrs{a} {}
|
||||
bool operator==(const SpecificIntrinsic &) const;
|
||||
std::ostream &AsFortran(std::ostream &) const;
|
||||
|
||||
IntrinsicProcedure name;
|
||||
|
|
|
@ -19,9 +19,14 @@
|
|||
#include "../common/fortran.h"
|
||||
#include "../common/idioms.h"
|
||||
#include "../common/indirection.h"
|
||||
#include "../parser/char-block.h"
|
||||
#include "../parser/message.h"
|
||||
#include <cinttypes>
|
||||
|
||||
namespace Fortran::semantics {
|
||||
class DerivedTypeSpec;
|
||||
}
|
||||
|
||||
namespace Fortran::evaluate {
|
||||
|
||||
using common::RelationalOperator;
|
||||
|
@ -145,7 +150,7 @@ using HostUnsignedInt =
|
|||
// need for std::monostate as a default constituent in a std::variant<>.
|
||||
// - There are full copy and move semantics for construction and assignment.
|
||||
// - Discriminated unions have a std::variant<> member "u" and support
|
||||
// explicit copy and move constructors.
|
||||
// explicit copy and move constructors as well as comparison for equality.
|
||||
#define DEFAULT_CONSTRUCTORS_AND_ASSIGNMENTS(t) \
|
||||
t(const t &) = default; \
|
||||
t(t &&) = default; \
|
||||
|
@ -161,7 +166,8 @@ using HostUnsignedInt =
|
|||
template<typename _A> explicit t(const _A &x) : u{x} {} \
|
||||
template<typename _A> \
|
||||
explicit t(std::enable_if_t<!std::is_reference_v<_A>, _A> &&x) \
|
||||
: u(std::move(x)) {}
|
||||
: u(std::move(x)) {} \
|
||||
bool operator==(const t &that) const { return u == that.u; }
|
||||
|
||||
// Force availability of copy construction and assignment
|
||||
template<typename A> using CopyableIndirection = common::Indirection<A, true>;
|
||||
|
@ -174,19 +180,21 @@ struct FoldingContext {
|
|||
explicit FoldingContext(const parser::ContextualMessages &m,
|
||||
Rounding round = defaultRounding, bool flush = false)
|
||||
: messages{m}, rounding{round}, flushDenormalsToZero{flush} {}
|
||||
FoldingContext(const parser::ContextualMessages &m, const FoldingContext &c)
|
||||
: messages{m}, rounding{c.rounding}, flushDenormalsToZero{
|
||||
c.flushDenormalsToZero} {}
|
||||
|
||||
// For narrowed contexts
|
||||
FoldingContext(const FoldingContext &c, const parser::ContextualMessages &m)
|
||||
: messages{m}, rounding{c.rounding}, flushDenormalsToZero{
|
||||
c.flushDenormalsToZero} {}
|
||||
FoldingContext(const FoldingContext &that)
|
||||
: messages{that.messages}, rounding{that.rounding},
|
||||
flushDenormalsToZero{that.flushDenormalsToZero}, pdtInstance{
|
||||
that.pdtInstance} {}
|
||||
FoldingContext(
|
||||
const FoldingContext &that, const parser::ContextualMessages &m)
|
||||
: messages{m}, rounding{that.rounding},
|
||||
flushDenormalsToZero{that.flushDenormalsToZero}, pdtInstance{
|
||||
that.pdtInstance} {}
|
||||
|
||||
parser::ContextualMessages messages;
|
||||
Rounding rounding{defaultRounding};
|
||||
bool flushDenormalsToZero{false};
|
||||
bool bigEndian{false};
|
||||
const semantics::DerivedTypeSpec *pdtInstance{nullptr};
|
||||
};
|
||||
|
||||
void RealFlagWarnings(FoldingContext &, const RealFlags &, const char *op);
|
||||
|
|
|
@ -32,6 +32,10 @@ public:
|
|||
constexpr Complex &operator=(const Complex &) = default;
|
||||
constexpr Complex &operator=(Complex &&) = default;
|
||||
|
||||
constexpr bool operator==(const Complex &that) const {
|
||||
return re_ == that.re_ && im_ == that.im_;
|
||||
}
|
||||
|
||||
constexpr const Part &REAL() const { return re_; }
|
||||
constexpr const Part &AIMAG() const { return im_; }
|
||||
constexpr Complex CONJG() const { return {re_, im_.Negate()}; }
|
||||
|
|
|
@ -191,7 +191,7 @@ template<typename T> DynamicType ArrayConstructor<T>::GetType() const {
|
|||
|
||||
template<typename A>
|
||||
std::optional<DynamicType> ExpressionBase<A>::GetType() const {
|
||||
if constexpr (IsSpecificIntrinsicType<Result>) {
|
||||
if constexpr (IsLengthlessIntrinsicType<Result>) {
|
||||
return Result::GetType();
|
||||
} else {
|
||||
return std::visit(
|
||||
|
@ -200,7 +200,7 @@ std::optional<DynamicType> ExpressionBase<A>::GetType() const {
|
|||
BOZLiteralConstant>) {
|
||||
return x.GetType();
|
||||
}
|
||||
return std::nullopt; // typeless -> no type
|
||||
return std::nullopt; // typeless really means "no type"
|
||||
},
|
||||
derived().u);
|
||||
}
|
||||
|
@ -219,6 +219,31 @@ template<typename A> int ExpressionBase<A>::Rank() const {
|
|||
derived().u);
|
||||
}
|
||||
|
||||
// Equality testing for classes without EVALUATE_UNION_CLASS_BOILERPLATE()
|
||||
|
||||
template<typename V, typename O>
|
||||
bool ImpliedDo<V, O>::operator==(const ImpliedDo<V, O> &that) const {
|
||||
return controlVariableName == that.controlVariableName &&
|
||||
lower == that.lower && upper == that.upper && stride == that.stride &&
|
||||
values == that.values;
|
||||
}
|
||||
|
||||
template<typename R>
|
||||
bool ArrayConstructorValues<R>::operator==(
|
||||
const ArrayConstructorValues<R> &that) const {
|
||||
return values == that.values;
|
||||
}
|
||||
|
||||
template<typename R>
|
||||
bool ArrayConstructor<R>::operator==(const ArrayConstructor<R> &that) const {
|
||||
return *static_cast<const ArrayConstructorValues<R> *>(this) == that &&
|
||||
result == that.result && typeParameterValues == that.typeParameterValues;
|
||||
}
|
||||
|
||||
bool GenericExprWrapper::operator==(const GenericExprWrapper &that) const {
|
||||
return v == that.v;
|
||||
}
|
||||
|
||||
// Template instantiations to resolve the "extern template" declarations
|
||||
// that appear in expression.h.
|
||||
|
||||
|
@ -229,6 +254,7 @@ FOR_EACH_REAL_KIND(template struct Relational)
|
|||
FOR_EACH_CHARACTER_KIND(template struct Relational)
|
||||
template struct Relational<SomeType>;
|
||||
FOR_EACH_TYPE_AND_KIND(template class ExpressionBase)
|
||||
FOR_EACH_SPECIFIC_TYPE(template struct ArrayConstructor)
|
||||
}
|
||||
|
||||
// For reclamation of analyzed expressions to which owning pointers have
|
||||
|
|
|
@ -19,7 +19,8 @@
|
|||
// Expressions are the sole owners of their constituents; i.e., there is no
|
||||
// context-independent hash table or sharing of common subexpressions, and
|
||||
// thus these are trees, not DAGs. Both deep copy and move semantics are
|
||||
// supported for expression construction.
|
||||
// supported for expression construction. Expressions may be compared
|
||||
// for equality.
|
||||
|
||||
#include "common.h"
|
||||
#include "type.h"
|
||||
|
@ -184,6 +185,10 @@ public:
|
|||
}
|
||||
}
|
||||
|
||||
bool operator==(const Operation &that) const {
|
||||
return operand_ == that.operand_;
|
||||
}
|
||||
|
||||
std::ostream &AsFortran(std::ostream &) const;
|
||||
|
||||
protected:
|
||||
|
@ -387,6 +392,7 @@ template<typename VALUES, typename OPERAND> struct ImpliedDo {
|
|||
using Operand = OPERAND;
|
||||
using Result = ResultType<Values>;
|
||||
static_assert(Operand::category == TypeCategory::Integer);
|
||||
bool operator==(const ImpliedDo &) const;
|
||||
parser::CharBlock controlVariableName;
|
||||
CopyableIndirection<Expr<Operand>> lower, upper, stride;
|
||||
CopyableIndirection<Values> values;
|
||||
|
@ -406,6 +412,7 @@ template<typename RESULT> struct ArrayConstructorValues {
|
|||
using Result = RESULT;
|
||||
CLASS_BOILERPLATE(ArrayConstructorValues)
|
||||
template<typename A> void Push(A &&x) { values.emplace_back(std::move(x)); }
|
||||
bool operator==(const ArrayConstructorValues &) const;
|
||||
std::vector<ArrayConstructorValue<Result>> values;
|
||||
};
|
||||
|
||||
|
@ -416,20 +423,20 @@ struct ArrayConstructor : public ArrayConstructorValues<RESULT> {
|
|||
DynamicType GetType() const;
|
||||
static constexpr int Rank() { return 1; }
|
||||
Expr<SubscriptInteger> LEN() const;
|
||||
bool operator==(const ArrayConstructor &) const;
|
||||
std::ostream &AsFortran(std::ostream &) const;
|
||||
|
||||
Result result;
|
||||
std::vector<Expr<SubscriptInteger>> typeParameterValues;
|
||||
};
|
||||
|
||||
// Per-category expression representations
|
||||
// Expression representations for each type category.
|
||||
|
||||
template<int KIND>
|
||||
class Expr<Type<TypeCategory::Integer, KIND>>
|
||||
: public ExpressionBase<Type<TypeCategory::Integer, KIND>> {
|
||||
public:
|
||||
using Result = Type<TypeCategory::Integer, KIND>;
|
||||
// TODO: R916 type-param-inquiry
|
||||
|
||||
EVALUATE_UNION_CLASS_BOILERPLATE(Expr)
|
||||
explicit Expr(const Scalar<Result> &x) : u{Constant<Result>{x}} {}
|
||||
|
@ -444,7 +451,7 @@ private:
|
|||
Add<Result>, Subtract<Result>, Multiply<Result>, Divide<Result>,
|
||||
Power<Result>, Extremum<Result>>;
|
||||
using Others = std::variant<Constant<Result>, ArrayConstructor<Result>,
|
||||
Designator<Result>, FunctionRef<Result>>;
|
||||
TypeParamInquiry<KIND>, Designator<Result>, FunctionRef<Result>>;
|
||||
|
||||
public:
|
||||
common::CombineVariants<Operations, Conversions, Others> u;
|
||||
|
@ -664,10 +671,12 @@ public:
|
|||
// from parse tree nodes.
|
||||
struct GenericExprWrapper {
|
||||
GenericExprWrapper(Expr<SomeType> &&x) : v{std::move(x)} {}
|
||||
bool operator==(const GenericExprWrapper &) const;
|
||||
Expr<SomeType> v;
|
||||
};
|
||||
|
||||
FOR_EACH_CATEGORY_TYPE(extern template class Expr)
|
||||
FOR_EACH_TYPE_AND_KIND(extern template class ExpressionBase)
|
||||
FOR_EACH_SPECIFIC_TYPE(extern template struct ArrayConstructor)
|
||||
}
|
||||
#endif // FORTRAN_EVALUATE_EXPRESSION_H_
|
||||
|
|
|
@ -180,6 +180,10 @@ public:
|
|||
|
||||
constexpr Integer &operator=(const Integer &) = default;
|
||||
|
||||
constexpr bool operator==(const Integer &that) const {
|
||||
return CompareUnsigned(that) == Ordering::Equal;
|
||||
}
|
||||
|
||||
// Left-justified mask (e.g., MASKL(1) has only its sign bit set)
|
||||
static constexpr Integer MASKL(int places) {
|
||||
if (places <= 0) {
|
||||
|
|
|
@ -28,6 +28,10 @@ public:
|
|||
constexpr Logical(bool truth) : word_{-std::uint64_t{truth}} {}
|
||||
constexpr Logical &operator=(const Logical &) = default;
|
||||
|
||||
template<int B> constexpr bool operator==(const Logical<B> &that) const {
|
||||
return IsTrue() == that.IsTrue();
|
||||
}
|
||||
|
||||
// For static expression evaluation, all the bits will have the same value.
|
||||
constexpr bool IsTrue() const { return word_.BTEST(0); }
|
||||
|
||||
|
|
|
@ -59,6 +59,10 @@ public:
|
|||
constexpr Real &operator=(const Real &) = default;
|
||||
constexpr Real &operator=(Real &&) = default;
|
||||
|
||||
constexpr bool operator==(const Real &that) const {
|
||||
return word_ == that.word_;
|
||||
}
|
||||
|
||||
// TODO ANINT, CEILING, FLOOR, DIM, MAX, MIN, DPROD, FRACTION
|
||||
// HUGE, INT/NINT, MAXEXPONENT, MINEXPONENT, NEAREST, OUT_OF_RANGE,
|
||||
// PRECISION, HUGE, TINY, RRSPACING/SPACING, SCALE, SET_EXPONENT, SIGN
|
||||
|
|
|
@ -24,16 +24,23 @@ using namespace std::literals::string_literals;
|
|||
|
||||
namespace Fortran::evaluate {
|
||||
|
||||
std::optional<DynamicType> GetSymbolType(const semantics::Symbol &symbol) {
|
||||
if (const auto *type{symbol.GetType()}) {
|
||||
if (const auto *intrinsic{type->AsIntrinsic()}) {
|
||||
TypeCategory category{intrinsic->category()};
|
||||
int kind{intrinsic->kind()};
|
||||
if (IsValidKindOfIntrinsicType(category, kind)) {
|
||||
return DynamicType{category, kind};
|
||||
bool DynamicType::operator==(const DynamicType &that) const {
|
||||
return category == that.category && kind == that.kind &&
|
||||
derived == that.derived && descriptor == that.descriptor;
|
||||
}
|
||||
|
||||
std::optional<DynamicType> GetSymbolType(const semantics::Symbol *symbol) {
|
||||
if (symbol != nullptr) {
|
||||
if (const auto *type{symbol->GetType()}) {
|
||||
if (const auto *intrinsic{type->AsIntrinsic()}) {
|
||||
TypeCategory category{intrinsic->category()};
|
||||
int kind{intrinsic->kind()};
|
||||
if (IsValidKindOfIntrinsicType(category, kind)) {
|
||||
return {DynamicType{category, kind}};
|
||||
}
|
||||
} else if (const auto *derived{type->AsDerived()}) {
|
||||
return {DynamicType{TypeCategory::Derived, 0, derived}};
|
||||
}
|
||||
} else if (const auto *derived{type->AsDerived()}) {
|
||||
return DynamicType{TypeCategory::Derived, 0, derived};
|
||||
}
|
||||
}
|
||||
return std::nullopt;
|
||||
|
@ -91,6 +98,11 @@ DynamicType DynamicType::ResultTypeForMultiply(const DynamicType &that) const {
|
|||
return *this;
|
||||
}
|
||||
|
||||
bool SomeKind<TypeCategory::Derived>::operator==(
|
||||
const SomeKind<TypeCategory::Derived> &that) const {
|
||||
return spec_ == that.spec_ && descriptor_ == that.descriptor_;
|
||||
}
|
||||
|
||||
std::string SomeDerived::AsFortran() const {
|
||||
return "TYPE("s + spec().name().ToString() + ')';
|
||||
}
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
// These definitions map Fortran's intrinsic types, characterized by byte
|
||||
// sizes encoded in KIND type parameter values, to their value representation
|
||||
// types in the evaluation library, which are parameterized in terms of
|
||||
// total bit width and real precision. Instances of these class templates
|
||||
// total bit width and real precision. Instances of the Type class template
|
||||
// are suitable for use as template parameters to instantiate other class
|
||||
// templates, like expressions, over the supported types and kinds.
|
||||
|
||||
|
@ -44,33 +44,33 @@ namespace Fortran::evaluate {
|
|||
|
||||
using common::TypeCategory;
|
||||
|
||||
// DynamicType is suitable for use as the result type for
|
||||
// GetType() functions and member functions.
|
||||
struct DynamicType {
|
||||
bool operator==(const DynamicType &that) const {
|
||||
return category == that.category && kind == that.kind &&
|
||||
derived == that.derived;
|
||||
}
|
||||
bool operator==(const DynamicType &that) const;
|
||||
std::string AsFortran() const;
|
||||
DynamicType ResultTypeForMultiply(const DynamicType &) const;
|
||||
|
||||
TypeCategory category;
|
||||
int kind{0};
|
||||
int kind{0}; // set only for intrinsic types
|
||||
const semantics::DerivedTypeSpec *derived{nullptr};
|
||||
// TODO pmk: descriptor for character length
|
||||
// TODO pmk: derived type kind parameters and descriptor for lengths
|
||||
const semantics::Symbol *descriptor{nullptr};
|
||||
};
|
||||
|
||||
std::optional<DynamicType> GetSymbolType(const semantics::Symbol &);
|
||||
// Result will be missing when a symbol is absent or
|
||||
// has an erroneous type, e.g., REAL(KIND=666).
|
||||
std::optional<DynamicType> GetSymbolType(const semantics::Symbol *);
|
||||
|
||||
// Specific intrinsic types are represented by specializations of
|
||||
// this class template Type<CATEGORY, KIND>.
|
||||
template<TypeCategory CATEGORY, int KIND = 0> class Type;
|
||||
|
||||
template<TypeCategory CATEGORY, int KIND> struct TypeBase {
|
||||
static constexpr DynamicType dynamicType{CATEGORY, KIND};
|
||||
static constexpr DynamicType GetType() { return {dynamicType}; }
|
||||
template<TypeCategory CATEGORY, int KIND = 0> struct TypeBase {
|
||||
static constexpr TypeCategory category{CATEGORY};
|
||||
static constexpr int kind{KIND};
|
||||
static std::string AsFortran() { return dynamicType.AsFortran(); }
|
||||
constexpr bool operator==(const TypeBase &) const { return true; }
|
||||
static constexpr DynamicType GetType() { return {category, kind}; }
|
||||
static std::string AsFortran() { return GetType().AsFortran(); }
|
||||
};
|
||||
|
||||
template<int KIND>
|
||||
|
@ -225,11 +225,20 @@ using FloatingTypes = common::CombineTuples<RealTypes, ComplexTypes>;
|
|||
using NumericTypes = common::CombineTuples<IntegerTypes, FloatingTypes>;
|
||||
using RelationalTypes = common::CombineTuples<NumericTypes, CharacterTypes>;
|
||||
using AllIntrinsicTypes = common::CombineTuples<RelationalTypes, LogicalTypes>;
|
||||
using LengthlessIntrinsicTypes =
|
||||
common::CombineTuples<NumericTypes, LogicalTypes>;
|
||||
|
||||
// Predicate: does a type represent a specific intrinsic type?
|
||||
template<typename T>
|
||||
constexpr bool IsSpecificIntrinsicType{common::HasMember<T, AllIntrinsicTypes>};
|
||||
|
||||
// Predicate: is a type an intrinsic type that is completely characterized
|
||||
// by its category and kind parameter value, or might it have a derived type
|
||||
// &/or a length type parameter?
|
||||
template<typename T>
|
||||
constexpr bool IsLengthlessIntrinsicType{
|
||||
common::HasMember<T, LengthlessIntrinsicTypes>};
|
||||
|
||||
// When Scalar<T> is S, then TypeOf<S> is T.
|
||||
// TypeOf is implemented by scanning all supported types for a match
|
||||
// with Type<T>::Scalar.
|
||||
|
@ -251,6 +260,7 @@ template<typename CONST> using TypeOf = typename TypeOfHelper<CONST>::type;
|
|||
// Represents a type of any supported kind within a particular category.
|
||||
template<TypeCategory CATEGORY> struct SomeKind {
|
||||
static constexpr TypeCategory category{CATEGORY};
|
||||
constexpr bool operator==(const SomeKind &) const { return true; }
|
||||
};
|
||||
|
||||
template<> class SomeKind<TypeCategory::Derived> {
|
||||
|
@ -258,14 +268,21 @@ public:
|
|||
static constexpr TypeCategory category{TypeCategory::Derived};
|
||||
|
||||
CLASS_BOILERPLATE(SomeKind)
|
||||
explicit SomeKind(const semantics::DerivedTypeSpec &s) : spec_{&s} {}
|
||||
explicit SomeKind(const semantics::DerivedTypeSpec &dts,
|
||||
const semantics::Symbol *sym = nullptr)
|
||||
: spec_{&dts}, descriptor_{sym} {}
|
||||
|
||||
DynamicType GetType() const { return DynamicType{category, 0, spec_}; }
|
||||
DynamicType GetType() const {
|
||||
return DynamicType{category, 0, spec_, descriptor_};
|
||||
}
|
||||
const semantics::DerivedTypeSpec &spec() const { return *spec_; }
|
||||
const semantics::Symbol *descriptor() const { return descriptor_; }
|
||||
bool operator==(const SomeKind &) const;
|
||||
std::string AsFortran() const;
|
||||
|
||||
private:
|
||||
const semantics::DerivedTypeSpec *spec_;
|
||||
const semantics::Symbol *descriptor_{nullptr};
|
||||
};
|
||||
|
||||
using SomeInteger = SomeKind<TypeCategory::Integer>;
|
||||
|
@ -280,36 +297,37 @@ using SomeCategory = std::tuple<SomeInteger, SomeReal, SomeComplex,
|
|||
SomeCharacter, SomeLogical, SomeDerived>;
|
||||
struct SomeType {};
|
||||
|
||||
// For "[extern] template class", &c. boilerplate
|
||||
// For generating "[extern] template class", &c. boilerplate
|
||||
#define EXPAND_FOR_EACH_INTEGER_KIND(M, P) \
|
||||
M(P, 1) M(P, 2) M(P, 4) M(P, 8) M(P, 16)
|
||||
#define EXPAND_FOR_EACH_REAL_KIND(M, P) \
|
||||
M(P, 2) M(P, 3) M(P, 4) M(P, 8) M(P, 10) M(P, 16)
|
||||
#define EXPAND_FOR_EACH_COMPLEX_KIND(M, P) EXPAND_FOR_EACH_REAL_KIND(M, P)
|
||||
#define EXPAND_FOR_EACH_CHARACTER_KIND(M, P) M(P, 1) M(P, 2) M(P, 4)
|
||||
#define EXPAND_FOR_EACH_LOGICAL_KIND(M, P) M(P, 1) M(P, 2) M(P, 4) M(P, 8)
|
||||
#define TEMPLATE_INSTANTIATION(P, ARG) P<ARG>;
|
||||
|
||||
#define FOR_EACH_INTEGER_KIND_HELP(PREFIX, K) \
|
||||
PREFIX<Type<TypeCategory::Integer, K>>;
|
||||
#define FOR_EACH_REAL_KIND_HELP(PREFIX, K) PREFIX<Type<TypeCategory::Real, K>>;
|
||||
#define FOR_EACH_COMPLEX_KIND_HELP(PREFIX, K) \
|
||||
PREFIX<Type<TypeCategory::Complex, K>>;
|
||||
#define FOR_EACH_CHARACTER_KIND_HELP(PREFIX, K) \
|
||||
PREFIX<Type<TypeCategory::Character, K>>;
|
||||
#define FOR_EACH_LOGICAL_KIND_HELP(PREFIX, K) \
|
||||
PREFIX<Type<TypeCategory::Logical, K>>;
|
||||
|
||||
#define FOR_EACH_INTEGER_KIND(PREFIX) \
|
||||
PREFIX<Type<TypeCategory::Integer, 1>>; \
|
||||
PREFIX<Type<TypeCategory::Integer, 2>>; \
|
||||
PREFIX<Type<TypeCategory::Integer, 4>>; \
|
||||
PREFIX<Type<TypeCategory::Integer, 8>>; \
|
||||
PREFIX<Type<TypeCategory::Integer, 16>>;
|
||||
EXPAND_FOR_EACH_INTEGER_KIND(FOR_EACH_INTEGER_KIND_HELP, PREFIX)
|
||||
#define FOR_EACH_REAL_KIND(PREFIX) \
|
||||
PREFIX<Type<TypeCategory::Real, 2>>; \
|
||||
PREFIX<Type<TypeCategory::Real, 3>>; \
|
||||
PREFIX<Type<TypeCategory::Real, 4>>; \
|
||||
PREFIX<Type<TypeCategory::Real, 8>>; \
|
||||
PREFIX<Type<TypeCategory::Real, 10>>; \
|
||||
PREFIX<Type<TypeCategory::Real, 16>>;
|
||||
EXPAND_FOR_EACH_REAL_KIND(FOR_EACH_REAL_KIND_HELP, PREFIX)
|
||||
#define FOR_EACH_COMPLEX_KIND(PREFIX) \
|
||||
PREFIX<Type<TypeCategory::Complex, 2>>; \
|
||||
PREFIX<Type<TypeCategory::Complex, 3>>; \
|
||||
PREFIX<Type<TypeCategory::Complex, 4>>; \
|
||||
PREFIX<Type<TypeCategory::Complex, 8>>; \
|
||||
PREFIX<Type<TypeCategory::Complex, 10>>; \
|
||||
PREFIX<Type<TypeCategory::Complex, 16>>;
|
||||
EXPAND_FOR_EACH_COMPLEX_KIND(FOR_EACH_COMPLEX_KIND_HELP, PREFIX)
|
||||
#define FOR_EACH_CHARACTER_KIND(PREFIX) \
|
||||
PREFIX<Type<TypeCategory::Character, 1>>; \
|
||||
PREFIX<Type<TypeCategory::Character, 2>>; \
|
||||
PREFIX<Type<TypeCategory::Character, 4>>;
|
||||
EXPAND_FOR_EACH_CHARACTER_KIND(FOR_EACH_CHARACTER_KIND_HELP, PREFIX)
|
||||
#define FOR_EACH_LOGICAL_KIND(PREFIX) \
|
||||
PREFIX<Type<TypeCategory::Logical, 1>>; \
|
||||
PREFIX<Type<TypeCategory::Logical, 2>>; \
|
||||
PREFIX<Type<TypeCategory::Logical, 4>>; \
|
||||
PREFIX<Type<TypeCategory::Logical, 8>>;
|
||||
EXPAND_FOR_EACH_LOGICAL_KIND(FOR_EACH_LOGICAL_KIND_HELP, PREFIX)
|
||||
|
||||
#define FOR_EACH_INTRINSIC_KIND(PREFIX) \
|
||||
FOR_EACH_INTEGER_KIND(PREFIX) \
|
||||
FOR_EACH_REAL_KIND(PREFIX) \
|
||||
|
@ -325,9 +343,10 @@ struct SomeType {};
|
|||
PREFIX<SomeComplex>; \
|
||||
PREFIX<SomeCharacter>; \
|
||||
PREFIX<SomeLogical>; \
|
||||
PREFIX<SomeDerived>; \
|
||||
PREFIX<SomeType>;
|
||||
#define FOR_EACH_TYPE_AND_KIND(PREFIX) \
|
||||
FOR_EACH_SPECIFIC_TYPE(PREFIX) \
|
||||
FOR_EACH_INTRINSIC_KIND(PREFIX) \
|
||||
FOR_EACH_CATEGORY_TYPE(PREFIX)
|
||||
|
||||
// Wraps a constant scalar value of a specific intrinsic type
|
||||
|
@ -348,6 +367,7 @@ template<typename T> struct Constant {
|
|||
|
||||
constexpr DynamicType GetType() const { return Result::GetType(); }
|
||||
int Rank() const { return 0; }
|
||||
bool operator==(const Constant &that) const { return value == that.value; }
|
||||
std::ostream &AsFortran(std::ostream &) const;
|
||||
|
||||
Value value;
|
||||
|
|
|
@ -27,12 +27,6 @@ using namespace Fortran::parser::literals;
|
|||
|
||||
namespace Fortran::evaluate {
|
||||
|
||||
int GetSymbolRank(const Symbol &symbol) { return symbol.Rank(); }
|
||||
|
||||
const parser::CharBlock &GetSymbolName(const Symbol &symbol) {
|
||||
return symbol.name();
|
||||
}
|
||||
|
||||
// Constructors, accessors, mutators
|
||||
|
||||
Triplet::Triplet(std::optional<Expr<SubscriptInteger>> &&l,
|
||||
|
@ -78,6 +72,22 @@ CoarrayRef::CoarrayRef(std::vector<const Symbol *> &&c,
|
|||
CHECK(!base_.empty());
|
||||
}
|
||||
|
||||
std::optional<Expr<SomeInteger>> CoarrayRef::stat() const {
|
||||
if (stat_.has_value()) {
|
||||
return {**stat_};
|
||||
} else {
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<Expr<SomeInteger>> CoarrayRef::team() const {
|
||||
if (team_.has_value()) {
|
||||
return {**team_};
|
||||
} else {
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
|
||||
CoarrayRef &CoarrayRef::set_stat(Expr<SomeInteger> &&v) {
|
||||
CHECK(IsVariable(v));
|
||||
stat_ = CopyableIndirection<Expr<SomeInteger>>::Make(std::move(v));
|
||||
|
@ -196,22 +206,30 @@ std::optional<Expr<SomeCharacter>> Substring::Fold(FoldingContext &context) {
|
|||
|
||||
// Variable formatting
|
||||
|
||||
std::ostream &Emit(std::ostream &o, const Symbol &symbol) {
|
||||
return o << symbol.name().ToString();
|
||||
}
|
||||
|
||||
std::ostream &Emit(std::ostream &o, const IntrinsicProcedure &p) {
|
||||
return o << p;
|
||||
}
|
||||
|
||||
std::ostream &Emit(std::ostream &o, const std::string &lit) {
|
||||
return o << parser::QuoteCharacterLiteral(lit);
|
||||
}
|
||||
|
||||
std::ostream &Emit(std::ostream &o, const std::u16string &lit) {
|
||||
return o << parser::QuoteCharacterLiteral(lit);
|
||||
}
|
||||
|
||||
std::ostream &Emit(std::ostream &o, const std::u32string &lit) {
|
||||
return o << parser::QuoteCharacterLiteral(lit);
|
||||
}
|
||||
|
||||
template<typename A> std::ostream &Emit(std::ostream &o, const A &x) {
|
||||
return x.AsFortran(o);
|
||||
}
|
||||
|
||||
template<> std::ostream &Emit(std::ostream &o, const std::string &lit) {
|
||||
return o << parser::QuoteCharacterLiteral(lit);
|
||||
}
|
||||
|
||||
template<> std::ostream &Emit(std::ostream &o, const std::u16string &lit) {
|
||||
return o << parser::QuoteCharacterLiteral(lit);
|
||||
}
|
||||
|
||||
template<> std::ostream &Emit(std::ostream &o, const std::u32string &lit) {
|
||||
return o << parser::QuoteCharacterLiteral(lit);
|
||||
}
|
||||
|
||||
template<typename A>
|
||||
std::ostream &Emit(std::ostream &o, const A *p, const char *kw = nullptr) {
|
||||
if (p != nullptr) {
|
||||
|
@ -257,21 +275,18 @@ std::ostream &Emit(std::ostream &o, const std::variant<A...> &u) {
|
|||
return o;
|
||||
}
|
||||
|
||||
template<> std::ostream &Emit(std::ostream &o, const Symbol &symbol) {
|
||||
return o << symbol.name().ToString();
|
||||
}
|
||||
|
||||
template<> std::ostream &Emit(std::ostream &o, const IntrinsicProcedure &p) {
|
||||
return o << p;
|
||||
}
|
||||
|
||||
std::ostream &BaseObject::AsFortran(std::ostream &o) const {
|
||||
return Emit(o, u);
|
||||
}
|
||||
|
||||
template<int KIND>
|
||||
std::ostream &TypeParamInquiry<KIND>::AsFortran(std::ostream &o) const {
|
||||
return Emit(o, u) << '%' << parameter.ToString();
|
||||
}
|
||||
|
||||
std::ostream &Component::AsFortran(std::ostream &o) const {
|
||||
base_->AsFortran(o);
|
||||
return Emit(o << '%', symbol_);
|
||||
return Emit(o << '%', *symbol_);
|
||||
}
|
||||
|
||||
std::ostream &Triplet::AsFortran(std::ostream &o) const {
|
||||
|
@ -368,6 +383,7 @@ Expr<SubscriptInteger> BaseObject::LEN() const {
|
|||
Expr<SubscriptInteger> Component::LEN() const {
|
||||
return SymbolLEN(GetLastSymbol());
|
||||
}
|
||||
|
||||
Expr<SubscriptInteger> ArrayRef::LEN() const {
|
||||
return std::visit(
|
||||
common::visitors{
|
||||
|
@ -376,9 +392,11 @@ Expr<SubscriptInteger> ArrayRef::LEN() const {
|
|||
},
|
||||
u);
|
||||
}
|
||||
|
||||
Expr<SubscriptInteger> CoarrayRef::LEN() const {
|
||||
return SymbolLEN(*base_.back());
|
||||
}
|
||||
|
||||
Expr<SubscriptInteger> DataRef::LEN() const {
|
||||
return std::visit(
|
||||
common::visitors{
|
||||
|
@ -387,11 +405,13 @@ Expr<SubscriptInteger> DataRef::LEN() const {
|
|||
},
|
||||
u);
|
||||
}
|
||||
|
||||
Expr<SubscriptInteger> Substring::LEN() const {
|
||||
return AsExpr(
|
||||
Extremum<SubscriptInteger>{AsExpr(Constant<SubscriptInteger>{0}),
|
||||
upper() - lower() + AsExpr(Constant<SubscriptInteger>{1})});
|
||||
}
|
||||
|
||||
template<typename T> Expr<SubscriptInteger> Designator<T>::LEN() const {
|
||||
if constexpr (Result::category == TypeCategory::Character) {
|
||||
return std::visit(
|
||||
|
@ -406,6 +426,7 @@ template<typename T> Expr<SubscriptInteger> Designator<T>::LEN() const {
|
|||
return AsExpr(Constant<SubscriptInteger>{0});
|
||||
}
|
||||
}
|
||||
|
||||
Expr<SubscriptInteger> ProcedureDesignator::LEN() const {
|
||||
return std::visit(
|
||||
common::visitors{
|
||||
|
@ -505,27 +526,6 @@ template<typename T> int Designator<T>::Rank() const {
|
|||
},
|
||||
u);
|
||||
}
|
||||
int ProcedureDesignator::Rank() const {
|
||||
if (const Symbol * symbol{GetSymbol()}) {
|
||||
return symbol->Rank();
|
||||
}
|
||||
if (const auto *intrinsic{std::get_if<SpecificIntrinsic>(&u)}) {
|
||||
return intrinsic->rank;
|
||||
}
|
||||
CHECK(!"ProcedureDesignator::Rank(): no case");
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool ProcedureDesignator::IsElemental() const {
|
||||
if (const Symbol * symbol{GetSymbol()}) {
|
||||
return symbol->attrs().test(semantics::Attr::ELEMENTAL);
|
||||
}
|
||||
if (const auto *intrinsic{std::get_if<SpecificIntrinsic>(&u)}) {
|
||||
return intrinsic->attrs.test(semantics::Attr::ELEMENTAL);
|
||||
}
|
||||
CHECK(!"ProcedureDesignator::IsElemental(): no case");
|
||||
return 0;
|
||||
}
|
||||
|
||||
// GetBaseObject(), GetFirstSymbol(), & GetLastSymbol()
|
||||
const Symbol &Component::GetFirstSymbol() const {
|
||||
|
@ -626,37 +626,53 @@ template<typename T> const Symbol *Designator<T>::GetLastSymbol() const {
|
|||
u);
|
||||
}
|
||||
|
||||
const Symbol *ProcedureDesignator::GetSymbol() const {
|
||||
return std::visit(
|
||||
common::visitors{
|
||||
[](const Symbol *sym) { return sym; },
|
||||
[](const Component &c) { return &c.GetLastSymbol(); },
|
||||
[](const auto &) -> const Symbol * { return nullptr; },
|
||||
},
|
||||
u);
|
||||
}
|
||||
|
||||
template<typename T> std::optional<DynamicType> Designator<T>::GetType() const {
|
||||
if constexpr (std::is_same_v<Result, SomeDerived>) {
|
||||
if (const Symbol * symbol{GetLastSymbol()}) {
|
||||
return GetSymbolType(*symbol);
|
||||
} else {
|
||||
return std::nullopt;
|
||||
}
|
||||
} else {
|
||||
if constexpr (IsLengthlessIntrinsicType<Result>) {
|
||||
return {Result::GetType()};
|
||||
} else if (const Symbol * symbol{GetLastSymbol()}) {
|
||||
return GetSymbolType(symbol);
|
||||
} else {
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<DynamicType> ProcedureDesignator::GetType() const {
|
||||
if (const Symbol * symbol{GetSymbol()}) {
|
||||
return {GetSymbolType(*symbol)};
|
||||
}
|
||||
if (const auto *intrinsic{std::get_if<SpecificIntrinsic>(&u)}) {
|
||||
return {intrinsic->type};
|
||||
}
|
||||
return std::nullopt;
|
||||
// Equality testing
|
||||
|
||||
bool BaseObject::operator==(const BaseObject &that) const {
|
||||
return u == that.u;
|
||||
}
|
||||
bool Component::operator==(const Component &that) const {
|
||||
return base_ == that.base_ && symbol_ == that.symbol_;
|
||||
}
|
||||
template<int KIND>
|
||||
bool TypeParamInquiry<KIND>::operator==(
|
||||
const TypeParamInquiry<KIND> &that) const {
|
||||
return parameter == that.parameter && u == that.u;
|
||||
}
|
||||
bool Triplet::operator==(const Triplet &that) const {
|
||||
return lower_ == that.lower_ && upper_ == that.upper_ &&
|
||||
stride_ == that.stride_;
|
||||
}
|
||||
bool ArrayRef::operator==(const ArrayRef &that) const {
|
||||
return u == that.u && subscript == that.subscript;
|
||||
}
|
||||
bool CoarrayRef::operator==(const CoarrayRef &that) const {
|
||||
return base_ == that.base_ && subscript_ == that.subscript_ &&
|
||||
cosubscript_ == that.cosubscript_ && stat_ == that.stat_ &&
|
||||
team_ == that.team_ && teamIsTeamNumber_ == that.teamIsTeamNumber_;
|
||||
}
|
||||
bool Substring::operator==(const Substring &that) const {
|
||||
return parent_ == that.parent_ && lower_ == that.lower_ &&
|
||||
upper_ == that.upper_;
|
||||
}
|
||||
bool ComplexPart::operator==(const ComplexPart &that) const {
|
||||
return part_ == that.part_ && complex_ == that.complex_;
|
||||
}
|
||||
bool ProcedureRef::operator==(const ProcedureRef &that) const {
|
||||
return proc_ == that.proc_ && arguments_ == that.arguments_;
|
||||
}
|
||||
|
||||
EXPAND_FOR_EACH_INTEGER_KIND(
|
||||
TEMPLATE_INSTANTIATION, template struct TypeParamInquiry)
|
||||
FOR_EACH_SPECIFIC_TYPE(template class Designator)
|
||||
}
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
#include "type.h"
|
||||
#include "../common/idioms.h"
|
||||
#include "../common/template.h"
|
||||
#include "../parser/char-block.h"
|
||||
#include <optional>
|
||||
#include <ostream>
|
||||
#include <variant>
|
||||
|
@ -52,6 +53,7 @@ struct BaseObject {
|
|||
explicit BaseObject(StaticDataObject::Pointer &&p) : u{std::move(p)} {}
|
||||
int Rank() const;
|
||||
Expr<SubscriptInteger> LEN() const;
|
||||
bool operator==(const BaseObject &) const;
|
||||
std::ostream &AsFortran(std::ostream &) const;
|
||||
std::variant<const Symbol *, StaticDataObject::Pointer> u;
|
||||
};
|
||||
|
@ -61,6 +63,8 @@ struct BaseObject {
|
|||
// that isn't explicit in the document). Pointer and allocatable components
|
||||
// are not explicitly indirected in this representation (TODO: yet?)
|
||||
// Complex components (%RE, %IM) are isolated below in ComplexPart.
|
||||
// (Type parameter inquiries look like component references but are distinct
|
||||
// constructs and not represented by this class.)
|
||||
class Component {
|
||||
public:
|
||||
CLASS_BOILERPLATE(Component)
|
||||
|
@ -75,6 +79,7 @@ public:
|
|||
const Symbol &GetFirstSymbol() const;
|
||||
const Symbol &GetLastSymbol() const { return *symbol_; }
|
||||
Expr<SubscriptInteger> LEN() const;
|
||||
bool operator==(const Component &) const;
|
||||
std::ostream &AsFortran(std::ostream &) const;
|
||||
|
||||
private:
|
||||
|
@ -82,6 +87,35 @@ private:
|
|||
const Symbol *symbol_;
|
||||
};
|
||||
|
||||
using SymbolOrComponent = std::variant<const Symbol *, Component>;
|
||||
|
||||
// R916 type-param-inquiry
|
||||
// N.B. x%LEN for CHARACTER is rewritten in semantics to LEN(x), which is
|
||||
// then handled via LEN() member functions in the various classes.
|
||||
// x%KIND for intrinsic types is similarly rewritten in semantics to
|
||||
// KIND(x), which is then folded to a constant value.
|
||||
// "Bare" type parameter references within a derived type definition do
|
||||
// not have base objects here.
|
||||
template<int KIND> struct TypeParamInquiry {
|
||||
using Result = Type<TypeCategory::Integer, KIND>;
|
||||
CLASS_BOILERPLATE(TypeParamInquiry)
|
||||
TypeParamInquiry(const Symbol &symbol, parser::CharBlock p)
|
||||
: u{&symbol}, parameter{std::move(p)} {}
|
||||
TypeParamInquiry(Component &&component, parser::CharBlock p)
|
||||
: u{component}, parameter{p} {}
|
||||
TypeParamInquiry(SymbolOrComponent &&x, parser::CharBlock p)
|
||||
: u{x}, parameter{p} {}
|
||||
explicit TypeParamInquiry(parser::CharBlock p) : parameter{p} {}
|
||||
static constexpr int Rank() { return 0; } // always scalar
|
||||
bool operator==(const TypeParamInquiry &) const;
|
||||
std::ostream &AsFortran(std::ostream &) const;
|
||||
SymbolOrComponent u{nullptr};
|
||||
parser::CharBlock parameter;
|
||||
};
|
||||
|
||||
EXPAND_FOR_EACH_INTEGER_KIND(
|
||||
TEMPLATE_INSTANTIATION, extern template struct TypeParamInquiry)
|
||||
|
||||
// R921 subscript-triplet
|
||||
class Triplet {
|
||||
public:
|
||||
|
@ -93,6 +127,7 @@ public:
|
|||
std::optional<Expr<SubscriptInteger>> lower() const;
|
||||
std::optional<Expr<SubscriptInteger>> upper() const;
|
||||
std::optional<Expr<SubscriptInteger>> stride() const;
|
||||
bool operator==(const Triplet &) const;
|
||||
std::ostream &AsFortran(std::ostream &) const;
|
||||
|
||||
private:
|
||||
|
@ -125,9 +160,10 @@ struct ArrayRef {
|
|||
const Symbol &GetFirstSymbol() const;
|
||||
const Symbol &GetLastSymbol() const;
|
||||
Expr<SubscriptInteger> LEN() const;
|
||||
bool operator==(const ArrayRef &) const;
|
||||
std::ostream &AsFortran(std::ostream &) const;
|
||||
|
||||
std::variant<const Symbol *, Component> u;
|
||||
SymbolOrComponent u;
|
||||
std::vector<Subscript> subscript;
|
||||
};
|
||||
|
||||
|
@ -144,15 +180,28 @@ public:
|
|||
CoarrayRef(std::vector<const Symbol *> &&,
|
||||
std::vector<Expr<SubscriptInteger>> &&,
|
||||
std::vector<Expr<SubscriptInteger>> &&);
|
||||
|
||||
const std::vector<const Symbol *> &base() const { return base_; }
|
||||
const std::vector<Expr<SubscriptInteger>> &subscript() const {
|
||||
return subscript_;
|
||||
}
|
||||
const std::vector<Expr<SubscriptInteger>> &cosubscript() const {
|
||||
return cosubscript_;
|
||||
}
|
||||
|
||||
// These integral expressions for STAT= and TEAM= must be variables
|
||||
// (i.e., Designator or pointer-valued FunctionRef).
|
||||
std::optional<Expr<SomeInteger>> stat() const;
|
||||
CoarrayRef &set_stat(Expr<SomeInteger> &&);
|
||||
std::optional<Expr<SomeInteger>> team() const;
|
||||
bool teamIsTeamNumber() const { return teamIsTeamNumber_; }
|
||||
CoarrayRef &set_team(Expr<SomeInteger> &&, bool isTeamNumber = false);
|
||||
|
||||
int Rank() const;
|
||||
const Symbol &GetFirstSymbol() const { return *base_.front(); }
|
||||
const Symbol &GetLastSymbol() const { return *base_.back(); }
|
||||
Expr<SubscriptInteger> LEN() const;
|
||||
bool operator==(const CoarrayRef &) const;
|
||||
std::ostream &AsFortran(std::ostream &) const;
|
||||
|
||||
private:
|
||||
|
@ -187,10 +236,10 @@ struct DataRef {
|
|||
class Substring {
|
||||
public:
|
||||
CLASS_BOILERPLATE(Substring)
|
||||
Substring(DataRef &&parent, std::optional<Expr<SubscriptInteger>> &&first,
|
||||
std::optional<Expr<SubscriptInteger>> &&last)
|
||||
Substring(DataRef &&parent, std::optional<Expr<SubscriptInteger>> &&lower,
|
||||
std::optional<Expr<SubscriptInteger>> &&upper)
|
||||
: parent_{std::move(parent)} {
|
||||
SetBounds(first, last);
|
||||
SetBounds(lower, upper);
|
||||
}
|
||||
Substring(StaticDataObject::Pointer &&parent,
|
||||
std::optional<Expr<SubscriptInteger>> &&lower,
|
||||
|
@ -202,9 +251,13 @@ public:
|
|||
Expr<SubscriptInteger> lower() const;
|
||||
Expr<SubscriptInteger> upper() const;
|
||||
int Rank() const;
|
||||
template<typename A> const A *GetParentIf() const {
|
||||
return std::get_if<A>(&parent_);
|
||||
}
|
||||
BaseObject GetBaseObject() const;
|
||||
const Symbol *GetLastSymbol() const;
|
||||
Expr<SubscriptInteger> LEN() const;
|
||||
bool operator==(const Substring &) const;
|
||||
std::ostream &AsFortran(std::ostream &) const;
|
||||
|
||||
std::optional<Expr<SomeCharacter>> Fold(FoldingContext &);
|
||||
|
@ -229,6 +282,7 @@ public:
|
|||
int Rank() const;
|
||||
const Symbol &GetFirstSymbol() const { return complex_.GetFirstSymbol(); }
|
||||
const Symbol &GetLastSymbol() const { return complex_.GetLastSymbol(); }
|
||||
bool operator==(const ComplexPart &) const;
|
||||
std::ostream &AsFortran(std::ostream &) const;
|
||||
|
||||
private:
|
||||
|
@ -238,20 +292,20 @@ private:
|
|||
|
||||
// R901 designator is the most general data reference object, apart from
|
||||
// calls to pointer-valued functions. Its variant holds everything that
|
||||
// a DataRef can, and possibly either a substring reference or a complex
|
||||
// part (%RE/%IM) reference.
|
||||
template<typename A> class Designator {
|
||||
// a DataRef can, and possibly also a substring reference or a
|
||||
// complex component (%RE/%IM) reference.
|
||||
template<typename T> class Designator {
|
||||
using DataRefs = decltype(DataRef::u);
|
||||
using MaybeSubstring =
|
||||
std::conditional_t<A::category == TypeCategory::Character,
|
||||
std::conditional_t<T::category == TypeCategory::Character,
|
||||
std::variant<Substring>, std::variant<>>;
|
||||
using MaybeComplexPart = std::conditional_t<A::category == TypeCategory::Real,
|
||||
using MaybeComplexPart = std::conditional_t<T::category == TypeCategory::Real,
|
||||
std::variant<ComplexPart>, std::variant<>>;
|
||||
using Variant =
|
||||
common::CombineVariants<DataRefs, MaybeSubstring, MaybeComplexPart>;
|
||||
|
||||
public:
|
||||
using Result = A;
|
||||
using Result = T;
|
||||
static_assert(IsSpecificIntrinsicType<Result> ||
|
||||
std::is_same_v<Result, SomeKind<TypeCategory::Derived>>);
|
||||
EVALUATE_UNION_CLASS_BOILERPLATE(Designator)
|
||||
|
@ -283,6 +337,7 @@ public:
|
|||
Expr<SubscriptInteger> LEN() const;
|
||||
int Rank() const { return proc_.Rank(); }
|
||||
bool IsElemental() const { return proc_.IsElemental(); }
|
||||
bool operator==(const ProcedureRef &) const;
|
||||
std::ostream &AsFortran(std::ostream &) const;
|
||||
|
||||
protected:
|
||||
|
@ -292,23 +347,12 @@ protected:
|
|||
|
||||
template<typename A> struct FunctionRef : public ProcedureRef {
|
||||
using Result = A;
|
||||
static_assert(IsSpecificIntrinsicType<Result> ||
|
||||
std::is_same_v<Result, SomeKind<TypeCategory::Derived>>);
|
||||
CLASS_BOILERPLATE(FunctionRef)
|
||||
FunctionRef(ProcedureRef &&pr) : ProcedureRef{std::move(pr)} {}
|
||||
FunctionRef(ProcedureDesignator &&p, ActualArguments &&a)
|
||||
: ProcedureRef{std::move(p), std::move(a)} {}
|
||||
|
||||
std::optional<DynamicType> GetType() const {
|
||||
if constexpr (std::is_same_v<Result, SomeDerived>) {
|
||||
if (const Symbol * symbol{proc_.GetSymbol()}) {
|
||||
return GetSymbolType(*symbol);
|
||||
}
|
||||
} else {
|
||||
return Result::GetType();
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
std::optional<DynamicType> GetType() const { return proc_.GetType(); }
|
||||
std::optional<Constant<Result>> Fold(FoldingContext &); // for intrinsics
|
||||
};
|
||||
|
||||
|
|
|
@ -287,7 +287,7 @@ MaybeExpr TypedWrapper(DynamicType &&dyType, WRAPPED &&x) {
|
|||
// Wraps a data reference in a typed Designator<>.
|
||||
static MaybeExpr Designate(DataRef &&dataRef) {
|
||||
const Symbol &symbol{dataRef.GetLastSymbol()};
|
||||
if (std::optional<DynamicType> dyType{GetSymbolType(symbol)}) {
|
||||
if (std::optional<DynamicType> dyType{GetSymbolType(&symbol)}) {
|
||||
return TypedWrapper<Designator, DataRef>(
|
||||
std::move(*dyType), std::move(dataRef));
|
||||
}
|
||||
|
@ -764,7 +764,7 @@ MaybeExpr AnalyzeExpr(
|
|||
std::optional<Expr<SubscriptInteger>> last{
|
||||
GetSubstringBound(context, std::get<1>(range.t))};
|
||||
const Symbol &symbol{checked->GetLastSymbol()};
|
||||
if (std::optional<DynamicType> dynamicType{GetSymbolType(symbol)}) {
|
||||
if (std::optional<DynamicType> dynamicType{GetSymbolType(&symbol)}) {
|
||||
if (dynamicType->category == TypeCategory::Character) {
|
||||
return WrapperHelper<TypeCategory::Character, Designator,
|
||||
Substring>(dynamicType->kind,
|
||||
|
|
|
@ -156,8 +156,8 @@ void TestIntrinsics() {
|
|||
.DoCall(); // bad intrinsic name
|
||||
TestCall{table, "abs"}
|
||||
.Push(Named("a", Const(Scalar<Int4>{})))
|
||||
.DoCall(Int4::dynamicType);
|
||||
TestCall{table, "abs"}.Push(Const(Scalar<Int4>{})).DoCall(Int4::dynamicType);
|
||||
.DoCall(Int4::GetType());
|
||||
TestCall{table, "abs"}.Push(Const(Scalar<Int4>{})).DoCall(Int4::GetType());
|
||||
TestCall{table, "abs"}
|
||||
.Push(Named("bad", Const(Scalar<Int4>{})))
|
||||
.DoCall(); // bad keyword
|
||||
|
@ -174,21 +174,17 @@ void TestIntrinsics() {
|
|||
.Push(Named("a", Const(Scalar<Int4>{})))
|
||||
.Push(Const(Scalar<Int4>{}))
|
||||
.DoCall();
|
||||
TestCall{table, "abs"}.Push(Const(Scalar<Int1>{})).DoCall(Int1::dynamicType);
|
||||
TestCall{table, "abs"}.Push(Const(Scalar<Int4>{})).DoCall(Int4::dynamicType);
|
||||
TestCall{table, "abs"}.Push(Const(Scalar<Int8>{})).DoCall(Int8::dynamicType);
|
||||
TestCall{table, "abs"}
|
||||
.Push(Const(Scalar<Real4>{}))
|
||||
.DoCall(Real4::dynamicType);
|
||||
TestCall{table, "abs"}
|
||||
.Push(Const(Scalar<Real8>{}))
|
||||
.DoCall(Real8::dynamicType);
|
||||
TestCall{table, "abs"}.Push(Const(Scalar<Int1>{})).DoCall(Int1::GetType());
|
||||
TestCall{table, "abs"}.Push(Const(Scalar<Int4>{})).DoCall(Int4::GetType());
|
||||
TestCall{table, "abs"}.Push(Const(Scalar<Int8>{})).DoCall(Int8::GetType());
|
||||
TestCall{table, "abs"}.Push(Const(Scalar<Real4>{})).DoCall(Real4::GetType());
|
||||
TestCall{table, "abs"}.Push(Const(Scalar<Real8>{})).DoCall(Real8::GetType());
|
||||
TestCall{table, "abs"}
|
||||
.Push(Const(Scalar<Complex4>{}))
|
||||
.DoCall(Real4::dynamicType);
|
||||
.DoCall(Real4::GetType());
|
||||
TestCall{table, "abs"}
|
||||
.Push(Const(Scalar<Complex8>{}))
|
||||
.DoCall(Real8::dynamicType);
|
||||
.DoCall(Real8::GetType());
|
||||
TestCall{table, "abs"}.Push(Const(Scalar<Char>{})).DoCall();
|
||||
TestCall{table, "abs"}.Push(Const(Scalar<Log4>{})).DoCall();
|
||||
|
||||
|
@ -202,10 +198,10 @@ void TestIntrinsics() {
|
|||
amin0Call.Push(Const(Scalar<Int4>{}));
|
||||
amin1Call.Push(Const(Scalar<Int4>{}));
|
||||
}
|
||||
maxCall.DoCall(Real4::dynamicType);
|
||||
maxCall.DoCall(Real4::GetType());
|
||||
max0Call.DoCall();
|
||||
max1Call.DoCall(Int4::dynamicType);
|
||||
amin0Call.DoCall(Real4::dynamicType);
|
||||
max1Call.DoCall(Int4::GetType());
|
||||
amin0Call.DoCall(Real4::GetType());
|
||||
amin1Call.DoCall();
|
||||
|
||||
// TODO: test other intrinsics
|
||||
|
|
Loading…
Reference in New Issue