| //===-- include/flang/Evaluate/variable.h -----------------------*- C++ -*-===// |
| // |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| // See https://llvm.org/LICENSE.txt for license information. |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #ifndef FORTRAN_EVALUATE_VARIABLE_H_ |
| #define FORTRAN_EVALUATE_VARIABLE_H_ |
| |
| // Defines data structures to represent data access and function calls |
| // for use in expressions and assignment statements. Both copy and move |
| // semantics are supported. The representation adheres closely to the |
| // Fortran 2018 language standard (q.v.) and uses strong typing to ensure |
| // that only admissable combinations can be constructed. |
| |
| #include "call.h" |
| #include "common.h" |
| #include "formatting.h" |
| #include "static-data.h" |
| #include "type.h" |
| #include "flang/Common/idioms.h" |
| #include "flang/Common/reference.h" |
| #include "flang/Common/template.h" |
| #include "flang/Parser/char-block.h" |
| #include <optional> |
| #include <variant> |
| #include <vector> |
| |
| namespace llvm { |
| class raw_ostream; |
| } |
| |
| namespace Fortran::semantics { |
| class Symbol; |
| } |
| |
| namespace Fortran::evaluate { |
| |
| using semantics::Symbol; |
| using SymbolRef = common::Reference<const Symbol>; |
| using SymbolVector = std::vector<SymbolRef>; |
| |
| // Forward declarations |
| struct DataRef; |
| template <typename T> struct Variable; |
| |
| // Reference a base object in memory. This can be a Fortran symbol, |
| // static data (e.g., CHARACTER literal), or compiler-created temporary. |
| struct BaseObject { |
| EVALUATE_UNION_CLASS_BOILERPLATE(BaseObject) |
| int Rank() const; |
| std::optional<Expr<SubscriptInteger>> LEN() const; |
| llvm::raw_ostream &AsFortran(llvm::raw_ostream &) const; |
| const Symbol *symbol() const { |
| if (const auto *result{std::get_if<SymbolRef>(&u)}) { |
| return &result->get(); |
| } else { |
| return nullptr; |
| } |
| } |
| std::variant<SymbolRef, StaticDataObject::Pointer> u; |
| }; |
| |
| // R913 structure-component & C920: Defined to be a multi-part |
| // data-ref whose last part has no subscripts (or image-selector, although |
| // that isn't explicit in the document). Pointer and allocatable components |
| // are not explicitly indirected in this representation. |
| // 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) |
| Component(const DataRef &b, const Symbol &c) : base_{b}, symbol_{c} {} |
| Component(DataRef &&b, const Symbol &c) : base_{std::move(b)}, symbol_{c} {} |
| Component(common::CopyableIndirection<DataRef> &&b, const Symbol &c) |
| : base_{std::move(b)}, symbol_{c} {} |
| |
| const DataRef &base() const { return base_.value(); } |
| DataRef &base() { return base_.value(); } |
| int Rank() const; |
| const Symbol &GetFirstSymbol() const; |
| const Symbol &GetLastSymbol() const { return symbol_; } |
| std::optional<Expr<SubscriptInteger>> LEN() const; |
| bool operator==(const Component &) const; |
| llvm::raw_ostream &AsFortran(llvm::raw_ostream &) const; |
| |
| private: |
| common::CopyableIndirection<DataRef> base_; |
| SymbolRef symbol_; |
| }; |
| |
| // A NamedEntity is either a whole Symbol or a component in an instance |
| // of a derived type. It may be a descriptor. |
| // TODO: this is basically a symbol with an optional DataRef base; |
| // could be used to replace Component. |
| class NamedEntity { |
| public: |
| CLASS_BOILERPLATE(NamedEntity) |
| explicit NamedEntity(const Symbol &symbol) : u_{symbol} {} |
| explicit NamedEntity(Component &&c) : u_{std::move(c)} {} |
| |
| bool IsSymbol() const { return std::holds_alternative<SymbolRef>(u_); } |
| const Symbol &GetFirstSymbol() const; |
| const Symbol &GetLastSymbol() const; |
| const Component &GetComponent() const { return std::get<Component>(u_); } |
| Component &GetComponent() { return std::get<Component>(u_); } |
| const Component *UnwrapComponent() const; // null if just a Symbol |
| Component *UnwrapComponent(); |
| |
| int Rank() const; |
| std::optional<Expr<SubscriptInteger>> LEN() const; |
| bool operator==(const NamedEntity &) const; |
| llvm::raw_ostream &AsFortran(llvm::raw_ostream &) const; |
| |
| private: |
| std::variant<SymbolRef, Component> u_; |
| }; |
| |
| // 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; |
| // it becomes a DescriptorInquiry with Field::Len for assumed-length |
| // CHARACTER objects. |
| // 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. |
| class TypeParamInquiry { |
| public: |
| using Result = SubscriptInteger; |
| CLASS_BOILERPLATE(TypeParamInquiry) |
| TypeParamInquiry(NamedEntity &&x, const Symbol ¶m) |
| : base_{std::move(x)}, parameter_{param} {} |
| TypeParamInquiry(std::optional<NamedEntity> &&x, const Symbol ¶m) |
| : base_{std::move(x)}, parameter_{param} {} |
| |
| const std::optional<NamedEntity> &base() const { return base_; } |
| std::optional<NamedEntity> &base() { return base_; } |
| const Symbol ¶meter() const { return parameter_; } |
| |
| static constexpr int Rank() { return 0; } // always scalar |
| bool operator==(const TypeParamInquiry &) const; |
| llvm::raw_ostream &AsFortran(llvm::raw_ostream &) const; |
| |
| private: |
| std::optional<NamedEntity> base_; |
| SymbolRef parameter_; |
| }; |
| |
| // R921 subscript-triplet |
| class Triplet { |
| public: |
| Triplet(); |
| DEFAULT_CONSTRUCTORS_AND_ASSIGNMENTS(Triplet) |
| Triplet(std::optional<Expr<SubscriptInteger>> &&, |
| std::optional<Expr<SubscriptInteger>> &&, |
| std::optional<Expr<SubscriptInteger>> &&); |
| |
| std::optional<Expr<SubscriptInteger>> lower() const; |
| Triplet &set_lower(Expr<SubscriptInteger> &&); |
| std::optional<Expr<SubscriptInteger>> upper() const; |
| Triplet &set_upper(Expr<SubscriptInteger> &&); |
| Expr<SubscriptInteger> stride() const; // N.B. result is not optional<> |
| Triplet &set_stride(Expr<SubscriptInteger> &&); |
| |
| bool operator==(const Triplet &) const; |
| bool IsStrideOne() const; |
| llvm::raw_ostream &AsFortran(llvm::raw_ostream &) const; |
| |
| private: |
| std::optional<IndirectSubscriptIntegerExpr> lower_, upper_; |
| IndirectSubscriptIntegerExpr stride_; |
| }; |
| |
| // R919 subscript when rank 0, R923 vector-subscript when rank 1 |
| struct Subscript { |
| EVALUATE_UNION_CLASS_BOILERPLATE(Subscript) |
| explicit Subscript(Expr<SubscriptInteger> &&s) |
| : u{IndirectSubscriptIntegerExpr::Make(std::move(s))} {} |
| int Rank() const; |
| llvm::raw_ostream &AsFortran(llvm::raw_ostream &) const; |
| std::variant<IndirectSubscriptIntegerExpr, Triplet> u; |
| }; |
| |
| // R917 array-element, R918 array-section; however, the case of an |
| // array-section that is a complex-part-designator is represented here |
| // as a ComplexPart instead. C919 & C925 require that at most one set of |
| // subscripts have rank greater than 0, but that is not explicit in |
| // these types. |
| class ArrayRef { |
| public: |
| CLASS_BOILERPLATE(ArrayRef) |
| ArrayRef(const Symbol &symbol, std::vector<Subscript> &&ss) |
| : base_{symbol}, subscript_(std::move(ss)) {} |
| ArrayRef(Component &&c, std::vector<Subscript> &&ss) |
| : base_{std::move(c)}, subscript_(std::move(ss)) {} |
| ArrayRef(NamedEntity &&base, std::vector<Subscript> &&ss) |
| : base_{std::move(base)}, subscript_(std::move(ss)) {} |
| |
| NamedEntity &base() { return base_; } |
| const NamedEntity &base() const { return base_; } |
| std::vector<Subscript> &subscript() { return subscript_; } |
| const std::vector<Subscript> &subscript() const { return subscript_; } |
| |
| int size() const { return static_cast<int>(subscript_.size()); } |
| Subscript &at(int n) { return subscript_.at(n); } |
| const Subscript &at(int n) const { return subscript_.at(n); } |
| template <typename A> common::IfNoLvalue<Subscript &, A> emplace_back(A &&x) { |
| return subscript_.emplace_back(std::move(x)); |
| } |
| |
| int Rank() const; |
| const Symbol &GetFirstSymbol() const; |
| const Symbol &GetLastSymbol() const; |
| std::optional<Expr<SubscriptInteger>> LEN() const; |
| bool operator==(const ArrayRef &) const; |
| llvm::raw_ostream &AsFortran(llvm::raw_ostream &) const; |
| |
| private: |
| NamedEntity base_; |
| std::vector<Subscript> subscript_; |
| }; |
| |
| // R914 coindexed-named-object |
| // R924 image-selector, R926 image-selector-spec. |
| // C824 severely limits the usage of derived types with coarray ultimate |
| // components: they can't be pointers, allocatables, arrays, coarrays, or |
| // function results. They can be components of other derived types. |
| // Although the F'2018 Standard never prohibits multiple image-selectors |
| // per se in the same data-ref or designator, nor the presence of an |
| // image-selector after a part-ref with rank, the constraints on the |
| // derived types that would have be involved make it impossible to declare |
| // an object that could be referenced in these ways (esp. C748 & C825). |
| // C930 precludes having both TEAM= and TEAM_NUMBER=. |
| // TODO C931 prohibits the use of a coindexed object as a stat-variable. |
| class CoarrayRef { |
| public: |
| CLASS_BOILERPLATE(CoarrayRef) |
| CoarrayRef(SymbolVector &&, std::vector<Subscript> &&, |
| std::vector<Expr<SubscriptInteger>> &&); |
| |
| const SymbolVector &base() const { return base_; } |
| SymbolVector &base() { return base_; } |
| const std::vector<Subscript> &subscript() const { return subscript_; } |
| std::vector<Subscript> &subscript() { return subscript_; } |
| const std::vector<Expr<SubscriptInteger>> &cosubscript() const { |
| return cosubscript_; |
| } |
| std::vector<Expr<SubscriptInteger>> &cosubscript() { 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; |
| const Symbol &GetLastSymbol() const; |
| NamedEntity GetBase() const; |
| std::optional<Expr<SubscriptInteger>> LEN() const; |
| bool operator==(const CoarrayRef &) const; |
| llvm::raw_ostream &AsFortran(llvm::raw_ostream &) const; |
| |
| private: |
| SymbolVector base_; |
| std::vector<Subscript> subscript_; |
| std::vector<Expr<SubscriptInteger>> cosubscript_; |
| std::optional<common::CopyableIndirection<Expr<SomeInteger>>> stat_, team_; |
| bool teamIsTeamNumber_{false}; // false: TEAM=, true: TEAM_NUMBER= |
| }; |
| |
| // R911 data-ref is defined syntactically as a series of part-refs, which |
| // would be far too expressive if the constraints were ignored. Here, the |
| // possible outcomes are spelled out. Note that a data-ref cannot include |
| // a terminal substring range or complex component designator; use |
| // R901 designator for that. |
| struct DataRef { |
| EVALUATE_UNION_CLASS_BOILERPLATE(DataRef) |
| int Rank() const; |
| const Symbol &GetFirstSymbol() const; |
| const Symbol &GetLastSymbol() const; |
| std::optional<Expr<SubscriptInteger>> LEN() const; |
| llvm::raw_ostream &AsFortran(llvm::raw_ostream &) const; |
| |
| std::variant<SymbolRef, Component, ArrayRef, CoarrayRef> u; |
| }; |
| |
| // R908 substring, R909 parent-string, R910 substring-range. |
| // The base object of a substring can be a literal. |
| // In the F2018 standard, substrings of array sections are parsed as |
| // variants of sections instead. |
| class Substring { |
| using Parent = std::variant<DataRef, StaticDataObject::Pointer>; |
| |
| public: |
| CLASS_BOILERPLATE(Substring) |
| Substring(DataRef &&parent, std::optional<Expr<SubscriptInteger>> &&lower, |
| std::optional<Expr<SubscriptInteger>> &&upper) |
| : parent_{std::move(parent)} { |
| SetBounds(lower, upper); |
| } |
| Substring(StaticDataObject::Pointer &&parent, |
| std::optional<Expr<SubscriptInteger>> &&lower, |
| std::optional<Expr<SubscriptInteger>> &&upper) |
| : parent_{std::move(parent)} { |
| SetBounds(lower, upper); |
| } |
| |
| Expr<SubscriptInteger> lower() const; |
| Substring &set_lower(Expr<SubscriptInteger> &&); |
| std::optional<Expr<SubscriptInteger>> upper() const; |
| Substring &set_upper(Expr<SubscriptInteger> &&); |
| const Parent &parent() const { return parent_; } |
| Parent &parent() { return parent_; } |
| |
| int Rank() const; |
| template <typename A> const A *GetParentIf() const { |
| return std::get_if<A>(&parent_); |
| } |
| BaseObject GetBaseObject() const; |
| const Symbol *GetLastSymbol() const; |
| std::optional<Expr<SubscriptInteger>> LEN() const; |
| bool operator==(const Substring &) const; |
| llvm::raw_ostream &AsFortran(llvm::raw_ostream &) const; |
| |
| std::optional<Expr<SomeCharacter>> Fold(FoldingContext &); |
| |
| private: |
| void SetBounds(std::optional<Expr<SubscriptInteger>> &, |
| std::optional<Expr<SubscriptInteger>> &); |
| Parent parent_; |
| std::optional<IndirectSubscriptIntegerExpr> lower_, upper_; |
| }; |
| |
| // R915 complex-part-designator |
| // In the F2018 standard, complex parts of array sections are parsed as |
| // variants of sections instead. |
| class ComplexPart { |
| public: |
| ENUM_CLASS(Part, RE, IM) |
| CLASS_BOILERPLATE(ComplexPart) |
| ComplexPart(DataRef &&z, Part p) : complex_{std::move(z)}, part_{p} {} |
| const DataRef &complex() const { return complex_; } |
| Part part() const { return part_; } |
| int Rank() const; |
| const Symbol &GetFirstSymbol() const { return complex_.GetFirstSymbol(); } |
| const Symbol &GetLastSymbol() const { return complex_.GetLastSymbol(); } |
| bool operator==(const ComplexPart &) const; |
| llvm::raw_ostream &AsFortran(llvm::raw_ostream &) const; |
| |
| private: |
| DataRef complex_; |
| Part part_; |
| }; |
| |
| // 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 also a substring reference or a |
| // complex component (%RE/%IM) reference. |
| template <typename T> class Designator { |
| using DataRefs = std::decay_t<decltype(DataRef::u)>; |
| using MaybeSubstring = |
| std::conditional_t<T::category == TypeCategory::Character, |
| std::variant<Substring>, std::variant<>>; |
| using MaybeComplexPart = std::conditional_t<T::category == TypeCategory::Real, |
| std::variant<ComplexPart>, std::variant<>>; |
| using Variant = |
| common::CombineVariants<DataRefs, MaybeSubstring, MaybeComplexPart>; |
| |
| public: |
| using Result = T; |
| static_assert( |
| IsSpecificIntrinsicType<Result> || std::is_same_v<Result, SomeDerived>); |
| EVALUATE_UNION_CLASS_BOILERPLATE(Designator) |
| Designator(const DataRef &that) : u{common::CopyVariant<Variant>(that.u)} {} |
| Designator(DataRef &&that) |
| : u{common::MoveVariant<Variant>(std::move(that.u))} {} |
| |
| std::optional<DynamicType> GetType() const; |
| int Rank() const; |
| BaseObject GetBaseObject() const; |
| const Symbol *GetLastSymbol() const; |
| std::optional<Expr<SubscriptInteger>> LEN() const; |
| llvm::raw_ostream &AsFortran(llvm::raw_ostream &o) const; |
| |
| Variant u; |
| }; |
| |
| FOR_EACH_CHARACTER_KIND(extern template class Designator, ) |
| |
| class DescriptorInquiry { |
| public: |
| using Result = SubscriptInteger; |
| ENUM_CLASS(Field, LowerBound, Extent, Stride, Rank, Len) |
| |
| CLASS_BOILERPLATE(DescriptorInquiry) |
| DescriptorInquiry(const NamedEntity &, Field, int = 0); |
| DescriptorInquiry(NamedEntity &&, Field, int = 0); |
| |
| NamedEntity &base() { return base_; } |
| const NamedEntity &base() const { return base_; } |
| Field field() const { return field_; } |
| int dimension() const { return dimension_; } |
| |
| static constexpr int Rank() { return 0; } // always scalar |
| bool operator==(const DescriptorInquiry &) const; |
| llvm::raw_ostream &AsFortran(llvm::raw_ostream &) const; |
| |
| private: |
| NamedEntity base_; |
| Field field_; |
| int dimension_{0}; // zero-based |
| }; |
| |
| #define INSTANTIATE_VARIABLE_TEMPLATES \ |
| FOR_EACH_SPECIFIC_TYPE(template class Designator, ) |
| } // namespace Fortran::evaluate |
| #endif // FORTRAN_EVALUATE_VARIABLE_H_ |