diff --git a/flang/lib/common/indirection.h b/flang/lib/common/indirection.h index 83901ae577af..b661e8455906 100644 --- a/flang/lib/common/indirection.h +++ b/flang/lib/common/indirection.h @@ -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 static Indirection Make(ARGS &&... args) { return {new A(std::forward(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 static Indirection Make(ARGS &&... args) { return {new A(std::forward(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}; }; diff --git a/flang/lib/common/unwrap.h b/flang/lib/common/unwrap.h index 5db318953de6..4ac8591f6e2f 100644 --- a/flang/lib/common/unwrap.h +++ b/flang/lib/common/unwrap.h @@ -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 auto Unwrap(B &x) -> Constify * { } // Prototypes of specializations, to enable mutual recursion -template -auto Unwrap(B *) -> Constify> *; +template auto Unwrap(B *p) -> Constify *; template auto Unwrap(const std::unique_ptr &) -> Constify *; template @@ -69,8 +76,7 @@ template auto Unwrap(const CountedReference &) -> Constify *; // Implementations of specializations -template -auto Unwrap(B *p) -> Constify> * { +template auto Unwrap(B *p) -> Constify * { if (p != nullptr) { return Unwrap(*p); } else { @@ -115,12 +121,22 @@ auto Unwrap(const std::optional &x) -> Constify * { } template A *Unwrap(std::variant &u) { - return std::visit([](auto &x) { return Unwrap(x); }, u); + return std::visit( + [](auto &x) -> A * { + using Ty = std::decay_t(x))>; + if constexpr (!std::is_const_v> || + std::is_const_v) { + return Unwrap(x); + } + return nullptr; + }, + u); } template auto Unwrap(const std::variant &u) -> std::add_const_t * { - return std::visit([](const auto &x) { return Unwrap(x); }, u); + return std::visit( + [](const auto &x) -> std::add_const_t * { return Unwrap(x); }, u); } template diff --git a/flang/lib/evaluate/call.cc b/flang/lib/evaluate/call.cc index 8f159c3239ca..9c643ac95b71 100644 --- a/flang/lib/evaluate/call.cc +++ b/flang/lib/evaluate/call.cc @@ -14,6 +14,7 @@ #include "call.h" #include "expression.h" +#include "../semantics/symbol.h" namespace Fortran::evaluate { @@ -23,6 +24,11 @@ std::optional 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 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 ProcedureDesignator::GetType() const { + if (const auto *intrinsic{std::get_if(&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(&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(&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{'('}; diff --git a/flang/lib/evaluate/call.h b/flang/lib/evaluate/call.h index 0f13cae67ceb..fc6581071d23 100644 --- a/flang/lib/evaluate/call.h +++ b/flang/lib/evaluate/call.h @@ -37,6 +37,7 @@ struct ActualArgument { std::optional GetType() const; int Rank() const; + bool operator==(const ActualArgument &) const; std::ostream &AsFortran(std::ostream &) const; std::optional VectorSize() const; @@ -64,6 +65,7 @@ struct SpecificIntrinsic { SpecificIntrinsic(IntrinsicProcedure n, std::optional &&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; diff --git a/flang/lib/evaluate/common.h b/flang/lib/evaluate/common.h index 617449467d45..4985fd89630f 100644 --- a/flang/lib/evaluate/common.h +++ b/flang/lib/evaluate/common.h @@ -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 +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 explicit t(const _A &x) : u{x} {} \ template \ explicit t(std::enable_if_t, _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 using CopyableIndirection = common::Indirection; @@ -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); diff --git a/flang/lib/evaluate/complex.h b/flang/lib/evaluate/complex.h index 3d6f8beb29ab..f870b7e28295 100644 --- a/flang/lib/evaluate/complex.h +++ b/flang/lib/evaluate/complex.h @@ -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()}; } diff --git a/flang/lib/evaluate/expression.cc b/flang/lib/evaluate/expression.cc index ff908a8fb01c..b961e32762b2 100644 --- a/flang/lib/evaluate/expression.cc +++ b/flang/lib/evaluate/expression.cc @@ -191,7 +191,7 @@ template DynamicType ArrayConstructor::GetType() const { template std::optional ExpressionBase::GetType() const { - if constexpr (IsSpecificIntrinsicType) { + if constexpr (IsLengthlessIntrinsicType) { return Result::GetType(); } else { return std::visit( @@ -200,7 +200,7 @@ std::optional ExpressionBase::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 int ExpressionBase::Rank() const { derived().u); } +// Equality testing for classes without EVALUATE_UNION_CLASS_BOILERPLATE() + +template +bool ImpliedDo::operator==(const ImpliedDo &that) const { + return controlVariableName == that.controlVariableName && + lower == that.lower && upper == that.upper && stride == that.stride && + values == that.values; +} + +template +bool ArrayConstructorValues::operator==( + const ArrayConstructorValues &that) const { + return values == that.values; +} + +template +bool ArrayConstructor::operator==(const ArrayConstructor &that) const { + return *static_cast *>(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; 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 diff --git a/flang/lib/evaluate/expression.h b/flang/lib/evaluate/expression.h index 2d8feeafba6d..c2300071fbd2 100644 --- a/flang/lib/evaluate/expression.h +++ b/flang/lib/evaluate/expression.h @@ -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 struct ImpliedDo { using Operand = OPERAND; using Result = ResultType; static_assert(Operand::category == TypeCategory::Integer); + bool operator==(const ImpliedDo &) const; parser::CharBlock controlVariableName; CopyableIndirection> lower, upper, stride; CopyableIndirection values; @@ -406,6 +412,7 @@ template struct ArrayConstructorValues { using Result = RESULT; CLASS_BOILERPLATE(ArrayConstructorValues) template void Push(A &&x) { values.emplace_back(std::move(x)); } + bool operator==(const ArrayConstructorValues &) const; std::vector> values; }; @@ -416,20 +423,20 @@ struct ArrayConstructor : public ArrayConstructorValues { DynamicType GetType() const; static constexpr int Rank() { return 1; } Expr LEN() const; + bool operator==(const ArrayConstructor &) const; std::ostream &AsFortran(std::ostream &) const; Result result; std::vector> typeParameterValues; }; -// Per-category expression representations +// Expression representations for each type category. template class Expr> : public ExpressionBase> { public: using Result = Type; - // TODO: R916 type-param-inquiry EVALUATE_UNION_CLASS_BOILERPLATE(Expr) explicit Expr(const Scalar &x) : u{Constant{x}} {} @@ -444,7 +451,7 @@ private: Add, Subtract, Multiply, Divide, Power, Extremum>; using Others = std::variant, ArrayConstructor, - Designator, FunctionRef>; + TypeParamInquiry, Designator, FunctionRef>; public: common::CombineVariants u; @@ -664,10 +671,12 @@ public: // from parse tree nodes. struct GenericExprWrapper { GenericExprWrapper(Expr &&x) : v{std::move(x)} {} + bool operator==(const GenericExprWrapper &) const; Expr 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_ diff --git a/flang/lib/evaluate/integer.h b/flang/lib/evaluate/integer.h index 75d41e4f9c05..eef6e3d1c356 100644 --- a/flang/lib/evaluate/integer.h +++ b/flang/lib/evaluate/integer.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) { diff --git a/flang/lib/evaluate/logical.h b/flang/lib/evaluate/logical.h index effd69a67123..e0007c58bf5b 100644 --- a/flang/lib/evaluate/logical.h +++ b/flang/lib/evaluate/logical.h @@ -28,6 +28,10 @@ public: constexpr Logical(bool truth) : word_{-std::uint64_t{truth}} {} constexpr Logical &operator=(const Logical &) = default; + template constexpr bool operator==(const Logical &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); } diff --git a/flang/lib/evaluate/real.h b/flang/lib/evaluate/real.h index 82c26078e30f..f9444a599c1c 100644 --- a/flang/lib/evaluate/real.h +++ b/flang/lib/evaluate/real.h @@ -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 diff --git a/flang/lib/evaluate/type.cc b/flang/lib/evaluate/type.cc index a6949ecf9309..06042fb94e2d 100644 --- a/flang/lib/evaluate/type.cc +++ b/flang/lib/evaluate/type.cc @@ -24,16 +24,23 @@ using namespace std::literals::string_literals; namespace Fortran::evaluate { -std::optional 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 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::operator==( + const SomeKind &that) const { + return spec_ == that.spec_ && descriptor_ == that.descriptor_; +} + std::string SomeDerived::AsFortran() const { return "TYPE("s + spec().name().ToString() + ')'; } diff --git a/flang/lib/evaluate/type.h b/flang/lib/evaluate/type.h index 1cd148f3df35..fd20aaf60fa4 100644 --- a/flang/lib/evaluate/type.h +++ b/flang/lib/evaluate/type.h @@ -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 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 GetSymbolType(const semantics::Symbol *); // Specific intrinsic types are represented by specializations of // this class template Type. template class Type; -template struct TypeBase { - static constexpr DynamicType dynamicType{CATEGORY, KIND}; - static constexpr DynamicType GetType() { return {dynamicType}; } +template 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 @@ -225,11 +225,20 @@ using FloatingTypes = common::CombineTuples; using NumericTypes = common::CombineTuples; using RelationalTypes = common::CombineTuples; using AllIntrinsicTypes = common::CombineTuples; +using LengthlessIntrinsicTypes = + common::CombineTuples; // Predicate: does a type represent a specific intrinsic type? template constexpr bool IsSpecificIntrinsicType{common::HasMember}; +// 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 +constexpr bool IsLengthlessIntrinsicType{ + common::HasMember}; + // When Scalar is S, then TypeOf is T. // TypeOf is implemented by scanning all supported types for a match // with Type::Scalar. @@ -251,6 +260,7 @@ template using TypeOf = typename TypeOfHelper::type; // Represents a type of any supported kind within a particular category. template struct SomeKind { static constexpr TypeCategory category{CATEGORY}; + constexpr bool operator==(const SomeKind &) const { return true; } }; template<> class SomeKind { @@ -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; @@ -280,36 +297,37 @@ using SomeCategory = std::tuple; 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; + +#define FOR_EACH_INTEGER_KIND_HELP(PREFIX, K) \ + PREFIX>; +#define FOR_EACH_REAL_KIND_HELP(PREFIX, K) PREFIX>; +#define FOR_EACH_COMPLEX_KIND_HELP(PREFIX, K) \ + PREFIX>; +#define FOR_EACH_CHARACTER_KIND_HELP(PREFIX, K) \ + PREFIX>; +#define FOR_EACH_LOGICAL_KIND_HELP(PREFIX, K) \ + PREFIX>; + #define FOR_EACH_INTEGER_KIND(PREFIX) \ - PREFIX>; \ - PREFIX>; \ - PREFIX>; \ - PREFIX>; \ - PREFIX>; + EXPAND_FOR_EACH_INTEGER_KIND(FOR_EACH_INTEGER_KIND_HELP, PREFIX) #define FOR_EACH_REAL_KIND(PREFIX) \ - PREFIX>; \ - PREFIX>; \ - PREFIX>; \ - PREFIX>; \ - PREFIX>; \ - PREFIX>; + EXPAND_FOR_EACH_REAL_KIND(FOR_EACH_REAL_KIND_HELP, PREFIX) #define FOR_EACH_COMPLEX_KIND(PREFIX) \ - PREFIX>; \ - PREFIX>; \ - PREFIX>; \ - PREFIX>; \ - PREFIX>; \ - PREFIX>; + EXPAND_FOR_EACH_COMPLEX_KIND(FOR_EACH_COMPLEX_KIND_HELP, PREFIX) #define FOR_EACH_CHARACTER_KIND(PREFIX) \ - PREFIX>; \ - PREFIX>; \ - PREFIX>; + EXPAND_FOR_EACH_CHARACTER_KIND(FOR_EACH_CHARACTER_KIND_HELP, PREFIX) #define FOR_EACH_LOGICAL_KIND(PREFIX) \ - PREFIX>; \ - PREFIX>; \ - PREFIX>; \ - PREFIX>; + 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; \ PREFIX; \ PREFIX; \ + PREFIX; \ PREFIX; #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 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; diff --git a/flang/lib/evaluate/variable.cc b/flang/lib/evaluate/variable.cc index 0a60a568e5d0..fd46c0d9ccb4 100644 --- a/flang/lib/evaluate/variable.cc +++ b/flang/lib/evaluate/variable.cc @@ -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> &&l, @@ -78,6 +72,22 @@ CoarrayRef::CoarrayRef(std::vector &&c, CHECK(!base_.empty()); } +std::optional> CoarrayRef::stat() const { + if (stat_.has_value()) { + return {**stat_}; + } else { + return std::nullopt; + } +} + +std::optional> CoarrayRef::team() const { + if (team_.has_value()) { + return {**team_}; + } else { + return std::nullopt; + } +} + CoarrayRef &CoarrayRef::set_stat(Expr &&v) { CHECK(IsVariable(v)); stat_ = CopyableIndirection>::Make(std::move(v)); @@ -196,22 +206,30 @@ std::optional> 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 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 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 &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 +std::ostream &TypeParamInquiry::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 BaseObject::LEN() const { Expr Component::LEN() const { return SymbolLEN(GetLastSymbol()); } + Expr ArrayRef::LEN() const { return std::visit( common::visitors{ @@ -376,9 +392,11 @@ Expr ArrayRef::LEN() const { }, u); } + Expr CoarrayRef::LEN() const { return SymbolLEN(*base_.back()); } + Expr DataRef::LEN() const { return std::visit( common::visitors{ @@ -387,11 +405,13 @@ Expr DataRef::LEN() const { }, u); } + Expr Substring::LEN() const { return AsExpr( Extremum{AsExpr(Constant{0}), upper() - lower() + AsExpr(Constant{1})}); } + template Expr Designator::LEN() const { if constexpr (Result::category == TypeCategory::Character) { return std::visit( @@ -406,6 +426,7 @@ template Expr Designator::LEN() const { return AsExpr(Constant{0}); } } + Expr ProcedureDesignator::LEN() const { return std::visit( common::visitors{ @@ -505,27 +526,6 @@ template int Designator::Rank() const { }, u); } -int ProcedureDesignator::Rank() const { - if (const Symbol * symbol{GetSymbol()}) { - return symbol->Rank(); - } - if (const auto *intrinsic{std::get_if(&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(&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 const Symbol *Designator::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 std::optional Designator::GetType() const { - if constexpr (std::is_same_v) { - if (const Symbol * symbol{GetLastSymbol()}) { - return GetSymbolType(*symbol); - } else { - return std::nullopt; - } - } else { + if constexpr (IsLengthlessIntrinsicType) { return {Result::GetType()}; + } else if (const Symbol * symbol{GetLastSymbol()}) { + return GetSymbolType(symbol); + } else { + return std::nullopt; } } -std::optional ProcedureDesignator::GetType() const { - if (const Symbol * symbol{GetSymbol()}) { - return {GetSymbolType(*symbol)}; - } - if (const auto *intrinsic{std::get_if(&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 +bool TypeParamInquiry::operator==( + const TypeParamInquiry &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) } diff --git a/flang/lib/evaluate/variable.h b/flang/lib/evaluate/variable.h index a42f78c849b8..59da5b07081b 100644 --- a/flang/lib/evaluate/variable.h +++ b/flang/lib/evaluate/variable.h @@ -27,6 +27,7 @@ #include "type.h" #include "../common/idioms.h" #include "../common/template.h" +#include "../parser/char-block.h" #include #include #include @@ -52,6 +53,7 @@ struct BaseObject { explicit BaseObject(StaticDataObject::Pointer &&p) : u{std::move(p)} {} int Rank() const; Expr LEN() const; + bool operator==(const BaseObject &) const; std::ostream &AsFortran(std::ostream &) const; std::variant 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 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; + +// 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 struct TypeParamInquiry { + using Result = Type; + 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> lower() const; std::optional> upper() const; std::optional> 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 LEN() const; + bool operator==(const ArrayRef &) const; std::ostream &AsFortran(std::ostream &) const; - std::variant u; + SymbolOrComponent u; std::vector subscript; }; @@ -144,15 +180,28 @@ public: CoarrayRef(std::vector &&, std::vector> &&, std::vector> &&); + + const std::vector &base() const { return base_; } + const std::vector> &subscript() const { + return subscript_; + } + const std::vector> &cosubscript() const { + return cosubscript_; + } + // These integral expressions for STAT= and TEAM= must be variables // (i.e., Designator or pointer-valued FunctionRef). + std::optional> stat() const; CoarrayRef &set_stat(Expr &&); + std::optional> team() const; + bool teamIsTeamNumber() const { return teamIsTeamNumber_; } CoarrayRef &set_team(Expr &&, bool isTeamNumber = false); int Rank() const; const Symbol &GetFirstSymbol() const { return *base_.front(); } const Symbol &GetLastSymbol() const { return *base_.back(); } Expr 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> &&first, - std::optional> &&last) + Substring(DataRef &&parent, std::optional> &&lower, + std::optional> &&upper) : parent_{std::move(parent)} { - SetBounds(first, last); + SetBounds(lower, upper); } Substring(StaticDataObject::Pointer &&parent, std::optional> &&lower, @@ -202,9 +251,13 @@ public: Expr lower() const; Expr upper() const; int Rank() const; + template const A *GetParentIf() const { + return std::get_if(&parent_); + } BaseObject GetBaseObject() const; const Symbol *GetLastSymbol() const; Expr LEN() const; + bool operator==(const Substring &) const; std::ostream &AsFortran(std::ostream &) const; std::optional> 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 class Designator { +// a DataRef can, and possibly also a substring reference or a +// complex component (%RE/%IM) reference. +template class Designator { using DataRefs = decltype(DataRef::u); using MaybeSubstring = - std::conditional_t, std::variant<>>; - using MaybeComplexPart = std::conditional_t, std::variant<>>; using Variant = common::CombineVariants; public: - using Result = A; + using Result = T; static_assert(IsSpecificIntrinsicType || std::is_same_v>); EVALUATE_UNION_CLASS_BOILERPLATE(Designator) @@ -283,6 +337,7 @@ public: Expr 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 struct FunctionRef : public ProcedureRef { using Result = A; - static_assert(IsSpecificIntrinsicType || - std::is_same_v>); CLASS_BOILERPLATE(FunctionRef) FunctionRef(ProcedureRef &&pr) : ProcedureRef{std::move(pr)} {} FunctionRef(ProcedureDesignator &&p, ActualArguments &&a) : ProcedureRef{std::move(p), std::move(a)} {} - std::optional GetType() const { - if constexpr (std::is_same_v) { - if (const Symbol * symbol{proc_.GetSymbol()}) { - return GetSymbolType(*symbol); - } - } else { - return Result::GetType(); - } - return std::nullopt; - } + std::optional GetType() const { return proc_.GetType(); } std::optional> Fold(FoldingContext &); // for intrinsics }; diff --git a/flang/lib/semantics/expression.cc b/flang/lib/semantics/expression.cc index c9f0058f8e69..62f31733ef35 100644 --- a/flang/lib/semantics/expression.cc +++ b/flang/lib/semantics/expression.cc @@ -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 dyType{GetSymbolType(symbol)}) { + if (std::optional dyType{GetSymbolType(&symbol)}) { return TypedWrapper( std::move(*dyType), std::move(dataRef)); } @@ -764,7 +764,7 @@ MaybeExpr AnalyzeExpr( std::optional> last{ GetSubstringBound(context, std::get<1>(range.t))}; const Symbol &symbol{checked->GetLastSymbol()}; - if (std::optional dynamicType{GetSymbolType(symbol)}) { + if (std::optional dynamicType{GetSymbolType(&symbol)}) { if (dynamicType->category == TypeCategory::Character) { return WrapperHelper(dynamicType->kind, diff --git a/flang/test/evaluate/intrinsics.cc b/flang/test/evaluate/intrinsics.cc index 8a2eaa92f69d..933e261aa5ab 100644 --- a/flang/test/evaluate/intrinsics.cc +++ b/flang/test/evaluate/intrinsics.cc @@ -156,8 +156,8 @@ void TestIntrinsics() { .DoCall(); // bad intrinsic name TestCall{table, "abs"} .Push(Named("a", Const(Scalar{}))) - .DoCall(Int4::dynamicType); - TestCall{table, "abs"}.Push(Const(Scalar{})).DoCall(Int4::dynamicType); + .DoCall(Int4::GetType()); + TestCall{table, "abs"}.Push(Const(Scalar{})).DoCall(Int4::GetType()); TestCall{table, "abs"} .Push(Named("bad", Const(Scalar{}))) .DoCall(); // bad keyword @@ -174,21 +174,17 @@ void TestIntrinsics() { .Push(Named("a", Const(Scalar{}))) .Push(Const(Scalar{})) .DoCall(); - TestCall{table, "abs"}.Push(Const(Scalar{})).DoCall(Int1::dynamicType); - TestCall{table, "abs"}.Push(Const(Scalar{})).DoCall(Int4::dynamicType); - TestCall{table, "abs"}.Push(Const(Scalar{})).DoCall(Int8::dynamicType); - TestCall{table, "abs"} - .Push(Const(Scalar{})) - .DoCall(Real4::dynamicType); - TestCall{table, "abs"} - .Push(Const(Scalar{})) - .DoCall(Real8::dynamicType); + TestCall{table, "abs"}.Push(Const(Scalar{})).DoCall(Int1::GetType()); + TestCall{table, "abs"}.Push(Const(Scalar{})).DoCall(Int4::GetType()); + TestCall{table, "abs"}.Push(Const(Scalar{})).DoCall(Int8::GetType()); + TestCall{table, "abs"}.Push(Const(Scalar{})).DoCall(Real4::GetType()); + TestCall{table, "abs"}.Push(Const(Scalar{})).DoCall(Real8::GetType()); TestCall{table, "abs"} .Push(Const(Scalar{})) - .DoCall(Real4::dynamicType); + .DoCall(Real4::GetType()); TestCall{table, "abs"} .Push(Const(Scalar{})) - .DoCall(Real8::dynamicType); + .DoCall(Real8::GetType()); TestCall{table, "abs"}.Push(Const(Scalar{})).DoCall(); TestCall{table, "abs"}.Push(Const(Scalar{})).DoCall(); @@ -202,10 +198,10 @@ void TestIntrinsics() { amin0Call.Push(Const(Scalar{})); amin1Call.Push(Const(Scalar{})); } - 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