xref: /llvm-project/flang/lib/Evaluate/tools.cpp (revision 3a8a52f4a52e0c301a5f3d6acce684c7fd4a6d57)
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   DynamicType sizedType{FROM, toType.kind()};
1324   if (auto sized{
1325           Fold(context, ConvertToType(sizedType, Expr<SomeType>{expr}))}) {
1326     if (const auto *someExpr{UnwrapExpr<Expr<SomeKind<FROM>>>(*sized)}) {
1327       return common::visit(
1328           [](const auto &w) -> std::optional<Expr<SomeType>> {
1329             using FromType = ResultType<decltype(w)>;
1330             static constexpr int kind{FromType::kind};
1331             if constexpr (IsValidKindOfIntrinsicType(TO, kind)) {
1332               if (const auto *fromConst{UnwrapExpr<Constant<FromType>>(w)}) {
1333                 using FromWordType = typename FromType::Scalar;
1334                 using LogicalType = value::Logical<FromWordType::bits>;
1335                 using ElementType =
1336                     std::conditional_t<TO == TypeCategory::Logical, LogicalType,
1337                         typename LogicalType::Word>;
1338                 std::vector<ElementType> values;
1339                 auto at{fromConst->lbounds()};
1340                 auto shape{fromConst->shape()};
1341                 for (auto n{GetSize(shape)}; n-- > 0;
1342                      fromConst->IncrementSubscripts(at)) {
1343                   auto elt{fromConst->At(at)};
1344                   if constexpr (TO == TypeCategory::Logical) {
1345                     values.emplace_back(std::move(elt));
1346                   } else {
1347                     values.emplace_back(elt.word());
1348                   }
1349                 }
1350                 return {AsGenericExpr(AsExpr(Constant<Type<TO, kind>>{
1351                     std::move(values), std::move(shape)}))};
1352               }
1353             }
1354             return std::nullopt;
1355           },
1356           someExpr->u);
1357     }
1358   }
1359   return std::nullopt;
1360 }
1361 
1362 std::optional<Expr<SomeType>> DataConstantConversionExtension(
1363     FoldingContext &context, const DynamicType &toType,
1364     const Expr<SomeType> &expr0) {
1365   Expr<SomeType> expr{Fold(context, Expr<SomeType>{expr0})};
1366   if (!IsActuallyConstant(expr)) {
1367     return std::nullopt;
1368   }
1369   if (auto fromType{expr.GetType()}) {
1370     if (toType.category() == TypeCategory::Logical &&
1371         fromType->category() == TypeCategory::Integer) {
1372       return DataConstantConversionHelper<TypeCategory::Logical,
1373           TypeCategory::Integer>(context, toType, expr);
1374     }
1375     if (toType.category() == TypeCategory::Integer &&
1376         fromType->category() == TypeCategory::Logical) {
1377       return DataConstantConversionHelper<TypeCategory::Integer,
1378           TypeCategory::Logical>(context, toType, expr);
1379     }
1380   }
1381   return std::nullopt;
1382 }
1383 
1384 bool IsAllocatableOrPointerObject(const Expr<SomeType> &expr) {
1385   const semantics::Symbol *sym{UnwrapWholeSymbolOrComponentDataRef(expr)};
1386   return (sym &&
1387              semantics::IsAllocatableOrObjectPointer(&sym->GetUltimate())) ||
1388       evaluate::IsObjectPointer(expr);
1389 }
1390 
1391 bool IsAllocatableDesignator(const Expr<SomeType> &expr) {
1392   // Allocatable sub-objects are not themselves allocatable (9.5.3.1 NOTE 2).
1393   if (const semantics::Symbol *
1394       sym{UnwrapWholeSymbolOrComponentOrCoarrayRef(expr)}) {
1395     return semantics::IsAllocatable(sym->GetUltimate());
1396   }
1397   return false;
1398 }
1399 
1400 bool MayBePassedAsAbsentOptional(const Expr<SomeType> &expr) {
1401   const semantics::Symbol *sym{UnwrapWholeSymbolOrComponentDataRef(expr)};
1402   // 15.5.2.12 1. is pretty clear that an unallocated allocatable/pointer actual
1403   // may be passed to a non-allocatable/non-pointer optional dummy. Note that
1404   // other compilers (like nag, nvfortran, ifort, gfortran and xlf) seems to
1405   // ignore this point in intrinsic contexts (e.g CMPLX argument).
1406   return (sym && semantics::IsOptional(*sym)) ||
1407       IsAllocatableOrPointerObject(expr);
1408 }
1409 
1410 std::optional<Expr<SomeType>> HollerithToBOZ(FoldingContext &context,
1411     const Expr<SomeType> &expr, const DynamicType &type) {
1412   if (std::optional<std::string> chValue{GetScalarConstantValue<Ascii>(expr)}) {
1413     // Pad on the right with spaces when short, truncate the right if long.
1414     auto bytes{static_cast<std::size_t>(
1415         ToInt64(type.MeasureSizeInBytes(context, false)).value())};
1416     BOZLiteralConstant bits{0};
1417     for (std::size_t j{0}; j < bytes; ++j) {
1418       auto idx{isHostLittleEndian ? j : bytes - j - 1};
1419       char ch{idx >= chValue->size() ? ' ' : chValue->at(idx)};
1420       BOZLiteralConstant chBOZ{static_cast<unsigned char>(ch)};
1421       bits = bits.IOR(chBOZ.SHIFTL(8 * j));
1422     }
1423     return ConvertToType(type, Expr<SomeType>{bits});
1424   } else {
1425     return std::nullopt;
1426   }
1427 }
1428 
1429 // Extracts a whole symbol being used as a bound of a dummy argument,
1430 // possibly wrapped with parentheses or MAX(0, ...).
1431 // Works with any integer expression.
1432 template <typename T> const Symbol *GetBoundSymbol(const Expr<T> &);
1433 template <int KIND>
1434 const Symbol *GetBoundSymbol(
1435     const Expr<Type<TypeCategory::Integer, KIND>> &expr) {
1436   using T = Type<TypeCategory::Integer, KIND>;
1437   return common::visit(
1438       common::visitors{
1439           [](const Extremum<T> &max) -> const Symbol * {
1440             if (max.ordering == Ordering::Greater) {
1441               if (auto zero{ToInt64(max.left())}; zero && *zero == 0) {
1442                 return GetBoundSymbol(max.right());
1443               }
1444             }
1445             return nullptr;
1446           },
1447           [](const Parentheses<T> &x) { return GetBoundSymbol(x.left()); },
1448           [](const Designator<T> &x) -> const Symbol * {
1449             if (const auto *ref{std::get_if<SymbolRef>(&x.u)}) {
1450               return &**ref;
1451             }
1452             return nullptr;
1453           },
1454           [](const Convert<T, TypeCategory::Integer> &x) {
1455             return common::visit(
1456                 [](const auto &y) -> const Symbol * {
1457                   using yType = std::decay_t<decltype(y)>;
1458                   using yResult = typename yType::Result;
1459                   if constexpr (yResult::kind <= KIND) {
1460                     return GetBoundSymbol(y);
1461                   } else {
1462                     return nullptr;
1463                   }
1464                 },
1465                 x.left().u);
1466           },
1467           [](const auto &) -> const Symbol * { return nullptr; },
1468       },
1469       expr.u);
1470 }
1471 template <>
1472 const Symbol *GetBoundSymbol<SomeInteger>(const Expr<SomeInteger> &expr) {
1473   return common::visit(
1474       [](const auto &kindExpr) { return GetBoundSymbol(kindExpr); }, expr.u);
1475 }
1476 
1477 template <typename T>
1478 std::optional<bool> AreEquivalentInInterface(
1479     const Expr<T> &x, const Expr<T> &y) {
1480   auto xVal{ToInt64(x)};
1481   auto yVal{ToInt64(y)};
1482   if (xVal && yVal) {
1483     return *xVal == *yVal;
1484   } else if (xVal || yVal) {
1485     return false;
1486   }
1487   const Symbol *xSym{GetBoundSymbol(x)};
1488   const Symbol *ySym{GetBoundSymbol(y)};
1489   if (xSym && ySym) {
1490     if (&xSym->GetUltimate() == &ySym->GetUltimate()) {
1491       return true; // USE/host associated same symbol
1492     }
1493     auto xNum{semantics::GetDummyArgumentNumber(xSym)};
1494     auto yNum{semantics::GetDummyArgumentNumber(ySym)};
1495     if (xNum && yNum) {
1496       if (*xNum == *yNum) {
1497         auto xType{DynamicType::From(*xSym)};
1498         auto yType{DynamicType::From(*ySym)};
1499         return xType && yType && xType->IsEquivalentTo(*yType);
1500       }
1501     }
1502     return false;
1503   } else if (xSym || ySym) {
1504     return false;
1505   }
1506   // Neither expression is an integer constant or a whole symbol.
1507   if (x == y) {
1508     return true;
1509   } else {
1510     return std::nullopt; // not sure
1511   }
1512 }
1513 template std::optional<bool> AreEquivalentInInterface<SubscriptInteger>(
1514     const Expr<SubscriptInteger> &, const Expr<SubscriptInteger> &);
1515 template std::optional<bool> AreEquivalentInInterface<SomeInteger>(
1516     const Expr<SomeInteger> &, const Expr<SomeInteger> &);
1517 
1518 bool CheckForCoindexedObject(parser::ContextualMessages &messages,
1519     const std::optional<ActualArgument> &arg, const std::string &procName,
1520     const std::string &argName) {
1521   if (arg && ExtractCoarrayRef(arg->UnwrapExpr())) {
1522     messages.Say(arg->sourceLocation(),
1523         "'%s' argument to '%s' may not be a coindexed object"_err_en_US,
1524         argName, procName);
1525     return false;
1526   } else {
1527     return true;
1528   }
1529 }
1530 
1531 } // namespace Fortran::evaluate
1532 
1533 namespace Fortran::semantics {
1534 
1535 const Symbol &ResolveAssociations(const Symbol &original) {
1536   const Symbol &symbol{original.GetUltimate()};
1537   if (const auto *details{symbol.detailsIf<AssocEntityDetails>()}) {
1538     if (!details->rank()) { // Not RANK(n) or RANK(*)
1539       if (const Symbol * nested{UnwrapWholeSymbolDataRef(details->expr())}) {
1540         return ResolveAssociations(*nested);
1541       }
1542     }
1543   }
1544   return symbol;
1545 }
1546 
1547 // When a construct association maps to a variable, and that variable
1548 // is not an array with a vector-valued subscript, return the base
1549 // Symbol of that variable, else nullptr.  Descends into other construct
1550 // associations when one associations maps to another.
1551 static const Symbol *GetAssociatedVariable(const AssocEntityDetails &details) {
1552   if (const auto &expr{details.expr()}) {
1553     if (IsVariable(*expr) && !HasVectorSubscript(*expr)) {
1554       if (const Symbol * varSymbol{GetFirstSymbol(*expr)}) {
1555         return &GetAssociationRoot(*varSymbol);
1556       }
1557     }
1558   }
1559   return nullptr;
1560 }
1561 
1562 const Symbol &GetAssociationRoot(const Symbol &original) {
1563   const Symbol &symbol{ResolveAssociations(original)};
1564   if (const auto *details{symbol.detailsIf<AssocEntityDetails>()}) {
1565     if (const Symbol * root{GetAssociatedVariable(*details)}) {
1566       return *root;
1567     }
1568   }
1569   return symbol;
1570 }
1571 
1572 const Symbol *GetMainEntry(const Symbol *symbol) {
1573   if (symbol) {
1574     if (const auto *subpDetails{symbol->detailsIf<SubprogramDetails>()}) {
1575       if (const Scope * scope{subpDetails->entryScope()}) {
1576         if (const Symbol * main{scope->symbol()}) {
1577           return main;
1578         }
1579       }
1580     }
1581   }
1582   return symbol;
1583 }
1584 
1585 bool IsVariableName(const Symbol &original) {
1586   const Symbol &ultimate{original.GetUltimate()};
1587   return !IsNamedConstant(ultimate) &&
1588       (ultimate.has<ObjectEntityDetails>() ||
1589           ultimate.has<AssocEntityDetails>());
1590 }
1591 
1592 static bool IsPureProcedureImpl(
1593     const Symbol &original, semantics::UnorderedSymbolSet &set) {
1594   // An ENTRY is pure if its containing subprogram is
1595   const Symbol &symbol{DEREF(GetMainEntry(&original.GetUltimate()))};
1596   if (set.find(symbol) != set.end()) {
1597     return true;
1598   }
1599   set.emplace(symbol);
1600   if (const auto *procDetails{symbol.detailsIf<ProcEntityDetails>()}) {
1601     if (procDetails->procInterface()) {
1602       // procedure with a pure interface
1603       return IsPureProcedureImpl(*procDetails->procInterface(), set);
1604     }
1605   } else if (const auto *details{symbol.detailsIf<ProcBindingDetails>()}) {
1606     return IsPureProcedureImpl(details->symbol(), set);
1607   } else if (!IsProcedure(symbol)) {
1608     return false;
1609   }
1610   if (IsStmtFunction(symbol)) {
1611     // Section 15.7(1) states that a statement function is PURE if it does not
1612     // reference an IMPURE procedure or a VOLATILE variable
1613     if (const auto &expr{symbol.get<SubprogramDetails>().stmtFunction()}) {
1614       for (const SymbolRef &ref : evaluate::CollectSymbols(*expr)) {
1615         if (&*ref == &symbol) {
1616           return false; // error recovery, recursion is caught elsewhere
1617         }
1618         if (IsFunction(*ref) && !IsPureProcedureImpl(*ref, set)) {
1619           return false;
1620         }
1621         if (ref->GetUltimate().attrs().test(Attr::VOLATILE)) {
1622           return false;
1623         }
1624       }
1625     }
1626     return true; // statement function was not found to be impure
1627   }
1628   return symbol.attrs().test(Attr::PURE) ||
1629       (symbol.attrs().test(Attr::ELEMENTAL) &&
1630           !symbol.attrs().test(Attr::IMPURE));
1631 }
1632 
1633 bool IsPureProcedure(const Symbol &original) {
1634   semantics::UnorderedSymbolSet set;
1635   return IsPureProcedureImpl(original, set);
1636 }
1637 
1638 bool IsPureProcedure(const Scope &scope) {
1639   const Symbol *symbol{scope.GetSymbol()};
1640   return symbol && IsPureProcedure(*symbol);
1641 }
1642 
1643 bool IsExplicitlyImpureProcedure(const Symbol &original) {
1644   // An ENTRY is IMPURE if its containing subprogram is so
1645   return DEREF(GetMainEntry(&original.GetUltimate()))
1646       .attrs()
1647       .test(Attr::IMPURE);
1648 }
1649 
1650 bool IsElementalProcedure(const Symbol &original) {
1651   // An ENTRY is elemental if its containing subprogram is
1652   const Symbol &symbol{DEREF(GetMainEntry(&original.GetUltimate()))};
1653   if (IsProcedure(symbol)) {
1654     auto &foldingContext{symbol.owner().context().foldingContext()};
1655     auto restorer{foldingContext.messages().DiscardMessages()};
1656     auto proc{evaluate::characteristics::Procedure::Characterize(
1657         symbol, foldingContext)};
1658     return proc &&
1659         proc->attrs.test(evaluate::characteristics::Procedure::Attr::Elemental);
1660   } else {
1661     return false;
1662   }
1663 }
1664 
1665 bool IsFunction(const Symbol &symbol) {
1666   const Symbol &ultimate{symbol.GetUltimate()};
1667   return ultimate.test(Symbol::Flag::Function) ||
1668       (!ultimate.test(Symbol::Flag::Subroutine) &&
1669           common::visit(
1670               common::visitors{
1671                   [](const SubprogramDetails &x) { return x.isFunction(); },
1672                   [](const ProcEntityDetails &x) {
1673                     const Symbol *ifc{x.procInterface()};
1674                     return x.type() || (ifc && IsFunction(*ifc));
1675                   },
1676                   [](const ProcBindingDetails &x) {
1677                     return IsFunction(x.symbol());
1678                   },
1679                   [](const auto &) { return false; },
1680               },
1681               ultimate.details()));
1682 }
1683 
1684 bool IsFunction(const Scope &scope) {
1685   const Symbol *symbol{scope.GetSymbol()};
1686   return symbol && IsFunction(*symbol);
1687 }
1688 
1689 bool IsProcedure(const Symbol &symbol) {
1690   return common::visit(common::visitors{
1691                            [&symbol](const SubprogramDetails &) {
1692                              const Scope *scope{symbol.scope()};
1693                              // Main programs & BLOCK DATA are not procedures.
1694                              return !scope ||
1695                                  scope->kind() == Scope::Kind::Subprogram;
1696                            },
1697                            [](const SubprogramNameDetails &) { return true; },
1698                            [](const ProcEntityDetails &) { return true; },
1699                            [](const GenericDetails &) { return true; },
1700                            [](const ProcBindingDetails &) { return true; },
1701                            [](const auto &) { return false; },
1702                        },
1703       symbol.GetUltimate().details());
1704 }
1705 
1706 bool IsProcedure(const Scope &scope) {
1707   const Symbol *symbol{scope.GetSymbol()};
1708   return symbol && IsProcedure(*symbol);
1709 }
1710 
1711 bool IsProcedurePointer(const Symbol &original) {
1712   const Symbol &symbol{GetAssociationRoot(original)};
1713   return IsPointer(symbol) && IsProcedure(symbol);
1714 }
1715 
1716 bool IsProcedurePointer(const Symbol *symbol) {
1717   return symbol && IsProcedurePointer(*symbol);
1718 }
1719 
1720 bool IsObjectPointer(const Symbol *original) {
1721   if (original) {
1722     const Symbol &symbol{GetAssociationRoot(*original)};
1723     return IsPointer(symbol) && !IsProcedure(symbol);
1724   } else {
1725     return false;
1726   }
1727 }
1728 
1729 bool IsAllocatableOrObjectPointer(const Symbol *original) {
1730   if (original) {
1731     const Symbol &ultimate{original->GetUltimate()};
1732     if (const auto *assoc{ultimate.detailsIf<AssocEntityDetails>()}) {
1733       // Only SELECT RANK construct entities can be ALLOCATABLE/POINTER.
1734       return (assoc->rank() || assoc->IsAssumedSize() ||
1735                  assoc->IsAssumedRank()) &&
1736           IsAllocatableOrObjectPointer(UnwrapWholeSymbolDataRef(assoc->expr()));
1737     } else {
1738       return IsAllocatable(ultimate) ||
1739           (IsPointer(ultimate) && !IsProcedure(ultimate));
1740     }
1741   } else {
1742     return false;
1743   }
1744 }
1745 
1746 const Symbol *FindCommonBlockContaining(const Symbol &original) {
1747   const Symbol &root{GetAssociationRoot(original)};
1748   const auto *details{root.detailsIf<ObjectEntityDetails>()};
1749   return details ? details->commonBlock() : nullptr;
1750 }
1751 
1752 // 3.11 automatic data object
1753 bool IsAutomatic(const Symbol &original) {
1754   const Symbol &symbol{original.GetUltimate()};
1755   if (const auto *object{symbol.detailsIf<ObjectEntityDetails>()}) {
1756     if (!object->isDummy() && !IsAllocatable(symbol) && !IsPointer(symbol)) {
1757       if (const DeclTypeSpec * type{symbol.GetType()}) {
1758         // If a type parameter value is not a constant expression, the
1759         // object is automatic.
1760         if (type->category() == DeclTypeSpec::Character) {
1761           if (const auto &length{
1762                   type->characterTypeSpec().length().GetExplicit()}) {
1763             if (!evaluate::IsConstantExpr(*length)) {
1764               return true;
1765             }
1766           }
1767         } else if (const DerivedTypeSpec * derived{type->AsDerived()}) {
1768           for (const auto &pair : derived->parameters()) {
1769             if (const auto &value{pair.second.GetExplicit()}) {
1770               if (!evaluate::IsConstantExpr(*value)) {
1771                 return true;
1772               }
1773             }
1774           }
1775         }
1776       }
1777       // If an array bound is not a constant expression, the object is
1778       // automatic.
1779       for (const ShapeSpec &dim : object->shape()) {
1780         if (const auto &lb{dim.lbound().GetExplicit()}) {
1781           if (!evaluate::IsConstantExpr(*lb)) {
1782             return true;
1783           }
1784         }
1785         if (const auto &ub{dim.ubound().GetExplicit()}) {
1786           if (!evaluate::IsConstantExpr(*ub)) {
1787             return true;
1788           }
1789         }
1790       }
1791     }
1792   }
1793   return false;
1794 }
1795 
1796 bool IsSaved(const Symbol &original) {
1797   const Symbol &symbol{GetAssociationRoot(original)};
1798   const Scope &scope{symbol.owner()};
1799   const common::LanguageFeatureControl &features{
1800       scope.context().languageFeatures()};
1801   auto scopeKind{scope.kind()};
1802   if (symbol.has<AssocEntityDetails>()) {
1803     return false; // ASSOCIATE(non-variable)
1804   } else if (scopeKind == Scope::Kind::DerivedType) {
1805     return false; // this is a component
1806   } else if (symbol.attrs().test(Attr::SAVE)) {
1807     return true; // explicit SAVE attribute
1808   } else if (IsDummy(symbol) || IsFunctionResult(symbol) ||
1809       IsAutomatic(symbol) || IsNamedConstant(symbol)) {
1810     return false;
1811   } else if (scopeKind == Scope::Kind::Module ||
1812       (scopeKind == Scope::Kind::MainProgram &&
1813           (symbol.attrs().test(Attr::TARGET) || evaluate::IsCoarray(symbol)) &&
1814           Fortran::evaluate::CanCUDASymbolHaveSaveAttr(symbol))) {
1815     // 8.5.16p4
1816     // In main programs, implied SAVE matters only for pointer
1817     // initialization targets and coarrays.
1818     return true;
1819   } else if (scopeKind == Scope::Kind::MainProgram &&
1820       (features.IsEnabled(common::LanguageFeature::SaveMainProgram) ||
1821           (features.IsEnabled(
1822                common::LanguageFeature::SaveBigMainProgramVariables) &&
1823               symbol.size() > 32)) &&
1824       Fortran::evaluate::CanCUDASymbolHaveSaveAttr(symbol)) {
1825     // With SaveBigMainProgramVariables, keeping all unsaved main program
1826     // variables of 32 bytes or less on the stack allows keeping numerical and
1827     // logical scalars, small scalar characters or derived, small arrays, and
1828     // scalar descriptors on the stack. This leaves more room for lower level
1829     // optimizers to do register promotion or get easy aliasing information.
1830     return true;
1831   } else if (features.IsEnabled(common::LanguageFeature::DefaultSave) &&
1832       (scopeKind == Scope::Kind::MainProgram ||
1833           (scope.kind() == Scope::Kind::Subprogram &&
1834               !(scope.symbol() &&
1835                   scope.symbol()->attrs().test(Attr::RECURSIVE))))) {
1836     // -fno-automatic/-save/-Msave option applies to all objects in executable
1837     // main programs and subprograms unless they are explicitly RECURSIVE.
1838     return true;
1839   } else if (symbol.test(Symbol::Flag::InDataStmt)) {
1840     return true;
1841   } else if (const auto *object{symbol.detailsIf<ObjectEntityDetails>()};
1842              object && object->init()) {
1843     return true;
1844   } else if (IsProcedurePointer(symbol) && symbol.has<ProcEntityDetails>() &&
1845       symbol.get<ProcEntityDetails>().init()) {
1846     return true;
1847   } else if (scope.hasSAVE()) {
1848     return true; // bare SAVE statement
1849   } else if (const Symbol * block{FindCommonBlockContaining(symbol)};
1850              block && block->attrs().test(Attr::SAVE)) {
1851     return true; // in COMMON with SAVE
1852   } else {
1853     return false;
1854   }
1855 }
1856 
1857 bool IsDummy(const Symbol &symbol) {
1858   return common::visit(
1859       common::visitors{[](const EntityDetails &x) { return x.isDummy(); },
1860           [](const ObjectEntityDetails &x) { return x.isDummy(); },
1861           [](const ProcEntityDetails &x) { return x.isDummy(); },
1862           [](const SubprogramDetails &x) { return x.isDummy(); },
1863           [](const auto &) { return false; }},
1864       ResolveAssociations(symbol).details());
1865 }
1866 
1867 bool IsAssumedShape(const Symbol &symbol) {
1868   const Symbol &ultimate{ResolveAssociations(symbol)};
1869   const auto *object{ultimate.detailsIf<ObjectEntityDetails>()};
1870   return object && object->IsAssumedShape() &&
1871       !semantics::IsAllocatableOrObjectPointer(&ultimate);
1872 }
1873 
1874 bool IsDeferredShape(const Symbol &symbol) {
1875   const Symbol &ultimate{ResolveAssociations(symbol)};
1876   const auto *object{ultimate.detailsIf<ObjectEntityDetails>()};
1877   return object && object->CanBeDeferredShape() &&
1878       semantics::IsAllocatableOrObjectPointer(&ultimate);
1879 }
1880 
1881 bool IsFunctionResult(const Symbol &original) {
1882   const Symbol &symbol{GetAssociationRoot(original)};
1883   return common::visit(
1884       common::visitors{
1885           [](const EntityDetails &x) { return x.isFuncResult(); },
1886           [](const ObjectEntityDetails &x) { return x.isFuncResult(); },
1887           [](const ProcEntityDetails &x) { return x.isFuncResult(); },
1888           [](const auto &) { return false; },
1889       },
1890       symbol.details());
1891 }
1892 
1893 bool IsKindTypeParameter(const Symbol &symbol) {
1894   const auto *param{symbol.GetUltimate().detailsIf<TypeParamDetails>()};
1895   return param && param->attr() == common::TypeParamAttr::Kind;
1896 }
1897 
1898 bool IsLenTypeParameter(const Symbol &symbol) {
1899   const auto *param{symbol.GetUltimate().detailsIf<TypeParamDetails>()};
1900   return param && param->attr() == common::TypeParamAttr::Len;
1901 }
1902 
1903 bool IsExtensibleType(const DerivedTypeSpec *derived) {
1904   return !IsSequenceOrBindCType(derived) && !IsIsoCType(derived);
1905 }
1906 
1907 bool IsSequenceOrBindCType(const DerivedTypeSpec *derived) {
1908   return derived &&
1909       (derived->typeSymbol().attrs().test(Attr::BIND_C) ||
1910           derived->typeSymbol().get<DerivedTypeDetails>().sequence());
1911 }
1912 
1913 static bool IsSameModule(const Scope *x, const Scope *y) {
1914   if (x == y) {
1915     return true;
1916   } else if (x && y) {
1917     // Allow for a builtin module to be read from distinct paths
1918     const Symbol *xSym{x->symbol()};
1919     const Symbol *ySym{y->symbol()};
1920     if (xSym && ySym && xSym->name() == ySym->name()) {
1921       const auto *xMod{xSym->detailsIf<ModuleDetails>()};
1922       const auto *yMod{ySym->detailsIf<ModuleDetails>()};
1923       if (xMod && yMod) {
1924         auto xHash{xMod->moduleFileHash()};
1925         auto yHash{yMod->moduleFileHash()};
1926         return xHash && yHash && *xHash == *yHash;
1927       }
1928     }
1929   }
1930   return false;
1931 }
1932 
1933 bool IsBuiltinDerivedType(const DerivedTypeSpec *derived, const char *name) {
1934   if (derived) {
1935     const auto &symbol{derived->typeSymbol()};
1936     const Scope &scope{symbol.owner()};
1937     return symbol.name() == "__builtin_"s + name &&
1938         IsSameModule(&scope, scope.context().GetBuiltinsScope());
1939   } else {
1940     return false;
1941   }
1942 }
1943 
1944 bool IsBuiltinCPtr(const Symbol &symbol) {
1945   if (const DeclTypeSpec *declType = symbol.GetType()) {
1946     if (const DerivedTypeSpec *derived = declType->AsDerived()) {
1947       return IsIsoCType(derived);
1948     }
1949   }
1950   return false;
1951 }
1952 
1953 bool IsIsoCType(const DerivedTypeSpec *derived) {
1954   return IsBuiltinDerivedType(derived, "c_ptr") ||
1955       IsBuiltinDerivedType(derived, "c_funptr");
1956 }
1957 
1958 bool IsEventType(const DerivedTypeSpec *derived) {
1959   return IsBuiltinDerivedType(derived, "event_type");
1960 }
1961 
1962 bool IsLockType(const DerivedTypeSpec *derived) {
1963   return IsBuiltinDerivedType(derived, "lock_type");
1964 }
1965 
1966 bool IsNotifyType(const DerivedTypeSpec *derived) {
1967   return IsBuiltinDerivedType(derived, "notify_type");
1968 }
1969 
1970 bool IsIeeeFlagType(const DerivedTypeSpec *derived) {
1971   return IsBuiltinDerivedType(derived, "ieee_flag_type");
1972 }
1973 
1974 bool IsIeeeRoundType(const DerivedTypeSpec *derived) {
1975   return IsBuiltinDerivedType(derived, "ieee_round_type");
1976 }
1977 
1978 bool IsTeamType(const DerivedTypeSpec *derived) {
1979   return IsBuiltinDerivedType(derived, "team_type");
1980 }
1981 
1982 bool IsBadCoarrayType(const DerivedTypeSpec *derived) {
1983   return IsTeamType(derived) || IsIsoCType(derived);
1984 }
1985 
1986 bool IsEventTypeOrLockType(const DerivedTypeSpec *derivedTypeSpec) {
1987   return IsEventType(derivedTypeSpec) || IsLockType(derivedTypeSpec);
1988 }
1989 
1990 int CountLenParameters(const DerivedTypeSpec &type) {
1991   return llvm::count_if(
1992       type.parameters(), [](const auto &pair) { return pair.second.isLen(); });
1993 }
1994 
1995 int CountNonConstantLenParameters(const DerivedTypeSpec &type) {
1996   return llvm::count_if(type.parameters(), [](const auto &pair) {
1997     if (!pair.second.isLen()) {
1998       return false;
1999     } else if (const auto &expr{pair.second.GetExplicit()}) {
2000       return !IsConstantExpr(*expr);
2001     } else {
2002       return true;
2003     }
2004   });
2005 }
2006 
2007 const Symbol &GetUsedModule(const UseDetails &details) {
2008   return DEREF(details.symbol().owner().symbol());
2009 }
2010 
2011 static const Symbol *FindFunctionResult(
2012     const Symbol &original, UnorderedSymbolSet &seen) {
2013   const Symbol &root{GetAssociationRoot(original)};
2014   ;
2015   if (!seen.insert(root).second) {
2016     return nullptr; // don't loop
2017   }
2018   return common::visit(
2019       common::visitors{[](const SubprogramDetails &subp) {
2020                          return subp.isFunction() ? &subp.result() : nullptr;
2021                        },
2022           [&](const ProcEntityDetails &proc) {
2023             const Symbol *iface{proc.procInterface()};
2024             return iface ? FindFunctionResult(*iface, seen) : nullptr;
2025           },
2026           [&](const ProcBindingDetails &binding) {
2027             return FindFunctionResult(binding.symbol(), seen);
2028           },
2029           [](const auto &) -> const Symbol * { return nullptr; }},
2030       root.details());
2031 }
2032 
2033 const Symbol *FindFunctionResult(const Symbol &symbol) {
2034   UnorderedSymbolSet seen;
2035   return FindFunctionResult(symbol, seen);
2036 }
2037 
2038 // These are here in Evaluate/tools.cpp so that Evaluate can use
2039 // them; they cannot be defined in symbol.h due to the dependence
2040 // on Scope.
2041 
2042 bool SymbolSourcePositionCompare::operator()(
2043     const SymbolRef &x, const SymbolRef &y) const {
2044   return x->GetSemanticsContext().allCookedSources().Precedes(
2045       x->name(), y->name());
2046 }
2047 bool SymbolSourcePositionCompare::operator()(
2048     const MutableSymbolRef &x, const MutableSymbolRef &y) const {
2049   return x->GetSemanticsContext().allCookedSources().Precedes(
2050       x->name(), y->name());
2051 }
2052 
2053 SemanticsContext &Symbol::GetSemanticsContext() const {
2054   return DEREF(owner_).context();
2055 }
2056 
2057 bool AreTkCompatibleTypes(const DeclTypeSpec *x, const DeclTypeSpec *y) {
2058   if (x && y) {
2059     if (auto xDt{evaluate::DynamicType::From(*x)}) {
2060       if (auto yDt{evaluate::DynamicType::From(*y)}) {
2061         return xDt->IsTkCompatibleWith(*yDt);
2062       }
2063     }
2064   }
2065   return false;
2066 }
2067 
2068 common::IgnoreTKRSet GetIgnoreTKR(const Symbol &symbol) {
2069   common::IgnoreTKRSet result;
2070   if (const auto *object{symbol.detailsIf<ObjectEntityDetails>()}) {
2071     result = object->ignoreTKR();
2072     if (const Symbol * ownerSymbol{symbol.owner().symbol()}) {
2073       if (const auto *ownerSubp{ownerSymbol->detailsIf<SubprogramDetails>()}) {
2074         if (ownerSubp->defaultIgnoreTKR()) {
2075           result |= common::ignoreTKRAll;
2076         }
2077       }
2078     }
2079   }
2080   return result;
2081 }
2082 
2083 std::optional<int> GetDummyArgumentNumber(const Symbol *symbol) {
2084   if (symbol) {
2085     if (IsDummy(*symbol)) {
2086       if (const Symbol * subpSym{symbol->owner().symbol()}) {
2087         if (const auto *subp{subpSym->detailsIf<SubprogramDetails>()}) {
2088           int j{0};
2089           for (const Symbol *dummy : subp->dummyArgs()) {
2090             if (dummy == symbol) {
2091               return j;
2092             }
2093             ++j;
2094           }
2095         }
2096       }
2097     }
2098   }
2099   return std::nullopt;
2100 }
2101 
2102 // Given a symbol that is a SubprogramNameDetails in a submodule, try to
2103 // find its interface definition in its module or ancestor submodule.
2104 const Symbol *FindAncestorModuleProcedure(const Symbol *symInSubmodule) {
2105   if (symInSubmodule && symInSubmodule->owner().IsSubmodule()) {
2106     if (const auto *nameDetails{
2107             symInSubmodule->detailsIf<semantics::SubprogramNameDetails>()};
2108         nameDetails &&
2109         nameDetails->kind() == semantics::SubprogramKind::Module) {
2110       const Symbol *next{symInSubmodule->owner().symbol()};
2111       while (const Symbol * submodSym{next}) {
2112         next = nullptr;
2113         if (const auto *modDetails{
2114                 submodSym->detailsIf<semantics::ModuleDetails>()};
2115             modDetails && modDetails->isSubmodule() && modDetails->scope()) {
2116           if (const semantics::Scope & parent{modDetails->scope()->parent()};
2117               parent.IsSubmodule() || parent.IsModule()) {
2118             if (auto iter{parent.find(symInSubmodule->name())};
2119                 iter != parent.end()) {
2120               const Symbol &proc{iter->second->GetUltimate()};
2121               if (IsProcedure(proc)) {
2122                 return &proc;
2123               }
2124             } else if (parent.IsSubmodule()) {
2125               next = parent.symbol();
2126             }
2127           }
2128         }
2129       }
2130     }
2131   }
2132   return nullptr;
2133 }
2134 
2135 } // namespace Fortran::semantics
2136