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