xref: /llvm-project/flang/include/flang/Evaluate/tools.h (revision 9696355484152eda5684e0ec6249f4c423f08e42)
1 //===-- include/flang/Evaluate/tools.h --------------------------*- C++ -*-===//
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 #ifndef FORTRAN_EVALUATE_TOOLS_H_
10 #define FORTRAN_EVALUATE_TOOLS_H_
11 
12 #include "traverse.h"
13 #include "flang/Common/idioms.h"
14 #include "flang/Common/template.h"
15 #include "flang/Common/unwrap.h"
16 #include "flang/Evaluate/constant.h"
17 #include "flang/Evaluate/expression.h"
18 #include "flang/Evaluate/shape.h"
19 #include "flang/Evaluate/type.h"
20 #include "flang/Parser/message.h"
21 #include "flang/Semantics/attr.h"
22 #include "flang/Semantics/scope.h"
23 #include "flang/Semantics/symbol.h"
24 #include <array>
25 #include <optional>
26 #include <set>
27 #include <type_traits>
28 #include <utility>
29 
30 namespace Fortran::evaluate {
31 
32 // Some expression predicates and extractors.
33 
34 // Predicate: true when an expression is a variable reference, not an
35 // operation.  Be advised: a call to a function that returns an object
36 // pointer is a "variable" in Fortran (it can be the left-hand side of
37 // an assignment).
38 struct IsVariableHelper
39     : public AnyTraverse<IsVariableHelper, std::optional<bool>> {
40   using Result = std::optional<bool>; // effectively tri-state
41   using Base = AnyTraverse<IsVariableHelper, Result>;
42   IsVariableHelper() : Base{*this} {}
43   using Base::operator();
44   Result operator()(const StaticDataObject &) const { return false; }
45   Result operator()(const Symbol &) const;
46   Result operator()(const Component &) const;
47   Result operator()(const ArrayRef &) const;
48   Result operator()(const Substring &) const;
49   Result operator()(const CoarrayRef &) const { return true; }
50   Result operator()(const ComplexPart &) const { return true; }
51   Result operator()(const ProcedureDesignator &) const;
52   template <typename T> Result operator()(const Expr<T> &x) const {
53     if constexpr (common::HasMember<T, AllIntrinsicTypes> ||
54         std::is_same_v<T, SomeDerived>) {
55       // Expression with a specific type
56       if (std::holds_alternative<Designator<T>>(x.u) ||
57           std::holds_alternative<FunctionRef<T>>(x.u)) {
58         if (auto known{(*this)(x.u)}) {
59           return known;
60         }
61       }
62       return false;
63     } else if constexpr (std::is_same_v<T, SomeType>) {
64       if (std::holds_alternative<ProcedureDesignator>(x.u) ||
65           std::holds_alternative<ProcedureRef>(x.u)) {
66         return false; // procedure pointer
67       } else {
68         return (*this)(x.u);
69       }
70     } else {
71       return (*this)(x.u);
72     }
73   }
74 };
75 
76 template <typename A> bool IsVariable(const A &x) {
77   if (auto known{IsVariableHelper{}(x)}) {
78     return *known;
79   } else {
80     return false;
81   }
82 }
83 
84 // Predicate: true when an expression is assumed-rank
85 bool IsAssumedRank(const Symbol &);
86 bool IsAssumedRank(const ActualArgument &);
87 template <typename A> bool IsAssumedRank(const A &) { return false; }
88 template <typename A> bool IsAssumedRank(const Designator<A> &designator) {
89   if (const auto *symbol{std::get_if<SymbolRef>(&designator.u)}) {
90     return IsAssumedRank(symbol->get());
91   } else {
92     return false;
93   }
94 }
95 template <typename T> bool IsAssumedRank(const Expr<T> &expr) {
96   return common::visit([](const auto &x) { return IsAssumedRank(x); }, expr.u);
97 }
98 template <typename A> bool IsAssumedRank(const std::optional<A> &x) {
99   return x && IsAssumedRank(*x);
100 }
101 template <typename A> bool IsAssumedRank(const A *x) {
102   return x && IsAssumedRank(*x);
103 }
104 
105 // Finds the corank of an entity, possibly packaged in various ways.
106 // Unlike rank, only data references have corank > 0.
107 int GetCorank(const ActualArgument &);
108 static inline int GetCorank(const Symbol &symbol) { return symbol.Corank(); }
109 template <typename A> int GetCorank(const A &) { return 0; }
110 template <typename T> int GetCorank(const Designator<T> &designator) {
111   return designator.Corank();
112 }
113 template <typename T> int GetCorank(const Expr<T> &expr) {
114   return common::visit([](const auto &x) { return GetCorank(x); }, expr.u);
115 }
116 template <typename A> int GetCorank(const std::optional<A> &x) {
117   return x ? GetCorank(*x) : 0;
118 }
119 template <typename A> int GetCorank(const A *x) {
120   return x ? GetCorank(*x) : 0;
121 }
122 
123 // Predicate: true when an expression is a coarray (corank > 0)
124 template <typename A> bool IsCoarray(const A &x) { return GetCorank(x) > 0; }
125 
126 // Generalizing packagers: these take operations and expressions of more
127 // specific types and wrap them in Expr<> containers of more abstract types.
128 
129 template <typename A> common::IfNoLvalue<Expr<ResultType<A>>, A> AsExpr(A &&x) {
130   return Expr<ResultType<A>>{std::move(x)};
131 }
132 
133 template <typename T> Expr<T> AsExpr(Expr<T> &&x) {
134   static_assert(IsSpecificIntrinsicType<T>);
135   return std::move(x);
136 }
137 
138 template <TypeCategory CATEGORY>
139 Expr<SomeKind<CATEGORY>> AsCategoryExpr(Expr<SomeKind<CATEGORY>> &&x) {
140   return std::move(x);
141 }
142 
143 template <typename A>
144 common::IfNoLvalue<Expr<SomeType>, A> AsGenericExpr(A &&x) {
145   if constexpr (common::HasMember<A, TypelessExpression>) {
146     return Expr<SomeType>{std::move(x)};
147   } else {
148     return Expr<SomeType>{AsCategoryExpr(std::move(x))};
149   }
150 }
151 
152 inline Expr<SomeType> AsGenericExpr(Expr<SomeType> &&x) { return std::move(x); }
153 
154 // These overloads wrap DataRefs and simple whole variables up into
155 // generic expressions if they have a known type.
156 std::optional<Expr<SomeType>> AsGenericExpr(DataRef &&);
157 std::optional<Expr<SomeType>> AsGenericExpr(const Symbol &);
158 
159 // Propagate std::optional from input to output.
160 template <typename A>
161 std::optional<Expr<SomeType>> AsGenericExpr(std::optional<A> &&x) {
162   if (x) {
163     return AsGenericExpr(std::move(*x));
164   } else {
165     return std::nullopt;
166   }
167 }
168 
169 template <typename A>
170 common::IfNoLvalue<Expr<SomeKind<ResultType<A>::category>>, A> AsCategoryExpr(
171     A &&x) {
172   return Expr<SomeKind<ResultType<A>::category>>{AsExpr(std::move(x))};
173 }
174 
175 Expr<SomeType> Parenthesize(Expr<SomeType> &&);
176 
177 template <typename A> constexpr bool IsNumericCategoryExpr() {
178   if constexpr (common::HasMember<A, TypelessExpression>) {
179     return false;
180   } else {
181     return common::HasMember<ResultType<A>, NumericCategoryTypes>;
182   }
183 }
184 
185 // Specializing extractor.  If an Expr wraps some type of object, perhaps
186 // in several layers, return a pointer to it; otherwise null.  Also works
187 // with expressions contained in ActualArgument.
188 template <typename A, typename B>
189 auto UnwrapExpr(B &x) -> common::Constify<A, B> * {
190   using Ty = std::decay_t<B>;
191   if constexpr (std::is_same_v<A, Ty>) {
192     return &x;
193   } else if constexpr (std::is_same_v<Ty, ActualArgument>) {
194     if (auto *expr{x.UnwrapExpr()}) {
195       return UnwrapExpr<A>(*expr);
196     }
197   } else if constexpr (std::is_same_v<Ty, Expr<SomeType>>) {
198     return common::visit([](auto &x) { return UnwrapExpr<A>(x); }, x.u);
199   } else if constexpr (!common::HasMember<A, TypelessExpression>) {
200     if constexpr (std::is_same_v<Ty, Expr<ResultType<A>>> ||
201         std::is_same_v<Ty, Expr<SomeKind<ResultType<A>::category>>>) {
202       return common::visit([](auto &x) { return UnwrapExpr<A>(x); }, x.u);
203     }
204   }
205   return nullptr;
206 }
207 
208 template <typename A, typename B>
209 const A *UnwrapExpr(const std::optional<B> &x) {
210   if (x) {
211     return UnwrapExpr<A>(*x);
212   } else {
213     return nullptr;
214   }
215 }
216 
217 template <typename A, typename B> A *UnwrapExpr(std::optional<B> &x) {
218   if (x) {
219     return UnwrapExpr<A>(*x);
220   } else {
221     return nullptr;
222   }
223 }
224 
225 template <typename A, typename B> const A *UnwrapExpr(const B *x) {
226   if (x) {
227     return UnwrapExpr<A>(*x);
228   } else {
229     return nullptr;
230   }
231 }
232 
233 template <typename A, typename B> A *UnwrapExpr(B *x) {
234   if (x) {
235     return UnwrapExpr<A>(*x);
236   } else {
237     return nullptr;
238   }
239 }
240 
241 // A variant of UnwrapExpr above that also skips through (parentheses)
242 // and conversions of kinds within a category.  Useful for extracting LEN
243 // type parameter inquiries, at least.
244 template <typename A, typename B>
245 auto UnwrapConvertedExpr(B &x) -> common::Constify<A, B> * {
246   using Ty = std::decay_t<B>;
247   if constexpr (std::is_same_v<A, Ty>) {
248     return &x;
249   } else if constexpr (std::is_same_v<Ty, ActualArgument>) {
250     if (auto *expr{x.UnwrapExpr()}) {
251       return UnwrapConvertedExpr<A>(*expr);
252     }
253   } else if constexpr (std::is_same_v<Ty, Expr<SomeType>>) {
254     return common::visit(
255         [](auto &x) { return UnwrapConvertedExpr<A>(x); }, x.u);
256   } else {
257     using DesiredResult = ResultType<A>;
258     if constexpr (std::is_same_v<Ty, Expr<DesiredResult>> ||
259         std::is_same_v<Ty, Expr<SomeKind<DesiredResult::category>>>) {
260       return common::visit(
261           [](auto &x) { return UnwrapConvertedExpr<A>(x); }, x.u);
262     } else {
263       using ThisResult = ResultType<B>;
264       if constexpr (std::is_same_v<Ty, Expr<ThisResult>>) {
265         return common::visit(
266             [](auto &x) { return UnwrapConvertedExpr<A>(x); }, x.u);
267       } else if constexpr (std::is_same_v<Ty, Parentheses<ThisResult>> ||
268           std::is_same_v<Ty, Convert<ThisResult, DesiredResult::category>>) {
269         return common::visit(
270             [](auto &x) { return UnwrapConvertedExpr<A>(x); }, x.left().u);
271       }
272     }
273   }
274   return nullptr;
275 }
276 
277 // UnwrapProcedureRef() returns a pointer to a ProcedureRef when the whole
278 // expression is a reference to a procedure.
279 template <typename A> inline const ProcedureRef *UnwrapProcedureRef(const A &) {
280   return nullptr;
281 }
282 
283 inline const ProcedureRef *UnwrapProcedureRef(const ProcedureRef &proc) {
284   // Reference to subroutine or to a function that returns
285   // an object pointer or procedure pointer
286   return &proc;
287 }
288 
289 template <typename T>
290 inline const ProcedureRef *UnwrapProcedureRef(const FunctionRef<T> &func) {
291   return &func; // reference to a function returning a non-pointer
292 }
293 
294 template <typename T>
295 inline const ProcedureRef *UnwrapProcedureRef(const Expr<T> &expr) {
296   return common::visit(
297       [](const auto &x) { return UnwrapProcedureRef(x); }, expr.u);
298 }
299 
300 // When an expression is a "bare" LEN= derived type parameter inquiry,
301 // possibly wrapped in integer kind conversions &/or parentheses, return
302 // a pointer to the Symbol with TypeParamDetails.
303 template <typename A> const Symbol *ExtractBareLenParameter(const A &expr) {
304   if (const auto *typeParam{
305           UnwrapConvertedExpr<evaluate::TypeParamInquiry>(expr)}) {
306     if (!typeParam->base()) {
307       const Symbol &symbol{typeParam->parameter()};
308       if (const auto *tpd{symbol.detailsIf<semantics::TypeParamDetails>()}) {
309         if (tpd->attr() == common::TypeParamAttr::Len) {
310           return &symbol;
311         }
312       }
313     }
314   }
315   return nullptr;
316 }
317 
318 // If an expression simply wraps a DataRef, extract and return it.
319 // The Boolean arguments control the handling of Substring and ComplexPart
320 // references: when true (not default), it extracts the base DataRef
321 // of a substring or complex part.
322 template <typename A>
323 common::IfNoLvalue<std::optional<DataRef>, A> ExtractDataRef(
324     const A &, bool intoSubstring, bool intoComplexPart) {
325   return std::nullopt; // default base case
326 }
327 template <typename T>
328 std::optional<DataRef> ExtractDataRef(const Designator<T> &d,
329     bool intoSubstring = false, bool intoComplexPart = false) {
330   return common::visit(
331       [=](const auto &x) -> std::optional<DataRef> {
332         if constexpr (common::HasMember<decltype(x), decltype(DataRef::u)>) {
333           return DataRef{x};
334         }
335         if constexpr (std::is_same_v<std::decay_t<decltype(x)>, Substring>) {
336           if (intoSubstring) {
337             return ExtractSubstringBase(x);
338           }
339         }
340         if constexpr (std::is_same_v<std::decay_t<decltype(x)>, ComplexPart>) {
341           if (intoComplexPart) {
342             return x.complex();
343           }
344         }
345         return std::nullopt; // w/o "else" to dodge bogus g++ 8.1 warning
346       },
347       d.u);
348 }
349 template <typename T>
350 std::optional<DataRef> ExtractDataRef(const Expr<T> &expr,
351     bool intoSubstring = false, bool intoComplexPart = false) {
352   return common::visit(
353       [=](const auto &x) {
354         return ExtractDataRef(x, intoSubstring, intoComplexPart);
355       },
356       expr.u);
357 }
358 template <typename A>
359 std::optional<DataRef> ExtractDataRef(const std::optional<A> &x,
360     bool intoSubstring = false, bool intoComplexPart = false) {
361   if (x) {
362     return ExtractDataRef(*x, intoSubstring, intoComplexPart);
363   } else {
364     return std::nullopt;
365   }
366 }
367 template <typename A>
368 std::optional<DataRef> ExtractDataRef(
369     A *p, bool intoSubstring = false, bool intoComplexPart = false) {
370   if (p) {
371     return ExtractDataRef(std::as_const(*p), intoSubstring, intoComplexPart);
372   } else {
373     return std::nullopt;
374   }
375 }
376 std::optional<DataRef> ExtractDataRef(const ActualArgument &,
377     bool intoSubstring = false, bool intoComplexPart = false);
378 
379 std::optional<DataRef> ExtractSubstringBase(const Substring &);
380 
381 // Predicate: is an expression is an array element reference?
382 template <typename T>
383 bool IsArrayElement(const Expr<T> &expr, bool intoSubstring = true,
384     bool skipComponents = false) {
385   if (auto dataRef{ExtractDataRef(expr, intoSubstring)}) {
386     const DataRef *ref{&*dataRef};
387     if (skipComponents) {
388       while (const Component * component{std::get_if<Component>(&ref->u)}) {
389         ref = &component->base();
390       }
391     }
392     if (const auto *coarrayRef{std::get_if<CoarrayRef>(&ref->u)}) {
393       return !coarrayRef->subscript().empty();
394     } else {
395       return std::holds_alternative<ArrayRef>(ref->u);
396     }
397   } else {
398     return false;
399   }
400 }
401 
402 template <typename A>
403 std::optional<NamedEntity> ExtractNamedEntity(const A &x) {
404   if (auto dataRef{ExtractDataRef(x)}) {
405     return common::visit(
406         common::visitors{
407             [](SymbolRef &&symbol) -> std::optional<NamedEntity> {
408               return NamedEntity{symbol};
409             },
410             [](Component &&component) -> std::optional<NamedEntity> {
411               return NamedEntity{std::move(component)};
412             },
413             [](CoarrayRef &&co) -> std::optional<NamedEntity> {
414               return co.GetBase();
415             },
416             [](auto &&) { return std::optional<NamedEntity>{}; },
417         },
418         std::move(dataRef->u));
419   } else {
420     return std::nullopt;
421   }
422 }
423 
424 struct ExtractCoindexedObjectHelper {
425   template <typename A> std::optional<CoarrayRef> operator()(const A &) const {
426     return std::nullopt;
427   }
428   std::optional<CoarrayRef> operator()(const CoarrayRef &x) const { return x; }
429   template <typename A>
430   std::optional<CoarrayRef> operator()(const Expr<A> &expr) const {
431     return common::visit(*this, expr.u);
432   }
433   std::optional<CoarrayRef> operator()(const DataRef &dataRef) const {
434     return common::visit(*this, dataRef.u);
435   }
436   std::optional<CoarrayRef> operator()(const NamedEntity &named) const {
437     if (const Component * component{named.UnwrapComponent()}) {
438       return (*this)(*component);
439     } else {
440       return std::nullopt;
441     }
442   }
443   std::optional<CoarrayRef> operator()(const ProcedureDesignator &des) const {
444     if (const auto *component{
445             std::get_if<common::CopyableIndirection<Component>>(&des.u)}) {
446       return (*this)(component->value());
447     } else {
448       return std::nullopt;
449     }
450   }
451   std::optional<CoarrayRef> operator()(const Component &component) const {
452     return (*this)(component.base());
453   }
454   std::optional<CoarrayRef> operator()(const ArrayRef &arrayRef) const {
455     return (*this)(arrayRef.base());
456   }
457 };
458 
459 template <typename A> std::optional<CoarrayRef> ExtractCoarrayRef(const A &x) {
460   if (auto dataRef{ExtractDataRef(x, true)}) {
461     return ExtractCoindexedObjectHelper{}(*dataRef);
462   } else {
463     return ExtractCoindexedObjectHelper{}(x);
464   }
465 }
466 
467 struct ExtractSubstringHelper {
468   template <typename T> static std::optional<Substring> visit(T &&) {
469     return std::nullopt;
470   }
471 
472   static std::optional<Substring> visit(const Substring &e) { return e; }
473 
474   template <typename T>
475   static std::optional<Substring> visit(const Designator<T> &e) {
476     return common::visit([](auto &&s) { return visit(s); }, e.u);
477   }
478 
479   template <typename T>
480   static std::optional<Substring> visit(const Expr<T> &e) {
481     return common::visit([](auto &&s) { return visit(s); }, e.u);
482   }
483 };
484 
485 template <typename A> std::optional<Substring> ExtractSubstring(const A &x) {
486   return ExtractSubstringHelper::visit(x);
487 }
488 
489 // If an expression is simply a whole symbol data designator,
490 // extract and return that symbol, else null.
491 template <typename A> const Symbol *UnwrapWholeSymbolDataRef(const A &x) {
492   if (auto dataRef{ExtractDataRef(x)}) {
493     if (const SymbolRef * p{std::get_if<SymbolRef>(&dataRef->u)}) {
494       return &p->get();
495     }
496   }
497   return nullptr;
498 }
499 
500 // If an expression is a whole symbol or a whole component desginator,
501 // extract and return that symbol, else null.
502 template <typename A>
503 const Symbol *UnwrapWholeSymbolOrComponentDataRef(const A &x) {
504   if (auto dataRef{ExtractDataRef(x)}) {
505     if (const SymbolRef * p{std::get_if<SymbolRef>(&dataRef->u)}) {
506       return &p->get();
507     } else if (const Component * c{std::get_if<Component>(&dataRef->u)}) {
508       if (c->base().Rank() == 0) {
509         return &c->GetLastSymbol();
510       }
511     }
512   }
513   return nullptr;
514 }
515 
516 // If an expression is a whole symbol or a whole component designator,
517 // potentially followed by an image selector, extract and return that symbol,
518 // else null.
519 template <typename A>
520 const Symbol *UnwrapWholeSymbolOrComponentOrCoarrayRef(const A &x) {
521   if (auto dataRef{ExtractDataRef(x)}) {
522     if (const SymbolRef * p{std::get_if<SymbolRef>(&dataRef->u)}) {
523       return &p->get();
524     } else if (const Component * c{std::get_if<Component>(&dataRef->u)}) {
525       if (c->base().Rank() == 0) {
526         return &c->GetLastSymbol();
527       }
528     } else if (const CoarrayRef * c{std::get_if<CoarrayRef>(&dataRef->u)}) {
529       if (c->subscript().empty()) {
530         return &c->GetLastSymbol();
531       }
532     }
533   }
534   return nullptr;
535 }
536 
537 // GetFirstSymbol(A%B%C[I]%D) -> A
538 template <typename A> const Symbol *GetFirstSymbol(const A &x) {
539   if (auto dataRef{ExtractDataRef(x, true)}) {
540     return &dataRef->GetFirstSymbol();
541   } else {
542     return nullptr;
543   }
544 }
545 
546 // GetLastPointerSymbol(A%PTR1%B%PTR2%C) -> PTR2
547 const Symbol *GetLastPointerSymbol(const evaluate::DataRef &);
548 
549 // Creation of conversion expressions can be done to either a known
550 // specific intrinsic type with ConvertToType<T>(x) or by converting
551 // one arbitrary expression to the type of another with ConvertTo(to, from).
552 
553 template <typename TO, TypeCategory FROMCAT>
554 Expr<TO> ConvertToType(Expr<SomeKind<FROMCAT>> &&x) {
555   static_assert(IsSpecificIntrinsicType<TO>);
556   if constexpr (FROMCAT == TO::category) {
557     if (auto *already{std::get_if<Expr<TO>>(&x.u)}) {
558       return std::move(*already);
559     } else {
560       return Expr<TO>{Convert<TO, FROMCAT>{std::move(x)}};
561     }
562   } else if constexpr (TO::category == TypeCategory::Complex) {
563     using Part = typename TO::Part;
564     Scalar<Part> zero;
565     return Expr<TO>{ComplexConstructor<TO::kind>{
566         ConvertToType<Part>(std::move(x)), Expr<Part>{Constant<Part>{zero}}}};
567   } else if constexpr (FROMCAT == TypeCategory::Complex) {
568     // Extract and convert the real component of a complex value
569     return common::visit(
570         [&](auto &&z) {
571           using ZType = ResultType<decltype(z)>;
572           using Part = typename ZType::Part;
573           return ConvertToType<TO, TypeCategory::Real>(Expr<SomeReal>{
574               Expr<Part>{ComplexComponent<Part::kind>{false, std::move(z)}}});
575         },
576         std::move(x.u));
577   } else {
578     return Expr<TO>{Convert<TO, FROMCAT>{std::move(x)}};
579   }
580 }
581 
582 template <typename TO, TypeCategory FROMCAT, int FROMKIND>
583 Expr<TO> ConvertToType(Expr<Type<FROMCAT, FROMKIND>> &&x) {
584   return ConvertToType<TO, FROMCAT>(Expr<SomeKind<FROMCAT>>{std::move(x)});
585 }
586 
587 template <typename TO> Expr<TO> ConvertToType(BOZLiteralConstant &&x) {
588   static_assert(IsSpecificIntrinsicType<TO>);
589   if constexpr (TO::category == TypeCategory::Integer ||
590       TO::category == TypeCategory::Unsigned) {
591     return Expr<TO>{
592         Constant<TO>{Scalar<TO>::ConvertUnsigned(std::move(x)).value}};
593   } else {
594     static_assert(TO::category == TypeCategory::Real);
595     using Word = typename Scalar<TO>::Word;
596     return Expr<TO>{
597         Constant<TO>{Scalar<TO>{Word::ConvertUnsigned(std::move(x)).value}}};
598   }
599 }
600 
601 template <typename T> bool IsBOZLiteral(const Expr<T> &expr) {
602   return std::holds_alternative<BOZLiteralConstant>(expr.u);
603 }
604 
605 // Conversions to dynamic types
606 std::optional<Expr<SomeType>> ConvertToType(
607     const DynamicType &, Expr<SomeType> &&);
608 std::optional<Expr<SomeType>> ConvertToType(
609     const DynamicType &, std::optional<Expr<SomeType>> &&);
610 std::optional<Expr<SomeType>> ConvertToType(const Symbol &, Expr<SomeType> &&);
611 std::optional<Expr<SomeType>> ConvertToType(
612     const Symbol &, std::optional<Expr<SomeType>> &&);
613 
614 // Conversions to the type of another expression
615 template <TypeCategory TC, int TK, typename FROM>
616 common::IfNoLvalue<Expr<Type<TC, TK>>, FROM> ConvertTo(
617     const Expr<Type<TC, TK>> &, FROM &&x) {
618   return ConvertToType<Type<TC, TK>>(std::move(x));
619 }
620 
621 template <TypeCategory TC, typename FROM>
622 common::IfNoLvalue<Expr<SomeKind<TC>>, FROM> ConvertTo(
623     const Expr<SomeKind<TC>> &to, FROM &&from) {
624   return common::visit(
625       [&](const auto &toKindExpr) {
626         using KindExpr = std::decay_t<decltype(toKindExpr)>;
627         return AsCategoryExpr(
628             ConvertToType<ResultType<KindExpr>>(std::move(from)));
629       },
630       to.u);
631 }
632 
633 template <typename FROM>
634 common::IfNoLvalue<Expr<SomeType>, FROM> ConvertTo(
635     const Expr<SomeType> &to, FROM &&from) {
636   return common::visit(
637       [&](const auto &toCatExpr) {
638         return AsGenericExpr(ConvertTo(toCatExpr, std::move(from)));
639       },
640       to.u);
641 }
642 
643 // Convert an expression of some known category to a dynamically chosen
644 // kind of some category (usually but not necessarily distinct).
645 template <TypeCategory TOCAT, typename VALUE> struct ConvertToKindHelper {
646   using Result = std::optional<Expr<SomeKind<TOCAT>>>;
647   using Types = CategoryTypes<TOCAT>;
648   ConvertToKindHelper(int k, VALUE &&x) : kind{k}, value{std::move(x)} {}
649   template <typename T> Result Test() {
650     if (kind == T::kind) {
651       return std::make_optional(
652           AsCategoryExpr(ConvertToType<T>(std::move(value))));
653     }
654     return std::nullopt;
655   }
656   int kind;
657   VALUE value;
658 };
659 
660 template <TypeCategory TOCAT, typename VALUE>
661 common::IfNoLvalue<Expr<SomeKind<TOCAT>>, VALUE> ConvertToKind(
662     int kind, VALUE &&x) {
663   auto result{common::SearchTypes(
664       ConvertToKindHelper<TOCAT, VALUE>{kind, std::move(x)})};
665   CHECK(result.has_value());
666   return *result;
667 }
668 
669 // Given a type category CAT, SameKindExprs<CAT, N> is a variant that
670 // holds an arrays of expressions of the same supported kind in that
671 // category.
672 template <typename A, int N = 2> using SameExprs = std::array<Expr<A>, N>;
673 template <int N = 2> struct SameKindExprsHelper {
674   template <typename A> using SameExprs = std::array<Expr<A>, N>;
675 };
676 template <TypeCategory CAT, int N = 2>
677 using SameKindExprs =
678     common::MapTemplate<SameKindExprsHelper<N>::template SameExprs,
679         CategoryTypes<CAT>>;
680 
681 // Given references to two expressions of arbitrary kind in the same type
682 // category, convert one to the kind of the other when it has the smaller kind,
683 // then return them in a type-safe package.
684 template <TypeCategory CAT>
685 SameKindExprs<CAT, 2> AsSameKindExprs(
686     Expr<SomeKind<CAT>> &&x, Expr<SomeKind<CAT>> &&y) {
687   return common::visit(
688       [&](auto &&kx, auto &&ky) -> SameKindExprs<CAT, 2> {
689         using XTy = ResultType<decltype(kx)>;
690         using YTy = ResultType<decltype(ky)>;
691         if constexpr (std::is_same_v<XTy, YTy>) {
692           return {SameExprs<XTy>{std::move(kx), std::move(ky)}};
693         } else if constexpr (XTy::kind < YTy::kind) {
694           return {SameExprs<YTy>{ConvertTo(ky, std::move(kx)), std::move(ky)}};
695         } else {
696           return {SameExprs<XTy>{std::move(kx), ConvertTo(kx, std::move(ky))}};
697         }
698 #if !__clang__ && 100 * __GNUC__ + __GNUC_MINOR__ == 801
699         // Silence a bogus warning about a missing return with G++ 8.1.0.
700         // Doesn't execute, but must be correctly typed.
701         CHECK(!"can't happen");
702         return {SameExprs<XTy>{std::move(kx), std::move(kx)}};
703 #endif
704       },
705       std::move(x.u), std::move(y.u));
706 }
707 
708 // Ensure that both operands of an intrinsic REAL operation (or CMPLX()
709 // constructor) are INTEGER or REAL, then convert them as necessary to the
710 // same kind of REAL.
711 using ConvertRealOperandsResult =
712     std::optional<SameKindExprs<TypeCategory::Real, 2>>;
713 ConvertRealOperandsResult ConvertRealOperands(parser::ContextualMessages &,
714     Expr<SomeType> &&, Expr<SomeType> &&, int defaultRealKind);
715 
716 // Per F'2018 R718, if both components are INTEGER, they are both converted
717 // to default REAL and the result is default COMPLEX.  Otherwise, the
718 // kind of the result is the kind of most precise REAL component, and the other
719 // component is converted if necessary to its type.
720 std::optional<Expr<SomeComplex>> ConstructComplex(parser::ContextualMessages &,
721     Expr<SomeType> &&, Expr<SomeType> &&, int defaultRealKind);
722 std::optional<Expr<SomeComplex>> ConstructComplex(parser::ContextualMessages &,
723     std::optional<Expr<SomeType>> &&, std::optional<Expr<SomeType>> &&,
724     int defaultRealKind);
725 
726 template <typename A> Expr<TypeOf<A>> ScalarConstantToExpr(const A &x) {
727   using Ty = TypeOf<A>;
728   static_assert(
729       std::is_same_v<Scalar<Ty>, std::decay_t<A>>, "TypeOf<> is broken");
730   return Expr<TypeOf<A>>{Constant<Ty>{x}};
731 }
732 
733 // Combine two expressions of the same specific numeric type with an operation
734 // to produce a new expression.
735 template <template <typename> class OPR, typename SPECIFIC>
736 Expr<SPECIFIC> Combine(Expr<SPECIFIC> &&x, Expr<SPECIFIC> &&y) {
737   static_assert(IsSpecificIntrinsicType<SPECIFIC>);
738   return AsExpr(OPR<SPECIFIC>{std::move(x), std::move(y)});
739 }
740 
741 // Given two expressions of arbitrary kind in the same intrinsic type
742 // category, convert one of them if necessary to the larger kind of the
743 // other, then combine the resulting homogenized operands with a given
744 // operation, returning a new expression in the same type category.
745 template <template <typename> class OPR, TypeCategory CAT>
746 Expr<SomeKind<CAT>> PromoteAndCombine(
747     Expr<SomeKind<CAT>> &&x, Expr<SomeKind<CAT>> &&y) {
748   return common::visit(
749       [](auto &&xy) {
750         using Ty = ResultType<decltype(xy[0])>;
751         return AsCategoryExpr(
752             Combine<OPR, Ty>(std::move(xy[0]), std::move(xy[1])));
753       },
754       AsSameKindExprs(std::move(x), std::move(y)));
755 }
756 
757 // Given two expressions of arbitrary type, try to combine them with a
758 // binary numeric operation (e.g., Add), possibly with data type conversion of
759 // one of the operands to the type of the other.  Handles special cases with
760 // typeless literal operands and with REAL/COMPLEX exponentiation to INTEGER
761 // powers.
762 template <template <typename> class OPR, bool CAN_BE_UNSIGNED = true>
763 std::optional<Expr<SomeType>> NumericOperation(parser::ContextualMessages &,
764     Expr<SomeType> &&, Expr<SomeType> &&, int defaultRealKind);
765 
766 extern template std::optional<Expr<SomeType>> NumericOperation<Power, false>(
767     parser::ContextualMessages &, Expr<SomeType> &&, Expr<SomeType> &&,
768     int defaultRealKind);
769 extern template std::optional<Expr<SomeType>> NumericOperation<Multiply>(
770     parser::ContextualMessages &, Expr<SomeType> &&, Expr<SomeType> &&,
771     int defaultRealKind);
772 extern template std::optional<Expr<SomeType>> NumericOperation<Divide>(
773     parser::ContextualMessages &, Expr<SomeType> &&, Expr<SomeType> &&,
774     int defaultRealKind);
775 extern template std::optional<Expr<SomeType>> NumericOperation<Add>(
776     parser::ContextualMessages &, Expr<SomeType> &&, Expr<SomeType> &&,
777     int defaultRealKind);
778 extern template std::optional<Expr<SomeType>> NumericOperation<Subtract>(
779     parser::ContextualMessages &, Expr<SomeType> &&, Expr<SomeType> &&,
780     int defaultRealKind);
781 
782 std::optional<Expr<SomeType>> Negation(
783     parser::ContextualMessages &, Expr<SomeType> &&);
784 
785 // Given two expressions of arbitrary type, try to combine them with a
786 // relational operator (e.g., .LT.), possibly with data type conversion.
787 std::optional<Expr<LogicalResult>> Relate(parser::ContextualMessages &,
788     RelationalOperator, Expr<SomeType> &&, Expr<SomeType> &&);
789 
790 // Create a relational operation between two identically-typed operands
791 // and wrap it up in an Expr<LogicalResult>.
792 template <typename T>
793 Expr<LogicalResult> PackageRelation(
794     RelationalOperator opr, Expr<T> &&x, Expr<T> &&y) {
795   static_assert(IsSpecificIntrinsicType<T>);
796   return Expr<LogicalResult>{
797       Relational<SomeType>{Relational<T>{opr, std::move(x), std::move(y)}}};
798 }
799 
800 template <int K>
801 Expr<Type<TypeCategory::Logical, K>> LogicalNegation(
802     Expr<Type<TypeCategory::Logical, K>> &&x) {
803   return AsExpr(Not<K>{std::move(x)});
804 }
805 
806 Expr<SomeLogical> LogicalNegation(Expr<SomeLogical> &&);
807 
808 template <int K>
809 Expr<Type<TypeCategory::Logical, K>> BinaryLogicalOperation(LogicalOperator opr,
810     Expr<Type<TypeCategory::Logical, K>> &&x,
811     Expr<Type<TypeCategory::Logical, K>> &&y) {
812   return AsExpr(LogicalOperation<K>{opr, std::move(x), std::move(y)});
813 }
814 
815 Expr<SomeLogical> BinaryLogicalOperation(
816     LogicalOperator, Expr<SomeLogical> &&, Expr<SomeLogical> &&);
817 
818 // Convenience functions and operator overloadings for expression construction.
819 // These interfaces are defined only for those situations that can never
820 // emit any message.  Use the more general templates (above) in other
821 // situations.
822 
823 template <TypeCategory C, int K>
824 Expr<Type<C, K>> operator-(Expr<Type<C, K>> &&x) {
825   return AsExpr(Negate<Type<C, K>>{std::move(x)});
826 }
827 
828 template <TypeCategory C, int K>
829 Expr<Type<C, K>> operator+(Expr<Type<C, K>> &&x, Expr<Type<C, K>> &&y) {
830   return AsExpr(Combine<Add, Type<C, K>>(std::move(x), std::move(y)));
831 }
832 
833 template <TypeCategory C, int K>
834 Expr<Type<C, K>> operator-(Expr<Type<C, K>> &&x, Expr<Type<C, K>> &&y) {
835   return AsExpr(Combine<Subtract, Type<C, K>>(std::move(x), std::move(y)));
836 }
837 
838 template <TypeCategory C, int K>
839 Expr<Type<C, K>> operator*(Expr<Type<C, K>> &&x, Expr<Type<C, K>> &&y) {
840   return AsExpr(Combine<Multiply, Type<C, K>>(std::move(x), std::move(y)));
841 }
842 
843 template <TypeCategory C, int K>
844 Expr<Type<C, K>> operator/(Expr<Type<C, K>> &&x, Expr<Type<C, K>> &&y) {
845   return AsExpr(Combine<Divide, Type<C, K>>(std::move(x), std::move(y)));
846 }
847 
848 template <TypeCategory C> Expr<SomeKind<C>> operator-(Expr<SomeKind<C>> &&x) {
849   return common::visit(
850       [](auto &xk) { return Expr<SomeKind<C>>{-std::move(xk)}; }, x.u);
851 }
852 
853 template <TypeCategory CAT>
854 Expr<SomeKind<CAT>> operator+(
855     Expr<SomeKind<CAT>> &&x, Expr<SomeKind<CAT>> &&y) {
856   return PromoteAndCombine<Add, CAT>(std::move(x), std::move(y));
857 }
858 
859 template <TypeCategory CAT>
860 Expr<SomeKind<CAT>> operator-(
861     Expr<SomeKind<CAT>> &&x, Expr<SomeKind<CAT>> &&y) {
862   return PromoteAndCombine<Subtract, CAT>(std::move(x), std::move(y));
863 }
864 
865 template <TypeCategory CAT>
866 Expr<SomeKind<CAT>> operator*(
867     Expr<SomeKind<CAT>> &&x, Expr<SomeKind<CAT>> &&y) {
868   return PromoteAndCombine<Multiply, CAT>(std::move(x), std::move(y));
869 }
870 
871 template <TypeCategory CAT>
872 Expr<SomeKind<CAT>> operator/(
873     Expr<SomeKind<CAT>> &&x, Expr<SomeKind<CAT>> &&y) {
874   return PromoteAndCombine<Divide, CAT>(std::move(x), std::move(y));
875 }
876 
877 // A utility for use with common::SearchTypes to create generic expressions
878 // when an intrinsic type category for (say) a variable is known
879 // but the kind parameter value is not.
880 template <TypeCategory CAT, template <typename> class TEMPLATE, typename VALUE>
881 struct TypeKindVisitor {
882   using Result = std::optional<Expr<SomeType>>;
883   using Types = CategoryTypes<CAT>;
884 
885   TypeKindVisitor(int k, VALUE &&x) : kind{k}, value{std::move(x)} {}
886   TypeKindVisitor(int k, const VALUE &x) : kind{k}, value{x} {}
887 
888   template <typename T> Result Test() {
889     if (kind == T::kind) {
890       return AsGenericExpr(TEMPLATE<T>{std::move(value)});
891     }
892     return std::nullopt;
893   }
894 
895   int kind;
896   VALUE value;
897 };
898 
899 // TypedWrapper() wraps a object in an explicitly typed representation
900 // (e.g., Designator<> or FunctionRef<>) that has been instantiated on
901 // a dynamically chosen Fortran type.
902 template <TypeCategory CATEGORY, template <typename> typename WRAPPER,
903     typename WRAPPED>
904 common::IfNoLvalue<std::optional<Expr<SomeType>>, WRAPPED> WrapperHelper(
905     int kind, WRAPPED &&x) {
906   return common::SearchTypes(
907       TypeKindVisitor<CATEGORY, WRAPPER, WRAPPED>{kind, std::move(x)});
908 }
909 
910 template <template <typename> typename WRAPPER, typename WRAPPED>
911 common::IfNoLvalue<std::optional<Expr<SomeType>>, WRAPPED> TypedWrapper(
912     const DynamicType &dyType, WRAPPED &&x) {
913   switch (dyType.category()) {
914     SWITCH_COVERS_ALL_CASES
915   case TypeCategory::Integer:
916     return WrapperHelper<TypeCategory::Integer, WRAPPER, WRAPPED>(
917         dyType.kind(), std::move(x));
918   case TypeCategory::Unsigned:
919     return WrapperHelper<TypeCategory::Unsigned, WRAPPER, WRAPPED>(
920         dyType.kind(), std::move(x));
921   case TypeCategory::Real:
922     return WrapperHelper<TypeCategory::Real, WRAPPER, WRAPPED>(
923         dyType.kind(), std::move(x));
924   case TypeCategory::Complex:
925     return WrapperHelper<TypeCategory::Complex, WRAPPER, WRAPPED>(
926         dyType.kind(), std::move(x));
927   case TypeCategory::Character:
928     return WrapperHelper<TypeCategory::Character, WRAPPER, WRAPPED>(
929         dyType.kind(), std::move(x));
930   case TypeCategory::Logical:
931     return WrapperHelper<TypeCategory::Logical, WRAPPER, WRAPPED>(
932         dyType.kind(), std::move(x));
933   case TypeCategory::Derived:
934     return AsGenericExpr(Expr<SomeDerived>{WRAPPER<SomeDerived>{std::move(x)}});
935   }
936 }
937 
938 // GetLastSymbol() returns the rightmost symbol in an object or procedure
939 // designator (which has perhaps been wrapped in an Expr<>), or a null pointer
940 // when none is found.  It will return an ASSOCIATE construct entity's symbol
941 // rather than descending into its expression.
942 struct GetLastSymbolHelper
943     : public AnyTraverse<GetLastSymbolHelper, std::optional<const Symbol *>> {
944   using Result = std::optional<const Symbol *>;
945   using Base = AnyTraverse<GetLastSymbolHelper, Result>;
946   GetLastSymbolHelper() : Base{*this} {}
947   using Base::operator();
948   Result operator()(const Symbol &x) const { return &x; }
949   Result operator()(const Component &x) const { return &x.GetLastSymbol(); }
950   Result operator()(const NamedEntity &x) const { return &x.GetLastSymbol(); }
951   Result operator()(const ProcedureDesignator &x) const {
952     return x.GetSymbol();
953   }
954   template <typename T> Result operator()(const Expr<T> &x) const {
955     if constexpr (common::HasMember<T, AllIntrinsicTypes> ||
956         std::is_same_v<T, SomeDerived>) {
957       if (const auto *designator{std::get_if<Designator<T>>(&x.u)}) {
958         if (auto known{(*this)(*designator)}) {
959           return known;
960         }
961       }
962       return nullptr;
963     } else {
964       return (*this)(x.u);
965     }
966   }
967 };
968 
969 template <typename A> const Symbol *GetLastSymbol(const A &x) {
970   if (auto known{GetLastSymbolHelper{}(x)}) {
971     return *known;
972   } else {
973     return nullptr;
974   }
975 }
976 
977 // For everyday variables: if GetLastSymbol() succeeds on the argument, return
978 // its set of attributes, otherwise the empty set.  Also works on variables that
979 // are pointer results of functions.
980 template <typename A> semantics::Attrs GetAttrs(const A &x) {
981   if (const Symbol * symbol{GetLastSymbol(x)}) {
982     return symbol->attrs();
983   } else {
984     return {};
985   }
986 }
987 
988 template <>
989 inline semantics::Attrs GetAttrs<Expr<SomeType>>(const Expr<SomeType> &x) {
990   if (IsVariable(x)) {
991     if (const auto *procRef{UnwrapProcedureRef(x)}) {
992       if (const Symbol * interface{procRef->proc().GetInterfaceSymbol()}) {
993         if (const auto *details{
994                 interface->detailsIf<semantics::SubprogramDetails>()}) {
995           if (details->isFunction() &&
996               details->result().attrs().test(semantics::Attr::POINTER)) {
997             // N.B.: POINTER becomes TARGET in SetAttrsFromAssociation()
998             return details->result().attrs();
999           }
1000         }
1001       }
1002     }
1003   }
1004   if (const Symbol * symbol{GetLastSymbol(x)}) {
1005     return symbol->attrs();
1006   } else {
1007     return {};
1008   }
1009 }
1010 
1011 template <typename A> semantics::Attrs GetAttrs(const std::optional<A> &x) {
1012   if (x) {
1013     return GetAttrs(*x);
1014   } else {
1015     return {};
1016   }
1017 }
1018 
1019 // GetBaseObject()
1020 template <typename A> std::optional<BaseObject> GetBaseObject(const A &) {
1021   return std::nullopt;
1022 }
1023 template <typename T>
1024 std::optional<BaseObject> GetBaseObject(const Designator<T> &x) {
1025   return x.GetBaseObject();
1026 }
1027 template <typename T>
1028 std::optional<BaseObject> GetBaseObject(const Expr<T> &x) {
1029   return common::visit([](const auto &y) { return GetBaseObject(y); }, x.u);
1030 }
1031 template <typename A>
1032 std::optional<BaseObject> GetBaseObject(const std::optional<A> &x) {
1033   if (x) {
1034     return GetBaseObject(*x);
1035   } else {
1036     return std::nullopt;
1037   }
1038 }
1039 
1040 // Like IsAllocatableOrPointer, but accepts pointer function results as being
1041 // pointers too.
1042 bool IsAllocatableOrPointerObject(const Expr<SomeType> &);
1043 
1044 bool IsAllocatableDesignator(const Expr<SomeType> &);
1045 
1046 // Procedure and pointer detection predicates
1047 bool IsProcedureDesignator(const Expr<SomeType> &);
1048 bool IsFunctionDesignator(const Expr<SomeType> &);
1049 bool IsPointer(const Expr<SomeType> &);
1050 bool IsProcedurePointer(const Expr<SomeType> &);
1051 bool IsProcedure(const Expr<SomeType> &);
1052 bool IsProcedurePointerTarget(const Expr<SomeType> &);
1053 bool IsBareNullPointer(const Expr<SomeType> *); // NULL() w/o MOLD= or type
1054 bool IsNullObjectPointer(const Expr<SomeType> &);
1055 bool IsNullProcedurePointer(const Expr<SomeType> &);
1056 bool IsNullPointer(const Expr<SomeType> &);
1057 bool IsObjectPointer(const Expr<SomeType> &);
1058 
1059 // Can Expr be passed as absent to an optional dummy argument.
1060 // See 15.5.2.12 point 1 for more details.
1061 bool MayBePassedAsAbsentOptional(const Expr<SomeType> &);
1062 
1063 // Extracts the chain of symbols from a designator, which has perhaps been
1064 // wrapped in an Expr<>, removing all of the (co)subscripts.  The
1065 // base object will be the first symbol in the result vector.
1066 struct GetSymbolVectorHelper
1067     : public Traverse<GetSymbolVectorHelper, SymbolVector> {
1068   using Result = SymbolVector;
1069   using Base = Traverse<GetSymbolVectorHelper, Result>;
1070   using Base::operator();
1071   GetSymbolVectorHelper() : Base{*this} {}
1072   Result Default() { return {}; }
1073   Result Combine(Result &&a, Result &&b) {
1074     a.insert(a.end(), b.begin(), b.end());
1075     return std::move(a);
1076   }
1077   Result operator()(const Symbol &) const;
1078   Result operator()(const Component &) const;
1079   Result operator()(const ArrayRef &) const;
1080   Result operator()(const CoarrayRef &) const;
1081 };
1082 template <typename A> SymbolVector GetSymbolVector(const A &x) {
1083   return GetSymbolVectorHelper{}(x);
1084 }
1085 
1086 // GetLastTarget() returns the rightmost symbol in an object designator's
1087 // SymbolVector that has the POINTER or TARGET attribute, or a null pointer
1088 // when none is found.
1089 const Symbol *GetLastTarget(const SymbolVector &);
1090 
1091 // Collects all of the Symbols in an expression
1092 template <typename A> semantics::UnorderedSymbolSet CollectSymbols(const A &);
1093 extern template semantics::UnorderedSymbolSet CollectSymbols(
1094     const Expr<SomeType> &);
1095 extern template semantics::UnorderedSymbolSet CollectSymbols(
1096     const Expr<SomeInteger> &);
1097 extern template semantics::UnorderedSymbolSet CollectSymbols(
1098     const Expr<SubscriptInteger> &);
1099 
1100 // Collects Symbols of interest for the CUDA data transfer in an expression
1101 template <typename A>
1102 semantics::UnorderedSymbolSet CollectCudaSymbols(const A &);
1103 extern template semantics::UnorderedSymbolSet CollectCudaSymbols(
1104     const Expr<SomeType> &);
1105 extern template semantics::UnorderedSymbolSet CollectCudaSymbols(
1106     const Expr<SomeInteger> &);
1107 extern template semantics::UnorderedSymbolSet CollectCudaSymbols(
1108     const Expr<SubscriptInteger> &);
1109 
1110 // Predicate: does a variable contain a vector-valued subscript (not a triplet)?
1111 bool HasVectorSubscript(const Expr<SomeType> &);
1112 
1113 // Predicate: does an expression contain constant?
1114 bool HasConstant(const Expr<SomeType> &);
1115 
1116 // Utilities for attaching the location of the declaration of a symbol
1117 // of interest to a message.  Handles the case of USE association gracefully.
1118 parser::Message *AttachDeclaration(parser::Message &, const Symbol &);
1119 parser::Message *AttachDeclaration(parser::Message *, const Symbol &);
1120 template <typename MESSAGES, typename... A>
1121 parser::Message *SayWithDeclaration(
1122     MESSAGES &messages, const Symbol &symbol, A &&...x) {
1123   return AttachDeclaration(messages.Say(std::forward<A>(x)...), symbol);
1124 }
1125 
1126 // Check for references to impure procedures; returns the name
1127 // of one to complain about, if any exist.
1128 std::optional<std::string> FindImpureCall(
1129     FoldingContext &, const Expr<SomeType> &);
1130 std::optional<std::string> FindImpureCall(
1131     FoldingContext &, const ProcedureRef &);
1132 
1133 // Predicate: is a scalar expression suitable for naive scalar expansion
1134 // in the flattening of an array expression?
1135 // TODO: capture such scalar expansions in temporaries, flatten everything
1136 class UnexpandabilityFindingVisitor
1137     : public AnyTraverse<UnexpandabilityFindingVisitor> {
1138 public:
1139   using Base = AnyTraverse<UnexpandabilityFindingVisitor>;
1140   using Base::operator();
1141   explicit UnexpandabilityFindingVisitor(bool admitPureCall)
1142       : Base{*this}, admitPureCall_{admitPureCall} {}
1143   template <typename T> bool operator()(const FunctionRef<T> &procRef) {
1144     return !admitPureCall_ || !procRef.proc().IsPure();
1145   }
1146   bool operator()(const CoarrayRef &) { return true; }
1147 
1148 private:
1149   bool admitPureCall_{false};
1150 };
1151 
1152 template <typename T>
1153 bool IsExpandableScalar(const Expr<T> &expr, FoldingContext &context,
1154     const Shape &shape, bool admitPureCall = false) {
1155   if (UnexpandabilityFindingVisitor{admitPureCall}(expr)) {
1156     auto extents{AsConstantExtents(context, shape)};
1157     return extents && !HasNegativeExtent(*extents) && GetSize(*extents) == 1;
1158   } else {
1159     return true;
1160   }
1161 }
1162 
1163 // Common handling for procedure pointer compatibility of left- and right-hand
1164 // sides.  Returns nullopt if they're compatible.  Otherwise, it returns a
1165 // message that needs to be augmented by the names of the left and right sides.
1166 std::optional<parser::MessageFixedText> CheckProcCompatibility(bool isCall,
1167     const std::optional<characteristics::Procedure> &lhsProcedure,
1168     const characteristics::Procedure *rhsProcedure,
1169     const SpecificIntrinsic *specificIntrinsic, std::string &whyNotCompatible,
1170     std::optional<std::string> &warning, bool ignoreImplicitVsExplicit);
1171 
1172 // Scalar constant expansion
1173 class ScalarConstantExpander {
1174 public:
1175   explicit ScalarConstantExpander(ConstantSubscripts &&extents)
1176       : extents_{std::move(extents)} {}
1177   ScalarConstantExpander(
1178       ConstantSubscripts &&extents, std::optional<ConstantSubscripts> &&lbounds)
1179       : extents_{std::move(extents)}, lbounds_{std::move(lbounds)} {}
1180   ScalarConstantExpander(
1181       ConstantSubscripts &&extents, ConstantSubscripts &&lbounds)
1182       : extents_{std::move(extents)}, lbounds_{std::move(lbounds)} {}
1183 
1184   template <typename A> A Expand(A &&x) const {
1185     return std::move(x); // default case
1186   }
1187   template <typename T> Constant<T> Expand(Constant<T> &&x) {
1188     auto expanded{x.Reshape(std::move(extents_))};
1189     if (lbounds_) {
1190       expanded.set_lbounds(std::move(*lbounds_));
1191     }
1192     return expanded;
1193   }
1194   template <typename T> Expr<T> Expand(Parentheses<T> &&x) {
1195     return Expand(std::move(x.left())); // Constant<> can be parenthesized
1196   }
1197   template <typename T> Expr<T> Expand(Expr<T> &&x) {
1198     return common::visit(
1199         [&](auto &&x) { return Expr<T>{Expand(std::move(x))}; },
1200         std::move(x.u));
1201   }
1202 
1203 private:
1204   ConstantSubscripts extents_;
1205   std::optional<ConstantSubscripts> lbounds_;
1206 };
1207 
1208 // Given a collection of element values, package them as a Constant.
1209 // If the type is Character or a derived type, take the length or type
1210 // (resp.) from a another Constant.
1211 template <typename T>
1212 Constant<T> PackageConstant(std::vector<Scalar<T>> &&elements,
1213     const Constant<T> &reference, const ConstantSubscripts &shape) {
1214   if constexpr (T::category == TypeCategory::Character) {
1215     return Constant<T>{
1216         reference.LEN(), std::move(elements), ConstantSubscripts{shape}};
1217   } else if constexpr (T::category == TypeCategory::Derived) {
1218     return Constant<T>{reference.GetType().GetDerivedTypeSpec(),
1219         std::move(elements), ConstantSubscripts{shape}};
1220   } else {
1221     return Constant<T>{std::move(elements), ConstantSubscripts{shape}};
1222   }
1223 }
1224 
1225 // Nonstandard conversions of constants (integer->logical, logical->integer)
1226 // that can appear in DATA statements as an extension.
1227 std::optional<Expr<SomeType>> DataConstantConversionExtension(
1228     FoldingContext &, const DynamicType &, const Expr<SomeType> &);
1229 
1230 // Convert Hollerith or short character to a another type as if the
1231 // Hollerith data had been BOZ.
1232 std::optional<Expr<SomeType>> HollerithToBOZ(
1233     FoldingContext &, const Expr<SomeType> &, const DynamicType &);
1234 
1235 // Set explicit lower bounds on a constant array.
1236 class ArrayConstantBoundChanger {
1237 public:
1238   explicit ArrayConstantBoundChanger(ConstantSubscripts &&lbounds)
1239       : lbounds_{std::move(lbounds)} {}
1240 
1241   template <typename A> A ChangeLbounds(A &&x) const {
1242     return std::move(x); // default case
1243   }
1244   template <typename T> Constant<T> ChangeLbounds(Constant<T> &&x) {
1245     x.set_lbounds(std::move(lbounds_));
1246     return std::move(x);
1247   }
1248   template <typename T> Expr<T> ChangeLbounds(Parentheses<T> &&x) {
1249     return ChangeLbounds(
1250         std::move(x.left())); // Constant<> can be parenthesized
1251   }
1252   template <typename T> Expr<T> ChangeLbounds(Expr<T> &&x) {
1253     return common::visit(
1254         [&](auto &&x) { return Expr<T>{ChangeLbounds(std::move(x))}; },
1255         std::move(x.u)); // recurse until we hit a constant
1256   }
1257 
1258 private:
1259   ConstantSubscripts &&lbounds_;
1260 };
1261 
1262 // Predicate: should two expressions be considered identical for the purposes
1263 // of determining whether two procedure interfaces are compatible, modulo
1264 // naming of corresponding dummy arguments?
1265 template <typename T>
1266 std::optional<bool> AreEquivalentInInterface(const Expr<T> &, const Expr<T> &);
1267 extern template std::optional<bool> AreEquivalentInInterface<SubscriptInteger>(
1268     const Expr<SubscriptInteger> &, const Expr<SubscriptInteger> &);
1269 extern template std::optional<bool> AreEquivalentInInterface<SomeInteger>(
1270     const Expr<SomeInteger> &, const Expr<SomeInteger> &);
1271 
1272 bool CheckForCoindexedObject(parser::ContextualMessages &,
1273     const std::optional<ActualArgument> &, const std::string &procName,
1274     const std::string &argName);
1275 
1276 inline bool CanCUDASymbolHaveSaveAttr(const Symbol &sym) {
1277   if (const auto *details =
1278           sym.GetUltimate().detailsIf<semantics::ObjectEntityDetails>()) {
1279     if (details->cudaDataAttr() &&
1280         *details->cudaDataAttr() != common::CUDADataAttr::Unified) {
1281       return false;
1282     }
1283   }
1284   return true;
1285 }
1286 
1287 inline bool IsCUDADeviceSymbol(const Symbol &sym) {
1288   if (const auto *details =
1289           sym.GetUltimate().detailsIf<semantics::ObjectEntityDetails>()) {
1290     if (details->cudaDataAttr() &&
1291         *details->cudaDataAttr() != common::CUDADataAttr::Pinned) {
1292       return true;
1293     }
1294   }
1295   return false;
1296 }
1297 
1298 // Get the number of distinct symbols with CUDA device
1299 // attribute in the expression.
1300 template <typename A> inline int GetNbOfCUDADeviceSymbols(const A &expr) {
1301   semantics::UnorderedSymbolSet symbols;
1302   for (const Symbol &sym : CollectCudaSymbols(expr)) {
1303     if (IsCUDADeviceSymbol(sym)) {
1304       symbols.insert(sym);
1305     }
1306   }
1307   return symbols.size();
1308 }
1309 
1310 // Check if any of the symbols part of the expression has a CUDA device
1311 // attribute.
1312 template <typename A> inline bool HasCUDADeviceAttrs(const A &expr) {
1313   return GetNbOfCUDADeviceSymbols(expr) > 0;
1314 }
1315 
1316 /// Check if the expression is a mix of host and device variables that require
1317 /// implicit data transfer.
1318 inline bool HasCUDAImplicitTransfer(const Expr<SomeType> &expr) {
1319   unsigned hostSymbols{0};
1320   unsigned deviceSymbols{0};
1321   for (const Symbol &sym : CollectCudaSymbols(expr)) {
1322     if (IsCUDADeviceSymbol(sym)) {
1323       ++deviceSymbols;
1324     } else {
1325       if (sym.owner().IsDerivedType()) {
1326         if (IsCUDADeviceSymbol(sym.owner().GetSymbol()->GetUltimate())) {
1327           ++deviceSymbols;
1328         }
1329       }
1330       ++hostSymbols;
1331     }
1332   }
1333   bool hasConstant{HasConstant(expr)};
1334   return (hasConstant || (hostSymbols > 0)) && deviceSymbols > 0;
1335 }
1336 
1337 } // namespace Fortran::evaluate
1338 
1339 namespace Fortran::semantics {
1340 
1341 class Scope;
1342 
1343 // If a symbol represents an ENTRY, return the symbol of the main entry
1344 // point to its subprogram.
1345 const Symbol *GetMainEntry(const Symbol *);
1346 
1347 // These functions are used in Evaluate so they are defined here rather than in
1348 // Semantics to avoid a link-time dependency on Semantics.
1349 // All of these apply GetUltimate() or ResolveAssociations() to their arguments.
1350 bool IsVariableName(const Symbol &);
1351 bool IsPureProcedure(const Symbol &);
1352 bool IsPureProcedure(const Scope &);
1353 bool IsExplicitlyImpureProcedure(const Symbol &);
1354 bool IsElementalProcedure(const Symbol &);
1355 bool IsFunction(const Symbol &);
1356 bool IsFunction(const Scope &);
1357 bool IsProcedure(const Symbol &);
1358 bool IsProcedure(const Scope &);
1359 bool IsProcedurePointer(const Symbol *);
1360 bool IsProcedurePointer(const Symbol &);
1361 bool IsObjectPointer(const Symbol *);
1362 bool IsAllocatableOrObjectPointer(const Symbol *);
1363 bool IsAutomatic(const Symbol &);
1364 bool IsSaved(const Symbol &); // saved implicitly or explicitly
1365 bool IsDummy(const Symbol &);
1366 bool IsAssumedShape(const Symbol &);
1367 bool IsDeferredShape(const Symbol &);
1368 bool IsFunctionResult(const Symbol &);
1369 bool IsKindTypeParameter(const Symbol &);
1370 bool IsLenTypeParameter(const Symbol &);
1371 bool IsExtensibleType(const DerivedTypeSpec *);
1372 bool IsSequenceOrBindCType(const DerivedTypeSpec *);
1373 bool IsBuiltinDerivedType(const DerivedTypeSpec *derived, const char *name);
1374 bool IsBuiltinCPtr(const Symbol &);
1375 bool IsEventType(const DerivedTypeSpec *);
1376 bool IsLockType(const DerivedTypeSpec *);
1377 bool IsNotifyType(const DerivedTypeSpec *);
1378 // Is this derived type IEEE_FLAG_TYPE from module ISO_IEEE_EXCEPTIONS?
1379 bool IsIeeeFlagType(const DerivedTypeSpec *);
1380 // Is this derived type IEEE_ROUND_TYPE from module ISO_IEEE_ARITHMETIC?
1381 bool IsIeeeRoundType(const DerivedTypeSpec *);
1382 // Is this derived type TEAM_TYPE from module ISO_FORTRAN_ENV?
1383 bool IsTeamType(const DerivedTypeSpec *);
1384 // Is this derived type TEAM_TYPE, C_PTR, or C_FUNPTR?
1385 bool IsBadCoarrayType(const DerivedTypeSpec *);
1386 // Is this derived type either C_PTR or C_FUNPTR from module ISO_C_BINDING
1387 bool IsIsoCType(const DerivedTypeSpec *);
1388 bool IsEventTypeOrLockType(const DerivedTypeSpec *);
1389 inline bool IsAssumedSizeArray(const Symbol &symbol) {
1390   if (const auto *object{symbol.detailsIf<ObjectEntityDetails>()}) {
1391     return (object->isDummy() || symbol.test(Symbol::Flag::CrayPointee)) &&
1392         object->shape().CanBeAssumedSize();
1393   } else if (const auto *assoc{symbol.detailsIf<AssocEntityDetails>()}) {
1394     return assoc->IsAssumedSize();
1395   } else {
1396     return false;
1397   }
1398 }
1399 
1400 // ResolveAssociations() traverses use associations and host associations
1401 // like GetUltimate(), but also resolves through whole variable associations
1402 // with ASSOCIATE(x => y) and related constructs.  GetAssociationRoot()
1403 // applies ResolveAssociations() and then, in the case of resolution to
1404 // a construct association with part of a variable that does not involve a
1405 // vector subscript, returns the first symbol of that variable instead
1406 // of the construct entity.
1407 // (E.g., for ASSOCIATE(x => y%z), ResolveAssociations(x) returns x,
1408 // while GetAssociationRoot(x) returns y.)
1409 // In a SELECT RANK construct, ResolveAssociations() stops at a
1410 // RANK(n) or RANK(*) case symbol, but traverses the selector for
1411 // RANK DEFAULT.
1412 const Symbol &ResolveAssociations(const Symbol &);
1413 const Symbol &GetAssociationRoot(const Symbol &);
1414 
1415 const Symbol *FindCommonBlockContaining(const Symbol &);
1416 int CountLenParameters(const DerivedTypeSpec &);
1417 int CountNonConstantLenParameters(const DerivedTypeSpec &);
1418 
1419 const Symbol &GetUsedModule(const UseDetails &);
1420 const Symbol *FindFunctionResult(const Symbol &);
1421 
1422 // Type compatibility predicate: are x and y effectively the same type?
1423 // Uses DynamicType::IsTkCompatible(), which handles the case of distinct
1424 // but identical derived types.
1425 bool AreTkCompatibleTypes(const DeclTypeSpec *x, const DeclTypeSpec *y);
1426 
1427 common::IgnoreTKRSet GetIgnoreTKR(const Symbol &);
1428 
1429 std::optional<int> GetDummyArgumentNumber(const Symbol *);
1430 
1431 const Symbol *FindAncestorModuleProcedure(const Symbol *symInSubmodule);
1432 
1433 } // namespace Fortran::semantics
1434 
1435 #endif // FORTRAN_EVALUATE_TOOLS_H_
1436