1 //===-- lib/Evaluate/tools.cpp --------------------------------------------===// 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 #include "flang/Evaluate/tools.h" 10 #include "flang/Common/idioms.h" 11 #include "flang/Evaluate/characteristics.h" 12 #include "flang/Evaluate/traverse.h" 13 #include "flang/Parser/message.h" 14 #include "flang/Semantics/tools.h" 15 #include <algorithm> 16 #include <variant> 17 18 using namespace Fortran::parser::literals; 19 20 namespace Fortran::evaluate { 21 22 // Can x*(a,b) be represented as (x*a,x*b)? This code duplication 23 // of the subexpression "x" cannot (yet?) be reliably undone by 24 // common subexpression elimination in lowering, so it's disabled 25 // here for now to avoid the risk of potential duplication of 26 // expensive subexpressions (e.g., large array expressions, references 27 // to expensive functions) in generate code. 28 static constexpr bool allowOperandDuplication{false}; 29 30 std::optional<Expr<SomeType>> AsGenericExpr(DataRef &&ref) { 31 if (auto dyType{DynamicType::From(ref.GetLastSymbol())}) { 32 return TypedWrapper<Designator, DataRef>(*dyType, std::move(ref)); 33 } else { 34 return std::nullopt; 35 } 36 } 37 38 std::optional<Expr<SomeType>> AsGenericExpr(const Symbol &symbol) { 39 return AsGenericExpr(DataRef{symbol}); 40 } 41 42 Expr<SomeType> Parenthesize(Expr<SomeType> &&expr) { 43 return common::visit( 44 [&](auto &&x) { 45 using T = std::decay_t<decltype(x)>; 46 if constexpr (common::HasMember<T, TypelessExpression>) { 47 return expr; // no parentheses around typeless 48 } else if constexpr (std::is_same_v<T, Expr<SomeDerived>>) { 49 return AsGenericExpr(Parentheses<SomeDerived>{std::move(x)}); 50 } else { 51 return common::visit( 52 [](auto &&y) { 53 using T = ResultType<decltype(y)>; 54 return AsGenericExpr(Parentheses<T>{std::move(y)}); 55 }, 56 std::move(x.u)); 57 } 58 }, 59 std::move(expr.u)); 60 } 61 62 std::optional<DataRef> ExtractDataRef( 63 const ActualArgument &arg, bool intoSubstring, bool intoComplexPart) { 64 return ExtractDataRef(arg.UnwrapExpr(), intoSubstring, intoComplexPart); 65 } 66 67 std::optional<DataRef> ExtractSubstringBase(const Substring &substring) { 68 return common::visit( 69 common::visitors{ 70 [&](const DataRef &x) -> std::optional<DataRef> { return x; }, 71 [&](const StaticDataObject::Pointer &) -> std::optional<DataRef> { 72 return std::nullopt; 73 }, 74 }, 75 substring.parent()); 76 } 77 78 // IsVariable() 79 80 auto IsVariableHelper::operator()(const Symbol &symbol) const -> Result { 81 // ASSOCIATE(x => expr) -- x counts as a variable, but undefinable 82 const Symbol &ultimate{symbol.GetUltimate()}; 83 return !IsNamedConstant(ultimate) && 84 (ultimate.has<semantics::ObjectEntityDetails>() || 85 (ultimate.has<semantics::EntityDetails>() && 86 ultimate.attrs().test(semantics::Attr::TARGET)) || 87 ultimate.has<semantics::AssocEntityDetails>()); 88 } 89 auto IsVariableHelper::operator()(const Component &x) const -> Result { 90 const Symbol &comp{x.GetLastSymbol()}; 91 return (*this)(comp) && (IsPointer(comp) || (*this)(x.base())); 92 } 93 auto IsVariableHelper::operator()(const ArrayRef &x) const -> Result { 94 return (*this)(x.base()); 95 } 96 auto IsVariableHelper::operator()(const Substring &x) const -> Result { 97 return (*this)(x.GetBaseObject()); 98 } 99 auto IsVariableHelper::operator()(const ProcedureDesignator &x) const 100 -> Result { 101 if (const Symbol * symbol{x.GetSymbol()}) { 102 const Symbol *result{FindFunctionResult(*symbol)}; 103 return result && IsPointer(*result) && !IsProcedurePointer(*result); 104 } 105 return false; 106 } 107 108 // Conversions of COMPLEX component expressions to REAL. 109 ConvertRealOperandsResult ConvertRealOperands( 110 parser::ContextualMessages &messages, Expr<SomeType> &&x, 111 Expr<SomeType> &&y, int defaultRealKind) { 112 return common::visit( 113 common::visitors{ 114 [&](Expr<SomeInteger> &&ix, 115 Expr<SomeInteger> &&iy) -> ConvertRealOperandsResult { 116 // Can happen in a CMPLX() constructor. Per F'2018, 117 // both integer operands are converted to default REAL. 118 return {AsSameKindExprs<TypeCategory::Real>( 119 ConvertToKind<TypeCategory::Real>( 120 defaultRealKind, std::move(ix)), 121 ConvertToKind<TypeCategory::Real>( 122 defaultRealKind, std::move(iy)))}; 123 }, 124 [&](Expr<SomeInteger> &&ix, 125 Expr<SomeUnsigned> &&iy) -> ConvertRealOperandsResult { 126 return {AsSameKindExprs<TypeCategory::Real>( 127 ConvertToKind<TypeCategory::Real>( 128 defaultRealKind, std::move(ix)), 129 ConvertToKind<TypeCategory::Real>( 130 defaultRealKind, std::move(iy)))}; 131 }, 132 [&](Expr<SomeUnsigned> &&ix, 133 Expr<SomeInteger> &&iy) -> ConvertRealOperandsResult { 134 return {AsSameKindExprs<TypeCategory::Real>( 135 ConvertToKind<TypeCategory::Real>( 136 defaultRealKind, std::move(ix)), 137 ConvertToKind<TypeCategory::Real>( 138 defaultRealKind, std::move(iy)))}; 139 }, 140 [&](Expr<SomeUnsigned> &&ix, 141 Expr<SomeUnsigned> &&iy) -> ConvertRealOperandsResult { 142 return {AsSameKindExprs<TypeCategory::Real>( 143 ConvertToKind<TypeCategory::Real>( 144 defaultRealKind, std::move(ix)), 145 ConvertToKind<TypeCategory::Real>( 146 defaultRealKind, std::move(iy)))}; 147 }, 148 [&](Expr<SomeInteger> &&ix, 149 Expr<SomeReal> &&ry) -> ConvertRealOperandsResult { 150 return {AsSameKindExprs<TypeCategory::Real>( 151 ConvertTo(ry, std::move(ix)), std::move(ry))}; 152 }, 153 [&](Expr<SomeUnsigned> &&ix, 154 Expr<SomeReal> &&ry) -> ConvertRealOperandsResult { 155 return {AsSameKindExprs<TypeCategory::Real>( 156 ConvertTo(ry, std::move(ix)), std::move(ry))}; 157 }, 158 [&](Expr<SomeReal> &&rx, 159 Expr<SomeInteger> &&iy) -> ConvertRealOperandsResult { 160 return {AsSameKindExprs<TypeCategory::Real>( 161 std::move(rx), ConvertTo(rx, std::move(iy)))}; 162 }, 163 [&](Expr<SomeReal> &&rx, 164 Expr<SomeUnsigned> &&iy) -> ConvertRealOperandsResult { 165 return {AsSameKindExprs<TypeCategory::Real>( 166 std::move(rx), ConvertTo(rx, std::move(iy)))}; 167 }, 168 [&](Expr<SomeReal> &&rx, 169 Expr<SomeReal> &&ry) -> ConvertRealOperandsResult { 170 return {AsSameKindExprs<TypeCategory::Real>( 171 std::move(rx), std::move(ry))}; 172 }, 173 [&](Expr<SomeInteger> &&ix, 174 BOZLiteralConstant &&by) -> ConvertRealOperandsResult { 175 return {AsSameKindExprs<TypeCategory::Real>( 176 ConvertToKind<TypeCategory::Real>( 177 defaultRealKind, std::move(ix)), 178 ConvertToKind<TypeCategory::Real>( 179 defaultRealKind, std::move(by)))}; 180 }, 181 [&](Expr<SomeUnsigned> &&ix, 182 BOZLiteralConstant &&by) -> ConvertRealOperandsResult { 183 return {AsSameKindExprs<TypeCategory::Real>( 184 ConvertToKind<TypeCategory::Real>( 185 defaultRealKind, std::move(ix)), 186 ConvertToKind<TypeCategory::Real>( 187 defaultRealKind, std::move(by)))}; 188 }, 189 [&](BOZLiteralConstant &&bx, 190 Expr<SomeInteger> &&iy) -> ConvertRealOperandsResult { 191 return {AsSameKindExprs<TypeCategory::Real>( 192 ConvertToKind<TypeCategory::Real>( 193 defaultRealKind, std::move(bx)), 194 ConvertToKind<TypeCategory::Real>( 195 defaultRealKind, std::move(iy)))}; 196 }, 197 [&](BOZLiteralConstant &&bx, 198 Expr<SomeUnsigned> &&iy) -> ConvertRealOperandsResult { 199 return {AsSameKindExprs<TypeCategory::Real>( 200 ConvertToKind<TypeCategory::Real>( 201 defaultRealKind, std::move(bx)), 202 ConvertToKind<TypeCategory::Real>( 203 defaultRealKind, std::move(iy)))}; 204 }, 205 [&](Expr<SomeReal> &&rx, 206 BOZLiteralConstant &&by) -> ConvertRealOperandsResult { 207 return {AsSameKindExprs<TypeCategory::Real>( 208 std::move(rx), ConvertTo(rx, std::move(by)))}; 209 }, 210 [&](BOZLiteralConstant &&bx, 211 Expr<SomeReal> &&ry) -> ConvertRealOperandsResult { 212 return {AsSameKindExprs<TypeCategory::Real>( 213 ConvertTo(ry, std::move(bx)), std::move(ry))}; 214 }, 215 [&](auto &&, auto &&) -> ConvertRealOperandsResult { // C718 216 messages.Say( 217 "operands must be INTEGER, UNSIGNED, REAL, or BOZ"_err_en_US); 218 return std::nullopt; 219 }, 220 }, 221 std::move(x.u), std::move(y.u)); 222 } 223 224 // Helpers for NumericOperation and its subroutines below. 225 static std::optional<Expr<SomeType>> NoExpr() { return std::nullopt; } 226 227 template <TypeCategory CAT> 228 std::optional<Expr<SomeType>> Package(Expr<SomeKind<CAT>> &&catExpr) { 229 return {AsGenericExpr(std::move(catExpr))}; 230 } 231 template <TypeCategory CAT> 232 std::optional<Expr<SomeType>> Package( 233 std::optional<Expr<SomeKind<CAT>>> &&catExpr) { 234 if (catExpr) { 235 return {AsGenericExpr(std::move(*catExpr))}; 236 } else { 237 return std::nullopt; 238 } 239 } 240 241 // Mixed REAL+INTEGER operations. REAL**INTEGER is a special case that 242 // does not require conversion of the exponent expression. 243 template <template <typename> class OPR> 244 std::optional<Expr<SomeType>> MixedRealLeft( 245 Expr<SomeReal> &&rx, Expr<SomeInteger> &&iy) { 246 return Package(common::visit( 247 [&](auto &&rxk) -> Expr<SomeReal> { 248 using resultType = ResultType<decltype(rxk)>; 249 if constexpr (std::is_same_v<OPR<resultType>, Power<resultType>>) { 250 return AsCategoryExpr( 251 RealToIntPower<resultType>{std::move(rxk), std::move(iy)}); 252 } 253 // G++ 8.1.0 emits bogus warnings about missing return statements if 254 // this statement is wrapped in an "else", as it should be. 255 return AsCategoryExpr(OPR<resultType>{ 256 std::move(rxk), ConvertToType<resultType>(std::move(iy))}); 257 }, 258 std::move(rx.u))); 259 } 260 261 template <int KIND> 262 Expr<SomeComplex> MakeComplex(Expr<Type<TypeCategory::Real, KIND>> &&re, 263 Expr<Type<TypeCategory::Real, KIND>> &&im) { 264 return AsCategoryExpr(ComplexConstructor<KIND>{std::move(re), std::move(im)}); 265 } 266 267 std::optional<Expr<SomeComplex>> ConstructComplex( 268 parser::ContextualMessages &messages, Expr<SomeType> &&real, 269 Expr<SomeType> &&imaginary, int defaultRealKind) { 270 if (auto converted{ConvertRealOperands( 271 messages, std::move(real), std::move(imaginary), defaultRealKind)}) { 272 return {common::visit( 273 [](auto &&pair) { 274 return MakeComplex(std::move(pair[0]), std::move(pair[1])); 275 }, 276 std::move(*converted))}; 277 } 278 return std::nullopt; 279 } 280 281 std::optional<Expr<SomeComplex>> ConstructComplex( 282 parser::ContextualMessages &messages, std::optional<Expr<SomeType>> &&real, 283 std::optional<Expr<SomeType>> &&imaginary, int defaultRealKind) { 284 if (auto parts{common::AllPresent(std::move(real), std::move(imaginary))}) { 285 return ConstructComplex(messages, std::get<0>(std::move(*parts)), 286 std::get<1>(std::move(*parts)), defaultRealKind); 287 } 288 return std::nullopt; 289 } 290 291 // Extracts the real or imaginary part of the result of a COMPLEX 292 // expression, when that expression is simple enough to be duplicated. 293 template <bool GET_IMAGINARY> struct ComplexPartExtractor { 294 template <typename A> static std::optional<Expr<SomeReal>> Get(const A &) { 295 return std::nullopt; 296 } 297 298 template <int KIND> 299 static std::optional<Expr<SomeReal>> Get( 300 const Parentheses<Type<TypeCategory::Complex, KIND>> &kz) { 301 if (auto x{Get(kz.left())}) { 302 return AsGenericExpr(AsSpecificExpr( 303 Parentheses<Type<TypeCategory::Real, KIND>>{std::move(*x)})); 304 } else { 305 return std::nullopt; 306 } 307 } 308 309 template <int KIND> 310 static std::optional<Expr<SomeReal>> Get( 311 const Negate<Type<TypeCategory::Complex, KIND>> &kz) { 312 if (auto x{Get(kz.left())}) { 313 return AsGenericExpr(AsSpecificExpr( 314 Negate<Type<TypeCategory::Real, KIND>>{std::move(*x)})); 315 } else { 316 return std::nullopt; 317 } 318 } 319 320 template <int KIND> 321 static std::optional<Expr<SomeReal>> Get( 322 const Convert<Type<TypeCategory::Complex, KIND>, TypeCategory::Complex> 323 &kz) { 324 if (auto x{Get(kz.left())}) { 325 return AsGenericExpr(AsSpecificExpr( 326 Convert<Type<TypeCategory::Real, KIND>, TypeCategory::Real>{ 327 AsGenericExpr(std::move(*x))})); 328 } else { 329 return std::nullopt; 330 } 331 } 332 333 template <int KIND> 334 static std::optional<Expr<SomeReal>> Get(const ComplexConstructor<KIND> &kz) { 335 return GET_IMAGINARY ? Get(kz.right()) : Get(kz.left()); 336 } 337 338 template <int KIND> 339 static std::optional<Expr<SomeReal>> Get( 340 const Constant<Type<TypeCategory::Complex, KIND>> &kz) { 341 if (auto cz{kz.GetScalarValue()}) { 342 return AsGenericExpr( 343 AsSpecificExpr(GET_IMAGINARY ? cz->AIMAG() : cz->REAL())); 344 } else { 345 return std::nullopt; 346 } 347 } 348 349 template <int KIND> 350 static std::optional<Expr<SomeReal>> Get( 351 const Designator<Type<TypeCategory::Complex, KIND>> &kz) { 352 if (const auto *symbolRef{std::get_if<SymbolRef>(&kz.u)}) { 353 return AsGenericExpr(AsSpecificExpr( 354 Designator<Type<TypeCategory::Complex, KIND>>{ComplexPart{ 355 DataRef{*symbolRef}, 356 GET_IMAGINARY ? ComplexPart::Part::IM : ComplexPart::Part::RE}})); 357 } else { 358 return std::nullopt; 359 } 360 } 361 362 template <int KIND> 363 static std::optional<Expr<SomeReal>> Get( 364 const Expr<Type<TypeCategory::Complex, KIND>> &kz) { 365 return Get(kz.u); 366 } 367 368 static std::optional<Expr<SomeReal>> Get(const Expr<SomeComplex> &z) { 369 return Get(z.u); 370 } 371 }; 372 373 // Convert REAL to COMPLEX of the same kind. Preserving the real operand kind 374 // and then applying complex operand promotion rules allows the result to have 375 // the highest precision of REAL and COMPLEX operands as required by Fortran 376 // 2018 10.9.1.3. 377 Expr<SomeComplex> PromoteRealToComplex(Expr<SomeReal> &&someX) { 378 return common::visit( 379 [](auto &&x) { 380 using RT = ResultType<decltype(x)>; 381 return AsCategoryExpr(ComplexConstructor<RT::kind>{ 382 std::move(x), AsExpr(Constant<RT>{Scalar<RT>{}})}); 383 }, 384 std::move(someX.u)); 385 } 386 387 // Handle mixed COMPLEX+REAL (or INTEGER) operations in a better way 388 // than just converting the second operand to COMPLEX and performing the 389 // corresponding COMPLEX+COMPLEX operation. 390 template <template <typename> class OPR, TypeCategory RCAT> 391 std::optional<Expr<SomeType>> MixedComplexLeft( 392 parser::ContextualMessages &messages, const Expr<SomeComplex> &zx, 393 const Expr<SomeKind<RCAT>> &iry, [[maybe_unused]] int defaultRealKind) { 394 if constexpr (RCAT == TypeCategory::Integer && 395 std::is_same_v<OPR<LargestReal>, Power<LargestReal>>) { 396 // COMPLEX**INTEGER is a special case that doesn't convert the exponent. 397 return Package(common::visit( 398 [&](const auto &zxk) { 399 using Ty = ResultType<decltype(zxk)>; 400 return AsCategoryExpr(AsExpr( 401 RealToIntPower<Ty>{common::Clone(zxk), common::Clone(iry)})); 402 }, 403 zx.u)); 404 } 405 std::optional<Expr<SomeReal>> zr{ComplexPartExtractor<false>{}.Get(zx)}; 406 std::optional<Expr<SomeReal>> zi{ComplexPartExtractor<true>{}.Get(zx)}; 407 if (!zr || !zi) { 408 } else if constexpr (std::is_same_v<OPR<LargestReal>, Add<LargestReal>> || 409 std::is_same_v<OPR<LargestReal>, Subtract<LargestReal>>) { 410 // (a,b) + x -> (a+x, b) 411 // (a,b) - x -> (a-x, b) 412 if (std::optional<Expr<SomeType>> rr{ 413 NumericOperation<OPR>(messages, AsGenericExpr(std::move(*zr)), 414 AsGenericExpr(common::Clone(iry)), defaultRealKind)}) { 415 return Package(ConstructComplex(messages, std::move(*rr), 416 AsGenericExpr(std::move(*zi)), defaultRealKind)); 417 } 418 } else if constexpr (allowOperandDuplication && 419 (std::is_same_v<OPR<LargestReal>, Multiply<LargestReal>> || 420 std::is_same_v<OPR<LargestReal>, Divide<LargestReal>>)) { 421 // (a,b) * x -> (a*x, b*x) 422 // (a,b) / x -> (a/x, b/x) 423 auto copy{iry}; 424 auto rr{NumericOperation<OPR>(messages, AsGenericExpr(std::move(*zr)), 425 AsGenericExpr(common::Clone(iry)), defaultRealKind)}; 426 auto ri{NumericOperation<OPR>(messages, AsGenericExpr(std::move(*zi)), 427 AsGenericExpr(std::move(copy)), defaultRealKind)}; 428 if (auto parts{common::AllPresent(std::move(rr), std::move(ri))}) { 429 return Package(ConstructComplex(messages, std::get<0>(std::move(*parts)), 430 std::get<1>(std::move(*parts)), defaultRealKind)); 431 } 432 } 433 return std::nullopt; 434 } 435 436 // Mixed COMPLEX operations with the COMPLEX operand on the right. 437 // x + (a,b) -> (x+a, b) 438 // x - (a,b) -> (x-a, -b) 439 // x * (a,b) -> (x*a, x*b) 440 // x / (a,b) -> (x,0) / (a,b) (and **) 441 template <template <typename> class OPR, TypeCategory LCAT> 442 std::optional<Expr<SomeType>> MixedComplexRight( 443 parser::ContextualMessages &messages, const Expr<SomeKind<LCAT>> &irx, 444 const Expr<SomeComplex> &zy, [[maybe_unused]] int defaultRealKind) { 445 if constexpr (std::is_same_v<OPR<LargestReal>, Add<LargestReal>>) { 446 // x + (a,b) -> (a,b) + x -> (a+x, b) 447 return MixedComplexLeft<OPR, LCAT>(messages, zy, irx, defaultRealKind); 448 } else if constexpr (allowOperandDuplication && 449 std::is_same_v<OPR<LargestReal>, Multiply<LargestReal>>) { 450 // x * (a,b) -> (a,b) * x -> (a*x, b*x) 451 return MixedComplexLeft<OPR, LCAT>(messages, zy, irx, defaultRealKind); 452 } else if constexpr (std::is_same_v<OPR<LargestReal>, 453 Subtract<LargestReal>>) { 454 // x - (a,b) -> (x-a, -b) 455 std::optional<Expr<SomeReal>> zr{ComplexPartExtractor<false>{}.Get(zy)}; 456 std::optional<Expr<SomeReal>> zi{ComplexPartExtractor<true>{}.Get(zy)}; 457 if (zr && zi) { 458 if (std::optional<Expr<SomeType>> rr{NumericOperation<Subtract>(messages, 459 AsGenericExpr(common::Clone(irx)), AsGenericExpr(std::move(*zr)), 460 defaultRealKind)}) { 461 return Package(ConstructComplex(messages, std::move(*rr), 462 AsGenericExpr(-std::move(*zi)), defaultRealKind)); 463 } 464 } 465 } 466 return std::nullopt; 467 } 468 469 // Promotes REAL(rk) and COMPLEX(zk) operands COMPLEX(max(rk,zk)) 470 // then combine them with an operator. 471 template <template <typename> class OPR, TypeCategory XCAT, TypeCategory YCAT> 472 Expr<SomeComplex> PromoteMixedComplexReal( 473 Expr<SomeKind<XCAT>> &&x, Expr<SomeKind<YCAT>> &&y) { 474 static_assert(XCAT == TypeCategory::Complex || YCAT == TypeCategory::Complex); 475 static_assert(XCAT == TypeCategory::Real || YCAT == TypeCategory::Real); 476 return common::visit( 477 [&](const auto &kx, const auto &ky) { 478 constexpr int maxKind{std::max( 479 ResultType<decltype(kx)>::kind, ResultType<decltype(ky)>::kind)}; 480 using ZTy = Type<TypeCategory::Complex, maxKind>; 481 return Expr<SomeComplex>{ 482 Expr<ZTy>{OPR<ZTy>{ConvertToType<ZTy>(std::move(x)), 483 ConvertToType<ZTy>(std::move(y))}}}; 484 }, 485 x.u, y.u); 486 } 487 488 // N.B. When a "typeless" BOZ literal constant appears as one (not both!) of 489 // the operands to a dyadic operation where one is permitted, it assumes the 490 // type and kind of the other operand. 491 template <template <typename> class OPR, bool CAN_BE_UNSIGNED> 492 std::optional<Expr<SomeType>> NumericOperation( 493 parser::ContextualMessages &messages, Expr<SomeType> &&x, 494 Expr<SomeType> &&y, int defaultRealKind) { 495 return common::visit( 496 common::visitors{ 497 [](Expr<SomeInteger> &&ix, Expr<SomeInteger> &&iy) { 498 return Package(PromoteAndCombine<OPR, TypeCategory::Integer>( 499 std::move(ix), std::move(iy))); 500 }, 501 [](Expr<SomeReal> &&rx, Expr<SomeReal> &&ry) { 502 return Package(PromoteAndCombine<OPR, TypeCategory::Real>( 503 std::move(rx), std::move(ry))); 504 }, 505 [&](Expr<SomeUnsigned> &&ix, Expr<SomeUnsigned> &&iy) { 506 if constexpr (CAN_BE_UNSIGNED) { 507 return Package(PromoteAndCombine<OPR, TypeCategory::Unsigned>( 508 std::move(ix), std::move(iy))); 509 } else { 510 messages.Say("Operands must not be UNSIGNED"_err_en_US); 511 return NoExpr(); 512 } 513 }, 514 // Mixed REAL/INTEGER operations 515 [](Expr<SomeReal> &&rx, Expr<SomeInteger> &&iy) { 516 return MixedRealLeft<OPR>(std::move(rx), std::move(iy)); 517 }, 518 [](Expr<SomeInteger> &&ix, Expr<SomeReal> &&ry) { 519 return Package(common::visit( 520 [&](auto &&ryk) -> Expr<SomeReal> { 521 using resultType = ResultType<decltype(ryk)>; 522 return AsCategoryExpr( 523 OPR<resultType>{ConvertToType<resultType>(std::move(ix)), 524 std::move(ryk)}); 525 }, 526 std::move(ry.u))); 527 }, 528 // Homogeneous and mixed COMPLEX operations 529 [](Expr<SomeComplex> &&zx, Expr<SomeComplex> &&zy) { 530 return Package(PromoteAndCombine<OPR, TypeCategory::Complex>( 531 std::move(zx), std::move(zy))); 532 }, 533 [&](Expr<SomeComplex> &&zx, Expr<SomeInteger> &&iy) { 534 if (auto result{ 535 MixedComplexLeft<OPR>(messages, zx, iy, defaultRealKind)}) { 536 return result; 537 } else { 538 return Package(PromoteAndCombine<OPR, TypeCategory::Complex>( 539 std::move(zx), ConvertTo(zx, std::move(iy)))); 540 } 541 }, 542 [&](Expr<SomeComplex> &&zx, Expr<SomeReal> &&ry) { 543 if (auto result{ 544 MixedComplexLeft<OPR>(messages, zx, ry, defaultRealKind)}) { 545 return result; 546 } else { 547 return Package( 548 PromoteMixedComplexReal<OPR>(std::move(zx), std::move(ry))); 549 } 550 }, 551 [&](Expr<SomeInteger> &&ix, Expr<SomeComplex> &&zy) { 552 if (auto result{MixedComplexRight<OPR>( 553 messages, ix, zy, defaultRealKind)}) { 554 return result; 555 } else { 556 return Package(PromoteAndCombine<OPR, TypeCategory::Complex>( 557 ConvertTo(zy, std::move(ix)), std::move(zy))); 558 } 559 }, 560 [&](Expr<SomeReal> &&rx, Expr<SomeComplex> &&zy) { 561 if (auto result{MixedComplexRight<OPR>( 562 messages, rx, zy, defaultRealKind)}) { 563 return result; 564 } else { 565 return Package( 566 PromoteMixedComplexReal<OPR>(std::move(rx), std::move(zy))); 567 } 568 }, 569 // Operations with one typeless operand 570 [&](BOZLiteralConstant &&bx, Expr<SomeInteger> &&iy) { 571 return NumericOperation<OPR, CAN_BE_UNSIGNED>(messages, 572 AsGenericExpr(ConvertTo(iy, std::move(bx))), std::move(y), 573 defaultRealKind); 574 }, 575 [&](BOZLiteralConstant &&bx, Expr<SomeUnsigned> &&iy) { 576 return NumericOperation<OPR, CAN_BE_UNSIGNED>(messages, 577 AsGenericExpr(ConvertTo(iy, std::move(bx))), std::move(y), 578 defaultRealKind); 579 }, 580 [&](BOZLiteralConstant &&bx, Expr<SomeReal> &&ry) { 581 return NumericOperation<OPR, CAN_BE_UNSIGNED>(messages, 582 AsGenericExpr(ConvertTo(ry, std::move(bx))), std::move(y), 583 defaultRealKind); 584 }, 585 [&](Expr<SomeInteger> &&ix, BOZLiteralConstant &&by) { 586 return NumericOperation<OPR, CAN_BE_UNSIGNED>(messages, 587 std::move(x), AsGenericExpr(ConvertTo(ix, std::move(by))), 588 defaultRealKind); 589 }, 590 [&](Expr<SomeUnsigned> &&ix, BOZLiteralConstant &&by) { 591 return NumericOperation<OPR, CAN_BE_UNSIGNED>(messages, 592 std::move(x), AsGenericExpr(ConvertTo(ix, std::move(by))), 593 defaultRealKind); 594 }, 595 [&](Expr<SomeReal> &&rx, BOZLiteralConstant &&by) { 596 return NumericOperation<OPR, CAN_BE_UNSIGNED>(messages, 597 std::move(x), AsGenericExpr(ConvertTo(rx, std::move(by))), 598 defaultRealKind); 599 }, 600 // Error cases 601 [&](Expr<SomeUnsigned> &&, auto &&) { 602 messages.Say("Both operands must be UNSIGNED"_err_en_US); 603 return NoExpr(); 604 }, 605 [&](auto &&, Expr<SomeUnsigned> &&) { 606 messages.Say("Both operands must be UNSIGNED"_err_en_US); 607 return NoExpr(); 608 }, 609 [&](auto &&, auto &&) { 610 messages.Say("non-numeric operands to numeric operation"_err_en_US); 611 return NoExpr(); 612 }, 613 }, 614 std::move(x.u), std::move(y.u)); 615 } 616 617 template std::optional<Expr<SomeType>> NumericOperation<Power, false>( 618 parser::ContextualMessages &, Expr<SomeType> &&, Expr<SomeType> &&, 619 int defaultRealKind); 620 template std::optional<Expr<SomeType>> NumericOperation<Multiply>( 621 parser::ContextualMessages &, Expr<SomeType> &&, Expr<SomeType> &&, 622 int defaultRealKind); 623 template std::optional<Expr<SomeType>> NumericOperation<Divide>( 624 parser::ContextualMessages &, Expr<SomeType> &&, Expr<SomeType> &&, 625 int defaultRealKind); 626 template std::optional<Expr<SomeType>> NumericOperation<Add>( 627 parser::ContextualMessages &, Expr<SomeType> &&, Expr<SomeType> &&, 628 int defaultRealKind); 629 template std::optional<Expr<SomeType>> NumericOperation<Subtract>( 630 parser::ContextualMessages &, Expr<SomeType> &&, Expr<SomeType> &&, 631 int defaultRealKind); 632 633 std::optional<Expr<SomeType>> Negation( 634 parser::ContextualMessages &messages, Expr<SomeType> &&x) { 635 return common::visit( 636 common::visitors{ 637 [&](BOZLiteralConstant &&) { 638 messages.Say("BOZ literal cannot be negated"_err_en_US); 639 return NoExpr(); 640 }, 641 [&](NullPointer &&) { 642 messages.Say("NULL() cannot be negated"_err_en_US); 643 return NoExpr(); 644 }, 645 [&](ProcedureDesignator &&) { 646 messages.Say("Subroutine cannot be negated"_err_en_US); 647 return NoExpr(); 648 }, 649 [&](ProcedureRef &&) { 650 messages.Say("Pointer to subroutine cannot be negated"_err_en_US); 651 return NoExpr(); 652 }, 653 [&](Expr<SomeInteger> &&x) { return Package(-std::move(x)); }, 654 [&](Expr<SomeReal> &&x) { return Package(-std::move(x)); }, 655 [&](Expr<SomeComplex> &&x) { return Package(-std::move(x)); }, 656 [&](Expr<SomeCharacter> &&) { 657 messages.Say("CHARACTER cannot be negated"_err_en_US); 658 return NoExpr(); 659 }, 660 [&](Expr<SomeLogical> &&) { 661 messages.Say("LOGICAL cannot be negated"_err_en_US); 662 return NoExpr(); 663 }, 664 [&](Expr<SomeUnsigned> &&x) { return Package(-std::move(x)); }, 665 [&](Expr<SomeDerived> &&) { 666 messages.Say("Operand cannot be negated"_err_en_US); 667 return NoExpr(); 668 }, 669 }, 670 std::move(x.u)); 671 } 672 673 Expr<SomeLogical> LogicalNegation(Expr<SomeLogical> &&x) { 674 return common::visit( 675 [](auto &&xk) { return AsCategoryExpr(LogicalNegation(std::move(xk))); }, 676 std::move(x.u)); 677 } 678 679 template <TypeCategory CAT> 680 Expr<LogicalResult> PromoteAndRelate( 681 RelationalOperator opr, Expr<SomeKind<CAT>> &&x, Expr<SomeKind<CAT>> &&y) { 682 return common::visit( 683 [=](auto &&xy) { 684 return PackageRelation(opr, std::move(xy[0]), std::move(xy[1])); 685 }, 686 AsSameKindExprs(std::move(x), std::move(y))); 687 } 688 689 std::optional<Expr<LogicalResult>> Relate(parser::ContextualMessages &messages, 690 RelationalOperator opr, Expr<SomeType> &&x, Expr<SomeType> &&y) { 691 return common::visit( 692 common::visitors{ 693 [=](Expr<SomeInteger> &&ix, 694 Expr<SomeInteger> &&iy) -> std::optional<Expr<LogicalResult>> { 695 return PromoteAndRelate(opr, std::move(ix), std::move(iy)); 696 }, 697 [=](Expr<SomeUnsigned> &&ix, 698 Expr<SomeUnsigned> &&iy) -> std::optional<Expr<LogicalResult>> { 699 return PromoteAndRelate(opr, std::move(ix), std::move(iy)); 700 }, 701 [=](Expr<SomeReal> &&rx, 702 Expr<SomeReal> &&ry) -> std::optional<Expr<LogicalResult>> { 703 return PromoteAndRelate(opr, std::move(rx), std::move(ry)); 704 }, 705 [&](Expr<SomeReal> &&rx, Expr<SomeInteger> &&iy) { 706 return Relate(messages, opr, std::move(x), 707 AsGenericExpr(ConvertTo(rx, std::move(iy)))); 708 }, 709 [&](Expr<SomeInteger> &&ix, Expr<SomeReal> &&ry) { 710 return Relate(messages, opr, 711 AsGenericExpr(ConvertTo(ry, std::move(ix))), std::move(y)); 712 }, 713 [&](Expr<SomeComplex> &&zx, 714 Expr<SomeComplex> &&zy) -> std::optional<Expr<LogicalResult>> { 715 if (opr == RelationalOperator::EQ || 716 opr == RelationalOperator::NE) { 717 return PromoteAndRelate(opr, std::move(zx), std::move(zy)); 718 } else { 719 messages.Say( 720 "COMPLEX data may be compared only for equality"_err_en_US); 721 return std::nullopt; 722 } 723 }, 724 [&](Expr<SomeComplex> &&zx, Expr<SomeInteger> &&iy) { 725 return Relate(messages, opr, std::move(x), 726 AsGenericExpr(ConvertTo(zx, std::move(iy)))); 727 }, 728 [&](Expr<SomeComplex> &&zx, Expr<SomeReal> &&ry) { 729 return Relate(messages, opr, std::move(x), 730 AsGenericExpr(ConvertTo(zx, std::move(ry)))); 731 }, 732 [&](Expr<SomeInteger> &&ix, Expr<SomeComplex> &&zy) { 733 return Relate(messages, opr, 734 AsGenericExpr(ConvertTo(zy, std::move(ix))), std::move(y)); 735 }, 736 [&](Expr<SomeReal> &&rx, Expr<SomeComplex> &&zy) { 737 return Relate(messages, opr, 738 AsGenericExpr(ConvertTo(zy, std::move(rx))), std::move(y)); 739 }, 740 [&](Expr<SomeCharacter> &&cx, Expr<SomeCharacter> &&cy) { 741 return common::visit( 742 [&](auto &&cxk, 743 auto &&cyk) -> std::optional<Expr<LogicalResult>> { 744 using Ty = ResultType<decltype(cxk)>; 745 if constexpr (std::is_same_v<Ty, ResultType<decltype(cyk)>>) { 746 return PackageRelation(opr, std::move(cxk), std::move(cyk)); 747 } else { 748 messages.Say( 749 "CHARACTER operands do not have same KIND"_err_en_US); 750 return std::nullopt; 751 } 752 }, 753 std::move(cx.u), std::move(cy.u)); 754 }, 755 // Default case 756 [&](auto &&, auto &&) { 757 DIE("invalid types for relational operator"); 758 return std::optional<Expr<LogicalResult>>{}; 759 }, 760 }, 761 std::move(x.u), std::move(y.u)); 762 } 763 764 Expr<SomeLogical> BinaryLogicalOperation( 765 LogicalOperator opr, Expr<SomeLogical> &&x, Expr<SomeLogical> &&y) { 766 CHECK(opr != LogicalOperator::Not); 767 return common::visit( 768 [=](auto &&xy) { 769 using Ty = ResultType<decltype(xy[0])>; 770 return Expr<SomeLogical>{BinaryLogicalOperation<Ty::kind>( 771 opr, std::move(xy[0]), std::move(xy[1]))}; 772 }, 773 AsSameKindExprs(std::move(x), std::move(y))); 774 } 775 776 template <TypeCategory TO> 777 std::optional<Expr<SomeType>> ConvertToNumeric(int kind, Expr<SomeType> &&x) { 778 static_assert(common::IsNumericTypeCategory(TO)); 779 return common::visit( 780 [=](auto &&cx) -> std::optional<Expr<SomeType>> { 781 using cxType = std::decay_t<decltype(cx)>; 782 if constexpr (!common::HasMember<cxType, TypelessExpression>) { 783 if constexpr (IsNumericTypeCategory(ResultType<cxType>::category)) { 784 return Expr<SomeType>{ConvertToKind<TO>(kind, std::move(cx))}; 785 } 786 } 787 return std::nullopt; 788 }, 789 std::move(x.u)); 790 } 791 792 std::optional<Expr<SomeType>> ConvertToType( 793 const DynamicType &type, Expr<SomeType> &&x) { 794 if (type.IsTypelessIntrinsicArgument()) { 795 return std::nullopt; 796 } 797 switch (type.category()) { 798 case TypeCategory::Integer: 799 if (auto *boz{std::get_if<BOZLiteralConstant>(&x.u)}) { 800 // Extension to C7109: allow BOZ literals to appear in integer contexts 801 // when the type is unambiguous. 802 return Expr<SomeType>{ 803 ConvertToKind<TypeCategory::Integer>(type.kind(), std::move(*boz))}; 804 } 805 return ConvertToNumeric<TypeCategory::Integer>(type.kind(), std::move(x)); 806 case TypeCategory::Unsigned: 807 if (auto *boz{std::get_if<BOZLiteralConstant>(&x.u)}) { 808 return Expr<SomeType>{ 809 ConvertToKind<TypeCategory::Unsigned>(type.kind(), std::move(*boz))}; 810 } 811 if (auto *cx{UnwrapExpr<Expr<SomeUnsigned>>(x)}) { 812 return Expr<SomeType>{ 813 ConvertToKind<TypeCategory::Unsigned>(type.kind(), std::move(*cx))}; 814 } 815 break; 816 case TypeCategory::Real: 817 if (auto *boz{std::get_if<BOZLiteralConstant>(&x.u)}) { 818 return Expr<SomeType>{ 819 ConvertToKind<TypeCategory::Real>(type.kind(), std::move(*boz))}; 820 } 821 return ConvertToNumeric<TypeCategory::Real>(type.kind(), std::move(x)); 822 case TypeCategory::Complex: 823 return ConvertToNumeric<TypeCategory::Complex>(type.kind(), std::move(x)); 824 case TypeCategory::Character: 825 if (auto *cx{UnwrapExpr<Expr<SomeCharacter>>(x)}) { 826 auto converted{ 827 ConvertToKind<TypeCategory::Character>(type.kind(), std::move(*cx))}; 828 if (auto length{type.GetCharLength()}) { 829 converted = common::visit( 830 [&](auto &&x) { 831 using CharacterType = ResultType<decltype(x)>; 832 return Expr<SomeCharacter>{ 833 Expr<CharacterType>{SetLength<CharacterType::kind>{ 834 std::move(x), std::move(*length)}}}; 835 }, 836 std::move(converted.u)); 837 } 838 return Expr<SomeType>{std::move(converted)}; 839 } 840 break; 841 case TypeCategory::Logical: 842 if (auto *cx{UnwrapExpr<Expr<SomeLogical>>(x)}) { 843 return Expr<SomeType>{ 844 ConvertToKind<TypeCategory::Logical>(type.kind(), std::move(*cx))}; 845 } 846 break; 847 case TypeCategory::Derived: 848 if (auto fromType{x.GetType()}) { 849 if (type.IsTkCompatibleWith(*fromType)) { 850 // "x" could be assigned or passed to "type", or appear in a 851 // structure constructor as a value for a component with "type" 852 return std::move(x); 853 } 854 } 855 break; 856 } 857 return std::nullopt; 858 } 859 860 std::optional<Expr<SomeType>> ConvertToType( 861 const DynamicType &to, std::optional<Expr<SomeType>> &&x) { 862 if (x) { 863 return ConvertToType(to, std::move(*x)); 864 } else { 865 return std::nullopt; 866 } 867 } 868 869 std::optional<Expr<SomeType>> ConvertToType( 870 const Symbol &symbol, Expr<SomeType> &&x) { 871 if (auto symType{DynamicType::From(symbol)}) { 872 return ConvertToType(*symType, std::move(x)); 873 } 874 return std::nullopt; 875 } 876 877 std::optional<Expr<SomeType>> ConvertToType( 878 const Symbol &to, std::optional<Expr<SomeType>> &&x) { 879 if (x) { 880 return ConvertToType(to, std::move(*x)); 881 } else { 882 return std::nullopt; 883 } 884 } 885 886 bool IsAssumedRank(const Symbol &original) { 887 if (const auto *assoc{original.detailsIf<semantics::AssocEntityDetails>()}) { 888 if (assoc->rank()) { 889 return false; // in RANK(n) or RANK(*) 890 } else if (assoc->IsAssumedRank()) { 891 return true; // RANK DEFAULT 892 } 893 } 894 const Symbol &symbol{semantics::ResolveAssociations(original)}; 895 const auto *object{symbol.detailsIf<semantics::ObjectEntityDetails>()}; 896 return object && object->IsAssumedRank(); 897 } 898 899 bool IsAssumedRank(const ActualArgument &arg) { 900 if (const auto *expr{arg.UnwrapExpr()}) { 901 return IsAssumedRank(*expr); 902 } else { 903 const Symbol *assumedTypeDummy{arg.GetAssumedTypeDummy()}; 904 CHECK(assumedTypeDummy); 905 return IsAssumedRank(*assumedTypeDummy); 906 } 907 } 908 909 int GetCorank(const ActualArgument &arg) { 910 const auto *expr{arg.UnwrapExpr()}; 911 return GetCorank(*expr); 912 } 913 914 bool IsProcedureDesignator(const Expr<SomeType> &expr) { 915 return std::holds_alternative<ProcedureDesignator>(expr.u); 916 } 917 bool IsFunctionDesignator(const Expr<SomeType> &expr) { 918 const auto *designator{std::get_if<ProcedureDesignator>(&expr.u)}; 919 return designator && designator->GetType().has_value(); 920 } 921 922 bool IsPointer(const Expr<SomeType> &expr) { 923 return IsObjectPointer(expr) || IsProcedurePointer(expr); 924 } 925 926 bool IsProcedurePointer(const Expr<SomeType> &expr) { 927 if (IsNullProcedurePointer(expr)) { 928 return true; 929 } else if (const auto *funcRef{UnwrapProcedureRef(expr)}) { 930 if (const Symbol * proc{funcRef->proc().GetSymbol()}) { 931 const Symbol *result{FindFunctionResult(*proc)}; 932 return result && IsProcedurePointer(*result); 933 } else { 934 return false; 935 } 936 } else if (const auto *proc{std::get_if<ProcedureDesignator>(&expr.u)}) { 937 return IsProcedurePointer(proc->GetSymbol()); 938 } else { 939 return false; 940 } 941 } 942 943 bool IsProcedure(const Expr<SomeType> &expr) { 944 return IsProcedureDesignator(expr) || IsProcedurePointer(expr); 945 } 946 947 bool IsProcedurePointerTarget(const Expr<SomeType> &expr) { 948 return common::visit(common::visitors{ 949 [](const NullPointer &) { return true; }, 950 [](const ProcedureDesignator &) { return true; }, 951 [](const ProcedureRef &) { return true; }, 952 [&](const auto &) { 953 const Symbol *last{GetLastSymbol(expr)}; 954 return last && IsProcedurePointer(*last); 955 }, 956 }, 957 expr.u); 958 } 959 960 bool IsObjectPointer(const Expr<SomeType> &expr) { 961 if (IsNullObjectPointer(expr)) { 962 return true; 963 } else if (IsProcedurePointerTarget(expr)) { 964 return false; 965 } else if (const auto *funcRef{UnwrapProcedureRef(expr)}) { 966 return IsVariable(*funcRef); 967 } else if (const Symbol * symbol{UnwrapWholeSymbolOrComponentDataRef(expr)}) { 968 return IsPointer(symbol->GetUltimate()); 969 } else { 970 return false; 971 } 972 } 973 974 // IsNullPointer() & variations 975 976 template <bool IS_PROC_PTR> struct IsNullPointerHelper { 977 template <typename A> bool operator()(const A &) const { return false; } 978 bool operator()(const ProcedureRef &call) const { 979 if constexpr (IS_PROC_PTR) { 980 const auto *intrinsic{call.proc().GetSpecificIntrinsic()}; 981 return intrinsic && 982 intrinsic->characteristics.value().attrs.test( 983 characteristics::Procedure::Attr::NullPointer); 984 } else { 985 return false; 986 } 987 } 988 template <typename T> bool operator()(const FunctionRef<T> &call) const { 989 if constexpr (IS_PROC_PTR) { 990 return false; 991 } else { 992 const auto *intrinsic{call.proc().GetSpecificIntrinsic()}; 993 return intrinsic && 994 intrinsic->characteristics.value().attrs.test( 995 characteristics::Procedure::Attr::NullPointer); 996 } 997 } 998 template <typename T> bool operator()(const Designator<T> &x) const { 999 if (const auto *component{std::get_if<Component>(&x.u)}) { 1000 if (const auto *baseSym{std::get_if<SymbolRef>(&component->base().u)}) { 1001 const Symbol &base{**baseSym}; 1002 if (const auto *object{ 1003 base.detailsIf<semantics::ObjectEntityDetails>()}) { 1004 // TODO: nested component and array references 1005 if (IsNamedConstant(base) && object->init()) { 1006 if (auto structCons{ 1007 GetScalarConstantValue<SomeDerived>(*object->init())}) { 1008 auto iter{structCons->values().find(component->GetLastSymbol())}; 1009 if (iter != structCons->values().end()) { 1010 return (*this)(iter->second.value()); 1011 } 1012 } 1013 } 1014 } 1015 } 1016 } 1017 return false; 1018 } 1019 bool operator()(const NullPointer &) const { return true; } 1020 template <typename T> bool operator()(const Parentheses<T> &x) const { 1021 return (*this)(x.left()); 1022 } 1023 template <typename T> bool operator()(const Expr<T> &x) const { 1024 return common::visit(*this, x.u); 1025 } 1026 }; 1027 1028 bool IsNullObjectPointer(const Expr<SomeType> &expr) { 1029 return IsNullPointerHelper<false>{}(expr); 1030 } 1031 1032 bool IsNullProcedurePointer(const Expr<SomeType> &expr) { 1033 return IsNullPointerHelper<true>{}(expr); 1034 } 1035 1036 bool IsNullPointer(const Expr<SomeType> &expr) { 1037 return IsNullObjectPointer(expr) || IsNullProcedurePointer(expr); 1038 } 1039 1040 bool IsBareNullPointer(const Expr<SomeType> *expr) { 1041 return expr && std::holds_alternative<NullPointer>(expr->u); 1042 } 1043 1044 // GetSymbolVector() 1045 auto GetSymbolVectorHelper::operator()(const Symbol &x) const -> Result { 1046 if (const auto *details{x.detailsIf<semantics::AssocEntityDetails>()}) { 1047 if (IsVariable(details->expr()) && !UnwrapProcedureRef(*details->expr())) { 1048 // associate(x => variable that is not a pointer returned by a function) 1049 return (*this)(details->expr()); 1050 } 1051 } 1052 return {x.GetUltimate()}; 1053 } 1054 auto GetSymbolVectorHelper::operator()(const Component &x) const -> Result { 1055 Result result{(*this)(x.base())}; 1056 result.emplace_back(x.GetLastSymbol()); 1057 return result; 1058 } 1059 auto GetSymbolVectorHelper::operator()(const ArrayRef &x) const -> Result { 1060 return GetSymbolVector(x.base()); 1061 } 1062 auto GetSymbolVectorHelper::operator()(const CoarrayRef &x) const -> Result { 1063 return x.base(); 1064 } 1065 1066 const Symbol *GetLastTarget(const SymbolVector &symbols) { 1067 auto end{std::crend(symbols)}; 1068 // N.B. Neither clang nor g++ recognizes "symbols.crbegin()" here. 1069 auto iter{std::find_if(std::crbegin(symbols), end, [](const Symbol &x) { 1070 return x.attrs().HasAny( 1071 {semantics::Attr::POINTER, semantics::Attr::TARGET}); 1072 })}; 1073 return iter == end ? nullptr : &**iter; 1074 } 1075 1076 struct CollectSymbolsHelper 1077 : public SetTraverse<CollectSymbolsHelper, semantics::UnorderedSymbolSet> { 1078 using Base = SetTraverse<CollectSymbolsHelper, semantics::UnorderedSymbolSet>; 1079 CollectSymbolsHelper() : Base{*this} {} 1080 using Base::operator(); 1081 semantics::UnorderedSymbolSet operator()(const Symbol &symbol) const { 1082 return {symbol}; 1083 } 1084 }; 1085 template <typename A> semantics::UnorderedSymbolSet CollectSymbols(const A &x) { 1086 return CollectSymbolsHelper{}(x); 1087 } 1088 template semantics::UnorderedSymbolSet CollectSymbols(const Expr<SomeType> &); 1089 template semantics::UnorderedSymbolSet CollectSymbols( 1090 const Expr<SomeInteger> &); 1091 template semantics::UnorderedSymbolSet CollectSymbols( 1092 const Expr<SubscriptInteger> &); 1093 1094 struct CollectCudaSymbolsHelper : public SetTraverse<CollectCudaSymbolsHelper, 1095 semantics::UnorderedSymbolSet> { 1096 using Base = 1097 SetTraverse<CollectCudaSymbolsHelper, semantics::UnorderedSymbolSet>; 1098 CollectCudaSymbolsHelper() : Base{*this} {} 1099 using Base::operator(); 1100 semantics::UnorderedSymbolSet operator()(const Symbol &symbol) const { 1101 return {symbol}; 1102 } 1103 // Overload some of the operator() to filter out the symbols that are not 1104 // of interest for CUDA data transfer logic. 1105 semantics::UnorderedSymbolSet operator()(const DescriptorInquiry &) const { 1106 return {}; 1107 } 1108 semantics::UnorderedSymbolSet operator()(const Subscript &) const { 1109 return {}; 1110 } 1111 semantics::UnorderedSymbolSet operator()(const ProcedureRef &) const { 1112 return {}; 1113 } 1114 }; 1115 template <typename A> 1116 semantics::UnorderedSymbolSet CollectCudaSymbols(const A &x) { 1117 return CollectCudaSymbolsHelper{}(x); 1118 } 1119 template semantics::UnorderedSymbolSet CollectCudaSymbols( 1120 const Expr<SomeType> &); 1121 template semantics::UnorderedSymbolSet CollectCudaSymbols( 1122 const Expr<SomeInteger> &); 1123 template semantics::UnorderedSymbolSet CollectCudaSymbols( 1124 const Expr<SubscriptInteger> &); 1125 1126 // HasVectorSubscript() 1127 struct HasVectorSubscriptHelper 1128 : public AnyTraverse<HasVectorSubscriptHelper, bool, 1129 /*TraverseAssocEntityDetails=*/false> { 1130 using Base = AnyTraverse<HasVectorSubscriptHelper, bool, false>; 1131 HasVectorSubscriptHelper() : Base{*this} {} 1132 using Base::operator(); 1133 bool operator()(const Subscript &ss) const { 1134 return !std::holds_alternative<Triplet>(ss.u) && ss.Rank() > 0; 1135 } 1136 bool operator()(const ProcedureRef &) const { 1137 return false; // don't descend into function call arguments 1138 } 1139 }; 1140 1141 bool HasVectorSubscript(const Expr<SomeType> &expr) { 1142 return HasVectorSubscriptHelper{}(expr); 1143 } 1144 1145 // HasConstant() 1146 struct HasConstantHelper : public AnyTraverse<HasConstantHelper, bool, 1147 /*TraverseAssocEntityDetails=*/false> { 1148 using Base = AnyTraverse<HasConstantHelper, bool, false>; 1149 HasConstantHelper() : Base{*this} {} 1150 using Base::operator(); 1151 template <typename T> bool operator()(const Constant<T> &) const { 1152 return true; 1153 } 1154 // Only look for constant not in subscript. 1155 bool operator()(const Subscript &) const { return false; } 1156 }; 1157 1158 bool HasConstant(const Expr<SomeType> &expr) { 1159 return HasConstantHelper{}(expr); 1160 } 1161 1162 parser::Message *AttachDeclaration( 1163 parser::Message &message, const Symbol &symbol) { 1164 const Symbol *unhosted{&symbol}; 1165 while ( 1166 const auto *assoc{unhosted->detailsIf<semantics::HostAssocDetails>()}) { 1167 unhosted = &assoc->symbol(); 1168 } 1169 if (const auto *binding{ 1170 unhosted->detailsIf<semantics::ProcBindingDetails>()}) { 1171 if (binding->symbol().name() != symbol.name()) { 1172 message.Attach(binding->symbol().name(), 1173 "Procedure '%s' of type '%s' is bound to '%s'"_en_US, symbol.name(), 1174 symbol.owner().GetName().value(), binding->symbol().name()); 1175 return &message; 1176 } 1177 unhosted = &binding->symbol(); 1178 } 1179 if (const auto *use{symbol.detailsIf<semantics::UseDetails>()}) { 1180 message.Attach(use->location(), 1181 "'%s' is USE-associated with '%s' in module '%s'"_en_US, symbol.name(), 1182 unhosted->name(), GetUsedModule(*use).name()); 1183 } else { 1184 message.Attach( 1185 unhosted->name(), "Declaration of '%s'"_en_US, unhosted->name()); 1186 } 1187 return &message; 1188 } 1189 1190 parser::Message *AttachDeclaration( 1191 parser::Message *message, const Symbol &symbol) { 1192 return message ? AttachDeclaration(*message, symbol) : nullptr; 1193 } 1194 1195 class FindImpureCallHelper 1196 : public AnyTraverse<FindImpureCallHelper, std::optional<std::string>, 1197 /*TraverseAssocEntityDetails=*/false> { 1198 using Result = std::optional<std::string>; 1199 using Base = AnyTraverse<FindImpureCallHelper, Result, false>; 1200 1201 public: 1202 explicit FindImpureCallHelper(FoldingContext &c) : Base{*this}, context_{c} {} 1203 using Base::operator(); 1204 Result operator()(const ProcedureRef &call) const { 1205 if (auto chars{characteristics::Procedure::Characterize( 1206 call.proc(), context_, /*emitError=*/false)}) { 1207 if (chars->attrs.test(characteristics::Procedure::Attr::Pure)) { 1208 return (*this)(call.arguments()); 1209 } 1210 } 1211 return call.proc().GetName(); 1212 } 1213 1214 private: 1215 FoldingContext &context_; 1216 }; 1217 1218 std::optional<std::string> FindImpureCall( 1219 FoldingContext &context, const Expr<SomeType> &expr) { 1220 return FindImpureCallHelper{context}(expr); 1221 } 1222 std::optional<std::string> FindImpureCall( 1223 FoldingContext &context, const ProcedureRef &proc) { 1224 return FindImpureCallHelper{context}(proc); 1225 } 1226 1227 // Common handling for procedure pointer compatibility of left- and right-hand 1228 // sides. Returns nullopt if they're compatible. Otherwise, it returns a 1229 // message that needs to be augmented by the names of the left and right sides 1230 // and the content of the "whyNotCompatible" string. 1231 std::optional<parser::MessageFixedText> CheckProcCompatibility(bool isCall, 1232 const std::optional<characteristics::Procedure> &lhsProcedure, 1233 const characteristics::Procedure *rhsProcedure, 1234 const SpecificIntrinsic *specificIntrinsic, std::string &whyNotCompatible, 1235 std::optional<std::string> &warning, bool ignoreImplicitVsExplicit) { 1236 std::optional<parser::MessageFixedText> msg; 1237 if (!lhsProcedure) { 1238 msg = "In assignment to object %s, the target '%s' is a procedure" 1239 " designator"_err_en_US; 1240 } else if (!rhsProcedure) { 1241 msg = "In assignment to procedure %s, the characteristics of the target" 1242 " procedure '%s' could not be determined"_err_en_US; 1243 } else if (!isCall && lhsProcedure->functionResult && 1244 rhsProcedure->functionResult && 1245 !lhsProcedure->functionResult->IsCompatibleWith( 1246 *rhsProcedure->functionResult, &whyNotCompatible)) { 1247 msg = 1248 "Function %s associated with incompatible function designator '%s': %s"_err_en_US; 1249 } else if (lhsProcedure->IsCompatibleWith(*rhsProcedure, 1250 ignoreImplicitVsExplicit, &whyNotCompatible, specificIntrinsic, 1251 &warning)) { 1252 // OK 1253 } else if (isCall) { 1254 msg = "Procedure %s associated with result of reference to function '%s'" 1255 " that is an incompatible procedure pointer: %s"_err_en_US; 1256 } else if (lhsProcedure->IsPure() && !rhsProcedure->IsPure()) { 1257 msg = "PURE procedure %s may not be associated with non-PURE" 1258 " procedure designator '%s'"_err_en_US; 1259 } else if (lhsProcedure->IsFunction() && rhsProcedure->IsSubroutine()) { 1260 msg = "Function %s may not be associated with subroutine" 1261 " designator '%s'"_err_en_US; 1262 } else if (lhsProcedure->IsSubroutine() && rhsProcedure->IsFunction()) { 1263 msg = "Subroutine %s may not be associated with function" 1264 " designator '%s'"_err_en_US; 1265 } else if (lhsProcedure->HasExplicitInterface() && 1266 !rhsProcedure->HasExplicitInterface()) { 1267 // Section 10.2.2.4, paragraph 3 prohibits associating a procedure pointer 1268 // that has an explicit interface with a procedure whose characteristics 1269 // don't match. That's the case if the target procedure has an implicit 1270 // interface. But this case is allowed by several other compilers as long 1271 // as the explicit interface can be called via an implicit interface. 1272 if (!lhsProcedure->CanBeCalledViaImplicitInterface()) { 1273 msg = "Procedure %s with explicit interface that cannot be called via " 1274 "an implicit interface cannot be associated with procedure " 1275 "designator with an implicit interface"_err_en_US; 1276 } 1277 } else if (!lhsProcedure->HasExplicitInterface() && 1278 rhsProcedure->HasExplicitInterface()) { 1279 // OK if the target can be called via an implicit interface 1280 if (!rhsProcedure->CanBeCalledViaImplicitInterface() && 1281 !specificIntrinsic) { 1282 msg = "Procedure %s with implicit interface may not be associated " 1283 "with procedure designator '%s' with explicit interface that " 1284 "cannot be called via an implicit interface"_err_en_US; 1285 } 1286 } else { 1287 msg = "Procedure %s associated with incompatible procedure" 1288 " designator '%s': %s"_err_en_US; 1289 } 1290 return msg; 1291 } 1292 1293 // GetLastPointerSymbol() 1294 static const Symbol *GetLastPointerSymbol(const Symbol &symbol) { 1295 return IsPointer(GetAssociationRoot(symbol)) ? &symbol : nullptr; 1296 } 1297 static const Symbol *GetLastPointerSymbol(const SymbolRef &symbol) { 1298 return GetLastPointerSymbol(*symbol); 1299 } 1300 static const Symbol *GetLastPointerSymbol(const Component &x) { 1301 const Symbol &c{x.GetLastSymbol()}; 1302 return IsPointer(c) ? &c : GetLastPointerSymbol(x.base()); 1303 } 1304 static const Symbol *GetLastPointerSymbol(const NamedEntity &x) { 1305 const auto *c{x.UnwrapComponent()}; 1306 return c ? GetLastPointerSymbol(*c) : GetLastPointerSymbol(x.GetLastSymbol()); 1307 } 1308 static const Symbol *GetLastPointerSymbol(const ArrayRef &x) { 1309 return GetLastPointerSymbol(x.base()); 1310 } 1311 static const Symbol *GetLastPointerSymbol(const CoarrayRef &x) { 1312 return nullptr; 1313 } 1314 const Symbol *GetLastPointerSymbol(const DataRef &x) { 1315 return common::visit( 1316 [](const auto &y) { return GetLastPointerSymbol(y); }, x.u); 1317 } 1318 1319 template <TypeCategory TO, TypeCategory FROM> 1320 static std::optional<Expr<SomeType>> DataConstantConversionHelper( 1321 FoldingContext &context, const DynamicType &toType, 1322 const Expr<SomeType> &expr) { 1323 if (!IsValidKindOfIntrinsicType(FROM, toType.kind())) { 1324 return std::nullopt; 1325 } 1326 DynamicType sizedType{FROM, toType.kind()}; 1327 if (auto sized{ 1328 Fold(context, ConvertToType(sizedType, Expr<SomeType>{expr}))}) { 1329 if (const auto *someExpr{UnwrapExpr<Expr<SomeKind<FROM>>>(*sized)}) { 1330 return common::visit( 1331 [](const auto &w) -> std::optional<Expr<SomeType>> { 1332 using FromType = ResultType<decltype(w)>; 1333 static constexpr int kind{FromType::kind}; 1334 if constexpr (IsValidKindOfIntrinsicType(TO, kind)) { 1335 if (const auto *fromConst{UnwrapExpr<Constant<FromType>>(w)}) { 1336 using FromWordType = typename FromType::Scalar; 1337 using LogicalType = value::Logical<FromWordType::bits>; 1338 using ElementType = 1339 std::conditional_t<TO == TypeCategory::Logical, LogicalType, 1340 typename LogicalType::Word>; 1341 std::vector<ElementType> values; 1342 auto at{fromConst->lbounds()}; 1343 auto shape{fromConst->shape()}; 1344 for (auto n{GetSize(shape)}; n-- > 0; 1345 fromConst->IncrementSubscripts(at)) { 1346 auto elt{fromConst->At(at)}; 1347 if constexpr (TO == TypeCategory::Logical) { 1348 values.emplace_back(std::move(elt)); 1349 } else { 1350 values.emplace_back(elt.word()); 1351 } 1352 } 1353 return {AsGenericExpr(AsExpr(Constant<Type<TO, kind>>{ 1354 std::move(values), std::move(shape)}))}; 1355 } 1356 } 1357 return std::nullopt; 1358 }, 1359 someExpr->u); 1360 } 1361 } 1362 return std::nullopt; 1363 } 1364 1365 std::optional<Expr<SomeType>> DataConstantConversionExtension( 1366 FoldingContext &context, const DynamicType &toType, 1367 const Expr<SomeType> &expr0) { 1368 Expr<SomeType> expr{Fold(context, Expr<SomeType>{expr0})}; 1369 if (!IsActuallyConstant(expr)) { 1370 return std::nullopt; 1371 } 1372 if (auto fromType{expr.GetType()}) { 1373 if (toType.category() == TypeCategory::Logical && 1374 fromType->category() == TypeCategory::Integer) { 1375 return DataConstantConversionHelper<TypeCategory::Logical, 1376 TypeCategory::Integer>(context, toType, expr); 1377 } 1378 if (toType.category() == TypeCategory::Integer && 1379 fromType->category() == TypeCategory::Logical) { 1380 return DataConstantConversionHelper<TypeCategory::Integer, 1381 TypeCategory::Logical>(context, toType, expr); 1382 } 1383 } 1384 return std::nullopt; 1385 } 1386 1387 bool IsAllocatableOrPointerObject(const Expr<SomeType> &expr) { 1388 const semantics::Symbol *sym{UnwrapWholeSymbolOrComponentDataRef(expr)}; 1389 return (sym && 1390 semantics::IsAllocatableOrObjectPointer(&sym->GetUltimate())) || 1391 evaluate::IsObjectPointer(expr); 1392 } 1393 1394 bool IsAllocatableDesignator(const Expr<SomeType> &expr) { 1395 // Allocatable sub-objects are not themselves allocatable (9.5.3.1 NOTE 2). 1396 if (const semantics::Symbol * 1397 sym{UnwrapWholeSymbolOrComponentOrCoarrayRef(expr)}) { 1398 return semantics::IsAllocatable(sym->GetUltimate()); 1399 } 1400 return false; 1401 } 1402 1403 bool MayBePassedAsAbsentOptional(const Expr<SomeType> &expr) { 1404 const semantics::Symbol *sym{UnwrapWholeSymbolOrComponentDataRef(expr)}; 1405 // 15.5.2.12 1. is pretty clear that an unallocated allocatable/pointer actual 1406 // may be passed to a non-allocatable/non-pointer optional dummy. Note that 1407 // other compilers (like nag, nvfortran, ifort, gfortran and xlf) seems to 1408 // ignore this point in intrinsic contexts (e.g CMPLX argument). 1409 return (sym && semantics::IsOptional(*sym)) || 1410 IsAllocatableOrPointerObject(expr); 1411 } 1412 1413 std::optional<Expr<SomeType>> HollerithToBOZ(FoldingContext &context, 1414 const Expr<SomeType> &expr, const DynamicType &type) { 1415 if (std::optional<std::string> chValue{GetScalarConstantValue<Ascii>(expr)}) { 1416 // Pad on the right with spaces when short, truncate the right if long. 1417 auto bytes{static_cast<std::size_t>( 1418 ToInt64(type.MeasureSizeInBytes(context, false)).value())}; 1419 BOZLiteralConstant bits{0}; 1420 for (std::size_t j{0}; j < bytes; ++j) { 1421 auto idx{isHostLittleEndian ? j : bytes - j - 1}; 1422 char ch{idx >= chValue->size() ? ' ' : chValue->at(idx)}; 1423 BOZLiteralConstant chBOZ{static_cast<unsigned char>(ch)}; 1424 bits = bits.IOR(chBOZ.SHIFTL(8 * j)); 1425 } 1426 return ConvertToType(type, Expr<SomeType>{bits}); 1427 } else { 1428 return std::nullopt; 1429 } 1430 } 1431 1432 // Extracts a whole symbol being used as a bound of a dummy argument, 1433 // possibly wrapped with parentheses or MAX(0, ...). 1434 // Works with any integer expression. 1435 template <typename T> const Symbol *GetBoundSymbol(const Expr<T> &); 1436 template <int KIND> 1437 const Symbol *GetBoundSymbol( 1438 const Expr<Type<TypeCategory::Integer, KIND>> &expr) { 1439 using T = Type<TypeCategory::Integer, KIND>; 1440 return common::visit( 1441 common::visitors{ 1442 [](const Extremum<T> &max) -> const Symbol * { 1443 if (max.ordering == Ordering::Greater) { 1444 if (auto zero{ToInt64(max.left())}; zero && *zero == 0) { 1445 return GetBoundSymbol(max.right()); 1446 } 1447 } 1448 return nullptr; 1449 }, 1450 [](const Parentheses<T> &x) { return GetBoundSymbol(x.left()); }, 1451 [](const Designator<T> &x) -> const Symbol * { 1452 if (const auto *ref{std::get_if<SymbolRef>(&x.u)}) { 1453 return &**ref; 1454 } 1455 return nullptr; 1456 }, 1457 [](const Convert<T, TypeCategory::Integer> &x) { 1458 return common::visit( 1459 [](const auto &y) -> const Symbol * { 1460 using yType = std::decay_t<decltype(y)>; 1461 using yResult = typename yType::Result; 1462 if constexpr (yResult::kind <= KIND) { 1463 return GetBoundSymbol(y); 1464 } else { 1465 return nullptr; 1466 } 1467 }, 1468 x.left().u); 1469 }, 1470 [](const auto &) -> const Symbol * { return nullptr; }, 1471 }, 1472 expr.u); 1473 } 1474 template <> 1475 const Symbol *GetBoundSymbol<SomeInteger>(const Expr<SomeInteger> &expr) { 1476 return common::visit( 1477 [](const auto &kindExpr) { return GetBoundSymbol(kindExpr); }, expr.u); 1478 } 1479 1480 template <typename T> 1481 std::optional<bool> AreEquivalentInInterface( 1482 const Expr<T> &x, const Expr<T> &y) { 1483 auto xVal{ToInt64(x)}; 1484 auto yVal{ToInt64(y)}; 1485 if (xVal && yVal) { 1486 return *xVal == *yVal; 1487 } else if (xVal || yVal) { 1488 return false; 1489 } 1490 const Symbol *xSym{GetBoundSymbol(x)}; 1491 const Symbol *ySym{GetBoundSymbol(y)}; 1492 if (xSym && ySym) { 1493 if (&xSym->GetUltimate() == &ySym->GetUltimate()) { 1494 return true; // USE/host associated same symbol 1495 } 1496 auto xNum{semantics::GetDummyArgumentNumber(xSym)}; 1497 auto yNum{semantics::GetDummyArgumentNumber(ySym)}; 1498 if (xNum && yNum) { 1499 if (*xNum == *yNum) { 1500 auto xType{DynamicType::From(*xSym)}; 1501 auto yType{DynamicType::From(*ySym)}; 1502 return xType && yType && xType->IsEquivalentTo(*yType); 1503 } 1504 } 1505 return false; 1506 } else if (xSym || ySym) { 1507 return false; 1508 } 1509 // Neither expression is an integer constant or a whole symbol. 1510 if (x == y) { 1511 return true; 1512 } else { 1513 return std::nullopt; // not sure 1514 } 1515 } 1516 template std::optional<bool> AreEquivalentInInterface<SubscriptInteger>( 1517 const Expr<SubscriptInteger> &, const Expr<SubscriptInteger> &); 1518 template std::optional<bool> AreEquivalentInInterface<SomeInteger>( 1519 const Expr<SomeInteger> &, const Expr<SomeInteger> &); 1520 1521 bool CheckForCoindexedObject(parser::ContextualMessages &messages, 1522 const std::optional<ActualArgument> &arg, const std::string &procName, 1523 const std::string &argName) { 1524 if (arg && ExtractCoarrayRef(arg->UnwrapExpr())) { 1525 messages.Say(arg->sourceLocation(), 1526 "'%s' argument to '%s' may not be a coindexed object"_err_en_US, 1527 argName, procName); 1528 return false; 1529 } else { 1530 return true; 1531 } 1532 } 1533 1534 } // namespace Fortran::evaluate 1535 1536 namespace Fortran::semantics { 1537 1538 const Symbol &ResolveAssociations(const Symbol &original) { 1539 const Symbol &symbol{original.GetUltimate()}; 1540 if (const auto *details{symbol.detailsIf<AssocEntityDetails>()}) { 1541 if (!details->rank()) { // Not RANK(n) or RANK(*) 1542 if (const Symbol * nested{UnwrapWholeSymbolDataRef(details->expr())}) { 1543 return ResolveAssociations(*nested); 1544 } 1545 } 1546 } 1547 return symbol; 1548 } 1549 1550 // When a construct association maps to a variable, and that variable 1551 // is not an array with a vector-valued subscript, return the base 1552 // Symbol of that variable, else nullptr. Descends into other construct 1553 // associations when one associations maps to another. 1554 static const Symbol *GetAssociatedVariable(const AssocEntityDetails &details) { 1555 if (const auto &expr{details.expr()}) { 1556 if (IsVariable(*expr) && !HasVectorSubscript(*expr)) { 1557 if (const Symbol * varSymbol{GetFirstSymbol(*expr)}) { 1558 return &GetAssociationRoot(*varSymbol); 1559 } 1560 } 1561 } 1562 return nullptr; 1563 } 1564 1565 const Symbol &GetAssociationRoot(const Symbol &original) { 1566 const Symbol &symbol{ResolveAssociations(original)}; 1567 if (const auto *details{symbol.detailsIf<AssocEntityDetails>()}) { 1568 if (const Symbol * root{GetAssociatedVariable(*details)}) { 1569 return *root; 1570 } 1571 } 1572 return symbol; 1573 } 1574 1575 const Symbol *GetMainEntry(const Symbol *symbol) { 1576 if (symbol) { 1577 if (const auto *subpDetails{symbol->detailsIf<SubprogramDetails>()}) { 1578 if (const Scope * scope{subpDetails->entryScope()}) { 1579 if (const Symbol * main{scope->symbol()}) { 1580 return main; 1581 } 1582 } 1583 } 1584 } 1585 return symbol; 1586 } 1587 1588 bool IsVariableName(const Symbol &original) { 1589 const Symbol &ultimate{original.GetUltimate()}; 1590 return !IsNamedConstant(ultimate) && 1591 (ultimate.has<ObjectEntityDetails>() || 1592 ultimate.has<AssocEntityDetails>()); 1593 } 1594 1595 static bool IsPureProcedureImpl( 1596 const Symbol &original, semantics::UnorderedSymbolSet &set) { 1597 // An ENTRY is pure if its containing subprogram is 1598 const Symbol &symbol{DEREF(GetMainEntry(&original.GetUltimate()))}; 1599 if (set.find(symbol) != set.end()) { 1600 return true; 1601 } 1602 set.emplace(symbol); 1603 if (const auto *procDetails{symbol.detailsIf<ProcEntityDetails>()}) { 1604 if (procDetails->procInterface()) { 1605 // procedure with a pure interface 1606 return IsPureProcedureImpl(*procDetails->procInterface(), set); 1607 } 1608 } else if (const auto *details{symbol.detailsIf<ProcBindingDetails>()}) { 1609 return IsPureProcedureImpl(details->symbol(), set); 1610 } else if (!IsProcedure(symbol)) { 1611 return false; 1612 } 1613 if (IsStmtFunction(symbol)) { 1614 // Section 15.7(1) states that a statement function is PURE if it does not 1615 // reference an IMPURE procedure or a VOLATILE variable 1616 if (const auto &expr{symbol.get<SubprogramDetails>().stmtFunction()}) { 1617 for (const SymbolRef &ref : evaluate::CollectSymbols(*expr)) { 1618 if (&*ref == &symbol) { 1619 return false; // error recovery, recursion is caught elsewhere 1620 } 1621 if (IsFunction(*ref) && !IsPureProcedureImpl(*ref, set)) { 1622 return false; 1623 } 1624 if (ref->GetUltimate().attrs().test(Attr::VOLATILE)) { 1625 return false; 1626 } 1627 } 1628 } 1629 return true; // statement function was not found to be impure 1630 } 1631 return symbol.attrs().test(Attr::PURE) || 1632 (symbol.attrs().test(Attr::ELEMENTAL) && 1633 !symbol.attrs().test(Attr::IMPURE)); 1634 } 1635 1636 bool IsPureProcedure(const Symbol &original) { 1637 semantics::UnorderedSymbolSet set; 1638 return IsPureProcedureImpl(original, set); 1639 } 1640 1641 bool IsPureProcedure(const Scope &scope) { 1642 const Symbol *symbol{scope.GetSymbol()}; 1643 return symbol && IsPureProcedure(*symbol); 1644 } 1645 1646 bool IsExplicitlyImpureProcedure(const Symbol &original) { 1647 // An ENTRY is IMPURE if its containing subprogram is so 1648 return DEREF(GetMainEntry(&original.GetUltimate())) 1649 .attrs() 1650 .test(Attr::IMPURE); 1651 } 1652 1653 bool IsElementalProcedure(const Symbol &original) { 1654 // An ENTRY is elemental if its containing subprogram is 1655 const Symbol &symbol{DEREF(GetMainEntry(&original.GetUltimate()))}; 1656 if (IsProcedure(symbol)) { 1657 auto &foldingContext{symbol.owner().context().foldingContext()}; 1658 auto restorer{foldingContext.messages().DiscardMessages()}; 1659 auto proc{evaluate::characteristics::Procedure::Characterize( 1660 symbol, foldingContext)}; 1661 return proc && 1662 proc->attrs.test(evaluate::characteristics::Procedure::Attr::Elemental); 1663 } else { 1664 return false; 1665 } 1666 } 1667 1668 bool IsFunction(const Symbol &symbol) { 1669 const Symbol &ultimate{symbol.GetUltimate()}; 1670 return ultimate.test(Symbol::Flag::Function) || 1671 (!ultimate.test(Symbol::Flag::Subroutine) && 1672 common::visit( 1673 common::visitors{ 1674 [](const SubprogramDetails &x) { return x.isFunction(); }, 1675 [](const ProcEntityDetails &x) { 1676 const Symbol *ifc{x.procInterface()}; 1677 return x.type() || (ifc && IsFunction(*ifc)); 1678 }, 1679 [](const ProcBindingDetails &x) { 1680 return IsFunction(x.symbol()); 1681 }, 1682 [](const auto &) { return false; }, 1683 }, 1684 ultimate.details())); 1685 } 1686 1687 bool IsFunction(const Scope &scope) { 1688 const Symbol *symbol{scope.GetSymbol()}; 1689 return symbol && IsFunction(*symbol); 1690 } 1691 1692 bool IsProcedure(const Symbol &symbol) { 1693 return common::visit(common::visitors{ 1694 [&symbol](const SubprogramDetails &) { 1695 const Scope *scope{symbol.scope()}; 1696 // Main programs & BLOCK DATA are not procedures. 1697 return !scope || 1698 scope->kind() == Scope::Kind::Subprogram; 1699 }, 1700 [](const SubprogramNameDetails &) { return true; }, 1701 [](const ProcEntityDetails &) { return true; }, 1702 [](const GenericDetails &) { return true; }, 1703 [](const ProcBindingDetails &) { return true; }, 1704 [](const auto &) { return false; }, 1705 }, 1706 symbol.GetUltimate().details()); 1707 } 1708 1709 bool IsProcedure(const Scope &scope) { 1710 const Symbol *symbol{scope.GetSymbol()}; 1711 return symbol && IsProcedure(*symbol); 1712 } 1713 1714 bool IsProcedurePointer(const Symbol &original) { 1715 const Symbol &symbol{GetAssociationRoot(original)}; 1716 return IsPointer(symbol) && IsProcedure(symbol); 1717 } 1718 1719 bool IsProcedurePointer(const Symbol *symbol) { 1720 return symbol && IsProcedurePointer(*symbol); 1721 } 1722 1723 bool IsObjectPointer(const Symbol *original) { 1724 if (original) { 1725 const Symbol &symbol{GetAssociationRoot(*original)}; 1726 return IsPointer(symbol) && !IsProcedure(symbol); 1727 } else { 1728 return false; 1729 } 1730 } 1731 1732 bool IsAllocatableOrObjectPointer(const Symbol *original) { 1733 if (original) { 1734 const Symbol &ultimate{original->GetUltimate()}; 1735 if (const auto *assoc{ultimate.detailsIf<AssocEntityDetails>()}) { 1736 // Only SELECT RANK construct entities can be ALLOCATABLE/POINTER. 1737 return (assoc->rank() || assoc->IsAssumedSize() || 1738 assoc->IsAssumedRank()) && 1739 IsAllocatableOrObjectPointer(UnwrapWholeSymbolDataRef(assoc->expr())); 1740 } else { 1741 return IsAllocatable(ultimate) || 1742 (IsPointer(ultimate) && !IsProcedure(ultimate)); 1743 } 1744 } else { 1745 return false; 1746 } 1747 } 1748 1749 const Symbol *FindCommonBlockContaining(const Symbol &original) { 1750 const Symbol &root{GetAssociationRoot(original)}; 1751 const auto *details{root.detailsIf<ObjectEntityDetails>()}; 1752 return details ? details->commonBlock() : nullptr; 1753 } 1754 1755 // 3.11 automatic data object 1756 bool IsAutomatic(const Symbol &original) { 1757 const Symbol &symbol{original.GetUltimate()}; 1758 if (const auto *object{symbol.detailsIf<ObjectEntityDetails>()}) { 1759 if (!object->isDummy() && !IsAllocatable(symbol) && !IsPointer(symbol)) { 1760 if (const DeclTypeSpec * type{symbol.GetType()}) { 1761 // If a type parameter value is not a constant expression, the 1762 // object is automatic. 1763 if (type->category() == DeclTypeSpec::Character) { 1764 if (const auto &length{ 1765 type->characterTypeSpec().length().GetExplicit()}) { 1766 if (!evaluate::IsConstantExpr(*length)) { 1767 return true; 1768 } 1769 } 1770 } else if (const DerivedTypeSpec * derived{type->AsDerived()}) { 1771 for (const auto &pair : derived->parameters()) { 1772 if (const auto &value{pair.second.GetExplicit()}) { 1773 if (!evaluate::IsConstantExpr(*value)) { 1774 return true; 1775 } 1776 } 1777 } 1778 } 1779 } 1780 // If an array bound is not a constant expression, the object is 1781 // automatic. 1782 for (const ShapeSpec &dim : object->shape()) { 1783 if (const auto &lb{dim.lbound().GetExplicit()}) { 1784 if (!evaluate::IsConstantExpr(*lb)) { 1785 return true; 1786 } 1787 } 1788 if (const auto &ub{dim.ubound().GetExplicit()}) { 1789 if (!evaluate::IsConstantExpr(*ub)) { 1790 return true; 1791 } 1792 } 1793 } 1794 } 1795 } 1796 return false; 1797 } 1798 1799 bool IsSaved(const Symbol &original) { 1800 const Symbol &symbol{GetAssociationRoot(original)}; 1801 const Scope &scope{symbol.owner()}; 1802 const common::LanguageFeatureControl &features{ 1803 scope.context().languageFeatures()}; 1804 auto scopeKind{scope.kind()}; 1805 if (symbol.has<AssocEntityDetails>()) { 1806 return false; // ASSOCIATE(non-variable) 1807 } else if (scopeKind == Scope::Kind::DerivedType) { 1808 return false; // this is a component 1809 } else if (symbol.attrs().test(Attr::SAVE)) { 1810 return true; // explicit SAVE attribute 1811 } else if (IsDummy(symbol) || IsFunctionResult(symbol) || 1812 IsAutomatic(symbol) || IsNamedConstant(symbol)) { 1813 return false; 1814 } else if (scopeKind == Scope::Kind::Module || 1815 (scopeKind == Scope::Kind::MainProgram && 1816 (symbol.attrs().test(Attr::TARGET) || evaluate::IsCoarray(symbol)) && 1817 Fortran::evaluate::CanCUDASymbolHaveSaveAttr(symbol))) { 1818 // 8.5.16p4 1819 // In main programs, implied SAVE matters only for pointer 1820 // initialization targets and coarrays. 1821 return true; 1822 } else if (scopeKind == Scope::Kind::MainProgram && 1823 (features.IsEnabled(common::LanguageFeature::SaveMainProgram) || 1824 (features.IsEnabled( 1825 common::LanguageFeature::SaveBigMainProgramVariables) && 1826 symbol.size() > 32)) && 1827 Fortran::evaluate::CanCUDASymbolHaveSaveAttr(symbol)) { 1828 // With SaveBigMainProgramVariables, keeping all unsaved main program 1829 // variables of 32 bytes or less on the stack allows keeping numerical and 1830 // logical scalars, small scalar characters or derived, small arrays, and 1831 // scalar descriptors on the stack. This leaves more room for lower level 1832 // optimizers to do register promotion or get easy aliasing information. 1833 return true; 1834 } else if (features.IsEnabled(common::LanguageFeature::DefaultSave) && 1835 (scopeKind == Scope::Kind::MainProgram || 1836 (scope.kind() == Scope::Kind::Subprogram && 1837 !(scope.symbol() && 1838 scope.symbol()->attrs().test(Attr::RECURSIVE))))) { 1839 // -fno-automatic/-save/-Msave option applies to all objects in executable 1840 // main programs and subprograms unless they are explicitly RECURSIVE. 1841 return true; 1842 } else if (symbol.test(Symbol::Flag::InDataStmt)) { 1843 return true; 1844 } else if (const auto *object{symbol.detailsIf<ObjectEntityDetails>()}; 1845 object && object->init()) { 1846 return true; 1847 } else if (IsProcedurePointer(symbol) && symbol.has<ProcEntityDetails>() && 1848 symbol.get<ProcEntityDetails>().init()) { 1849 return true; 1850 } else if (scope.hasSAVE()) { 1851 return true; // bare SAVE statement 1852 } else if (const Symbol * block{FindCommonBlockContaining(symbol)}; 1853 block && block->attrs().test(Attr::SAVE)) { 1854 return true; // in COMMON with SAVE 1855 } else { 1856 return false; 1857 } 1858 } 1859 1860 bool IsDummy(const Symbol &symbol) { 1861 return common::visit( 1862 common::visitors{[](const EntityDetails &x) { return x.isDummy(); }, 1863 [](const ObjectEntityDetails &x) { return x.isDummy(); }, 1864 [](const ProcEntityDetails &x) { return x.isDummy(); }, 1865 [](const SubprogramDetails &x) { return x.isDummy(); }, 1866 [](const auto &) { return false; }}, 1867 ResolveAssociations(symbol).details()); 1868 } 1869 1870 bool IsAssumedShape(const Symbol &symbol) { 1871 const Symbol &ultimate{ResolveAssociations(symbol)}; 1872 const auto *object{ultimate.detailsIf<ObjectEntityDetails>()}; 1873 return object && object->IsAssumedShape() && 1874 !semantics::IsAllocatableOrObjectPointer(&ultimate); 1875 } 1876 1877 bool IsDeferredShape(const Symbol &symbol) { 1878 const Symbol &ultimate{ResolveAssociations(symbol)}; 1879 const auto *object{ultimate.detailsIf<ObjectEntityDetails>()}; 1880 return object && object->CanBeDeferredShape() && 1881 semantics::IsAllocatableOrObjectPointer(&ultimate); 1882 } 1883 1884 bool IsFunctionResult(const Symbol &original) { 1885 const Symbol &symbol{GetAssociationRoot(original)}; 1886 return common::visit( 1887 common::visitors{ 1888 [](const EntityDetails &x) { return x.isFuncResult(); }, 1889 [](const ObjectEntityDetails &x) { return x.isFuncResult(); }, 1890 [](const ProcEntityDetails &x) { return x.isFuncResult(); }, 1891 [](const auto &) { return false; }, 1892 }, 1893 symbol.details()); 1894 } 1895 1896 bool IsKindTypeParameter(const Symbol &symbol) { 1897 const auto *param{symbol.GetUltimate().detailsIf<TypeParamDetails>()}; 1898 return param && param->attr() == common::TypeParamAttr::Kind; 1899 } 1900 1901 bool IsLenTypeParameter(const Symbol &symbol) { 1902 const auto *param{symbol.GetUltimate().detailsIf<TypeParamDetails>()}; 1903 return param && param->attr() == common::TypeParamAttr::Len; 1904 } 1905 1906 bool IsExtensibleType(const DerivedTypeSpec *derived) { 1907 return !IsSequenceOrBindCType(derived) && !IsIsoCType(derived); 1908 } 1909 1910 bool IsSequenceOrBindCType(const DerivedTypeSpec *derived) { 1911 return derived && 1912 (derived->typeSymbol().attrs().test(Attr::BIND_C) || 1913 derived->typeSymbol().get<DerivedTypeDetails>().sequence()); 1914 } 1915 1916 static bool IsSameModule(const Scope *x, const Scope *y) { 1917 if (x == y) { 1918 return true; 1919 } else if (x && y) { 1920 // Allow for a builtin module to be read from distinct paths 1921 const Symbol *xSym{x->symbol()}; 1922 const Symbol *ySym{y->symbol()}; 1923 if (xSym && ySym && xSym->name() == ySym->name()) { 1924 const auto *xMod{xSym->detailsIf<ModuleDetails>()}; 1925 const auto *yMod{ySym->detailsIf<ModuleDetails>()}; 1926 if (xMod && yMod) { 1927 auto xHash{xMod->moduleFileHash()}; 1928 auto yHash{yMod->moduleFileHash()}; 1929 return xHash && yHash && *xHash == *yHash; 1930 } 1931 } 1932 } 1933 return false; 1934 } 1935 1936 bool IsBuiltinDerivedType(const DerivedTypeSpec *derived, const char *name) { 1937 if (derived) { 1938 const auto &symbol{derived->typeSymbol()}; 1939 const Scope &scope{symbol.owner()}; 1940 return symbol.name() == "__builtin_"s + name && 1941 IsSameModule(&scope, scope.context().GetBuiltinsScope()); 1942 } else { 1943 return false; 1944 } 1945 } 1946 1947 bool IsBuiltinCPtr(const Symbol &symbol) { 1948 if (const DeclTypeSpec *declType = symbol.GetType()) { 1949 if (const DerivedTypeSpec *derived = declType->AsDerived()) { 1950 return IsIsoCType(derived); 1951 } 1952 } 1953 return false; 1954 } 1955 1956 bool IsIsoCType(const DerivedTypeSpec *derived) { 1957 return IsBuiltinDerivedType(derived, "c_ptr") || 1958 IsBuiltinDerivedType(derived, "c_funptr"); 1959 } 1960 1961 bool IsEventType(const DerivedTypeSpec *derived) { 1962 return IsBuiltinDerivedType(derived, "event_type"); 1963 } 1964 1965 bool IsLockType(const DerivedTypeSpec *derived) { 1966 return IsBuiltinDerivedType(derived, "lock_type"); 1967 } 1968 1969 bool IsNotifyType(const DerivedTypeSpec *derived) { 1970 return IsBuiltinDerivedType(derived, "notify_type"); 1971 } 1972 1973 bool IsIeeeFlagType(const DerivedTypeSpec *derived) { 1974 return IsBuiltinDerivedType(derived, "ieee_flag_type"); 1975 } 1976 1977 bool IsIeeeRoundType(const DerivedTypeSpec *derived) { 1978 return IsBuiltinDerivedType(derived, "ieee_round_type"); 1979 } 1980 1981 bool IsTeamType(const DerivedTypeSpec *derived) { 1982 return IsBuiltinDerivedType(derived, "team_type"); 1983 } 1984 1985 bool IsBadCoarrayType(const DerivedTypeSpec *derived) { 1986 return IsTeamType(derived) || IsIsoCType(derived); 1987 } 1988 1989 bool IsEventTypeOrLockType(const DerivedTypeSpec *derivedTypeSpec) { 1990 return IsEventType(derivedTypeSpec) || IsLockType(derivedTypeSpec); 1991 } 1992 1993 int CountLenParameters(const DerivedTypeSpec &type) { 1994 return llvm::count_if( 1995 type.parameters(), [](const auto &pair) { return pair.second.isLen(); }); 1996 } 1997 1998 int CountNonConstantLenParameters(const DerivedTypeSpec &type) { 1999 return llvm::count_if(type.parameters(), [](const auto &pair) { 2000 if (!pair.second.isLen()) { 2001 return false; 2002 } else if (const auto &expr{pair.second.GetExplicit()}) { 2003 return !IsConstantExpr(*expr); 2004 } else { 2005 return true; 2006 } 2007 }); 2008 } 2009 2010 const Symbol &GetUsedModule(const UseDetails &details) { 2011 return DEREF(details.symbol().owner().symbol()); 2012 } 2013 2014 static const Symbol *FindFunctionResult( 2015 const Symbol &original, UnorderedSymbolSet &seen) { 2016 const Symbol &root{GetAssociationRoot(original)}; 2017 ; 2018 if (!seen.insert(root).second) { 2019 return nullptr; // don't loop 2020 } 2021 return common::visit( 2022 common::visitors{[](const SubprogramDetails &subp) { 2023 return subp.isFunction() ? &subp.result() : nullptr; 2024 }, 2025 [&](const ProcEntityDetails &proc) { 2026 const Symbol *iface{proc.procInterface()}; 2027 return iface ? FindFunctionResult(*iface, seen) : nullptr; 2028 }, 2029 [&](const ProcBindingDetails &binding) { 2030 return FindFunctionResult(binding.symbol(), seen); 2031 }, 2032 [](const auto &) -> const Symbol * { return nullptr; }}, 2033 root.details()); 2034 } 2035 2036 const Symbol *FindFunctionResult(const Symbol &symbol) { 2037 UnorderedSymbolSet seen; 2038 return FindFunctionResult(symbol, seen); 2039 } 2040 2041 // These are here in Evaluate/tools.cpp so that Evaluate can use 2042 // them; they cannot be defined in symbol.h due to the dependence 2043 // on Scope. 2044 2045 bool SymbolSourcePositionCompare::operator()( 2046 const SymbolRef &x, const SymbolRef &y) const { 2047 return x->GetSemanticsContext().allCookedSources().Precedes( 2048 x->name(), y->name()); 2049 } 2050 bool SymbolSourcePositionCompare::operator()( 2051 const MutableSymbolRef &x, const MutableSymbolRef &y) const { 2052 return x->GetSemanticsContext().allCookedSources().Precedes( 2053 x->name(), y->name()); 2054 } 2055 2056 SemanticsContext &Symbol::GetSemanticsContext() const { 2057 return DEREF(owner_).context(); 2058 } 2059 2060 bool AreTkCompatibleTypes(const DeclTypeSpec *x, const DeclTypeSpec *y) { 2061 if (x && y) { 2062 if (auto xDt{evaluate::DynamicType::From(*x)}) { 2063 if (auto yDt{evaluate::DynamicType::From(*y)}) { 2064 return xDt->IsTkCompatibleWith(*yDt); 2065 } 2066 } 2067 } 2068 return false; 2069 } 2070 2071 common::IgnoreTKRSet GetIgnoreTKR(const Symbol &symbol) { 2072 common::IgnoreTKRSet result; 2073 if (const auto *object{symbol.detailsIf<ObjectEntityDetails>()}) { 2074 result = object->ignoreTKR(); 2075 if (const Symbol * ownerSymbol{symbol.owner().symbol()}) { 2076 if (const auto *ownerSubp{ownerSymbol->detailsIf<SubprogramDetails>()}) { 2077 if (ownerSubp->defaultIgnoreTKR()) { 2078 result |= common::ignoreTKRAll; 2079 } 2080 } 2081 } 2082 } 2083 return result; 2084 } 2085 2086 std::optional<int> GetDummyArgumentNumber(const Symbol *symbol) { 2087 if (symbol) { 2088 if (IsDummy(*symbol)) { 2089 if (const Symbol * subpSym{symbol->owner().symbol()}) { 2090 if (const auto *subp{subpSym->detailsIf<SubprogramDetails>()}) { 2091 int j{0}; 2092 for (const Symbol *dummy : subp->dummyArgs()) { 2093 if (dummy == symbol) { 2094 return j; 2095 } 2096 ++j; 2097 } 2098 } 2099 } 2100 } 2101 } 2102 return std::nullopt; 2103 } 2104 2105 // Given a symbol that is a SubprogramNameDetails in a submodule, try to 2106 // find its interface definition in its module or ancestor submodule. 2107 const Symbol *FindAncestorModuleProcedure(const Symbol *symInSubmodule) { 2108 if (symInSubmodule && symInSubmodule->owner().IsSubmodule()) { 2109 if (const auto *nameDetails{ 2110 symInSubmodule->detailsIf<semantics::SubprogramNameDetails>()}; 2111 nameDetails && 2112 nameDetails->kind() == semantics::SubprogramKind::Module) { 2113 const Symbol *next{symInSubmodule->owner().symbol()}; 2114 while (const Symbol * submodSym{next}) { 2115 next = nullptr; 2116 if (const auto *modDetails{ 2117 submodSym->detailsIf<semantics::ModuleDetails>()}; 2118 modDetails && modDetails->isSubmodule() && modDetails->scope()) { 2119 if (const semantics::Scope & parent{modDetails->scope()->parent()}; 2120 parent.IsSubmodule() || parent.IsModule()) { 2121 if (auto iter{parent.find(symInSubmodule->name())}; 2122 iter != parent.end()) { 2123 const Symbol &proc{iter->second->GetUltimate()}; 2124 if (IsProcedure(proc)) { 2125 return &proc; 2126 } 2127 } else if (parent.IsSubmodule()) { 2128 next = parent.symbol(); 2129 } 2130 } 2131 } 2132 } 2133 } 2134 } 2135 return nullptr; 2136 } 2137 2138 } // namespace Fortran::semantics 2139