1 //===-- include/flang/Evaluate/tools.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 #ifndef FORTRAN_EVALUATE_TOOLS_H_ 10 #define FORTRAN_EVALUATE_TOOLS_H_ 11 12 #include "traverse.h" 13 #include "flang/Common/idioms.h" 14 #include "flang/Common/template.h" 15 #include "flang/Common/unwrap.h" 16 #include "flang/Evaluate/constant.h" 17 #include "flang/Evaluate/expression.h" 18 #include "flang/Evaluate/shape.h" 19 #include "flang/Evaluate/type.h" 20 #include "flang/Parser/message.h" 21 #include "flang/Semantics/attr.h" 22 #include "flang/Semantics/scope.h" 23 #include "flang/Semantics/symbol.h" 24 #include <array> 25 #include <optional> 26 #include <set> 27 #include <type_traits> 28 #include <utility> 29 30 namespace Fortran::evaluate { 31 32 // Some expression predicates and extractors. 33 34 // Predicate: true when an expression is a variable reference, not an 35 // operation. Be advised: a call to a function that returns an object 36 // pointer is a "variable" in Fortran (it can be the left-hand side of 37 // an assignment). 38 struct IsVariableHelper 39 : public AnyTraverse<IsVariableHelper, std::optional<bool>> { 40 using Result = std::optional<bool>; // effectively tri-state 41 using Base = AnyTraverse<IsVariableHelper, Result>; 42 IsVariableHelper() : Base{*this} {} 43 using Base::operator(); 44 Result operator()(const StaticDataObject &) const { return false; } 45 Result operator()(const Symbol &) const; 46 Result operator()(const Component &) const; 47 Result operator()(const ArrayRef &) const; 48 Result operator()(const Substring &) const; 49 Result operator()(const CoarrayRef &) const { return true; } 50 Result operator()(const ComplexPart &) const { return true; } 51 Result operator()(const ProcedureDesignator &) const; 52 template <typename T> Result operator()(const Expr<T> &x) const { 53 if constexpr (common::HasMember<T, AllIntrinsicTypes> || 54 std::is_same_v<T, SomeDerived>) { 55 // Expression with a specific type 56 if (std::holds_alternative<Designator<T>>(x.u) || 57 std::holds_alternative<FunctionRef<T>>(x.u)) { 58 if (auto known{(*this)(x.u)}) { 59 return known; 60 } 61 } 62 return false; 63 } else if constexpr (std::is_same_v<T, SomeType>) { 64 if (std::holds_alternative<ProcedureDesignator>(x.u) || 65 std::holds_alternative<ProcedureRef>(x.u)) { 66 return false; // procedure pointer 67 } else { 68 return (*this)(x.u); 69 } 70 } else { 71 return (*this)(x.u); 72 } 73 } 74 }; 75 76 template <typename A> bool IsVariable(const A &x) { 77 if (auto known{IsVariableHelper{}(x)}) { 78 return *known; 79 } else { 80 return false; 81 } 82 } 83 84 // Predicate: true when an expression is assumed-rank 85 bool IsAssumedRank(const Symbol &); 86 bool IsAssumedRank(const ActualArgument &); 87 template <typename A> bool IsAssumedRank(const A &) { return false; } 88 template <typename A> bool IsAssumedRank(const Designator<A> &designator) { 89 if (const auto *symbol{std::get_if<SymbolRef>(&designator.u)}) { 90 return IsAssumedRank(symbol->get()); 91 } else { 92 return false; 93 } 94 } 95 template <typename T> bool IsAssumedRank(const Expr<T> &expr) { 96 return common::visit([](const auto &x) { return IsAssumedRank(x); }, expr.u); 97 } 98 template <typename A> bool IsAssumedRank(const std::optional<A> &x) { 99 return x && IsAssumedRank(*x); 100 } 101 template <typename A> bool IsAssumedRank(const A *x) { 102 return x && IsAssumedRank(*x); 103 } 104 105 // Finds the corank of an entity, possibly packaged in various ways. 106 // Unlike rank, only data references have corank > 0. 107 int GetCorank(const ActualArgument &); 108 static inline int GetCorank(const Symbol &symbol) { return symbol.Corank(); } 109 template <typename A> int GetCorank(const A &) { return 0; } 110 template <typename T> int GetCorank(const Designator<T> &designator) { 111 return designator.Corank(); 112 } 113 template <typename T> int GetCorank(const Expr<T> &expr) { 114 return common::visit([](const auto &x) { return GetCorank(x); }, expr.u); 115 } 116 template <typename A> int GetCorank(const std::optional<A> &x) { 117 return x ? GetCorank(*x) : 0; 118 } 119 template <typename A> int GetCorank(const A *x) { 120 return x ? GetCorank(*x) : 0; 121 } 122 123 // Predicate: true when an expression is a coarray (corank > 0) 124 template <typename A> bool IsCoarray(const A &x) { return GetCorank(x) > 0; } 125 126 // Generalizing packagers: these take operations and expressions of more 127 // specific types and wrap them in Expr<> containers of more abstract types. 128 129 template <typename A> common::IfNoLvalue<Expr<ResultType<A>>, A> AsExpr(A &&x) { 130 return Expr<ResultType<A>>{std::move(x)}; 131 } 132 133 template <typename T> Expr<T> AsExpr(Expr<T> &&x) { 134 static_assert(IsSpecificIntrinsicType<T>); 135 return std::move(x); 136 } 137 138 template <TypeCategory CATEGORY> 139 Expr<SomeKind<CATEGORY>> AsCategoryExpr(Expr<SomeKind<CATEGORY>> &&x) { 140 return std::move(x); 141 } 142 143 template <typename A> 144 common::IfNoLvalue<Expr<SomeType>, A> AsGenericExpr(A &&x) { 145 if constexpr (common::HasMember<A, TypelessExpression>) { 146 return Expr<SomeType>{std::move(x)}; 147 } else { 148 return Expr<SomeType>{AsCategoryExpr(std::move(x))}; 149 } 150 } 151 152 inline Expr<SomeType> AsGenericExpr(Expr<SomeType> &&x) { return std::move(x); } 153 154 // These overloads wrap DataRefs and simple whole variables up into 155 // generic expressions if they have a known type. 156 std::optional<Expr<SomeType>> AsGenericExpr(DataRef &&); 157 std::optional<Expr<SomeType>> AsGenericExpr(const Symbol &); 158 159 // Propagate std::optional from input to output. 160 template <typename A> 161 std::optional<Expr<SomeType>> AsGenericExpr(std::optional<A> &&x) { 162 if (x) { 163 return AsGenericExpr(std::move(*x)); 164 } else { 165 return std::nullopt; 166 } 167 } 168 169 template <typename A> 170 common::IfNoLvalue<Expr<SomeKind<ResultType<A>::category>>, A> AsCategoryExpr( 171 A &&x) { 172 return Expr<SomeKind<ResultType<A>::category>>{AsExpr(std::move(x))}; 173 } 174 175 Expr<SomeType> Parenthesize(Expr<SomeType> &&); 176 177 template <typename A> constexpr bool IsNumericCategoryExpr() { 178 if constexpr (common::HasMember<A, TypelessExpression>) { 179 return false; 180 } else { 181 return common::HasMember<ResultType<A>, NumericCategoryTypes>; 182 } 183 } 184 185 // Specializing extractor. If an Expr wraps some type of object, perhaps 186 // in several layers, return a pointer to it; otherwise null. Also works 187 // with expressions contained in ActualArgument. 188 template <typename A, typename B> 189 auto UnwrapExpr(B &x) -> common::Constify<A, B> * { 190 using Ty = std::decay_t<B>; 191 if constexpr (std::is_same_v<A, Ty>) { 192 return &x; 193 } else if constexpr (std::is_same_v<Ty, ActualArgument>) { 194 if (auto *expr{x.UnwrapExpr()}) { 195 return UnwrapExpr<A>(*expr); 196 } 197 } else if constexpr (std::is_same_v<Ty, Expr<SomeType>>) { 198 return common::visit([](auto &x) { return UnwrapExpr<A>(x); }, x.u); 199 } else if constexpr (!common::HasMember<A, TypelessExpression>) { 200 if constexpr (std::is_same_v<Ty, Expr<ResultType<A>>> || 201 std::is_same_v<Ty, Expr<SomeKind<ResultType<A>::category>>>) { 202 return common::visit([](auto &x) { return UnwrapExpr<A>(x); }, x.u); 203 } 204 } 205 return nullptr; 206 } 207 208 template <typename A, typename B> 209 const A *UnwrapExpr(const std::optional<B> &x) { 210 if (x) { 211 return UnwrapExpr<A>(*x); 212 } else { 213 return nullptr; 214 } 215 } 216 217 template <typename A, typename B> A *UnwrapExpr(std::optional<B> &x) { 218 if (x) { 219 return UnwrapExpr<A>(*x); 220 } else { 221 return nullptr; 222 } 223 } 224 225 template <typename A, typename B> const A *UnwrapExpr(const B *x) { 226 if (x) { 227 return UnwrapExpr<A>(*x); 228 } else { 229 return nullptr; 230 } 231 } 232 233 template <typename A, typename B> A *UnwrapExpr(B *x) { 234 if (x) { 235 return UnwrapExpr<A>(*x); 236 } else { 237 return nullptr; 238 } 239 } 240 241 // A variant of UnwrapExpr above that also skips through (parentheses) 242 // and conversions of kinds within a category. Useful for extracting LEN 243 // type parameter inquiries, at least. 244 template <typename A, typename B> 245 auto UnwrapConvertedExpr(B &x) -> common::Constify<A, B> * { 246 using Ty = std::decay_t<B>; 247 if constexpr (std::is_same_v<A, Ty>) { 248 return &x; 249 } else if constexpr (std::is_same_v<Ty, ActualArgument>) { 250 if (auto *expr{x.UnwrapExpr()}) { 251 return UnwrapConvertedExpr<A>(*expr); 252 } 253 } else if constexpr (std::is_same_v<Ty, Expr<SomeType>>) { 254 return common::visit( 255 [](auto &x) { return UnwrapConvertedExpr<A>(x); }, x.u); 256 } else { 257 using DesiredResult = ResultType<A>; 258 if constexpr (std::is_same_v<Ty, Expr<DesiredResult>> || 259 std::is_same_v<Ty, Expr<SomeKind<DesiredResult::category>>>) { 260 return common::visit( 261 [](auto &x) { return UnwrapConvertedExpr<A>(x); }, x.u); 262 } else { 263 using ThisResult = ResultType<B>; 264 if constexpr (std::is_same_v<Ty, Expr<ThisResult>>) { 265 return common::visit( 266 [](auto &x) { return UnwrapConvertedExpr<A>(x); }, x.u); 267 } else if constexpr (std::is_same_v<Ty, Parentheses<ThisResult>> || 268 std::is_same_v<Ty, Convert<ThisResult, DesiredResult::category>>) { 269 return common::visit( 270 [](auto &x) { return UnwrapConvertedExpr<A>(x); }, x.left().u); 271 } 272 } 273 } 274 return nullptr; 275 } 276 277 // UnwrapProcedureRef() returns a pointer to a ProcedureRef when the whole 278 // expression is a reference to a procedure. 279 template <typename A> inline const ProcedureRef *UnwrapProcedureRef(const A &) { 280 return nullptr; 281 } 282 283 inline const ProcedureRef *UnwrapProcedureRef(const ProcedureRef &proc) { 284 // Reference to subroutine or to a function that returns 285 // an object pointer or procedure pointer 286 return &proc; 287 } 288 289 template <typename T> 290 inline const ProcedureRef *UnwrapProcedureRef(const FunctionRef<T> &func) { 291 return &func; // reference to a function returning a non-pointer 292 } 293 294 template <typename T> 295 inline const ProcedureRef *UnwrapProcedureRef(const Expr<T> &expr) { 296 return common::visit( 297 [](const auto &x) { return UnwrapProcedureRef(x); }, expr.u); 298 } 299 300 // When an expression is a "bare" LEN= derived type parameter inquiry, 301 // possibly wrapped in integer kind conversions &/or parentheses, return 302 // a pointer to the Symbol with TypeParamDetails. 303 template <typename A> const Symbol *ExtractBareLenParameter(const A &expr) { 304 if (const auto *typeParam{ 305 UnwrapConvertedExpr<evaluate::TypeParamInquiry>(expr)}) { 306 if (!typeParam->base()) { 307 const Symbol &symbol{typeParam->parameter()}; 308 if (const auto *tpd{symbol.detailsIf<semantics::TypeParamDetails>()}) { 309 if (tpd->attr() == common::TypeParamAttr::Len) { 310 return &symbol; 311 } 312 } 313 } 314 } 315 return nullptr; 316 } 317 318 // If an expression simply wraps a DataRef, extract and return it. 319 // The Boolean arguments control the handling of Substring and ComplexPart 320 // references: when true (not default), it extracts the base DataRef 321 // of a substring or complex part. 322 template <typename A> 323 common::IfNoLvalue<std::optional<DataRef>, A> ExtractDataRef( 324 const A &, bool intoSubstring, bool intoComplexPart) { 325 return std::nullopt; // default base case 326 } 327 template <typename T> 328 std::optional<DataRef> ExtractDataRef(const Designator<T> &d, 329 bool intoSubstring = false, bool intoComplexPart = false) { 330 return common::visit( 331 [=](const auto &x) -> std::optional<DataRef> { 332 if constexpr (common::HasMember<decltype(x), decltype(DataRef::u)>) { 333 return DataRef{x}; 334 } 335 if constexpr (std::is_same_v<std::decay_t<decltype(x)>, Substring>) { 336 if (intoSubstring) { 337 return ExtractSubstringBase(x); 338 } 339 } 340 if constexpr (std::is_same_v<std::decay_t<decltype(x)>, ComplexPart>) { 341 if (intoComplexPart) { 342 return x.complex(); 343 } 344 } 345 return std::nullopt; // w/o "else" to dodge bogus g++ 8.1 warning 346 }, 347 d.u); 348 } 349 template <typename T> 350 std::optional<DataRef> ExtractDataRef(const Expr<T> &expr, 351 bool intoSubstring = false, bool intoComplexPart = false) { 352 return common::visit( 353 [=](const auto &x) { 354 return ExtractDataRef(x, intoSubstring, intoComplexPart); 355 }, 356 expr.u); 357 } 358 template <typename A> 359 std::optional<DataRef> ExtractDataRef(const std::optional<A> &x, 360 bool intoSubstring = false, bool intoComplexPart = false) { 361 if (x) { 362 return ExtractDataRef(*x, intoSubstring, intoComplexPart); 363 } else { 364 return std::nullopt; 365 } 366 } 367 template <typename A> 368 std::optional<DataRef> ExtractDataRef( 369 A *p, bool intoSubstring = false, bool intoComplexPart = false) { 370 if (p) { 371 return ExtractDataRef(std::as_const(*p), intoSubstring, intoComplexPart); 372 } else { 373 return std::nullopt; 374 } 375 } 376 std::optional<DataRef> ExtractDataRef(const ActualArgument &, 377 bool intoSubstring = false, bool intoComplexPart = false); 378 379 std::optional<DataRef> ExtractSubstringBase(const Substring &); 380 381 // Predicate: is an expression is an array element reference? 382 template <typename T> 383 bool IsArrayElement(const Expr<T> &expr, bool intoSubstring = true, 384 bool skipComponents = false) { 385 if (auto dataRef{ExtractDataRef(expr, intoSubstring)}) { 386 const DataRef *ref{&*dataRef}; 387 if (skipComponents) { 388 while (const Component * component{std::get_if<Component>(&ref->u)}) { 389 ref = &component->base(); 390 } 391 } 392 if (const auto *coarrayRef{std::get_if<CoarrayRef>(&ref->u)}) { 393 return !coarrayRef->subscript().empty(); 394 } else { 395 return std::holds_alternative<ArrayRef>(ref->u); 396 } 397 } else { 398 return false; 399 } 400 } 401 402 template <typename A> 403 std::optional<NamedEntity> ExtractNamedEntity(const A &x) { 404 if (auto dataRef{ExtractDataRef(x)}) { 405 return common::visit( 406 common::visitors{ 407 [](SymbolRef &&symbol) -> std::optional<NamedEntity> { 408 return NamedEntity{symbol}; 409 }, 410 [](Component &&component) -> std::optional<NamedEntity> { 411 return NamedEntity{std::move(component)}; 412 }, 413 [](CoarrayRef &&co) -> std::optional<NamedEntity> { 414 return co.GetBase(); 415 }, 416 [](auto &&) { return std::optional<NamedEntity>{}; }, 417 }, 418 std::move(dataRef->u)); 419 } else { 420 return std::nullopt; 421 } 422 } 423 424 struct ExtractCoindexedObjectHelper { 425 template <typename A> std::optional<CoarrayRef> operator()(const A &) const { 426 return std::nullopt; 427 } 428 std::optional<CoarrayRef> operator()(const CoarrayRef &x) const { return x; } 429 template <typename A> 430 std::optional<CoarrayRef> operator()(const Expr<A> &expr) const { 431 return common::visit(*this, expr.u); 432 } 433 std::optional<CoarrayRef> operator()(const DataRef &dataRef) const { 434 return common::visit(*this, dataRef.u); 435 } 436 std::optional<CoarrayRef> operator()(const NamedEntity &named) const { 437 if (const Component * component{named.UnwrapComponent()}) { 438 return (*this)(*component); 439 } else { 440 return std::nullopt; 441 } 442 } 443 std::optional<CoarrayRef> operator()(const ProcedureDesignator &des) const { 444 if (const auto *component{ 445 std::get_if<common::CopyableIndirection<Component>>(&des.u)}) { 446 return (*this)(component->value()); 447 } else { 448 return std::nullopt; 449 } 450 } 451 std::optional<CoarrayRef> operator()(const Component &component) const { 452 return (*this)(component.base()); 453 } 454 std::optional<CoarrayRef> operator()(const ArrayRef &arrayRef) const { 455 return (*this)(arrayRef.base()); 456 } 457 }; 458 459 template <typename A> std::optional<CoarrayRef> ExtractCoarrayRef(const A &x) { 460 if (auto dataRef{ExtractDataRef(x, true)}) { 461 return ExtractCoindexedObjectHelper{}(*dataRef); 462 } else { 463 return ExtractCoindexedObjectHelper{}(x); 464 } 465 } 466 467 struct ExtractSubstringHelper { 468 template <typename T> static std::optional<Substring> visit(T &&) { 469 return std::nullopt; 470 } 471 472 static std::optional<Substring> visit(const Substring &e) { return e; } 473 474 template <typename T> 475 static std::optional<Substring> visit(const Designator<T> &e) { 476 return common::visit([](auto &&s) { return visit(s); }, e.u); 477 } 478 479 template <typename T> 480 static std::optional<Substring> visit(const Expr<T> &e) { 481 return common::visit([](auto &&s) { return visit(s); }, e.u); 482 } 483 }; 484 485 template <typename A> std::optional<Substring> ExtractSubstring(const A &x) { 486 return ExtractSubstringHelper::visit(x); 487 } 488 489 // If an expression is simply a whole symbol data designator, 490 // extract and return that symbol, else null. 491 template <typename A> const Symbol *UnwrapWholeSymbolDataRef(const A &x) { 492 if (auto dataRef{ExtractDataRef(x)}) { 493 if (const SymbolRef * p{std::get_if<SymbolRef>(&dataRef->u)}) { 494 return &p->get(); 495 } 496 } 497 return nullptr; 498 } 499 500 // If an expression is a whole symbol or a whole component desginator, 501 // extract and return that symbol, else null. 502 template <typename A> 503 const Symbol *UnwrapWholeSymbolOrComponentDataRef(const A &x) { 504 if (auto dataRef{ExtractDataRef(x)}) { 505 if (const SymbolRef * p{std::get_if<SymbolRef>(&dataRef->u)}) { 506 return &p->get(); 507 } else if (const Component * c{std::get_if<Component>(&dataRef->u)}) { 508 if (c->base().Rank() == 0) { 509 return &c->GetLastSymbol(); 510 } 511 } 512 } 513 return nullptr; 514 } 515 516 // If an expression is a whole symbol or a whole component designator, 517 // potentially followed by an image selector, extract and return that symbol, 518 // else null. 519 template <typename A> 520 const Symbol *UnwrapWholeSymbolOrComponentOrCoarrayRef(const A &x) { 521 if (auto dataRef{ExtractDataRef(x)}) { 522 if (const SymbolRef * p{std::get_if<SymbolRef>(&dataRef->u)}) { 523 return &p->get(); 524 } else if (const Component * c{std::get_if<Component>(&dataRef->u)}) { 525 if (c->base().Rank() == 0) { 526 return &c->GetLastSymbol(); 527 } 528 } else if (const CoarrayRef * c{std::get_if<CoarrayRef>(&dataRef->u)}) { 529 if (c->subscript().empty()) { 530 return &c->GetLastSymbol(); 531 } 532 } 533 } 534 return nullptr; 535 } 536 537 // GetFirstSymbol(A%B%C[I]%D) -> A 538 template <typename A> const Symbol *GetFirstSymbol(const A &x) { 539 if (auto dataRef{ExtractDataRef(x, true)}) { 540 return &dataRef->GetFirstSymbol(); 541 } else { 542 return nullptr; 543 } 544 } 545 546 // GetLastPointerSymbol(A%PTR1%B%PTR2%C) -> PTR2 547 const Symbol *GetLastPointerSymbol(const evaluate::DataRef &); 548 549 // Creation of conversion expressions can be done to either a known 550 // specific intrinsic type with ConvertToType<T>(x) or by converting 551 // one arbitrary expression to the type of another with ConvertTo(to, from). 552 553 template <typename TO, TypeCategory FROMCAT> 554 Expr<TO> ConvertToType(Expr<SomeKind<FROMCAT>> &&x) { 555 static_assert(IsSpecificIntrinsicType<TO>); 556 if constexpr (FROMCAT == TO::category) { 557 if (auto *already{std::get_if<Expr<TO>>(&x.u)}) { 558 return std::move(*already); 559 } else { 560 return Expr<TO>{Convert<TO, FROMCAT>{std::move(x)}}; 561 } 562 } else if constexpr (TO::category == TypeCategory::Complex) { 563 using Part = typename TO::Part; 564 Scalar<Part> zero; 565 return Expr<TO>{ComplexConstructor<TO::kind>{ 566 ConvertToType<Part>(std::move(x)), Expr<Part>{Constant<Part>{zero}}}}; 567 } else if constexpr (FROMCAT == TypeCategory::Complex) { 568 // Extract and convert the real component of a complex value 569 return common::visit( 570 [&](auto &&z) { 571 using ZType = ResultType<decltype(z)>; 572 using Part = typename ZType::Part; 573 return ConvertToType<TO, TypeCategory::Real>(Expr<SomeReal>{ 574 Expr<Part>{ComplexComponent<Part::kind>{false, std::move(z)}}}); 575 }, 576 std::move(x.u)); 577 } else { 578 return Expr<TO>{Convert<TO, FROMCAT>{std::move(x)}}; 579 } 580 } 581 582 template <typename TO, TypeCategory FROMCAT, int FROMKIND> 583 Expr<TO> ConvertToType(Expr<Type<FROMCAT, FROMKIND>> &&x) { 584 return ConvertToType<TO, FROMCAT>(Expr<SomeKind<FROMCAT>>{std::move(x)}); 585 } 586 587 template <typename TO> Expr<TO> ConvertToType(BOZLiteralConstant &&x) { 588 static_assert(IsSpecificIntrinsicType<TO>); 589 if constexpr (TO::category == TypeCategory::Integer || 590 TO::category == TypeCategory::Unsigned) { 591 return Expr<TO>{ 592 Constant<TO>{Scalar<TO>::ConvertUnsigned(std::move(x)).value}}; 593 } else { 594 static_assert(TO::category == TypeCategory::Real); 595 using Word = typename Scalar<TO>::Word; 596 return Expr<TO>{ 597 Constant<TO>{Scalar<TO>{Word::ConvertUnsigned(std::move(x)).value}}}; 598 } 599 } 600 601 template <typename T> bool IsBOZLiteral(const Expr<T> &expr) { 602 return std::holds_alternative<BOZLiteralConstant>(expr.u); 603 } 604 605 // Conversions to dynamic types 606 std::optional<Expr<SomeType>> ConvertToType( 607 const DynamicType &, Expr<SomeType> &&); 608 std::optional<Expr<SomeType>> ConvertToType( 609 const DynamicType &, std::optional<Expr<SomeType>> &&); 610 std::optional<Expr<SomeType>> ConvertToType(const Symbol &, Expr<SomeType> &&); 611 std::optional<Expr<SomeType>> ConvertToType( 612 const Symbol &, std::optional<Expr<SomeType>> &&); 613 614 // Conversions to the type of another expression 615 template <TypeCategory TC, int TK, typename FROM> 616 common::IfNoLvalue<Expr<Type<TC, TK>>, FROM> ConvertTo( 617 const Expr<Type<TC, TK>> &, FROM &&x) { 618 return ConvertToType<Type<TC, TK>>(std::move(x)); 619 } 620 621 template <TypeCategory TC, typename FROM> 622 common::IfNoLvalue<Expr<SomeKind<TC>>, FROM> ConvertTo( 623 const Expr<SomeKind<TC>> &to, FROM &&from) { 624 return common::visit( 625 [&](const auto &toKindExpr) { 626 using KindExpr = std::decay_t<decltype(toKindExpr)>; 627 return AsCategoryExpr( 628 ConvertToType<ResultType<KindExpr>>(std::move(from))); 629 }, 630 to.u); 631 } 632 633 template <typename FROM> 634 common::IfNoLvalue<Expr<SomeType>, FROM> ConvertTo( 635 const Expr<SomeType> &to, FROM &&from) { 636 return common::visit( 637 [&](const auto &toCatExpr) { 638 return AsGenericExpr(ConvertTo(toCatExpr, std::move(from))); 639 }, 640 to.u); 641 } 642 643 // Convert an expression of some known category to a dynamically chosen 644 // kind of some category (usually but not necessarily distinct). 645 template <TypeCategory TOCAT, typename VALUE> struct ConvertToKindHelper { 646 using Result = std::optional<Expr<SomeKind<TOCAT>>>; 647 using Types = CategoryTypes<TOCAT>; 648 ConvertToKindHelper(int k, VALUE &&x) : kind{k}, value{std::move(x)} {} 649 template <typename T> Result Test() { 650 if (kind == T::kind) { 651 return std::make_optional( 652 AsCategoryExpr(ConvertToType<T>(std::move(value)))); 653 } 654 return std::nullopt; 655 } 656 int kind; 657 VALUE value; 658 }; 659 660 template <TypeCategory TOCAT, typename VALUE> 661 common::IfNoLvalue<Expr<SomeKind<TOCAT>>, VALUE> ConvertToKind( 662 int kind, VALUE &&x) { 663 auto result{common::SearchTypes( 664 ConvertToKindHelper<TOCAT, VALUE>{kind, std::move(x)})}; 665 CHECK(result.has_value()); 666 return *result; 667 } 668 669 // Given a type category CAT, SameKindExprs<CAT, N> is a variant that 670 // holds an arrays of expressions of the same supported kind in that 671 // category. 672 template <typename A, int N = 2> using SameExprs = std::array<Expr<A>, N>; 673 template <int N = 2> struct SameKindExprsHelper { 674 template <typename A> using SameExprs = std::array<Expr<A>, N>; 675 }; 676 template <TypeCategory CAT, int N = 2> 677 using SameKindExprs = 678 common::MapTemplate<SameKindExprsHelper<N>::template SameExprs, 679 CategoryTypes<CAT>>; 680 681 // Given references to two expressions of arbitrary kind in the same type 682 // category, convert one to the kind of the other when it has the smaller kind, 683 // then return them in a type-safe package. 684 template <TypeCategory CAT> 685 SameKindExprs<CAT, 2> AsSameKindExprs( 686 Expr<SomeKind<CAT>> &&x, Expr<SomeKind<CAT>> &&y) { 687 return common::visit( 688 [&](auto &&kx, auto &&ky) -> SameKindExprs<CAT, 2> { 689 using XTy = ResultType<decltype(kx)>; 690 using YTy = ResultType<decltype(ky)>; 691 if constexpr (std::is_same_v<XTy, YTy>) { 692 return {SameExprs<XTy>{std::move(kx), std::move(ky)}}; 693 } else if constexpr (XTy::kind < YTy::kind) { 694 return {SameExprs<YTy>{ConvertTo(ky, std::move(kx)), std::move(ky)}}; 695 } else { 696 return {SameExprs<XTy>{std::move(kx), ConvertTo(kx, std::move(ky))}}; 697 } 698 #if !__clang__ && 100 * __GNUC__ + __GNUC_MINOR__ == 801 699 // Silence a bogus warning about a missing return with G++ 8.1.0. 700 // Doesn't execute, but must be correctly typed. 701 CHECK(!"can't happen"); 702 return {SameExprs<XTy>{std::move(kx), std::move(kx)}}; 703 #endif 704 }, 705 std::move(x.u), std::move(y.u)); 706 } 707 708 // Ensure that both operands of an intrinsic REAL operation (or CMPLX() 709 // constructor) are INTEGER or REAL, then convert them as necessary to the 710 // same kind of REAL. 711 using ConvertRealOperandsResult = 712 std::optional<SameKindExprs<TypeCategory::Real, 2>>; 713 ConvertRealOperandsResult ConvertRealOperands(parser::ContextualMessages &, 714 Expr<SomeType> &&, Expr<SomeType> &&, int defaultRealKind); 715 716 // Per F'2018 R718, if both components are INTEGER, they are both converted 717 // to default REAL and the result is default COMPLEX. Otherwise, the 718 // kind of the result is the kind of most precise REAL component, and the other 719 // component is converted if necessary to its type. 720 std::optional<Expr<SomeComplex>> ConstructComplex(parser::ContextualMessages &, 721 Expr<SomeType> &&, Expr<SomeType> &&, int defaultRealKind); 722 std::optional<Expr<SomeComplex>> ConstructComplex(parser::ContextualMessages &, 723 std::optional<Expr<SomeType>> &&, std::optional<Expr<SomeType>> &&, 724 int defaultRealKind); 725 726 template <typename A> Expr<TypeOf<A>> ScalarConstantToExpr(const A &x) { 727 using Ty = TypeOf<A>; 728 static_assert( 729 std::is_same_v<Scalar<Ty>, std::decay_t<A>>, "TypeOf<> is broken"); 730 return Expr<TypeOf<A>>{Constant<Ty>{x}}; 731 } 732 733 // Combine two expressions of the same specific numeric type with an operation 734 // to produce a new expression. 735 template <template <typename> class OPR, typename SPECIFIC> 736 Expr<SPECIFIC> Combine(Expr<SPECIFIC> &&x, Expr<SPECIFIC> &&y) { 737 static_assert(IsSpecificIntrinsicType<SPECIFIC>); 738 return AsExpr(OPR<SPECIFIC>{std::move(x), std::move(y)}); 739 } 740 741 // Given two expressions of arbitrary kind in the same intrinsic type 742 // category, convert one of them if necessary to the larger kind of the 743 // other, then combine the resulting homogenized operands with a given 744 // operation, returning a new expression in the same type category. 745 template <template <typename> class OPR, TypeCategory CAT> 746 Expr<SomeKind<CAT>> PromoteAndCombine( 747 Expr<SomeKind<CAT>> &&x, Expr<SomeKind<CAT>> &&y) { 748 return common::visit( 749 [](auto &&xy) { 750 using Ty = ResultType<decltype(xy[0])>; 751 return AsCategoryExpr( 752 Combine<OPR, Ty>(std::move(xy[0]), std::move(xy[1]))); 753 }, 754 AsSameKindExprs(std::move(x), std::move(y))); 755 } 756 757 // Given two expressions of arbitrary type, try to combine them with a 758 // binary numeric operation (e.g., Add), possibly with data type conversion of 759 // one of the operands to the type of the other. Handles special cases with 760 // typeless literal operands and with REAL/COMPLEX exponentiation to INTEGER 761 // powers. 762 template <template <typename> class OPR, bool CAN_BE_UNSIGNED = true> 763 std::optional<Expr<SomeType>> NumericOperation(parser::ContextualMessages &, 764 Expr<SomeType> &&, Expr<SomeType> &&, int defaultRealKind); 765 766 extern template std::optional<Expr<SomeType>> NumericOperation<Power, false>( 767 parser::ContextualMessages &, Expr<SomeType> &&, Expr<SomeType> &&, 768 int defaultRealKind); 769 extern template std::optional<Expr<SomeType>> NumericOperation<Multiply>( 770 parser::ContextualMessages &, Expr<SomeType> &&, Expr<SomeType> &&, 771 int defaultRealKind); 772 extern template std::optional<Expr<SomeType>> NumericOperation<Divide>( 773 parser::ContextualMessages &, Expr<SomeType> &&, Expr<SomeType> &&, 774 int defaultRealKind); 775 extern template std::optional<Expr<SomeType>> NumericOperation<Add>( 776 parser::ContextualMessages &, Expr<SomeType> &&, Expr<SomeType> &&, 777 int defaultRealKind); 778 extern template std::optional<Expr<SomeType>> NumericOperation<Subtract>( 779 parser::ContextualMessages &, Expr<SomeType> &&, Expr<SomeType> &&, 780 int defaultRealKind); 781 782 std::optional<Expr<SomeType>> Negation( 783 parser::ContextualMessages &, Expr<SomeType> &&); 784 785 // Given two expressions of arbitrary type, try to combine them with a 786 // relational operator (e.g., .LT.), possibly with data type conversion. 787 std::optional<Expr<LogicalResult>> Relate(parser::ContextualMessages &, 788 RelationalOperator, Expr<SomeType> &&, Expr<SomeType> &&); 789 790 // Create a relational operation between two identically-typed operands 791 // and wrap it up in an Expr<LogicalResult>. 792 template <typename T> 793 Expr<LogicalResult> PackageRelation( 794 RelationalOperator opr, Expr<T> &&x, Expr<T> &&y) { 795 static_assert(IsSpecificIntrinsicType<T>); 796 return Expr<LogicalResult>{ 797 Relational<SomeType>{Relational<T>{opr, std::move(x), std::move(y)}}}; 798 } 799 800 template <int K> 801 Expr<Type<TypeCategory::Logical, K>> LogicalNegation( 802 Expr<Type<TypeCategory::Logical, K>> &&x) { 803 return AsExpr(Not<K>{std::move(x)}); 804 } 805 806 Expr<SomeLogical> LogicalNegation(Expr<SomeLogical> &&); 807 808 template <int K> 809 Expr<Type<TypeCategory::Logical, K>> BinaryLogicalOperation(LogicalOperator opr, 810 Expr<Type<TypeCategory::Logical, K>> &&x, 811 Expr<Type<TypeCategory::Logical, K>> &&y) { 812 return AsExpr(LogicalOperation<K>{opr, std::move(x), std::move(y)}); 813 } 814 815 Expr<SomeLogical> BinaryLogicalOperation( 816 LogicalOperator, Expr<SomeLogical> &&, Expr<SomeLogical> &&); 817 818 // Convenience functions and operator overloadings for expression construction. 819 // These interfaces are defined only for those situations that can never 820 // emit any message. Use the more general templates (above) in other 821 // situations. 822 823 template <TypeCategory C, int K> 824 Expr<Type<C, K>> operator-(Expr<Type<C, K>> &&x) { 825 return AsExpr(Negate<Type<C, K>>{std::move(x)}); 826 } 827 828 template <TypeCategory C, int K> 829 Expr<Type<C, K>> operator+(Expr<Type<C, K>> &&x, Expr<Type<C, K>> &&y) { 830 return AsExpr(Combine<Add, Type<C, K>>(std::move(x), std::move(y))); 831 } 832 833 template <TypeCategory C, int K> 834 Expr<Type<C, K>> operator-(Expr<Type<C, K>> &&x, Expr<Type<C, K>> &&y) { 835 return AsExpr(Combine<Subtract, Type<C, K>>(std::move(x), std::move(y))); 836 } 837 838 template <TypeCategory C, int K> 839 Expr<Type<C, K>> operator*(Expr<Type<C, K>> &&x, Expr<Type<C, K>> &&y) { 840 return AsExpr(Combine<Multiply, Type<C, K>>(std::move(x), std::move(y))); 841 } 842 843 template <TypeCategory C, int K> 844 Expr<Type<C, K>> operator/(Expr<Type<C, K>> &&x, Expr<Type<C, K>> &&y) { 845 return AsExpr(Combine<Divide, Type<C, K>>(std::move(x), std::move(y))); 846 } 847 848 template <TypeCategory C> Expr<SomeKind<C>> operator-(Expr<SomeKind<C>> &&x) { 849 return common::visit( 850 [](auto &xk) { return Expr<SomeKind<C>>{-std::move(xk)}; }, x.u); 851 } 852 853 template <TypeCategory CAT> 854 Expr<SomeKind<CAT>> operator+( 855 Expr<SomeKind<CAT>> &&x, Expr<SomeKind<CAT>> &&y) { 856 return PromoteAndCombine<Add, CAT>(std::move(x), std::move(y)); 857 } 858 859 template <TypeCategory CAT> 860 Expr<SomeKind<CAT>> operator-( 861 Expr<SomeKind<CAT>> &&x, Expr<SomeKind<CAT>> &&y) { 862 return PromoteAndCombine<Subtract, CAT>(std::move(x), std::move(y)); 863 } 864 865 template <TypeCategory CAT> 866 Expr<SomeKind<CAT>> operator*( 867 Expr<SomeKind<CAT>> &&x, Expr<SomeKind<CAT>> &&y) { 868 return PromoteAndCombine<Multiply, CAT>(std::move(x), std::move(y)); 869 } 870 871 template <TypeCategory CAT> 872 Expr<SomeKind<CAT>> operator/( 873 Expr<SomeKind<CAT>> &&x, Expr<SomeKind<CAT>> &&y) { 874 return PromoteAndCombine<Divide, CAT>(std::move(x), std::move(y)); 875 } 876 877 // A utility for use with common::SearchTypes to create generic expressions 878 // when an intrinsic type category for (say) a variable is known 879 // but the kind parameter value is not. 880 template <TypeCategory CAT, template <typename> class TEMPLATE, typename VALUE> 881 struct TypeKindVisitor { 882 using Result = std::optional<Expr<SomeType>>; 883 using Types = CategoryTypes<CAT>; 884 885 TypeKindVisitor(int k, VALUE &&x) : kind{k}, value{std::move(x)} {} 886 TypeKindVisitor(int k, const VALUE &x) : kind{k}, value{x} {} 887 888 template <typename T> Result Test() { 889 if (kind == T::kind) { 890 return AsGenericExpr(TEMPLATE<T>{std::move(value)}); 891 } 892 return std::nullopt; 893 } 894 895 int kind; 896 VALUE value; 897 }; 898 899 // TypedWrapper() wraps a object in an explicitly typed representation 900 // (e.g., Designator<> or FunctionRef<>) that has been instantiated on 901 // a dynamically chosen Fortran type. 902 template <TypeCategory CATEGORY, template <typename> typename WRAPPER, 903 typename WRAPPED> 904 common::IfNoLvalue<std::optional<Expr<SomeType>>, WRAPPED> WrapperHelper( 905 int kind, WRAPPED &&x) { 906 return common::SearchTypes( 907 TypeKindVisitor<CATEGORY, WRAPPER, WRAPPED>{kind, std::move(x)}); 908 } 909 910 template <template <typename> typename WRAPPER, typename WRAPPED> 911 common::IfNoLvalue<std::optional<Expr<SomeType>>, WRAPPED> TypedWrapper( 912 const DynamicType &dyType, WRAPPED &&x) { 913 switch (dyType.category()) { 914 SWITCH_COVERS_ALL_CASES 915 case TypeCategory::Integer: 916 return WrapperHelper<TypeCategory::Integer, WRAPPER, WRAPPED>( 917 dyType.kind(), std::move(x)); 918 case TypeCategory::Unsigned: 919 return WrapperHelper<TypeCategory::Unsigned, WRAPPER, WRAPPED>( 920 dyType.kind(), std::move(x)); 921 case TypeCategory::Real: 922 return WrapperHelper<TypeCategory::Real, WRAPPER, WRAPPED>( 923 dyType.kind(), std::move(x)); 924 case TypeCategory::Complex: 925 return WrapperHelper<TypeCategory::Complex, WRAPPER, WRAPPED>( 926 dyType.kind(), std::move(x)); 927 case TypeCategory::Character: 928 return WrapperHelper<TypeCategory::Character, WRAPPER, WRAPPED>( 929 dyType.kind(), std::move(x)); 930 case TypeCategory::Logical: 931 return WrapperHelper<TypeCategory::Logical, WRAPPER, WRAPPED>( 932 dyType.kind(), std::move(x)); 933 case TypeCategory::Derived: 934 return AsGenericExpr(Expr<SomeDerived>{WRAPPER<SomeDerived>{std::move(x)}}); 935 } 936 } 937 938 // GetLastSymbol() returns the rightmost symbol in an object or procedure 939 // designator (which has perhaps been wrapped in an Expr<>), or a null pointer 940 // when none is found. It will return an ASSOCIATE construct entity's symbol 941 // rather than descending into its expression. 942 struct GetLastSymbolHelper 943 : public AnyTraverse<GetLastSymbolHelper, std::optional<const Symbol *>> { 944 using Result = std::optional<const Symbol *>; 945 using Base = AnyTraverse<GetLastSymbolHelper, Result>; 946 GetLastSymbolHelper() : Base{*this} {} 947 using Base::operator(); 948 Result operator()(const Symbol &x) const { return &x; } 949 Result operator()(const Component &x) const { return &x.GetLastSymbol(); } 950 Result operator()(const NamedEntity &x) const { return &x.GetLastSymbol(); } 951 Result operator()(const ProcedureDesignator &x) const { 952 return x.GetSymbol(); 953 } 954 template <typename T> Result operator()(const Expr<T> &x) const { 955 if constexpr (common::HasMember<T, AllIntrinsicTypes> || 956 std::is_same_v<T, SomeDerived>) { 957 if (const auto *designator{std::get_if<Designator<T>>(&x.u)}) { 958 if (auto known{(*this)(*designator)}) { 959 return known; 960 } 961 } 962 return nullptr; 963 } else { 964 return (*this)(x.u); 965 } 966 } 967 }; 968 969 template <typename A> const Symbol *GetLastSymbol(const A &x) { 970 if (auto known{GetLastSymbolHelper{}(x)}) { 971 return *known; 972 } else { 973 return nullptr; 974 } 975 } 976 977 // For everyday variables: if GetLastSymbol() succeeds on the argument, return 978 // its set of attributes, otherwise the empty set. Also works on variables that 979 // are pointer results of functions. 980 template <typename A> semantics::Attrs GetAttrs(const A &x) { 981 if (const Symbol * symbol{GetLastSymbol(x)}) { 982 return symbol->attrs(); 983 } else { 984 return {}; 985 } 986 } 987 988 template <> 989 inline semantics::Attrs GetAttrs<Expr<SomeType>>(const Expr<SomeType> &x) { 990 if (IsVariable(x)) { 991 if (const auto *procRef{UnwrapProcedureRef(x)}) { 992 if (const Symbol * interface{procRef->proc().GetInterfaceSymbol()}) { 993 if (const auto *details{ 994 interface->detailsIf<semantics::SubprogramDetails>()}) { 995 if (details->isFunction() && 996 details->result().attrs().test(semantics::Attr::POINTER)) { 997 // N.B.: POINTER becomes TARGET in SetAttrsFromAssociation() 998 return details->result().attrs(); 999 } 1000 } 1001 } 1002 } 1003 } 1004 if (const Symbol * symbol{GetLastSymbol(x)}) { 1005 return symbol->attrs(); 1006 } else { 1007 return {}; 1008 } 1009 } 1010 1011 template <typename A> semantics::Attrs GetAttrs(const std::optional<A> &x) { 1012 if (x) { 1013 return GetAttrs(*x); 1014 } else { 1015 return {}; 1016 } 1017 } 1018 1019 // GetBaseObject() 1020 template <typename A> std::optional<BaseObject> GetBaseObject(const A &) { 1021 return std::nullopt; 1022 } 1023 template <typename T> 1024 std::optional<BaseObject> GetBaseObject(const Designator<T> &x) { 1025 return x.GetBaseObject(); 1026 } 1027 template <typename T> 1028 std::optional<BaseObject> GetBaseObject(const Expr<T> &x) { 1029 return common::visit([](const auto &y) { return GetBaseObject(y); }, x.u); 1030 } 1031 template <typename A> 1032 std::optional<BaseObject> GetBaseObject(const std::optional<A> &x) { 1033 if (x) { 1034 return GetBaseObject(*x); 1035 } else { 1036 return std::nullopt; 1037 } 1038 } 1039 1040 // Like IsAllocatableOrPointer, but accepts pointer function results as being 1041 // pointers too. 1042 bool IsAllocatableOrPointerObject(const Expr<SomeType> &); 1043 1044 bool IsAllocatableDesignator(const Expr<SomeType> &); 1045 1046 // Procedure and pointer detection predicates 1047 bool IsProcedureDesignator(const Expr<SomeType> &); 1048 bool IsFunctionDesignator(const Expr<SomeType> &); 1049 bool IsPointer(const Expr<SomeType> &); 1050 bool IsProcedurePointer(const Expr<SomeType> &); 1051 bool IsProcedure(const Expr<SomeType> &); 1052 bool IsProcedurePointerTarget(const Expr<SomeType> &); 1053 bool IsBareNullPointer(const Expr<SomeType> *); // NULL() w/o MOLD= or type 1054 bool IsNullObjectPointer(const Expr<SomeType> &); 1055 bool IsNullProcedurePointer(const Expr<SomeType> &); 1056 bool IsNullPointer(const Expr<SomeType> &); 1057 bool IsObjectPointer(const Expr<SomeType> &); 1058 1059 // Can Expr be passed as absent to an optional dummy argument. 1060 // See 15.5.2.12 point 1 for more details. 1061 bool MayBePassedAsAbsentOptional(const Expr<SomeType> &); 1062 1063 // Extracts the chain of symbols from a designator, which has perhaps been 1064 // wrapped in an Expr<>, removing all of the (co)subscripts. The 1065 // base object will be the first symbol in the result vector. 1066 struct GetSymbolVectorHelper 1067 : public Traverse<GetSymbolVectorHelper, SymbolVector> { 1068 using Result = SymbolVector; 1069 using Base = Traverse<GetSymbolVectorHelper, Result>; 1070 using Base::operator(); 1071 GetSymbolVectorHelper() : Base{*this} {} 1072 Result Default() { return {}; } 1073 Result Combine(Result &&a, Result &&b) { 1074 a.insert(a.end(), b.begin(), b.end()); 1075 return std::move(a); 1076 } 1077 Result operator()(const Symbol &) const; 1078 Result operator()(const Component &) const; 1079 Result operator()(const ArrayRef &) const; 1080 Result operator()(const CoarrayRef &) const; 1081 }; 1082 template <typename A> SymbolVector GetSymbolVector(const A &x) { 1083 return GetSymbolVectorHelper{}(x); 1084 } 1085 1086 // GetLastTarget() returns the rightmost symbol in an object designator's 1087 // SymbolVector that has the POINTER or TARGET attribute, or a null pointer 1088 // when none is found. 1089 const Symbol *GetLastTarget(const SymbolVector &); 1090 1091 // Collects all of the Symbols in an expression 1092 template <typename A> semantics::UnorderedSymbolSet CollectSymbols(const A &); 1093 extern template semantics::UnorderedSymbolSet CollectSymbols( 1094 const Expr<SomeType> &); 1095 extern template semantics::UnorderedSymbolSet CollectSymbols( 1096 const Expr<SomeInteger> &); 1097 extern template semantics::UnorderedSymbolSet CollectSymbols( 1098 const Expr<SubscriptInteger> &); 1099 1100 // Collects Symbols of interest for the CUDA data transfer in an expression 1101 template <typename A> 1102 semantics::UnorderedSymbolSet CollectCudaSymbols(const A &); 1103 extern template semantics::UnorderedSymbolSet CollectCudaSymbols( 1104 const Expr<SomeType> &); 1105 extern template semantics::UnorderedSymbolSet CollectCudaSymbols( 1106 const Expr<SomeInteger> &); 1107 extern template semantics::UnorderedSymbolSet CollectCudaSymbols( 1108 const Expr<SubscriptInteger> &); 1109 1110 // Predicate: does a variable contain a vector-valued subscript (not a triplet)? 1111 bool HasVectorSubscript(const Expr<SomeType> &); 1112 1113 // Predicate: does an expression contain constant? 1114 bool HasConstant(const Expr<SomeType> &); 1115 1116 // Utilities for attaching the location of the declaration of a symbol 1117 // of interest to a message. Handles the case of USE association gracefully. 1118 parser::Message *AttachDeclaration(parser::Message &, const Symbol &); 1119 parser::Message *AttachDeclaration(parser::Message *, const Symbol &); 1120 template <typename MESSAGES, typename... A> 1121 parser::Message *SayWithDeclaration( 1122 MESSAGES &messages, const Symbol &symbol, A &&...x) { 1123 return AttachDeclaration(messages.Say(std::forward<A>(x)...), symbol); 1124 } 1125 1126 // Check for references to impure procedures; returns the name 1127 // of one to complain about, if any exist. 1128 std::optional<std::string> FindImpureCall( 1129 FoldingContext &, const Expr<SomeType> &); 1130 std::optional<std::string> FindImpureCall( 1131 FoldingContext &, const ProcedureRef &); 1132 1133 // Predicate: is a scalar expression suitable for naive scalar expansion 1134 // in the flattening of an array expression? 1135 // TODO: capture such scalar expansions in temporaries, flatten everything 1136 class UnexpandabilityFindingVisitor 1137 : public AnyTraverse<UnexpandabilityFindingVisitor> { 1138 public: 1139 using Base = AnyTraverse<UnexpandabilityFindingVisitor>; 1140 using Base::operator(); 1141 explicit UnexpandabilityFindingVisitor(bool admitPureCall) 1142 : Base{*this}, admitPureCall_{admitPureCall} {} 1143 template <typename T> bool operator()(const FunctionRef<T> &procRef) { 1144 return !admitPureCall_ || !procRef.proc().IsPure(); 1145 } 1146 bool operator()(const CoarrayRef &) { return true; } 1147 1148 private: 1149 bool admitPureCall_{false}; 1150 }; 1151 1152 template <typename T> 1153 bool IsExpandableScalar(const Expr<T> &expr, FoldingContext &context, 1154 const Shape &shape, bool admitPureCall = false) { 1155 if (UnexpandabilityFindingVisitor{admitPureCall}(expr)) { 1156 auto extents{AsConstantExtents(context, shape)}; 1157 return extents && !HasNegativeExtent(*extents) && GetSize(*extents) == 1; 1158 } else { 1159 return true; 1160 } 1161 } 1162 1163 // Common handling for procedure pointer compatibility of left- and right-hand 1164 // sides. Returns nullopt if they're compatible. Otherwise, it returns a 1165 // message that needs to be augmented by the names of the left and right sides. 1166 std::optional<parser::MessageFixedText> CheckProcCompatibility(bool isCall, 1167 const std::optional<characteristics::Procedure> &lhsProcedure, 1168 const characteristics::Procedure *rhsProcedure, 1169 const SpecificIntrinsic *specificIntrinsic, std::string &whyNotCompatible, 1170 std::optional<std::string> &warning, bool ignoreImplicitVsExplicit); 1171 1172 // Scalar constant expansion 1173 class ScalarConstantExpander { 1174 public: 1175 explicit ScalarConstantExpander(ConstantSubscripts &&extents) 1176 : extents_{std::move(extents)} {} 1177 ScalarConstantExpander( 1178 ConstantSubscripts &&extents, std::optional<ConstantSubscripts> &&lbounds) 1179 : extents_{std::move(extents)}, lbounds_{std::move(lbounds)} {} 1180 ScalarConstantExpander( 1181 ConstantSubscripts &&extents, ConstantSubscripts &&lbounds) 1182 : extents_{std::move(extents)}, lbounds_{std::move(lbounds)} {} 1183 1184 template <typename A> A Expand(A &&x) const { 1185 return std::move(x); // default case 1186 } 1187 template <typename T> Constant<T> Expand(Constant<T> &&x) { 1188 auto expanded{x.Reshape(std::move(extents_))}; 1189 if (lbounds_) { 1190 expanded.set_lbounds(std::move(*lbounds_)); 1191 } 1192 return expanded; 1193 } 1194 template <typename T> Expr<T> Expand(Parentheses<T> &&x) { 1195 return Expand(std::move(x.left())); // Constant<> can be parenthesized 1196 } 1197 template <typename T> Expr<T> Expand(Expr<T> &&x) { 1198 return common::visit( 1199 [&](auto &&x) { return Expr<T>{Expand(std::move(x))}; }, 1200 std::move(x.u)); 1201 } 1202 1203 private: 1204 ConstantSubscripts extents_; 1205 std::optional<ConstantSubscripts> lbounds_; 1206 }; 1207 1208 // Given a collection of element values, package them as a Constant. 1209 // If the type is Character or a derived type, take the length or type 1210 // (resp.) from a another Constant. 1211 template <typename T> 1212 Constant<T> PackageConstant(std::vector<Scalar<T>> &&elements, 1213 const Constant<T> &reference, const ConstantSubscripts &shape) { 1214 if constexpr (T::category == TypeCategory::Character) { 1215 return Constant<T>{ 1216 reference.LEN(), std::move(elements), ConstantSubscripts{shape}}; 1217 } else if constexpr (T::category == TypeCategory::Derived) { 1218 return Constant<T>{reference.GetType().GetDerivedTypeSpec(), 1219 std::move(elements), ConstantSubscripts{shape}}; 1220 } else { 1221 return Constant<T>{std::move(elements), ConstantSubscripts{shape}}; 1222 } 1223 } 1224 1225 // Nonstandard conversions of constants (integer->logical, logical->integer) 1226 // that can appear in DATA statements as an extension. 1227 std::optional<Expr<SomeType>> DataConstantConversionExtension( 1228 FoldingContext &, const DynamicType &, const Expr<SomeType> &); 1229 1230 // Convert Hollerith or short character to a another type as if the 1231 // Hollerith data had been BOZ. 1232 std::optional<Expr<SomeType>> HollerithToBOZ( 1233 FoldingContext &, const Expr<SomeType> &, const DynamicType &); 1234 1235 // Set explicit lower bounds on a constant array. 1236 class ArrayConstantBoundChanger { 1237 public: 1238 explicit ArrayConstantBoundChanger(ConstantSubscripts &&lbounds) 1239 : lbounds_{std::move(lbounds)} {} 1240 1241 template <typename A> A ChangeLbounds(A &&x) const { 1242 return std::move(x); // default case 1243 } 1244 template <typename T> Constant<T> ChangeLbounds(Constant<T> &&x) { 1245 x.set_lbounds(std::move(lbounds_)); 1246 return std::move(x); 1247 } 1248 template <typename T> Expr<T> ChangeLbounds(Parentheses<T> &&x) { 1249 return ChangeLbounds( 1250 std::move(x.left())); // Constant<> can be parenthesized 1251 } 1252 template <typename T> Expr<T> ChangeLbounds(Expr<T> &&x) { 1253 return common::visit( 1254 [&](auto &&x) { return Expr<T>{ChangeLbounds(std::move(x))}; }, 1255 std::move(x.u)); // recurse until we hit a constant 1256 } 1257 1258 private: 1259 ConstantSubscripts &&lbounds_; 1260 }; 1261 1262 // Predicate: should two expressions be considered identical for the purposes 1263 // of determining whether two procedure interfaces are compatible, modulo 1264 // naming of corresponding dummy arguments? 1265 template <typename T> 1266 std::optional<bool> AreEquivalentInInterface(const Expr<T> &, const Expr<T> &); 1267 extern template std::optional<bool> AreEquivalentInInterface<SubscriptInteger>( 1268 const Expr<SubscriptInteger> &, const Expr<SubscriptInteger> &); 1269 extern template std::optional<bool> AreEquivalentInInterface<SomeInteger>( 1270 const Expr<SomeInteger> &, const Expr<SomeInteger> &); 1271 1272 bool CheckForCoindexedObject(parser::ContextualMessages &, 1273 const std::optional<ActualArgument> &, const std::string &procName, 1274 const std::string &argName); 1275 1276 inline bool CanCUDASymbolHaveSaveAttr(const Symbol &sym) { 1277 if (const auto *details = 1278 sym.GetUltimate().detailsIf<semantics::ObjectEntityDetails>()) { 1279 if (details->cudaDataAttr() && 1280 *details->cudaDataAttr() != common::CUDADataAttr::Unified) { 1281 return false; 1282 } 1283 } 1284 return true; 1285 } 1286 1287 inline bool IsCUDADeviceSymbol(const Symbol &sym) { 1288 if (const auto *details = 1289 sym.GetUltimate().detailsIf<semantics::ObjectEntityDetails>()) { 1290 if (details->cudaDataAttr() && 1291 *details->cudaDataAttr() != common::CUDADataAttr::Pinned) { 1292 return true; 1293 } 1294 } 1295 return false; 1296 } 1297 1298 // Get the number of distinct symbols with CUDA device 1299 // attribute in the expression. 1300 template <typename A> inline int GetNbOfCUDADeviceSymbols(const A &expr) { 1301 semantics::UnorderedSymbolSet symbols; 1302 for (const Symbol &sym : CollectCudaSymbols(expr)) { 1303 if (IsCUDADeviceSymbol(sym)) { 1304 symbols.insert(sym); 1305 } 1306 } 1307 return symbols.size(); 1308 } 1309 1310 // Check if any of the symbols part of the expression has a CUDA device 1311 // attribute. 1312 template <typename A> inline bool HasCUDADeviceAttrs(const A &expr) { 1313 return GetNbOfCUDADeviceSymbols(expr) > 0; 1314 } 1315 1316 /// Check if the expression is a mix of host and device variables that require 1317 /// implicit data transfer. 1318 inline bool HasCUDAImplicitTransfer(const Expr<SomeType> &expr) { 1319 unsigned hostSymbols{0}; 1320 unsigned deviceSymbols{0}; 1321 for (const Symbol &sym : CollectCudaSymbols(expr)) { 1322 if (IsCUDADeviceSymbol(sym)) { 1323 ++deviceSymbols; 1324 } else { 1325 if (sym.owner().IsDerivedType()) { 1326 if (IsCUDADeviceSymbol(sym.owner().GetSymbol()->GetUltimate())) { 1327 ++deviceSymbols; 1328 } 1329 } 1330 ++hostSymbols; 1331 } 1332 } 1333 bool hasConstant{HasConstant(expr)}; 1334 return (hasConstant || (hostSymbols > 0)) && deviceSymbols > 0; 1335 } 1336 1337 } // namespace Fortran::evaluate 1338 1339 namespace Fortran::semantics { 1340 1341 class Scope; 1342 1343 // If a symbol represents an ENTRY, return the symbol of the main entry 1344 // point to its subprogram. 1345 const Symbol *GetMainEntry(const Symbol *); 1346 1347 // These functions are used in Evaluate so they are defined here rather than in 1348 // Semantics to avoid a link-time dependency on Semantics. 1349 // All of these apply GetUltimate() or ResolveAssociations() to their arguments. 1350 bool IsVariableName(const Symbol &); 1351 bool IsPureProcedure(const Symbol &); 1352 bool IsPureProcedure(const Scope &); 1353 bool IsExplicitlyImpureProcedure(const Symbol &); 1354 bool IsElementalProcedure(const Symbol &); 1355 bool IsFunction(const Symbol &); 1356 bool IsFunction(const Scope &); 1357 bool IsProcedure(const Symbol &); 1358 bool IsProcedure(const Scope &); 1359 bool IsProcedurePointer(const Symbol *); 1360 bool IsProcedurePointer(const Symbol &); 1361 bool IsObjectPointer(const Symbol *); 1362 bool IsAllocatableOrObjectPointer(const Symbol *); 1363 bool IsAutomatic(const Symbol &); 1364 bool IsSaved(const Symbol &); // saved implicitly or explicitly 1365 bool IsDummy(const Symbol &); 1366 bool IsAssumedShape(const Symbol &); 1367 bool IsDeferredShape(const Symbol &); 1368 bool IsFunctionResult(const Symbol &); 1369 bool IsKindTypeParameter(const Symbol &); 1370 bool IsLenTypeParameter(const Symbol &); 1371 bool IsExtensibleType(const DerivedTypeSpec *); 1372 bool IsSequenceOrBindCType(const DerivedTypeSpec *); 1373 bool IsBuiltinDerivedType(const DerivedTypeSpec *derived, const char *name); 1374 bool IsBuiltinCPtr(const Symbol &); 1375 bool IsEventType(const DerivedTypeSpec *); 1376 bool IsLockType(const DerivedTypeSpec *); 1377 bool IsNotifyType(const DerivedTypeSpec *); 1378 // Is this derived type IEEE_FLAG_TYPE from module ISO_IEEE_EXCEPTIONS? 1379 bool IsIeeeFlagType(const DerivedTypeSpec *); 1380 // Is this derived type IEEE_ROUND_TYPE from module ISO_IEEE_ARITHMETIC? 1381 bool IsIeeeRoundType(const DerivedTypeSpec *); 1382 // Is this derived type TEAM_TYPE from module ISO_FORTRAN_ENV? 1383 bool IsTeamType(const DerivedTypeSpec *); 1384 // Is this derived type TEAM_TYPE, C_PTR, or C_FUNPTR? 1385 bool IsBadCoarrayType(const DerivedTypeSpec *); 1386 // Is this derived type either C_PTR or C_FUNPTR from module ISO_C_BINDING 1387 bool IsIsoCType(const DerivedTypeSpec *); 1388 bool IsEventTypeOrLockType(const DerivedTypeSpec *); 1389 inline bool IsAssumedSizeArray(const Symbol &symbol) { 1390 if (const auto *object{symbol.detailsIf<ObjectEntityDetails>()}) { 1391 return (object->isDummy() || symbol.test(Symbol::Flag::CrayPointee)) && 1392 object->shape().CanBeAssumedSize(); 1393 } else if (const auto *assoc{symbol.detailsIf<AssocEntityDetails>()}) { 1394 return assoc->IsAssumedSize(); 1395 } else { 1396 return false; 1397 } 1398 } 1399 1400 // ResolveAssociations() traverses use associations and host associations 1401 // like GetUltimate(), but also resolves through whole variable associations 1402 // with ASSOCIATE(x => y) and related constructs. GetAssociationRoot() 1403 // applies ResolveAssociations() and then, in the case of resolution to 1404 // a construct association with part of a variable that does not involve a 1405 // vector subscript, returns the first symbol of that variable instead 1406 // of the construct entity. 1407 // (E.g., for ASSOCIATE(x => y%z), ResolveAssociations(x) returns x, 1408 // while GetAssociationRoot(x) returns y.) 1409 // In a SELECT RANK construct, ResolveAssociations() stops at a 1410 // RANK(n) or RANK(*) case symbol, but traverses the selector for 1411 // RANK DEFAULT. 1412 const Symbol &ResolveAssociations(const Symbol &); 1413 const Symbol &GetAssociationRoot(const Symbol &); 1414 1415 const Symbol *FindCommonBlockContaining(const Symbol &); 1416 int CountLenParameters(const DerivedTypeSpec &); 1417 int CountNonConstantLenParameters(const DerivedTypeSpec &); 1418 1419 const Symbol &GetUsedModule(const UseDetails &); 1420 const Symbol *FindFunctionResult(const Symbol &); 1421 1422 // Type compatibility predicate: are x and y effectively the same type? 1423 // Uses DynamicType::IsTkCompatible(), which handles the case of distinct 1424 // but identical derived types. 1425 bool AreTkCompatibleTypes(const DeclTypeSpec *x, const DeclTypeSpec *y); 1426 1427 common::IgnoreTKRSet GetIgnoreTKR(const Symbol &); 1428 1429 std::optional<int> GetDummyArgumentNumber(const Symbol *); 1430 1431 const Symbol *FindAncestorModuleProcedure(const Symbol *symInSubmodule); 1432 1433 } // namespace Fortran::semantics 1434 1435 #endif // FORTRAN_EVALUATE_TOOLS_H_ 1436