xref: /llvm-project/flang/lib/Evaluate/tools.cpp (revision c8202db43ad0cafdc59903dadc4ea9f95a73de9b)
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