1 //===-- include/flang/Evaluate/characteristics.h ----------------*- C++ -*-===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 9 // Defines data structures to represent "characteristics" of Fortran 10 // procedures and other entities as they are specified in section 15.3 11 // of Fortran 2018. 12 13 #ifndef FORTRAN_EVALUATE_CHARACTERISTICS_H_ 14 #define FORTRAN_EVALUATE_CHARACTERISTICS_H_ 15 16 #include "common.h" 17 #include "expression.h" 18 #include "shape.h" 19 #include "type.h" 20 #include "flang/Common/Fortran.h" 21 #include "flang/Common/enum-set.h" 22 #include "flang/Common/idioms.h" 23 #include "flang/Common/indirection.h" 24 #include "flang/Parser/char-block.h" 25 #include "flang/Semantics/symbol.h" 26 #include <optional> 27 #include <string> 28 #include <variant> 29 #include <vector> 30 31 namespace llvm { 32 class raw_ostream; 33 } 34 35 namespace Fortran::evaluate::characteristics { 36 struct Procedure; 37 } 38 extern template class Fortran::common::Indirection< 39 Fortran::evaluate::characteristics::Procedure, true>; 40 41 namespace Fortran::evaluate::characteristics { 42 43 using common::CopyableIndirection; 44 45 // Are these procedures distinguishable for a generic name or FINAL? 46 bool Distinguishable(const Procedure &, const Procedure &); 47 // Are these procedures distinguishable for a generic operator or assignment? 48 bool DistinguishableOpOrAssign(const Procedure &, const Procedure &); 49 50 // Shapes of function results and dummy arguments have to have 51 // the same rank, the same deferred dimensions, and the same 52 // values for explicit dimensions when constant. 53 bool ShapesAreCompatible(const Shape &, const Shape &); 54 55 class TypeAndShape { 56 public: 57 ENUM_CLASS( 58 Attr, AssumedRank, AssumedShape, AssumedSize, DeferredShape, Coarray) 59 using Attrs = common::EnumSet<Attr, Attr_enumSize>; 60 TypeAndShape(DynamicType t)61 explicit TypeAndShape(DynamicType t) : type_{t} { AcquireLEN(); } TypeAndShape(DynamicType t,int rank)62 TypeAndShape(DynamicType t, int rank) : type_{t}, shape_(rank) { 63 AcquireLEN(); 64 } TypeAndShape(DynamicType t,Shape && s)65 TypeAndShape(DynamicType t, Shape &&s) : type_{t}, shape_{std::move(s)} { 66 AcquireLEN(); 67 } TypeAndShape(DynamicType t,std::optional<Shape> && s)68 TypeAndShape(DynamicType t, std::optional<Shape> &&s) : type_{t} { 69 if (s) { 70 shape_ = std::move(*s); 71 } 72 AcquireLEN(); 73 } 74 DEFAULT_CONSTRUCTORS_AND_ASSIGNMENTS(TypeAndShape) 75 76 bool operator==(const TypeAndShape &) const; 77 bool operator!=(const TypeAndShape &that) const { return !(*this == that); } 78 79 static std::optional<TypeAndShape> Characterize( 80 const semantics::Symbol &, FoldingContext &); 81 static std::optional<TypeAndShape> Characterize( 82 const semantics::ObjectEntityDetails &, FoldingContext &); 83 static std::optional<TypeAndShape> Characterize( 84 const semantics::ProcInterface &); 85 static std::optional<TypeAndShape> Characterize( 86 const semantics::DeclTypeSpec &); 87 static std::optional<TypeAndShape> Characterize( 88 const ActualArgument &, FoldingContext &); 89 90 template <typename A> Characterize(const A & x,FoldingContext & context)91 static std::optional<TypeAndShape> Characterize( 92 const A &x, FoldingContext &context) { 93 if (const auto *symbol{UnwrapWholeSymbolDataRef(x)}) { 94 if (auto result{Characterize(*symbol, context)}) { 95 return result; 96 } 97 } 98 if (auto type{x.GetType()}) { 99 if (auto shape{GetShape(context, x)}) { 100 TypeAndShape result{*type, std::move(*shape)}; 101 if (type->category() == TypeCategory::Character) { 102 if (const auto *chExpr{UnwrapExpr<Expr<SomeCharacter>>(x)}) { 103 if (auto length{chExpr->LEN()}) { 104 result.set_LEN(Fold(context, std::move(*length))); 105 } 106 } 107 } 108 return result; 109 } 110 } 111 return std::nullopt; 112 } 113 template <typename A> Characterize(const std::optional<A> & x,FoldingContext & context)114 static std::optional<TypeAndShape> Characterize( 115 const std::optional<A> &x, FoldingContext &context) { 116 if (x) { 117 return Characterize(*x, context); 118 } else { 119 return std::nullopt; 120 } 121 } 122 template <typename A> Characterize(const A * x,FoldingContext & context)123 static std::optional<TypeAndShape> Characterize( 124 const A *x, FoldingContext &context) { 125 if (x) { 126 return Characterize(*x, context); 127 } else { 128 return std::nullopt; 129 } 130 } 131 type()132 DynamicType type() const { return type_; } set_type(DynamicType t)133 TypeAndShape &set_type(DynamicType t) { 134 type_ = t; 135 return *this; 136 } LEN()137 const std::optional<Expr<SubscriptInteger>> &LEN() const { return LEN_; } set_LEN(Expr<SubscriptInteger> && len)138 TypeAndShape &set_LEN(Expr<SubscriptInteger> &&len) { 139 LEN_ = std::move(len); 140 return *this; 141 } shape()142 const Shape &shape() const { return shape_; } attrs()143 const Attrs &attrs() const { return attrs_; } corank()144 int corank() const { return corank_; } 145 Rank()146 int Rank() const { return GetRank(shape_); } 147 bool IsCompatibleWith(parser::ContextualMessages &, const TypeAndShape &that, 148 const char *thisIs = "POINTER", const char *thatIs = "TARGET", 149 bool isElemental = false) const; 150 std::optional<Expr<SubscriptInteger>> MeasureSizeInBytes( 151 FoldingContext * = nullptr) const; 152 153 llvm::raw_ostream &Dump(llvm::raw_ostream &) const; 154 155 private: 156 static std::optional<TypeAndShape> Characterize( 157 const semantics::AssocEntityDetails &, FoldingContext &); 158 static std::optional<TypeAndShape> Characterize( 159 const semantics::ProcEntityDetails &); 160 void AcquireShape(const semantics::ObjectEntityDetails &, FoldingContext &); 161 void AcquireLEN(); 162 163 protected: 164 DynamicType type_; 165 std::optional<Expr<SubscriptInteger>> LEN_; 166 Shape shape_; 167 Attrs attrs_; 168 int corank_{0}; 169 }; 170 171 // 15.3.2.2 172 struct DummyDataObject { 173 ENUM_CLASS(Attr, Optional, Allocatable, Asynchronous, Contiguous, Value, 174 Volatile, Pointer, Target) 175 using Attrs = common::EnumSet<Attr, Attr_enumSize>; DEFAULT_CONSTRUCTORS_AND_ASSIGNMENTSDummyDataObject176 DEFAULT_CONSTRUCTORS_AND_ASSIGNMENTS(DummyDataObject) 177 explicit DummyDataObject(const TypeAndShape &t) : type{t} {} DummyDataObjectDummyDataObject178 explicit DummyDataObject(TypeAndShape &&t) : type{std::move(t)} {} DummyDataObjectDummyDataObject179 explicit DummyDataObject(DynamicType t) : type{t} {} 180 bool operator==(const DummyDataObject &) const; 181 bool operator!=(const DummyDataObject &that) const { 182 return !(*this == that); 183 } 184 static std::optional<DummyDataObject> Characterize( 185 const semantics::Symbol &, FoldingContext &); 186 bool CanBePassedViaImplicitInterface() const; 187 llvm::raw_ostream &Dump(llvm::raw_ostream &) const; 188 TypeAndShape type; 189 std::vector<Expr<SubscriptInteger>> coshape; 190 common::Intent intent{common::Intent::Default}; 191 Attrs attrs; 192 }; 193 194 // 15.3.2.3 195 struct DummyProcedure { 196 ENUM_CLASS(Attr, Pointer, Optional) 197 using Attrs = common::EnumSet<Attr, Attr_enumSize>; 198 DECLARE_CONSTRUCTORS_AND_ASSIGNMENTS(DummyProcedure) 199 explicit DummyProcedure(Procedure &&); 200 bool operator==(const DummyProcedure &) const; 201 bool operator!=(const DummyProcedure &that) const { return !(*this == that); } 202 static std::optional<DummyProcedure> Characterize( 203 const semantics::Symbol &, FoldingContext &context); 204 llvm::raw_ostream &Dump(llvm::raw_ostream &) const; 205 CopyableIndirection<Procedure> procedure; 206 common::Intent intent{common::Intent::Default}; 207 Attrs attrs; 208 }; 209 210 // 15.3.2.4 211 struct AlternateReturn { 212 bool operator==(const AlternateReturn &) const { return true; } 213 bool operator!=(const AlternateReturn &) const { return false; } 214 llvm::raw_ostream &Dump(llvm::raw_ostream &) const; 215 }; 216 217 // 15.3.2.1 218 struct DummyArgument { 219 DECLARE_CONSTRUCTORS_AND_ASSIGNMENTS(DummyArgument) DummyArgumentDummyArgument220 DummyArgument(std::string &&name, DummyDataObject &&x) 221 : name{std::move(name)}, u{std::move(x)} {} DummyArgumentDummyArgument222 DummyArgument(std::string &&name, DummyProcedure &&x) 223 : name{std::move(name)}, u{std::move(x)} {} DummyArgumentDummyArgument224 explicit DummyArgument(AlternateReturn &&x) : u{std::move(x)} {} 225 ~DummyArgument(); 226 bool operator==(const DummyArgument &) const; 227 bool operator!=(const DummyArgument &that) const { return !(*this == that); } 228 static std::optional<DummyArgument> Characterize( 229 const semantics::Symbol &, FoldingContext &); 230 static std::optional<DummyArgument> FromActual( 231 std::string &&, const Expr<SomeType> &, FoldingContext &); 232 bool IsOptional() const; 233 void SetOptional(bool = true); 234 common::Intent GetIntent() const; 235 void SetIntent(common::Intent); 236 bool CanBePassedViaImplicitInterface() const; 237 bool IsTypelessIntrinsicDummy() const; 238 llvm::raw_ostream &Dump(llvm::raw_ostream &) const; 239 // name and pass are not characteristics and so does not participate in 240 // operator== but are needed to determine if procedures are distinguishable 241 std::string name; 242 bool pass{false}; // is this the PASS argument of its procedure 243 std::variant<DummyDataObject, DummyProcedure, AlternateReturn> u; 244 }; 245 246 using DummyArguments = std::vector<DummyArgument>; 247 248 // 15.3.3 249 struct FunctionResult { 250 ENUM_CLASS(Attr, Allocatable, Pointer, Contiguous) 251 using Attrs = common::EnumSet<Attr, Attr_enumSize>; 252 DECLARE_CONSTRUCTORS_AND_ASSIGNMENTS(FunctionResult) 253 explicit FunctionResult(DynamicType); 254 explicit FunctionResult(TypeAndShape &&); 255 explicit FunctionResult(Procedure &&); 256 ~FunctionResult(); 257 bool operator==(const FunctionResult &) const; 258 bool operator!=(const FunctionResult &that) const { return !(*this == that); } 259 static std::optional<FunctionResult> Characterize( 260 const Symbol &, FoldingContext &); 261 262 bool IsAssumedLengthCharacter() const; 263 IsProcedurePointerFunctionResult264 const Procedure *IsProcedurePointer() const { 265 if (const auto *pp{std::get_if<CopyableIndirection<Procedure>>(&u)}) { 266 return &pp->value(); 267 } else { 268 return nullptr; 269 } 270 } GetTypeAndShapeFunctionResult271 const TypeAndShape *GetTypeAndShape() const { 272 return std::get_if<TypeAndShape>(&u); 273 } SetTypeFunctionResult274 void SetType(DynamicType t) { std::get<TypeAndShape>(u).set_type(t); } 275 bool CanBeReturnedViaImplicitInterface() const; 276 277 llvm::raw_ostream &Dump(llvm::raw_ostream &) const; 278 279 Attrs attrs; 280 std::variant<TypeAndShape, CopyableIndirection<Procedure>> u; 281 }; 282 283 // 15.3.1 284 struct Procedure { 285 ENUM_CLASS( 286 Attr, Pure, Elemental, BindC, ImplicitInterface, NullPointer, Subroutine) 287 using Attrs = common::EnumSet<Attr, Attr_enumSize>; 288 Procedure(FunctionResult &&, DummyArguments &&, Attrs); 289 Procedure(DummyArguments &&, Attrs); // for subroutines and NULL() 290 DECLARE_CONSTRUCTORS_AND_ASSIGNMENTS(Procedure) 291 ~Procedure(); 292 bool operator==(const Procedure &) const; 293 bool operator!=(const Procedure &that) const { return !(*this == that); } 294 295 // Characterizes the procedure represented by a symbol, which may be an 296 // "unrestricted specific intrinsic function". 297 static std::optional<Procedure> Characterize( 298 const semantics::Symbol &, FoldingContext &); 299 static std::optional<Procedure> Characterize( 300 const ProcedureDesignator &, FoldingContext &); 301 static std::optional<Procedure> Characterize( 302 const ProcedureRef &, FoldingContext &); 303 304 // At most one of these will return true. 305 // For "EXTERNAL P" with no type for or calls to P, both will be false. IsFunctionProcedure306 bool IsFunction() const { return functionResult.has_value(); } IsSubroutineProcedure307 bool IsSubroutine() const { return attrs.test(Attr::Subroutine); } 308 IsPureProcedure309 bool IsPure() const { return attrs.test(Attr::Pure); } IsElementalProcedure310 bool IsElemental() const { return attrs.test(Attr::Elemental); } IsBindCProcedure311 bool IsBindC() const { return attrs.test(Attr::BindC); } HasExplicitInterfaceProcedure312 bool HasExplicitInterface() const { 313 return !attrs.test(Attr::ImplicitInterface); 314 } 315 int FindPassIndex(std::optional<parser::CharBlock>) const; 316 bool CanBeCalledViaImplicitInterface() const; 317 bool CanOverride(const Procedure &, std::optional<int> passIndex) const; 318 llvm::raw_ostream &Dump(llvm::raw_ostream &) const; 319 320 std::optional<FunctionResult> functionResult; 321 DummyArguments dummyArguments; 322 Attrs attrs; 323 324 private: ProcedureProcedure325 Procedure() {} 326 }; 327 328 } // namespace Fortran::evaluate::characteristics 329 #endif // FORTRAN_EVALUATE_CHARACTERISTICS_H_ 330