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