xref: /llvm-project/flang/lib/Evaluate/tools.cpp (revision 8c3b6bd0cb04a6edc3b294f9048cbfb1ea6c200c)
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 Subscript &) const {
1015     return {};
1016   }
1017   semantics::UnorderedSymbolSet operator()(const ProcedureRef &) const {
1018     return {};
1019   }
1020 };
1021 template <typename A>
1022 semantics::UnorderedSymbolSet CollectCudaSymbols(const A &x) {
1023   return CollectCudaSymbolsHelper{}(x);
1024 }
1025 template semantics::UnorderedSymbolSet CollectCudaSymbols(
1026     const Expr<SomeType> &);
1027 template semantics::UnorderedSymbolSet CollectCudaSymbols(
1028     const Expr<SomeInteger> &);
1029 template semantics::UnorderedSymbolSet CollectCudaSymbols(
1030     const Expr<SubscriptInteger> &);
1031 
1032 // HasVectorSubscript()
1033 struct HasVectorSubscriptHelper
1034     : public AnyTraverse<HasVectorSubscriptHelper, bool,
1035           /*TraverseAssocEntityDetails=*/false> {
1036   using Base = AnyTraverse<HasVectorSubscriptHelper, bool, false>;
1037   HasVectorSubscriptHelper() : Base{*this} {}
1038   using Base::operator();
1039   bool operator()(const Subscript &ss) const {
1040     return !std::holds_alternative<Triplet>(ss.u) && ss.Rank() > 0;
1041   }
1042   bool operator()(const ProcedureRef &) const {
1043     return false; // don't descend into function call arguments
1044   }
1045 };
1046 
1047 bool HasVectorSubscript(const Expr<SomeType> &expr) {
1048   return HasVectorSubscriptHelper{}(expr);
1049 }
1050 
1051 parser::Message *AttachDeclaration(
1052     parser::Message &message, const Symbol &symbol) {
1053   const Symbol *unhosted{&symbol};
1054   while (
1055       const auto *assoc{unhosted->detailsIf<semantics::HostAssocDetails>()}) {
1056     unhosted = &assoc->symbol();
1057   }
1058   if (const auto *binding{
1059           unhosted->detailsIf<semantics::ProcBindingDetails>()}) {
1060     if (binding->symbol().name() != symbol.name()) {
1061       message.Attach(binding->symbol().name(),
1062           "Procedure '%s' of type '%s' is bound to '%s'"_en_US, symbol.name(),
1063           symbol.owner().GetName().value(), binding->symbol().name());
1064       return &message;
1065     }
1066     unhosted = &binding->symbol();
1067   }
1068   if (const auto *use{symbol.detailsIf<semantics::UseDetails>()}) {
1069     message.Attach(use->location(),
1070         "'%s' is USE-associated with '%s' in module '%s'"_en_US, symbol.name(),
1071         unhosted->name(), GetUsedModule(*use).name());
1072   } else {
1073     message.Attach(
1074         unhosted->name(), "Declaration of '%s'"_en_US, unhosted->name());
1075   }
1076   return &message;
1077 }
1078 
1079 parser::Message *AttachDeclaration(
1080     parser::Message *message, const Symbol &symbol) {
1081   return message ? AttachDeclaration(*message, symbol) : nullptr;
1082 }
1083 
1084 class FindImpureCallHelper
1085     : public AnyTraverse<FindImpureCallHelper, std::optional<std::string>,
1086           /*TraverseAssocEntityDetails=*/false> {
1087   using Result = std::optional<std::string>;
1088   using Base = AnyTraverse<FindImpureCallHelper, Result, false>;
1089 
1090 public:
1091   explicit FindImpureCallHelper(FoldingContext &c) : Base{*this}, context_{c} {}
1092   using Base::operator();
1093   Result operator()(const ProcedureRef &call) const {
1094     if (auto chars{characteristics::Procedure::Characterize(
1095             call.proc(), context_, /*emitError=*/false)}) {
1096       if (chars->attrs.test(characteristics::Procedure::Attr::Pure)) {
1097         return (*this)(call.arguments());
1098       }
1099     }
1100     return call.proc().GetName();
1101   }
1102 
1103 private:
1104   FoldingContext &context_;
1105 };
1106 
1107 std::optional<std::string> FindImpureCall(
1108     FoldingContext &context, const Expr<SomeType> &expr) {
1109   return FindImpureCallHelper{context}(expr);
1110 }
1111 std::optional<std::string> FindImpureCall(
1112     FoldingContext &context, const ProcedureRef &proc) {
1113   return FindImpureCallHelper{context}(proc);
1114 }
1115 
1116 // Common handling for procedure pointer compatibility of left- and right-hand
1117 // sides.  Returns nullopt if they're compatible.  Otherwise, it returns a
1118 // message that needs to be augmented by the names of the left and right sides
1119 // and the content of the "whyNotCompatible" string.
1120 std::optional<parser::MessageFixedText> CheckProcCompatibility(bool isCall,
1121     const std::optional<characteristics::Procedure> &lhsProcedure,
1122     const characteristics::Procedure *rhsProcedure,
1123     const SpecificIntrinsic *specificIntrinsic, std::string &whyNotCompatible,
1124     std::optional<std::string> &warning, bool ignoreImplicitVsExplicit) {
1125   std::optional<parser::MessageFixedText> msg;
1126   if (!lhsProcedure) {
1127     msg = "In assignment to object %s, the target '%s' is a procedure"
1128           " designator"_err_en_US;
1129   } else if (!rhsProcedure) {
1130     msg = "In assignment to procedure %s, the characteristics of the target"
1131           " procedure '%s' could not be determined"_err_en_US;
1132   } else if (!isCall && lhsProcedure->functionResult &&
1133       rhsProcedure->functionResult &&
1134       !lhsProcedure->functionResult->IsCompatibleWith(
1135           *rhsProcedure->functionResult, &whyNotCompatible)) {
1136     msg =
1137         "Function %s associated with incompatible function designator '%s': %s"_err_en_US;
1138   } else if (lhsProcedure->IsCompatibleWith(*rhsProcedure,
1139                  ignoreImplicitVsExplicit, &whyNotCompatible, specificIntrinsic,
1140                  &warning)) {
1141     // OK
1142   } else if (isCall) {
1143     msg = "Procedure %s associated with result of reference to function '%s'"
1144           " that is an incompatible procedure pointer: %s"_err_en_US;
1145   } else if (lhsProcedure->IsPure() && !rhsProcedure->IsPure()) {
1146     msg = "PURE procedure %s may not be associated with non-PURE"
1147           " procedure designator '%s'"_err_en_US;
1148   } else if (lhsProcedure->IsFunction() && rhsProcedure->IsSubroutine()) {
1149     msg = "Function %s may not be associated with subroutine"
1150           " designator '%s'"_err_en_US;
1151   } else if (lhsProcedure->IsSubroutine() && rhsProcedure->IsFunction()) {
1152     msg = "Subroutine %s may not be associated with function"
1153           " designator '%s'"_err_en_US;
1154   } else if (lhsProcedure->HasExplicitInterface() &&
1155       !rhsProcedure->HasExplicitInterface()) {
1156     // Section 10.2.2.4, paragraph 3 prohibits associating a procedure pointer
1157     // that has an explicit interface with a procedure whose characteristics
1158     // don't match.  That's the case if the target procedure has an implicit
1159     // interface.  But this case is allowed by several other compilers as long
1160     // as the explicit interface can be called via an implicit interface.
1161     if (!lhsProcedure->CanBeCalledViaImplicitInterface()) {
1162       msg = "Procedure %s with explicit interface that cannot be called via "
1163             "an implicit interface cannot be associated with procedure "
1164             "designator with an implicit interface"_err_en_US;
1165     }
1166   } else if (!lhsProcedure->HasExplicitInterface() &&
1167       rhsProcedure->HasExplicitInterface()) {
1168     // OK if the target can be called via an implicit interface
1169     if (!rhsProcedure->CanBeCalledViaImplicitInterface() &&
1170         !specificIntrinsic) {
1171       msg = "Procedure %s with implicit interface may not be associated "
1172             "with procedure designator '%s' with explicit interface that "
1173             "cannot be called via an implicit interface"_err_en_US;
1174     }
1175   } else {
1176     msg = "Procedure %s associated with incompatible procedure"
1177           " designator '%s': %s"_err_en_US;
1178   }
1179   return msg;
1180 }
1181 
1182 // GetLastPointerSymbol()
1183 static const Symbol *GetLastPointerSymbol(const Symbol &symbol) {
1184   return IsPointer(GetAssociationRoot(symbol)) ? &symbol : nullptr;
1185 }
1186 static const Symbol *GetLastPointerSymbol(const SymbolRef &symbol) {
1187   return GetLastPointerSymbol(*symbol);
1188 }
1189 static const Symbol *GetLastPointerSymbol(const Component &x) {
1190   const Symbol &c{x.GetLastSymbol()};
1191   return IsPointer(c) ? &c : GetLastPointerSymbol(x.base());
1192 }
1193 static const Symbol *GetLastPointerSymbol(const NamedEntity &x) {
1194   const auto *c{x.UnwrapComponent()};
1195   return c ? GetLastPointerSymbol(*c) : GetLastPointerSymbol(x.GetLastSymbol());
1196 }
1197 static const Symbol *GetLastPointerSymbol(const ArrayRef &x) {
1198   return GetLastPointerSymbol(x.base());
1199 }
1200 static const Symbol *GetLastPointerSymbol(const CoarrayRef &x) {
1201   return nullptr;
1202 }
1203 const Symbol *GetLastPointerSymbol(const DataRef &x) {
1204   return common::visit(
1205       [](const auto &y) { return GetLastPointerSymbol(y); }, x.u);
1206 }
1207 
1208 template <TypeCategory TO, TypeCategory FROM>
1209 static std::optional<Expr<SomeType>> DataConstantConversionHelper(
1210     FoldingContext &context, const DynamicType &toType,
1211     const Expr<SomeType> &expr) {
1212   DynamicType sizedType{FROM, toType.kind()};
1213   if (auto sized{
1214           Fold(context, ConvertToType(sizedType, Expr<SomeType>{expr}))}) {
1215     if (const auto *someExpr{UnwrapExpr<Expr<SomeKind<FROM>>>(*sized)}) {
1216       return common::visit(
1217           [](const auto &w) -> std::optional<Expr<SomeType>> {
1218             using FromType = ResultType<decltype(w)>;
1219             static constexpr int kind{FromType::kind};
1220             if constexpr (IsValidKindOfIntrinsicType(TO, kind)) {
1221               if (const auto *fromConst{UnwrapExpr<Constant<FromType>>(w)}) {
1222                 using FromWordType = typename FromType::Scalar;
1223                 using LogicalType = value::Logical<FromWordType::bits>;
1224                 using ElementType =
1225                     std::conditional_t<TO == TypeCategory::Logical, LogicalType,
1226                         typename LogicalType::Word>;
1227                 std::vector<ElementType> values;
1228                 auto at{fromConst->lbounds()};
1229                 auto shape{fromConst->shape()};
1230                 for (auto n{GetSize(shape)}; n-- > 0;
1231                      fromConst->IncrementSubscripts(at)) {
1232                   auto elt{fromConst->At(at)};
1233                   if constexpr (TO == TypeCategory::Logical) {
1234                     values.emplace_back(std::move(elt));
1235                   } else {
1236                     values.emplace_back(elt.word());
1237                   }
1238                 }
1239                 return {AsGenericExpr(AsExpr(Constant<Type<TO, kind>>{
1240                     std::move(values), std::move(shape)}))};
1241               }
1242             }
1243             return std::nullopt;
1244           },
1245           someExpr->u);
1246     }
1247   }
1248   return std::nullopt;
1249 }
1250 
1251 std::optional<Expr<SomeType>> DataConstantConversionExtension(
1252     FoldingContext &context, const DynamicType &toType,
1253     const Expr<SomeType> &expr0) {
1254   Expr<SomeType> expr{Fold(context, Expr<SomeType>{expr0})};
1255   if (!IsActuallyConstant(expr)) {
1256     return std::nullopt;
1257   }
1258   if (auto fromType{expr.GetType()}) {
1259     if (toType.category() == TypeCategory::Logical &&
1260         fromType->category() == TypeCategory::Integer) {
1261       return DataConstantConversionHelper<TypeCategory::Logical,
1262           TypeCategory::Integer>(context, toType, expr);
1263     }
1264     if (toType.category() == TypeCategory::Integer &&
1265         fromType->category() == TypeCategory::Logical) {
1266       return DataConstantConversionHelper<TypeCategory::Integer,
1267           TypeCategory::Logical>(context, toType, expr);
1268     }
1269   }
1270   return std::nullopt;
1271 }
1272 
1273 bool IsAllocatableOrPointerObject(const Expr<SomeType> &expr) {
1274   const semantics::Symbol *sym{UnwrapWholeSymbolOrComponentDataRef(expr)};
1275   return (sym &&
1276              semantics::IsAllocatableOrObjectPointer(&sym->GetUltimate())) ||
1277       evaluate::IsObjectPointer(expr);
1278 }
1279 
1280 bool IsAllocatableDesignator(const Expr<SomeType> &expr) {
1281   // Allocatable sub-objects are not themselves allocatable (9.5.3.1 NOTE 2).
1282   if (const semantics::Symbol *
1283       sym{UnwrapWholeSymbolOrComponentOrCoarrayRef(expr)}) {
1284     return semantics::IsAllocatable(sym->GetUltimate());
1285   }
1286   return false;
1287 }
1288 
1289 bool MayBePassedAsAbsentOptional(const Expr<SomeType> &expr) {
1290   const semantics::Symbol *sym{UnwrapWholeSymbolOrComponentDataRef(expr)};
1291   // 15.5.2.12 1. is pretty clear that an unallocated allocatable/pointer actual
1292   // may be passed to a non-allocatable/non-pointer optional dummy. Note that
1293   // other compilers (like nag, nvfortran, ifort, gfortran and xlf) seems to
1294   // ignore this point in intrinsic contexts (e.g CMPLX argument).
1295   return (sym && semantics::IsOptional(*sym)) ||
1296       IsAllocatableOrPointerObject(expr);
1297 }
1298 
1299 std::optional<Expr<SomeType>> HollerithToBOZ(FoldingContext &context,
1300     const Expr<SomeType> &expr, const DynamicType &type) {
1301   if (std::optional<std::string> chValue{GetScalarConstantValue<Ascii>(expr)}) {
1302     // Pad on the right with spaces when short, truncate the right if long.
1303     // TODO: big-endian targets
1304     auto bytes{static_cast<std::size_t>(
1305         ToInt64(type.MeasureSizeInBytes(context, false)).value())};
1306     BOZLiteralConstant bits{0};
1307     for (std::size_t j{0}; j < bytes; ++j) {
1308       char ch{j >= chValue->size() ? ' ' : chValue->at(j)};
1309       BOZLiteralConstant chBOZ{static_cast<unsigned char>(ch)};
1310       bits = bits.IOR(chBOZ.SHIFTL(8 * j));
1311     }
1312     return ConvertToType(type, Expr<SomeType>{bits});
1313   } else {
1314     return std::nullopt;
1315   }
1316 }
1317 
1318 // Extracts a whole symbol being used as a bound of a dummy argument,
1319 // possibly wrapped with parentheses or MAX(0, ...).
1320 template <int KIND>
1321 static const Symbol *GetBoundSymbol(
1322     const Expr<Type<TypeCategory::Integer, KIND>> &expr) {
1323   using T = Type<TypeCategory::Integer, KIND>;
1324   return common::visit(
1325       common::visitors{
1326           [](const Extremum<T> &max) -> const Symbol * {
1327             if (max.ordering == Ordering::Greater) {
1328               if (auto zero{ToInt64(max.left())}; zero && *zero == 0) {
1329                 return GetBoundSymbol(max.right());
1330               }
1331             }
1332             return nullptr;
1333           },
1334           [](const Parentheses<T> &x) { return GetBoundSymbol(x.left()); },
1335           [](const Designator<T> &x) -> const Symbol * {
1336             if (const auto *ref{std::get_if<SymbolRef>(&x.u)}) {
1337               return &**ref;
1338             }
1339             return nullptr;
1340           },
1341           [](const Convert<T, TypeCategory::Integer> &x) {
1342             return common::visit(
1343                 [](const auto &y) -> const Symbol * {
1344                   using yType = std::decay_t<decltype(y)>;
1345                   using yResult = typename yType::Result;
1346                   if constexpr (yResult::kind <= KIND) {
1347                     return GetBoundSymbol(y);
1348                   } else {
1349                     return nullptr;
1350                   }
1351                 },
1352                 x.left().u);
1353           },
1354           [](const auto &) -> const Symbol * { return nullptr; },
1355       },
1356       expr.u);
1357 }
1358 
1359 std::optional<bool> AreEquivalentInInterface(
1360     const Expr<SubscriptInteger> &x, const Expr<SubscriptInteger> &y) {
1361   auto xVal{ToInt64(x)};
1362   auto yVal{ToInt64(y)};
1363   if (xVal && yVal) {
1364     return *xVal == *yVal;
1365   } else if (xVal || yVal) {
1366     return false;
1367   }
1368   const Symbol *xSym{GetBoundSymbol(x)};
1369   const Symbol *ySym{GetBoundSymbol(y)};
1370   if (xSym && ySym) {
1371     if (&xSym->GetUltimate() == &ySym->GetUltimate()) {
1372       return true; // USE/host associated same symbol
1373     }
1374     auto xNum{semantics::GetDummyArgumentNumber(xSym)};
1375     auto yNum{semantics::GetDummyArgumentNumber(ySym)};
1376     if (xNum && yNum) {
1377       if (*xNum == *yNum) {
1378         auto xType{DynamicType::From(*xSym)};
1379         auto yType{DynamicType::From(*ySym)};
1380         return xType && yType && xType->IsEquivalentTo(*yType);
1381       }
1382     }
1383     return false;
1384   } else if (xSym || ySym) {
1385     return false;
1386   }
1387   // Neither expression is an integer constant or a whole symbol.
1388   if (x == y) {
1389     return true;
1390   } else {
1391     return std::nullopt; // not sure
1392   }
1393 }
1394 
1395 bool CheckForCoindexedObject(parser::ContextualMessages &messages,
1396     const std::optional<ActualArgument> &arg, const std::string &procName,
1397     const std::string &argName) {
1398   if (arg && ExtractCoarrayRef(arg->UnwrapExpr())) {
1399     messages.Say(arg->sourceLocation(),
1400         "'%s' argument to '%s' may not be a coindexed object"_err_en_US,
1401         argName, procName);
1402     return false;
1403   } else {
1404     return true;
1405   }
1406 }
1407 
1408 } // namespace Fortran::evaluate
1409 
1410 namespace Fortran::semantics {
1411 
1412 const Symbol &ResolveAssociations(const Symbol &original) {
1413   const Symbol &symbol{original.GetUltimate()};
1414   if (const auto *details{symbol.detailsIf<AssocEntityDetails>()}) {
1415     if (!details->rank()) { // Not RANK(n) or RANK(*)
1416       if (const Symbol * nested{UnwrapWholeSymbolDataRef(details->expr())}) {
1417         return ResolveAssociations(*nested);
1418       }
1419     }
1420   }
1421   return symbol;
1422 }
1423 
1424 // When a construct association maps to a variable, and that variable
1425 // is not an array with a vector-valued subscript, return the base
1426 // Symbol of that variable, else nullptr.  Descends into other construct
1427 // associations when one associations maps to another.
1428 static const Symbol *GetAssociatedVariable(const AssocEntityDetails &details) {
1429   if (const auto &expr{details.expr()}) {
1430     if (IsVariable(*expr) && !HasVectorSubscript(*expr)) {
1431       if (const Symbol * varSymbol{GetFirstSymbol(*expr)}) {
1432         return &GetAssociationRoot(*varSymbol);
1433       }
1434     }
1435   }
1436   return nullptr;
1437 }
1438 
1439 const Symbol &GetAssociationRoot(const Symbol &original) {
1440   const Symbol &symbol{ResolveAssociations(original)};
1441   if (const auto *details{symbol.detailsIf<AssocEntityDetails>()}) {
1442     if (const Symbol * root{GetAssociatedVariable(*details)}) {
1443       return *root;
1444     }
1445   }
1446   return symbol;
1447 }
1448 
1449 const Symbol *GetMainEntry(const Symbol *symbol) {
1450   if (symbol) {
1451     if (const auto *subpDetails{symbol->detailsIf<SubprogramDetails>()}) {
1452       if (const Scope * scope{subpDetails->entryScope()}) {
1453         if (const Symbol * main{scope->symbol()}) {
1454           return main;
1455         }
1456       }
1457     }
1458   }
1459   return symbol;
1460 }
1461 
1462 bool IsVariableName(const Symbol &original) {
1463   const Symbol &ultimate{original.GetUltimate()};
1464   return !IsNamedConstant(ultimate) &&
1465       (ultimate.has<ObjectEntityDetails>() ||
1466           ultimate.has<AssocEntityDetails>());
1467 }
1468 
1469 static bool IsPureProcedureImpl(
1470     const Symbol &original, semantics::UnorderedSymbolSet &set) {
1471   // An ENTRY is pure if its containing subprogram is
1472   const Symbol &symbol{DEREF(GetMainEntry(&original.GetUltimate()))};
1473   if (set.find(symbol) != set.end()) {
1474     return true;
1475   }
1476   set.emplace(symbol);
1477   if (const auto *procDetails{symbol.detailsIf<ProcEntityDetails>()}) {
1478     if (procDetails->procInterface()) {
1479       // procedure with a pure interface
1480       return IsPureProcedureImpl(*procDetails->procInterface(), set);
1481     }
1482   } else if (const auto *details{symbol.detailsIf<ProcBindingDetails>()}) {
1483     return IsPureProcedureImpl(details->symbol(), set);
1484   } else if (!IsProcedure(symbol)) {
1485     return false;
1486   }
1487   if (IsStmtFunction(symbol)) {
1488     // Section 15.7(1) states that a statement function is PURE if it does not
1489     // reference an IMPURE procedure or a VOLATILE variable
1490     if (const auto &expr{symbol.get<SubprogramDetails>().stmtFunction()}) {
1491       for (const SymbolRef &ref : evaluate::CollectSymbols(*expr)) {
1492         if (&*ref == &symbol) {
1493           return false; // error recovery, recursion is caught elsewhere
1494         }
1495         if (IsFunction(*ref) && !IsPureProcedureImpl(*ref, set)) {
1496           return false;
1497         }
1498         if (ref->GetUltimate().attrs().test(Attr::VOLATILE)) {
1499           return false;
1500         }
1501       }
1502     }
1503     return true; // statement function was not found to be impure
1504   }
1505   return symbol.attrs().test(Attr::PURE) ||
1506       (symbol.attrs().test(Attr::ELEMENTAL) &&
1507           !symbol.attrs().test(Attr::IMPURE));
1508 }
1509 
1510 bool IsPureProcedure(const Symbol &original) {
1511   semantics::UnorderedSymbolSet set;
1512   return IsPureProcedureImpl(original, set);
1513 }
1514 
1515 bool IsPureProcedure(const Scope &scope) {
1516   const Symbol *symbol{scope.GetSymbol()};
1517   return symbol && IsPureProcedure(*symbol);
1518 }
1519 
1520 bool IsExplicitlyImpureProcedure(const Symbol &original) {
1521   // An ENTRY is IMPURE if its containing subprogram is so
1522   return DEREF(GetMainEntry(&original.GetUltimate()))
1523       .attrs()
1524       .test(Attr::IMPURE);
1525 }
1526 
1527 bool IsElementalProcedure(const Symbol &original) {
1528   // An ENTRY is elemental if its containing subprogram is
1529   const Symbol &symbol{DEREF(GetMainEntry(&original.GetUltimate()))};
1530   if (IsProcedure(symbol)) {
1531     auto &foldingContext{symbol.owner().context().foldingContext()};
1532     auto restorer{foldingContext.messages().DiscardMessages()};
1533     auto proc{evaluate::characteristics::Procedure::Characterize(
1534         symbol, foldingContext)};
1535     return proc &&
1536         proc->attrs.test(evaluate::characteristics::Procedure::Attr::Elemental);
1537   } else {
1538     return false;
1539   }
1540 }
1541 
1542 bool IsFunction(const Symbol &symbol) {
1543   const Symbol &ultimate{symbol.GetUltimate()};
1544   return ultimate.test(Symbol::Flag::Function) ||
1545       (!ultimate.test(Symbol::Flag::Subroutine) &&
1546           common::visit(
1547               common::visitors{
1548                   [](const SubprogramDetails &x) { return x.isFunction(); },
1549                   [](const ProcEntityDetails &x) {
1550                     const Symbol *ifc{x.procInterface()};
1551                     return x.type() || (ifc && IsFunction(*ifc));
1552                   },
1553                   [](const ProcBindingDetails &x) {
1554                     return IsFunction(x.symbol());
1555                   },
1556                   [](const auto &) { return false; },
1557               },
1558               ultimate.details()));
1559 }
1560 
1561 bool IsFunction(const Scope &scope) {
1562   const Symbol *symbol{scope.GetSymbol()};
1563   return symbol && IsFunction(*symbol);
1564 }
1565 
1566 bool IsProcedure(const Symbol &symbol) {
1567   return common::visit(common::visitors{
1568                            [&symbol](const SubprogramDetails &) {
1569                              const Scope *scope{symbol.scope()};
1570                              // Main programs & BLOCK DATA are not procedures.
1571                              return !scope ||
1572                                  scope->kind() == Scope::Kind::Subprogram;
1573                            },
1574                            [](const SubprogramNameDetails &) { return true; },
1575                            [](const ProcEntityDetails &) { return true; },
1576                            [](const GenericDetails &) { return true; },
1577                            [](const ProcBindingDetails &) { return true; },
1578                            [](const auto &) { return false; },
1579                        },
1580       symbol.GetUltimate().details());
1581 }
1582 
1583 bool IsProcedure(const Scope &scope) {
1584   const Symbol *symbol{scope.GetSymbol()};
1585   return symbol && IsProcedure(*symbol);
1586 }
1587 
1588 bool IsProcedurePointer(const Symbol &original) {
1589   const Symbol &symbol{GetAssociationRoot(original)};
1590   return IsPointer(symbol) && IsProcedure(symbol);
1591 }
1592 
1593 bool IsProcedurePointer(const Symbol *symbol) {
1594   return symbol && IsProcedurePointer(*symbol);
1595 }
1596 
1597 bool IsObjectPointer(const Symbol *original) {
1598   if (original) {
1599     const Symbol &symbol{GetAssociationRoot(*original)};
1600     return IsPointer(symbol) && !IsProcedure(symbol);
1601   } else {
1602     return false;
1603   }
1604 }
1605 
1606 bool IsAllocatableOrObjectPointer(const Symbol *original) {
1607   if (original) {
1608     const Symbol &ultimate{original->GetUltimate()};
1609     if (const auto *assoc{ultimate.detailsIf<AssocEntityDetails>()}) {
1610       // Only SELECT RANK construct entities can be ALLOCATABLE/POINTER.
1611       return (assoc->rank() || assoc->IsAssumedSize() ||
1612                  assoc->IsAssumedRank()) &&
1613           IsAllocatableOrObjectPointer(UnwrapWholeSymbolDataRef(assoc->expr()));
1614     } else {
1615       return IsAllocatable(ultimate) ||
1616           (IsPointer(ultimate) && !IsProcedure(ultimate));
1617     }
1618   } else {
1619     return false;
1620   }
1621 }
1622 
1623 const Symbol *FindCommonBlockContaining(const Symbol &original) {
1624   const Symbol &root{GetAssociationRoot(original)};
1625   const auto *details{root.detailsIf<ObjectEntityDetails>()};
1626   return details ? details->commonBlock() : nullptr;
1627 }
1628 
1629 // 3.11 automatic data object
1630 bool IsAutomatic(const Symbol &original) {
1631   const Symbol &symbol{original.GetUltimate()};
1632   if (const auto *object{symbol.detailsIf<ObjectEntityDetails>()}) {
1633     if (!object->isDummy() && !IsAllocatable(symbol) && !IsPointer(symbol)) {
1634       if (const DeclTypeSpec * type{symbol.GetType()}) {
1635         // If a type parameter value is not a constant expression, the
1636         // object is automatic.
1637         if (type->category() == DeclTypeSpec::Character) {
1638           if (const auto &length{
1639                   type->characterTypeSpec().length().GetExplicit()}) {
1640             if (!evaluate::IsConstantExpr(*length)) {
1641               return true;
1642             }
1643           }
1644         } else if (const DerivedTypeSpec * derived{type->AsDerived()}) {
1645           for (const auto &pair : derived->parameters()) {
1646             if (const auto &value{pair.second.GetExplicit()}) {
1647               if (!evaluate::IsConstantExpr(*value)) {
1648                 return true;
1649               }
1650             }
1651           }
1652         }
1653       }
1654       // If an array bound is not a constant expression, the object is
1655       // automatic.
1656       for (const ShapeSpec &dim : object->shape()) {
1657         if (const auto &lb{dim.lbound().GetExplicit()}) {
1658           if (!evaluate::IsConstantExpr(*lb)) {
1659             return true;
1660           }
1661         }
1662         if (const auto &ub{dim.ubound().GetExplicit()}) {
1663           if (!evaluate::IsConstantExpr(*ub)) {
1664             return true;
1665           }
1666         }
1667       }
1668     }
1669   }
1670   return false;
1671 }
1672 
1673 bool IsSaved(const Symbol &original) {
1674   const Symbol &symbol{GetAssociationRoot(original)};
1675   const Scope &scope{symbol.owner()};
1676   const common::LanguageFeatureControl &features{
1677       scope.context().languageFeatures()};
1678   auto scopeKind{scope.kind()};
1679   if (symbol.has<AssocEntityDetails>()) {
1680     return false; // ASSOCIATE(non-variable)
1681   } else if (scopeKind == Scope::Kind::DerivedType) {
1682     return false; // this is a component
1683   } else if (symbol.attrs().test(Attr::SAVE)) {
1684     return true; // explicit SAVE attribute
1685   } else if (IsDummy(symbol) || IsFunctionResult(symbol) ||
1686       IsAutomatic(symbol) || IsNamedConstant(symbol)) {
1687     return false;
1688   } else if (scopeKind == Scope::Kind::Module ||
1689       (scopeKind == Scope::Kind::MainProgram &&
1690           (symbol.attrs().test(Attr::TARGET) || evaluate::IsCoarray(symbol)))) {
1691     // 8.5.16p4
1692     // In main programs, implied SAVE matters only for pointer
1693     // initialization targets and coarrays.
1694     return true;
1695   } else if (scopeKind == Scope::Kind::MainProgram &&
1696       (features.IsEnabled(common::LanguageFeature::SaveMainProgram) ||
1697           (features.IsEnabled(
1698                common::LanguageFeature::SaveBigMainProgramVariables) &&
1699               symbol.size() > 32)) &&
1700       Fortran::evaluate::CanCUDASymbolHasSave(symbol)) {
1701     // With SaveBigMainProgramVariables, keeping all unsaved main program
1702     // variables of 32 bytes or less on the stack allows keeping numerical and
1703     // logical scalars, small scalar characters or derived, small arrays, and
1704     // scalar descriptors on the stack. This leaves more room for lower level
1705     // optimizers to do register promotion or get easy aliasing information.
1706     return true;
1707   } else if (features.IsEnabled(common::LanguageFeature::DefaultSave) &&
1708       (scopeKind == Scope::Kind::MainProgram ||
1709           (scope.kind() == Scope::Kind::Subprogram &&
1710               !(scope.symbol() &&
1711                   scope.symbol()->attrs().test(Attr::RECURSIVE))))) {
1712     // -fno-automatic/-save/-Msave option applies to all objects in executable
1713     // main programs and subprograms unless they are explicitly RECURSIVE.
1714     return true;
1715   } else if (symbol.test(Symbol::Flag::InDataStmt)) {
1716     return true;
1717   } else if (const auto *object{symbol.detailsIf<ObjectEntityDetails>()};
1718              object && object->init()) {
1719     return true;
1720   } else if (IsProcedurePointer(symbol) && symbol.has<ProcEntityDetails>() &&
1721       symbol.get<ProcEntityDetails>().init()) {
1722     return true;
1723   } else if (scope.hasSAVE()) {
1724     return true; // bare SAVE statement
1725   } else if (const Symbol * block{FindCommonBlockContaining(symbol)};
1726              block && block->attrs().test(Attr::SAVE)) {
1727     return true; // in COMMON with SAVE
1728   } else {
1729     return false;
1730   }
1731 }
1732 
1733 bool IsDummy(const Symbol &symbol) {
1734   return common::visit(
1735       common::visitors{[](const EntityDetails &x) { return x.isDummy(); },
1736           [](const ObjectEntityDetails &x) { return x.isDummy(); },
1737           [](const ProcEntityDetails &x) { return x.isDummy(); },
1738           [](const SubprogramDetails &x) { return x.isDummy(); },
1739           [](const auto &) { return false; }},
1740       ResolveAssociations(symbol).details());
1741 }
1742 
1743 bool IsAssumedShape(const Symbol &symbol) {
1744   const Symbol &ultimate{ResolveAssociations(symbol)};
1745   const auto *object{ultimate.detailsIf<ObjectEntityDetails>()};
1746   return object && object->IsAssumedShape() &&
1747       !semantics::IsAllocatableOrObjectPointer(&ultimate);
1748 }
1749 
1750 bool IsDeferredShape(const Symbol &symbol) {
1751   const Symbol &ultimate{ResolveAssociations(symbol)};
1752   const auto *object{ultimate.detailsIf<ObjectEntityDetails>()};
1753   return object && object->CanBeDeferredShape() &&
1754       semantics::IsAllocatableOrObjectPointer(&ultimate);
1755 }
1756 
1757 bool IsFunctionResult(const Symbol &original) {
1758   const Symbol &symbol{GetAssociationRoot(original)};
1759   return common::visit(
1760       common::visitors{
1761           [](const EntityDetails &x) { return x.isFuncResult(); },
1762           [](const ObjectEntityDetails &x) { return x.isFuncResult(); },
1763           [](const ProcEntityDetails &x) { return x.isFuncResult(); },
1764           [](const auto &) { return false; },
1765       },
1766       symbol.details());
1767 }
1768 
1769 bool IsKindTypeParameter(const Symbol &symbol) {
1770   const auto *param{symbol.GetUltimate().detailsIf<TypeParamDetails>()};
1771   return param && param->attr() == common::TypeParamAttr::Kind;
1772 }
1773 
1774 bool IsLenTypeParameter(const Symbol &symbol) {
1775   const auto *param{symbol.GetUltimate().detailsIf<TypeParamDetails>()};
1776   return param && param->attr() == common::TypeParamAttr::Len;
1777 }
1778 
1779 bool IsExtensibleType(const DerivedTypeSpec *derived) {
1780   return !IsSequenceOrBindCType(derived) && !IsIsoCType(derived);
1781 }
1782 
1783 bool IsSequenceOrBindCType(const DerivedTypeSpec *derived) {
1784   return derived &&
1785       (derived->typeSymbol().attrs().test(Attr::BIND_C) ||
1786           derived->typeSymbol().get<DerivedTypeDetails>().sequence());
1787 }
1788 
1789 static bool IsSameModule(const Scope *x, const Scope *y) {
1790   if (x == y) {
1791     return true;
1792   } else if (x && y) {
1793     // Allow for a builtin module to be read from distinct paths
1794     const Symbol *xSym{x->symbol()};
1795     const Symbol *ySym{y->symbol()};
1796     if (xSym && ySym && xSym->name() == ySym->name()) {
1797       const auto *xMod{xSym->detailsIf<ModuleDetails>()};
1798       const auto *yMod{ySym->detailsIf<ModuleDetails>()};
1799       if (xMod && yMod) {
1800         auto xHash{xMod->moduleFileHash()};
1801         auto yHash{yMod->moduleFileHash()};
1802         return xHash && yHash && *xHash == *yHash;
1803       }
1804     }
1805   }
1806   return false;
1807 }
1808 
1809 bool IsBuiltinDerivedType(const DerivedTypeSpec *derived, const char *name) {
1810   if (derived) {
1811     const auto &symbol{derived->typeSymbol()};
1812     const Scope &scope{symbol.owner()};
1813     return symbol.name() == "__builtin_"s + name &&
1814         IsSameModule(&scope, scope.context().GetBuiltinsScope());
1815   } else {
1816     return false;
1817   }
1818 }
1819 
1820 bool IsBuiltinCPtr(const Symbol &symbol) {
1821   if (const DeclTypeSpec *declType = symbol.GetType()) {
1822     if (const DerivedTypeSpec *derived = declType->AsDerived()) {
1823       return IsIsoCType(derived);
1824     }
1825   }
1826   return false;
1827 }
1828 
1829 bool IsIsoCType(const DerivedTypeSpec *derived) {
1830   return IsBuiltinDerivedType(derived, "c_ptr") ||
1831       IsBuiltinDerivedType(derived, "c_funptr");
1832 }
1833 
1834 bool IsEventType(const DerivedTypeSpec *derived) {
1835   return IsBuiltinDerivedType(derived, "event_type");
1836 }
1837 
1838 bool IsLockType(const DerivedTypeSpec *derived) {
1839   return IsBuiltinDerivedType(derived, "lock_type");
1840 }
1841 
1842 bool IsNotifyType(const DerivedTypeSpec *derived) {
1843   return IsBuiltinDerivedType(derived, "notify_type");
1844 }
1845 
1846 bool IsIeeeFlagType(const DerivedTypeSpec *derived) {
1847   return IsBuiltinDerivedType(derived, "ieee_flag_type");
1848 }
1849 
1850 bool IsIeeeRoundType(const DerivedTypeSpec *derived) {
1851   return IsBuiltinDerivedType(derived, "ieee_round_type");
1852 }
1853 
1854 bool IsTeamType(const DerivedTypeSpec *derived) {
1855   return IsBuiltinDerivedType(derived, "team_type");
1856 }
1857 
1858 bool IsBadCoarrayType(const DerivedTypeSpec *derived) {
1859   return IsTeamType(derived) || IsIsoCType(derived);
1860 }
1861 
1862 bool IsEventTypeOrLockType(const DerivedTypeSpec *derivedTypeSpec) {
1863   return IsEventType(derivedTypeSpec) || IsLockType(derivedTypeSpec);
1864 }
1865 
1866 int CountLenParameters(const DerivedTypeSpec &type) {
1867   return llvm::count_if(
1868       type.parameters(), [](const auto &pair) { return pair.second.isLen(); });
1869 }
1870 
1871 int CountNonConstantLenParameters(const DerivedTypeSpec &type) {
1872   return llvm::count_if(type.parameters(), [](const auto &pair) {
1873     if (!pair.second.isLen()) {
1874       return false;
1875     } else if (const auto &expr{pair.second.GetExplicit()}) {
1876       return !IsConstantExpr(*expr);
1877     } else {
1878       return true;
1879     }
1880   });
1881 }
1882 
1883 const Symbol &GetUsedModule(const UseDetails &details) {
1884   return DEREF(details.symbol().owner().symbol());
1885 }
1886 
1887 static const Symbol *FindFunctionResult(
1888     const Symbol &original, UnorderedSymbolSet &seen) {
1889   const Symbol &root{GetAssociationRoot(original)};
1890   ;
1891   if (!seen.insert(root).second) {
1892     return nullptr; // don't loop
1893   }
1894   return common::visit(
1895       common::visitors{[](const SubprogramDetails &subp) {
1896                          return subp.isFunction() ? &subp.result() : nullptr;
1897                        },
1898           [&](const ProcEntityDetails &proc) {
1899             const Symbol *iface{proc.procInterface()};
1900             return iface ? FindFunctionResult(*iface, seen) : nullptr;
1901           },
1902           [&](const ProcBindingDetails &binding) {
1903             return FindFunctionResult(binding.symbol(), seen);
1904           },
1905           [](const auto &) -> const Symbol * { return nullptr; }},
1906       root.details());
1907 }
1908 
1909 const Symbol *FindFunctionResult(const Symbol &symbol) {
1910   UnorderedSymbolSet seen;
1911   return FindFunctionResult(symbol, seen);
1912 }
1913 
1914 // These are here in Evaluate/tools.cpp so that Evaluate can use
1915 // them; they cannot be defined in symbol.h due to the dependence
1916 // on Scope.
1917 
1918 bool SymbolSourcePositionCompare::operator()(
1919     const SymbolRef &x, const SymbolRef &y) const {
1920   return x->GetSemanticsContext().allCookedSources().Precedes(
1921       x->name(), y->name());
1922 }
1923 bool SymbolSourcePositionCompare::operator()(
1924     const MutableSymbolRef &x, const MutableSymbolRef &y) const {
1925   return x->GetSemanticsContext().allCookedSources().Precedes(
1926       x->name(), y->name());
1927 }
1928 
1929 SemanticsContext &Symbol::GetSemanticsContext() const {
1930   return DEREF(owner_).context();
1931 }
1932 
1933 bool AreTkCompatibleTypes(const DeclTypeSpec *x, const DeclTypeSpec *y) {
1934   if (x && y) {
1935     if (auto xDt{evaluate::DynamicType::From(*x)}) {
1936       if (auto yDt{evaluate::DynamicType::From(*y)}) {
1937         return xDt->IsTkCompatibleWith(*yDt);
1938       }
1939     }
1940   }
1941   return false;
1942 }
1943 
1944 common::IgnoreTKRSet GetIgnoreTKR(const Symbol &symbol) {
1945   common::IgnoreTKRSet result;
1946   if (const auto *object{symbol.detailsIf<ObjectEntityDetails>()}) {
1947     result = object->ignoreTKR();
1948     if (const Symbol * ownerSymbol{symbol.owner().symbol()}) {
1949       if (const auto *ownerSubp{ownerSymbol->detailsIf<SubprogramDetails>()}) {
1950         if (ownerSubp->defaultIgnoreTKR()) {
1951           result |= common::ignoreTKRAll;
1952         }
1953       }
1954     }
1955   }
1956   return result;
1957 }
1958 
1959 std::optional<int> GetDummyArgumentNumber(const Symbol *symbol) {
1960   if (symbol) {
1961     if (IsDummy(*symbol)) {
1962       if (const Symbol * subpSym{symbol->owner().symbol()}) {
1963         if (const auto *subp{subpSym->detailsIf<SubprogramDetails>()}) {
1964           int j{0};
1965           for (const Symbol *dummy : subp->dummyArgs()) {
1966             if (dummy == symbol) {
1967               return j;
1968             }
1969             ++j;
1970           }
1971         }
1972       }
1973     }
1974   }
1975   return std::nullopt;
1976 }
1977 
1978 } // namespace Fortran::semantics
1979