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/Parser/message.h"
19 #include "flang/Semantics/attr.h"
20 #include "flang/Semantics/symbol.h"
21 #include <array>
22 #include <optional>
23 #include <set>
24 #include <type_traits>
25 #include <utility>
26 
27 namespace Fortran::evaluate {
28 
29 // Some expression predicates and extractors.
30 
31 // Predicate: true when an expression is a variable reference, not an
32 // operation.  Be advised: a call to a function that returns an object
33 // pointer is a "variable" in Fortran (it can be the left-hand side of
34 // an assignment).
35 struct IsVariableHelper
36     : public AnyTraverse<IsVariableHelper, std::optional<bool>> {
37   using Result = std::optional<bool>; // effectively tri-state
38   using Base = AnyTraverse<IsVariableHelper, Result>;
IsVariableHelperIsVariableHelper39   IsVariableHelper() : Base{*this} {}
40   using Base::operator();
operatorIsVariableHelper41   Result operator()(const StaticDataObject &) const { return false; }
42   Result operator()(const Symbol &) const;
43   Result operator()(const Component &) const;
44   Result operator()(const ArrayRef &) const;
45   Result operator()(const Substring &) const;
operatorIsVariableHelper46   Result operator()(const CoarrayRef &) const { return true; }
operatorIsVariableHelper47   Result operator()(const ComplexPart &) const { return true; }
48   Result operator()(const ProcedureDesignator &) const;
operatorIsVariableHelper49   template <typename T> Result operator()(const Expr<T> &x) const {
50     if constexpr (common::HasMember<T, AllIntrinsicTypes> ||
51         std::is_same_v<T, SomeDerived>) {
52       // Expression with a specific type
53       if (std::holds_alternative<Designator<T>>(x.u) ||
54           std::holds_alternative<FunctionRef<T>>(x.u)) {
55         if (auto known{(*this)(x.u)}) {
56           return known;
57         }
58       }
59       return false;
60     } else {
61       return (*this)(x.u);
62     }
63   }
64 };
65 
IsVariable(const A & x)66 template <typename A> bool IsVariable(const A &x) {
67   if (auto known{IsVariableHelper{}(x)}) {
68     return *known;
69   } else {
70     return false;
71   }
72 }
73 
74 // Predicate: true when an expression is assumed-rank
75 bool IsAssumedRank(const Symbol &);
76 bool IsAssumedRank(const ActualArgument &);
IsAssumedRank(const A &)77 template <typename A> bool IsAssumedRank(const A &) { return false; }
IsAssumedRank(const Designator<A> & designator)78 template <typename A> bool IsAssumedRank(const Designator<A> &designator) {
79   if (const auto *symbol{std::get_if<SymbolRef>(&designator.u)}) {
80     return IsAssumedRank(symbol->get());
81   } else {
82     return false;
83   }
84 }
IsAssumedRank(const Expr<T> & expr)85 template <typename T> bool IsAssumedRank(const Expr<T> &expr) {
86   return common::visit([](const auto &x) { return IsAssumedRank(x); }, expr.u);
87 }
IsAssumedRank(const std::optional<A> & x)88 template <typename A> bool IsAssumedRank(const std::optional<A> &x) {
89   return x && IsAssumedRank(*x);
90 }
91 
92 // Predicate: true when an expression is a coarray (corank > 0)
93 bool IsCoarray(const ActualArgument &);
94 bool IsCoarray(const Symbol &);
IsCoarray(const A &)95 template <typename A> bool IsCoarray(const A &) { return false; }
IsCoarray(const Designator<A> & designator)96 template <typename A> bool IsCoarray(const Designator<A> &designator) {
97   if (const auto *symbol{std::get_if<SymbolRef>(&designator.u)}) {
98     return IsCoarray(**symbol);
99   }
100   return false;
101 }
IsCoarray(const Expr<T> & expr)102 template <typename T> bool IsCoarray(const Expr<T> &expr) {
103   return common::visit([](const auto &x) { return IsCoarray(x); }, expr.u);
104 }
IsCoarray(const std::optional<A> & x)105 template <typename A> bool IsCoarray(const std::optional<A> &x) {
106   return x && IsCoarray(*x);
107 }
108 
109 // Generalizing packagers: these take operations and expressions of more
110 // specific types and wrap them in Expr<> containers of more abstract types.
111 
AsExpr(A && x)112 template <typename A> common::IfNoLvalue<Expr<ResultType<A>>, A> AsExpr(A &&x) {
113   return Expr<ResultType<A>>{std::move(x)};
114 }
115 
AsExpr(Expr<T> && x)116 template <typename T> Expr<T> AsExpr(Expr<T> &&x) {
117   static_assert(IsSpecificIntrinsicType<T>);
118   return std::move(x);
119 }
120 
121 template <TypeCategory CATEGORY>
AsCategoryExpr(Expr<SomeKind<CATEGORY>> && x)122 Expr<SomeKind<CATEGORY>> AsCategoryExpr(Expr<SomeKind<CATEGORY>> &&x) {
123   return std::move(x);
124 }
125 
126 template <typename A>
AsGenericExpr(A && x)127 common::IfNoLvalue<Expr<SomeType>, A> AsGenericExpr(A &&x) {
128   if constexpr (common::HasMember<A, TypelessExpression>) {
129     return Expr<SomeType>{std::move(x)};
130   } else {
131     return Expr<SomeType>{AsCategoryExpr(std::move(x))};
132   }
133 }
134 
AsGenericExpr(Expr<SomeType> && x)135 inline Expr<SomeType> AsGenericExpr(Expr<SomeType> &&x) { return std::move(x); }
136 
137 // These overloads wrap DataRefs and simple whole variables up into
138 // generic expressions if they have a known type.
139 std::optional<Expr<SomeType>> AsGenericExpr(DataRef &&);
140 std::optional<Expr<SomeType>> AsGenericExpr(const Symbol &);
141 
142 template <typename A>
AsCategoryExpr(A && x)143 common::IfNoLvalue<Expr<SomeKind<ResultType<A>::category>>, A> AsCategoryExpr(
144     A &&x) {
145   return Expr<SomeKind<ResultType<A>::category>>{AsExpr(std::move(x))};
146 }
147 
148 Expr<SomeType> Parenthesize(Expr<SomeType> &&);
149 
150 Expr<SomeReal> GetComplexPart(
151     const Expr<SomeComplex> &, bool isImaginary = false);
152 Expr<SomeReal> GetComplexPart(Expr<SomeComplex> &&, bool isImaginary = false);
153 
154 template <int KIND>
MakeComplex(Expr<Type<TypeCategory::Real,KIND>> && re,Expr<Type<TypeCategory::Real,KIND>> && im)155 Expr<SomeComplex> MakeComplex(Expr<Type<TypeCategory::Real, KIND>> &&re,
156     Expr<Type<TypeCategory::Real, KIND>> &&im) {
157   return AsCategoryExpr(ComplexConstructor<KIND>{std::move(re), std::move(im)});
158 }
159 
IsNumericCategoryExpr()160 template <typename A> constexpr bool IsNumericCategoryExpr() {
161   if constexpr (common::HasMember<A, TypelessExpression>) {
162     return false;
163   } else {
164     return common::HasMember<ResultType<A>, NumericCategoryTypes>;
165   }
166 }
167 
168 // Specializing extractor.  If an Expr wraps some type of object, perhaps
169 // in several layers, return a pointer to it; otherwise null.  Also works
170 // with expressions contained in ActualArgument.
171 template <typename A, typename B>
172 auto UnwrapExpr(B &x) -> common::Constify<A, B> * {
173   using Ty = std::decay_t<B>;
174   if constexpr (std::is_same_v<A, Ty>) {
175     return &x;
176   } else if constexpr (std::is_same_v<Ty, ActualArgument>) {
177     if (auto *expr{x.UnwrapExpr()}) {
178       return UnwrapExpr<A>(*expr);
179     }
180   } else if constexpr (std::is_same_v<Ty, Expr<SomeType>>) {
181     return common::visit([](auto &x) { return UnwrapExpr<A>(x); }, x.u);
182   } else if constexpr (!common::HasMember<A, TypelessExpression>) {
183     if constexpr (std::is_same_v<Ty, Expr<ResultType<A>>> ||
184         std::is_same_v<Ty, Expr<SomeKind<ResultType<A>::category>>>) {
185       return common::visit([](auto &x) { return UnwrapExpr<A>(x); }, x.u);
186     }
187   }
188   return nullptr;
189 }
190 
191 template <typename A, typename B>
UnwrapExpr(const std::optional<B> & x)192 const A *UnwrapExpr(const std::optional<B> &x) {
193   if (x) {
194     return UnwrapExpr<A>(*x);
195   } else {
196     return nullptr;
197   }
198 }
199 
UnwrapExpr(std::optional<B> & x)200 template <typename A, typename B> A *UnwrapExpr(std::optional<B> &x) {
201   if (x) {
202     return UnwrapExpr<A>(*x);
203   } else {
204     return nullptr;
205   }
206 }
207 
208 // A variant of UnwrapExpr above that also skips through (parentheses)
209 // and conversions of kinds within a category.  Useful for extracting LEN
210 // type parameter inquiries, at least.
211 template <typename A, typename B>
212 auto UnwrapConvertedExpr(B &x) -> common::Constify<A, B> * {
213   using Ty = std::decay_t<B>;
214   if constexpr (std::is_same_v<A, Ty>) {
215     return &x;
216   } else if constexpr (std::is_same_v<Ty, ActualArgument>) {
217     if (auto *expr{x.UnwrapExpr()}) {
218       return UnwrapConvertedExpr<A>(*expr);
219     }
220   } else if constexpr (std::is_same_v<Ty, Expr<SomeType>>) {
221     return common::visit(
222         [](auto &x) { return UnwrapConvertedExpr<A>(x); }, x.u);
223   } else {
224     using DesiredResult = ResultType<A>;
225     if constexpr (std::is_same_v<Ty, Expr<DesiredResult>> ||
226         std::is_same_v<Ty, Expr<SomeKind<DesiredResult::category>>>) {
227       return common::visit(
228           [](auto &x) { return UnwrapConvertedExpr<A>(x); }, x.u);
229     } else {
230       using ThisResult = ResultType<B>;
231       if constexpr (std::is_same_v<Ty, Expr<ThisResult>>) {
232         return common::visit(
233             [](auto &x) { return UnwrapConvertedExpr<A>(x); }, x.u);
234       } else if constexpr (std::is_same_v<Ty, Parentheses<ThisResult>> ||
235           std::is_same_v<Ty, Convert<ThisResult, DesiredResult::category>>) {
236         return common::visit(
237             [](auto &x) { return UnwrapConvertedExpr<A>(x); }, x.left().u);
238       }
239     }
240   }
241   return nullptr;
242 }
243 
244 // When an expression is a "bare" LEN= derived type parameter inquiry,
245 // possibly wrapped in integer kind conversions &/or parentheses, return
246 // a pointer to the Symbol with TypeParamDetails.
ExtractBareLenParameter(const A & expr)247 template <typename A> const Symbol *ExtractBareLenParameter(const A &expr) {
248   if (const auto *typeParam{
249           UnwrapConvertedExpr<evaluate::TypeParamInquiry>(expr)}) {
250     if (!typeParam->base()) {
251       const Symbol &symbol{typeParam->parameter()};
252       if (const auto *tpd{symbol.detailsIf<semantics::TypeParamDetails>()}) {
253         if (tpd->attr() == common::TypeParamAttr::Len) {
254           return &symbol;
255         }
256       }
257     }
258   }
259   return nullptr;
260 }
261 
262 // If an expression simply wraps a DataRef, extract and return it.
263 // The Boolean argument controls the handling of Substring and ComplexPart
264 // references: when true (not default), it extracts the base DataRef
265 // of a substring or complex part, if it has one.
266 template <typename A>
ExtractDataRef(const A &,bool intoSubstring,bool intoComplexPart)267 common::IfNoLvalue<std::optional<DataRef>, A> ExtractDataRef(
268     const A &, bool intoSubstring, bool intoComplexPart) {
269   return std::nullopt; // default base case
270 }
271 template <typename T>
272 std::optional<DataRef> ExtractDataRef(const Designator<T> &d,
273     bool intoSubstring = false, bool intoComplexPart = false) {
274   return common::visit(
275       [=](const auto &x) -> std::optional<DataRef> {
276         if constexpr (common::HasMember<decltype(x), decltype(DataRef::u)>) {
277           return DataRef{x};
278         }
279         if constexpr (std::is_same_v<std::decay_t<decltype(x)>, Substring>) {
280           if (intoSubstring) {
281             return ExtractSubstringBase(x);
282           }
283         }
284         if constexpr (std::is_same_v<std::decay_t<decltype(x)>, ComplexPart>) {
285           if (intoComplexPart) {
286             return x.complex();
287           }
288         }
289         return std::nullopt; // w/o "else" to dodge bogus g++ 8.1 warning
290       },
291       d.u);
292 }
293 template <typename T>
294 std::optional<DataRef> ExtractDataRef(const Expr<T> &expr,
295     bool intoSubstring = false, bool intoComplexPart = false) {
296   return common::visit(
297       [=](const auto &x) {
298         return ExtractDataRef(x, intoSubstring, intoComplexPart);
299       },
300       expr.u);
301 }
302 template <typename A>
303 std::optional<DataRef> ExtractDataRef(const std::optional<A> &x,
304     bool intoSubstring = false, bool intoComplexPart = false) {
305   if (x) {
306     return ExtractDataRef(*x, intoSubstring, intoComplexPart);
307   } else {
308     return std::nullopt;
309   }
310 }
311 template <typename A>
312 std::optional<DataRef> ExtractDataRef(
313     const A *p, bool intoSubstring = false, bool intoComplexPart = false) {
314   if (p) {
315     return ExtractDataRef(*p, intoSubstring, intoComplexPart);
316   } else {
317     return std::nullopt;
318   }
319 }
320 std::optional<DataRef> ExtractDataRef(
321     const ActualArgument &, bool intoSubstring = false);
322 
323 std::optional<DataRef> ExtractSubstringBase(const Substring &);
324 
325 // Predicate: is an expression is an array element reference?
326 template <typename T>
327 bool IsArrayElement(const Expr<T> &expr, bool intoSubstring = true,
328     bool skipComponents = false) {
329   if (auto dataRef{ExtractDataRef(expr, intoSubstring)}) {
330     const DataRef *ref{&*dataRef};
331     if (skipComponents) {
332       while (const Component * component{std::get_if<Component>(&ref->u)}) {
333         ref = &component->base();
334       }
335     }
336     if (const auto *coarrayRef{std::get_if<CoarrayRef>(&ref->u)}) {
337       return !coarrayRef->subscript().empty();
338     } else {
339       return std::holds_alternative<ArrayRef>(ref->u);
340     }
341   } else {
342     return false;
343   }
344 }
345 
346 template <typename A>
ExtractNamedEntity(const A & x)347 std::optional<NamedEntity> ExtractNamedEntity(const A &x) {
348   if (auto dataRef{ExtractDataRef(x)}) {
349     return common::visit(
350         common::visitors{
351             [](SymbolRef &&symbol) -> std::optional<NamedEntity> {
352               return NamedEntity{symbol};
353             },
354             [](Component &&component) -> std::optional<NamedEntity> {
355               return NamedEntity{std::move(component)};
356             },
357             [](CoarrayRef &&co) -> std::optional<NamedEntity> {
358               return co.GetBase();
359             },
360             [](auto &&) { return std::optional<NamedEntity>{}; },
361         },
362         std::move(dataRef->u));
363   } else {
364     return std::nullopt;
365   }
366 }
367 
368 struct ExtractCoindexedObjectHelper {
operatorExtractCoindexedObjectHelper369   template <typename A> std::optional<CoarrayRef> operator()(const A &) const {
370     return std::nullopt;
371   }
operatorExtractCoindexedObjectHelper372   std::optional<CoarrayRef> operator()(const CoarrayRef &x) const { return x; }
373   template <typename A>
operatorExtractCoindexedObjectHelper374   std::optional<CoarrayRef> operator()(const Expr<A> &expr) const {
375     return common::visit(*this, expr.u);
376   }
operatorExtractCoindexedObjectHelper377   std::optional<CoarrayRef> operator()(const DataRef &dataRef) const {
378     return common::visit(*this, dataRef.u);
379   }
operatorExtractCoindexedObjectHelper380   std::optional<CoarrayRef> operator()(const NamedEntity &named) const {
381     if (const Component * component{named.UnwrapComponent()}) {
382       return (*this)(*component);
383     } else {
384       return std::nullopt;
385     }
386   }
operatorExtractCoindexedObjectHelper387   std::optional<CoarrayRef> operator()(const ProcedureDesignator &des) const {
388     if (const auto *component{
389             std::get_if<common::CopyableIndirection<Component>>(&des.u)}) {
390       return (*this)(component->value());
391     } else {
392       return std::nullopt;
393     }
394   }
operatorExtractCoindexedObjectHelper395   std::optional<CoarrayRef> operator()(const Component &component) const {
396     return (*this)(component.base());
397   }
operatorExtractCoindexedObjectHelper398   std::optional<CoarrayRef> operator()(const ArrayRef &arrayRef) const {
399     return (*this)(arrayRef.base());
400   }
401 };
402 
ExtractCoarrayRef(const A & x)403 template <typename A> std::optional<CoarrayRef> ExtractCoarrayRef(const A &x) {
404   if (auto dataRef{ExtractDataRef(x, true)}) {
405     return ExtractCoindexedObjectHelper{}(*dataRef);
406   } else {
407     return ExtractCoindexedObjectHelper{}(x);
408   }
409 }
410 
411 // If an expression is simply a whole symbol data designator,
412 // extract and return that symbol, else null.
UnwrapWholeSymbolDataRef(const A & x)413 template <typename A> const Symbol *UnwrapWholeSymbolDataRef(const A &x) {
414   if (auto dataRef{ExtractDataRef(x)}) {
415     if (const SymbolRef * p{std::get_if<SymbolRef>(&dataRef->u)}) {
416       return &p->get();
417     }
418   }
419   return nullptr;
420 }
421 
422 // If an expression is a whole symbol or a whole component desginator,
423 // extract and return that symbol, else null.
424 template <typename A>
UnwrapWholeSymbolOrComponentDataRef(const A & x)425 const Symbol *UnwrapWholeSymbolOrComponentDataRef(const A &x) {
426   if (auto dataRef{ExtractDataRef(x)}) {
427     if (const SymbolRef * p{std::get_if<SymbolRef>(&dataRef->u)}) {
428       return &p->get();
429     } else if (const Component * c{std::get_if<Component>(&dataRef->u)}) {
430       if (c->base().Rank() == 0) {
431         return &c->GetLastSymbol();
432       }
433     }
434   }
435   return nullptr;
436 }
437 
438 // If an expression is a whole symbol or a whole component designator,
439 // potentially followed by an image selector, extract and return that symbol,
440 // else null.
441 template <typename A>
UnwrapWholeSymbolOrComponentOrCoarrayRef(const A & x)442 const Symbol *UnwrapWholeSymbolOrComponentOrCoarrayRef(const A &x) {
443   if (auto dataRef{ExtractDataRef(x)}) {
444     if (const SymbolRef * p{std::get_if<SymbolRef>(&dataRef->u)}) {
445       return &p->get();
446     } else if (const Component * c{std::get_if<Component>(&dataRef->u)}) {
447       if (c->base().Rank() == 0) {
448         return &c->GetLastSymbol();
449       }
450     } else if (const CoarrayRef * c{std::get_if<CoarrayRef>(&dataRef->u)}) {
451       if (c->subscript().empty()) {
452         return &c->GetLastSymbol();
453       }
454     }
455   }
456   return nullptr;
457 }
458 
459 // GetFirstSymbol(A%B%C[I]%D) -> A
GetFirstSymbol(const A & x)460 template <typename A> const Symbol *GetFirstSymbol(const A &x) {
461   if (auto dataRef{ExtractDataRef(x, true)}) {
462     return &dataRef->GetFirstSymbol();
463   } else {
464     return nullptr;
465   }
466 }
467 
468 // GetLastPointerSymbol(A%PTR1%B%PTR2%C) -> PTR2
469 const Symbol *GetLastPointerSymbol(const evaluate::DataRef &);
470 
471 // Creation of conversion expressions can be done to either a known
472 // specific intrinsic type with ConvertToType<T>(x) or by converting
473 // one arbitrary expression to the type of another with ConvertTo(to, from).
474 
475 template <typename TO, TypeCategory FROMCAT>
ConvertToType(Expr<SomeKind<FROMCAT>> && x)476 Expr<TO> ConvertToType(Expr<SomeKind<FROMCAT>> &&x) {
477   static_assert(IsSpecificIntrinsicType<TO>);
478   if constexpr (FROMCAT == TO::category) {
479     if (auto *already{std::get_if<Expr<TO>>(&x.u)}) {
480       return std::move(*already);
481     } else {
482       return Expr<TO>{Convert<TO, FROMCAT>{std::move(x)}};
483     }
484   } else if constexpr (TO::category == TypeCategory::Complex) {
485     using Part = typename TO::Part;
486     Scalar<Part> zero;
487     return Expr<TO>{ComplexConstructor<TO::kind>{
488         ConvertToType<Part>(std::move(x)), Expr<Part>{Constant<Part>{zero}}}};
489   } else if constexpr (FROMCAT == TypeCategory::Complex) {
490     // Extract and convert the real component of a complex value
491     return common::visit(
492         [&](auto &&z) {
493           using ZType = ResultType<decltype(z)>;
494           using Part = typename ZType::Part;
495           return ConvertToType<TO, TypeCategory::Real>(Expr<SomeReal>{
496               Expr<Part>{ComplexComponent<Part::kind>{false, std::move(z)}}});
497         },
498         std::move(x.u));
499   } else {
500     return Expr<TO>{Convert<TO, FROMCAT>{std::move(x)}};
501   }
502 }
503 
504 template <typename TO, TypeCategory FROMCAT, int FROMKIND>
ConvertToType(Expr<Type<FROMCAT,FROMKIND>> && x)505 Expr<TO> ConvertToType(Expr<Type<FROMCAT, FROMKIND>> &&x) {
506   return ConvertToType<TO, FROMCAT>(Expr<SomeKind<FROMCAT>>{std::move(x)});
507 }
508 
ConvertToType(BOZLiteralConstant && x)509 template <typename TO> Expr<TO> ConvertToType(BOZLiteralConstant &&x) {
510   static_assert(IsSpecificIntrinsicType<TO>);
511   if constexpr (TO::category == TypeCategory::Integer) {
512     return Expr<TO>{
513         Constant<TO>{Scalar<TO>::ConvertUnsigned(std::move(x)).value}};
514   } else {
515     static_assert(TO::category == TypeCategory::Real);
516     using Word = typename Scalar<TO>::Word;
517     return Expr<TO>{
518         Constant<TO>{Scalar<TO>{Word::ConvertUnsigned(std::move(x)).value}}};
519   }
520 }
521 
IsBOZLiteral(const Expr<T> & expr)522 template <typename T> bool IsBOZLiteral(const Expr<T> &expr) {
523   return std::holds_alternative<BOZLiteralConstant>(expr.u);
524 }
525 
526 // Conversions to dynamic types
527 std::optional<Expr<SomeType>> ConvertToType(
528     const DynamicType &, Expr<SomeType> &&);
529 std::optional<Expr<SomeType>> ConvertToType(
530     const DynamicType &, std::optional<Expr<SomeType>> &&);
531 std::optional<Expr<SomeType>> ConvertToType(const Symbol &, Expr<SomeType> &&);
532 std::optional<Expr<SomeType>> ConvertToType(
533     const Symbol &, std::optional<Expr<SomeType>> &&);
534 
535 // Conversions to the type of another expression
536 template <TypeCategory TC, int TK, typename FROM>
ConvertTo(const Expr<Type<TC,TK>> &,FROM && x)537 common::IfNoLvalue<Expr<Type<TC, TK>>, FROM> ConvertTo(
538     const Expr<Type<TC, TK>> &, FROM &&x) {
539   return ConvertToType<Type<TC, TK>>(std::move(x));
540 }
541 
542 template <TypeCategory TC, typename FROM>
ConvertTo(const Expr<SomeKind<TC>> & to,FROM && from)543 common::IfNoLvalue<Expr<SomeKind<TC>>, FROM> ConvertTo(
544     const Expr<SomeKind<TC>> &to, FROM &&from) {
545   return common::visit(
546       [&](const auto &toKindExpr) {
547         using KindExpr = std::decay_t<decltype(toKindExpr)>;
548         return AsCategoryExpr(
549             ConvertToType<ResultType<KindExpr>>(std::move(from)));
550       },
551       to.u);
552 }
553 
554 template <typename FROM>
ConvertTo(const Expr<SomeType> & to,FROM && from)555 common::IfNoLvalue<Expr<SomeType>, FROM> ConvertTo(
556     const Expr<SomeType> &to, FROM &&from) {
557   return common::visit(
558       [&](const auto &toCatExpr) {
559         return AsGenericExpr(ConvertTo(toCatExpr, std::move(from)));
560       },
561       to.u);
562 }
563 
564 // Convert an expression of some known category to a dynamically chosen
565 // kind of some category (usually but not necessarily distinct).
566 template <TypeCategory TOCAT, typename VALUE> struct ConvertToKindHelper {
567   using Result = std::optional<Expr<SomeKind<TOCAT>>>;
568   using Types = CategoryTypes<TOCAT>;
ConvertToKindHelperConvertToKindHelper569   ConvertToKindHelper(int k, VALUE &&x) : kind{k}, value{std::move(x)} {}
TestConvertToKindHelper570   template <typename T> Result Test() {
571     if (kind == T::kind) {
572       return std::make_optional(
573           AsCategoryExpr(ConvertToType<T>(std::move(value))));
574     }
575     return std::nullopt;
576   }
577   int kind;
578   VALUE value;
579 };
580 
581 template <TypeCategory TOCAT, typename VALUE>
ConvertToKind(int kind,VALUE && x)582 common::IfNoLvalue<Expr<SomeKind<TOCAT>>, VALUE> ConvertToKind(
583     int kind, VALUE &&x) {
584   return common::SearchTypes(
585       ConvertToKindHelper<TOCAT, VALUE>{kind, std::move(x)})
586       .value();
587 }
588 
589 // Given a type category CAT, SameKindExprs<CAT, N> is a variant that
590 // holds an arrays of expressions of the same supported kind in that
591 // category.
592 template <typename A, int N = 2> using SameExprs = std::array<Expr<A>, N>;
593 template <int N = 2> struct SameKindExprsHelper {
594   template <typename A> using SameExprs = std::array<Expr<A>, N>;
595 };
596 template <TypeCategory CAT, int N = 2>
597 using SameKindExprs =
598     common::MapTemplate<SameKindExprsHelper<N>::template SameExprs,
599         CategoryTypes<CAT>>;
600 
601 // Given references to two expressions of arbitrary kind in the same type
602 // category, convert one to the kind of the other when it has the smaller kind,
603 // then return them in a type-safe package.
604 template <TypeCategory CAT>
AsSameKindExprs(Expr<SomeKind<CAT>> && x,Expr<SomeKind<CAT>> && y)605 SameKindExprs<CAT, 2> AsSameKindExprs(
606     Expr<SomeKind<CAT>> &&x, Expr<SomeKind<CAT>> &&y) {
607   return common::visit(
608       [&](auto &&kx, auto &&ky) -> SameKindExprs<CAT, 2> {
609         using XTy = ResultType<decltype(kx)>;
610         using YTy = ResultType<decltype(ky)>;
611         if constexpr (std::is_same_v<XTy, YTy>) {
612           return {SameExprs<XTy>{std::move(kx), std::move(ky)}};
613         } else if constexpr (XTy::kind < YTy::kind) {
614           return {SameExprs<YTy>{ConvertTo(ky, std::move(kx)), std::move(ky)}};
615         } else {
616           return {SameExprs<XTy>{std::move(kx), ConvertTo(kx, std::move(ky))}};
617         }
618 #if !__clang__ && 100 * __GNUC__ + __GNUC_MINOR__ == 801
619         // Silence a bogus warning about a missing return with G++ 8.1.0.
620         // Doesn't execute, but must be correctly typed.
621         CHECK(!"can't happen");
622         return {SameExprs<XTy>{std::move(kx), std::move(kx)}};
623 #endif
624       },
625       std::move(x.u), std::move(y.u));
626 }
627 
628 // Ensure that both operands of an intrinsic REAL operation (or CMPLX()
629 // constructor) are INTEGER or REAL, then convert them as necessary to the
630 // same kind of REAL.
631 using ConvertRealOperandsResult =
632     std::optional<SameKindExprs<TypeCategory::Real, 2>>;
633 ConvertRealOperandsResult ConvertRealOperands(parser::ContextualMessages &,
634     Expr<SomeType> &&, Expr<SomeType> &&, int defaultRealKind);
635 
636 // Per F'2018 R718, if both components are INTEGER, they are both converted
637 // to default REAL and the result is default COMPLEX.  Otherwise, the
638 // kind of the result is the kind of most precise REAL component, and the other
639 // component is converted if necessary to its type.
640 std::optional<Expr<SomeComplex>> ConstructComplex(parser::ContextualMessages &,
641     Expr<SomeType> &&, Expr<SomeType> &&, int defaultRealKind);
642 std::optional<Expr<SomeComplex>> ConstructComplex(parser::ContextualMessages &,
643     std::optional<Expr<SomeType>> &&, std::optional<Expr<SomeType>> &&,
644     int defaultRealKind);
645 
ScalarConstantToExpr(const A & x)646 template <typename A> Expr<TypeOf<A>> ScalarConstantToExpr(const A &x) {
647   using Ty = TypeOf<A>;
648   static_assert(
649       std::is_same_v<Scalar<Ty>, std::decay_t<A>>, "TypeOf<> is broken");
650   return Expr<TypeOf<A>>{Constant<Ty>{x}};
651 }
652 
653 // Combine two expressions of the same specific numeric type with an operation
654 // to produce a new expression.
655 template <template <typename> class OPR, typename SPECIFIC>
Combine(Expr<SPECIFIC> && x,Expr<SPECIFIC> && y)656 Expr<SPECIFIC> Combine(Expr<SPECIFIC> &&x, Expr<SPECIFIC> &&y) {
657   static_assert(IsSpecificIntrinsicType<SPECIFIC>);
658   return AsExpr(OPR<SPECIFIC>{std::move(x), std::move(y)});
659 }
660 
661 // Given two expressions of arbitrary kind in the same intrinsic type
662 // category, convert one of them if necessary to the larger kind of the
663 // other, then combine the resulting homogenized operands with a given
664 // operation, returning a new expression in the same type category.
665 template <template <typename> class OPR, TypeCategory CAT>
PromoteAndCombine(Expr<SomeKind<CAT>> && x,Expr<SomeKind<CAT>> && y)666 Expr<SomeKind<CAT>> PromoteAndCombine(
667     Expr<SomeKind<CAT>> &&x, Expr<SomeKind<CAT>> &&y) {
668   return common::visit(
669       [](auto &&xy) {
670         using Ty = ResultType<decltype(xy[0])>;
671         return AsCategoryExpr(
672             Combine<OPR, Ty>(std::move(xy[0]), std::move(xy[1])));
673       },
674       AsSameKindExprs(std::move(x), std::move(y)));
675 }
676 
677 // Given two expressions of arbitrary type, try to combine them with a
678 // binary numeric operation (e.g., Add), possibly with data type conversion of
679 // one of the operands to the type of the other.  Handles special cases with
680 // typeless literal operands and with REAL/COMPLEX exponentiation to INTEGER
681 // powers.
682 template <template <typename> class OPR>
683 std::optional<Expr<SomeType>> NumericOperation(parser::ContextualMessages &,
684     Expr<SomeType> &&, Expr<SomeType> &&, int defaultRealKind);
685 
686 extern template std::optional<Expr<SomeType>> NumericOperation<Power>(
687     parser::ContextualMessages &, Expr<SomeType> &&, Expr<SomeType> &&,
688     int defaultRealKind);
689 extern template std::optional<Expr<SomeType>> NumericOperation<Multiply>(
690     parser::ContextualMessages &, Expr<SomeType> &&, Expr<SomeType> &&,
691     int defaultRealKind);
692 extern template std::optional<Expr<SomeType>> NumericOperation<Divide>(
693     parser::ContextualMessages &, Expr<SomeType> &&, Expr<SomeType> &&,
694     int defaultRealKind);
695 extern template std::optional<Expr<SomeType>> NumericOperation<Add>(
696     parser::ContextualMessages &, Expr<SomeType> &&, Expr<SomeType> &&,
697     int defaultRealKind);
698 extern template std::optional<Expr<SomeType>> NumericOperation<Subtract>(
699     parser::ContextualMessages &, Expr<SomeType> &&, Expr<SomeType> &&,
700     int defaultRealKind);
701 
702 std::optional<Expr<SomeType>> Negation(
703     parser::ContextualMessages &, Expr<SomeType> &&);
704 
705 // Given two expressions of arbitrary type, try to combine them with a
706 // relational operator (e.g., .LT.), possibly with data type conversion.
707 std::optional<Expr<LogicalResult>> Relate(parser::ContextualMessages &,
708     RelationalOperator, Expr<SomeType> &&, Expr<SomeType> &&);
709 
710 // Create a relational operation between two identically-typed operands
711 // and wrap it up in an Expr<LogicalResult>.
712 template <typename T>
PackageRelation(RelationalOperator opr,Expr<T> && x,Expr<T> && y)713 Expr<LogicalResult> PackageRelation(
714     RelationalOperator opr, Expr<T> &&x, Expr<T> &&y) {
715   static_assert(IsSpecificIntrinsicType<T>);
716   return Expr<LogicalResult>{
717       Relational<SomeType>{Relational<T>{opr, std::move(x), std::move(y)}}};
718 }
719 
720 template <int K>
LogicalNegation(Expr<Type<TypeCategory::Logical,K>> && x)721 Expr<Type<TypeCategory::Logical, K>> LogicalNegation(
722     Expr<Type<TypeCategory::Logical, K>> &&x) {
723   return AsExpr(Not<K>{std::move(x)});
724 }
725 
726 Expr<SomeLogical> LogicalNegation(Expr<SomeLogical> &&);
727 
728 template <int K>
BinaryLogicalOperation(LogicalOperator opr,Expr<Type<TypeCategory::Logical,K>> && x,Expr<Type<TypeCategory::Logical,K>> && y)729 Expr<Type<TypeCategory::Logical, K>> BinaryLogicalOperation(LogicalOperator opr,
730     Expr<Type<TypeCategory::Logical, K>> &&x,
731     Expr<Type<TypeCategory::Logical, K>> &&y) {
732   return AsExpr(LogicalOperation<K>{opr, std::move(x), std::move(y)});
733 }
734 
735 Expr<SomeLogical> BinaryLogicalOperation(
736     LogicalOperator, Expr<SomeLogical> &&, Expr<SomeLogical> &&);
737 
738 // Convenience functions and operator overloadings for expression construction.
739 // These interfaces are defined only for those situations that can never
740 // emit any message.  Use the more general templates (above) in other
741 // situations.
742 
743 template <TypeCategory C, int K>
744 Expr<Type<C, K>> operator-(Expr<Type<C, K>> &&x) {
745   return AsExpr(Negate<Type<C, K>>{std::move(x)});
746 }
747 
748 template <TypeCategory C, int K>
749 Expr<Type<C, K>> operator+(Expr<Type<C, K>> &&x, Expr<Type<C, K>> &&y) {
750   return AsExpr(Combine<Add, Type<C, K>>(std::move(x), std::move(y)));
751 }
752 
753 template <TypeCategory C, int K>
754 Expr<Type<C, K>> operator-(Expr<Type<C, K>> &&x, Expr<Type<C, K>> &&y) {
755   return AsExpr(Combine<Subtract, Type<C, K>>(std::move(x), std::move(y)));
756 }
757 
758 template <TypeCategory C, int K>
759 Expr<Type<C, K>> operator*(Expr<Type<C, K>> &&x, Expr<Type<C, K>> &&y) {
760   return AsExpr(Combine<Multiply, Type<C, K>>(std::move(x), std::move(y)));
761 }
762 
763 template <TypeCategory C, int K>
764 Expr<Type<C, K>> operator/(Expr<Type<C, K>> &&x, Expr<Type<C, K>> &&y) {
765   return AsExpr(Combine<Divide, Type<C, K>>(std::move(x), std::move(y)));
766 }
767 
768 template <TypeCategory C> Expr<SomeKind<C>> operator-(Expr<SomeKind<C>> &&x) {
769   return common::visit(
770       [](auto &xk) { return Expr<SomeKind<C>>{-std::move(xk)}; }, x.u);
771 }
772 
773 template <TypeCategory CAT>
774 Expr<SomeKind<CAT>> operator+(
775     Expr<SomeKind<CAT>> &&x, Expr<SomeKind<CAT>> &&y) {
776   return PromoteAndCombine<Add, CAT>(std::move(x), std::move(y));
777 }
778 
779 template <TypeCategory CAT>
780 Expr<SomeKind<CAT>> operator-(
781     Expr<SomeKind<CAT>> &&x, Expr<SomeKind<CAT>> &&y) {
782   return PromoteAndCombine<Subtract, CAT>(std::move(x), std::move(y));
783 }
784 
785 template <TypeCategory CAT>
786 Expr<SomeKind<CAT>> operator*(
787     Expr<SomeKind<CAT>> &&x, Expr<SomeKind<CAT>> &&y) {
788   return PromoteAndCombine<Multiply, CAT>(std::move(x), std::move(y));
789 }
790 
791 template <TypeCategory CAT>
792 Expr<SomeKind<CAT>> operator/(
793     Expr<SomeKind<CAT>> &&x, Expr<SomeKind<CAT>> &&y) {
794   return PromoteAndCombine<Divide, CAT>(std::move(x), std::move(y));
795 }
796 
797 // A utility for use with common::SearchTypes to create generic expressions
798 // when an intrinsic type category for (say) a variable is known
799 // but the kind parameter value is not.
800 template <TypeCategory CAT, template <typename> class TEMPLATE, typename VALUE>
801 struct TypeKindVisitor {
802   using Result = std::optional<Expr<SomeType>>;
803   using Types = CategoryTypes<CAT>;
804 
TypeKindVisitorTypeKindVisitor805   TypeKindVisitor(int k, VALUE &&x) : kind{k}, value{std::move(x)} {}
TypeKindVisitorTypeKindVisitor806   TypeKindVisitor(int k, const VALUE &x) : kind{k}, value{x} {}
807 
TestTypeKindVisitor808   template <typename T> Result Test() {
809     if (kind == T::kind) {
810       return AsGenericExpr(TEMPLATE<T>{std::move(value)});
811     }
812     return std::nullopt;
813   }
814 
815   int kind;
816   VALUE value;
817 };
818 
819 // TypedWrapper() wraps a object in an explicitly typed representation
820 // (e.g., Designator<> or FunctionRef<>) that has been instantiated on
821 // a dynamically chosen Fortran type.
822 template <TypeCategory CATEGORY, template <typename> typename WRAPPER,
823     typename WRAPPED>
WrapperHelper(int kind,WRAPPED && x)824 common::IfNoLvalue<std::optional<Expr<SomeType>>, WRAPPED> WrapperHelper(
825     int kind, WRAPPED &&x) {
826   return common::SearchTypes(
827       TypeKindVisitor<CATEGORY, WRAPPER, WRAPPED>{kind, std::move(x)});
828 }
829 
830 template <template <typename> typename WRAPPER, typename WRAPPED>
TypedWrapper(const DynamicType & dyType,WRAPPED && x)831 common::IfNoLvalue<std::optional<Expr<SomeType>>, WRAPPED> TypedWrapper(
832     const DynamicType &dyType, WRAPPED &&x) {
833   switch (dyType.category()) {
834     SWITCH_COVERS_ALL_CASES
835   case TypeCategory::Integer:
836     return WrapperHelper<TypeCategory::Integer, WRAPPER, WRAPPED>(
837         dyType.kind(), std::move(x));
838   case TypeCategory::Real:
839     return WrapperHelper<TypeCategory::Real, WRAPPER, WRAPPED>(
840         dyType.kind(), std::move(x));
841   case TypeCategory::Complex:
842     return WrapperHelper<TypeCategory::Complex, WRAPPER, WRAPPED>(
843         dyType.kind(), std::move(x));
844   case TypeCategory::Character:
845     return WrapperHelper<TypeCategory::Character, WRAPPER, WRAPPED>(
846         dyType.kind(), std::move(x));
847   case TypeCategory::Logical:
848     return WrapperHelper<TypeCategory::Logical, WRAPPER, WRAPPED>(
849         dyType.kind(), std::move(x));
850   case TypeCategory::Derived:
851     return AsGenericExpr(Expr<SomeDerived>{WRAPPER<SomeDerived>{std::move(x)}});
852   }
853 }
854 
855 // GetLastSymbol() returns the rightmost symbol in an object or procedure
856 // designator (which has perhaps been wrapped in an Expr<>), or a null pointer
857 // when none is found.  It will return an ASSOCIATE construct entity's symbol
858 // rather than descending into its expression.
859 struct GetLastSymbolHelper
860     : public AnyTraverse<GetLastSymbolHelper, std::optional<const Symbol *>> {
861   using Result = std::optional<const Symbol *>;
862   using Base = AnyTraverse<GetLastSymbolHelper, Result>;
GetLastSymbolHelperGetLastSymbolHelper863   GetLastSymbolHelper() : Base{*this} {}
864   using Base::operator();
operatorGetLastSymbolHelper865   Result operator()(const Symbol &x) const { return &x; }
operatorGetLastSymbolHelper866   Result operator()(const Component &x) const { return &x.GetLastSymbol(); }
operatorGetLastSymbolHelper867   Result operator()(const NamedEntity &x) const { return &x.GetLastSymbol(); }
operatorGetLastSymbolHelper868   Result operator()(const ProcedureDesignator &x) const {
869     return x.GetSymbol();
870   }
operatorGetLastSymbolHelper871   template <typename T> Result operator()(const Expr<T> &x) const {
872     if constexpr (common::HasMember<T, AllIntrinsicTypes> ||
873         std::is_same_v<T, SomeDerived>) {
874       if (const auto *designator{std::get_if<Designator<T>>(&x.u)}) {
875         if (auto known{(*this)(*designator)}) {
876           return known;
877         }
878       }
879       return nullptr;
880     } else {
881       return (*this)(x.u);
882     }
883   }
884 };
885 
GetLastSymbol(const A & x)886 template <typename A> const Symbol *GetLastSymbol(const A &x) {
887   if (auto known{GetLastSymbolHelper{}(x)}) {
888     return *known;
889   } else {
890     return nullptr;
891   }
892 }
893 
894 // Convenience: If GetLastSymbol() succeeds on the argument, return its
895 // set of attributes, otherwise the empty set.
GetAttrs(const A & x)896 template <typename A> semantics::Attrs GetAttrs(const A &x) {
897   if (const Symbol * symbol{GetLastSymbol(x)}) {
898     return symbol->attrs();
899   } else {
900     return {};
901   }
902 }
903 
904 // GetBaseObject()
GetBaseObject(const A &)905 template <typename A> std::optional<BaseObject> GetBaseObject(const A &) {
906   return std::nullopt;
907 }
908 template <typename T>
GetBaseObject(const Designator<T> & x)909 std::optional<BaseObject> GetBaseObject(const Designator<T> &x) {
910   return x.GetBaseObject();
911 }
912 template <typename T>
GetBaseObject(const Expr<T> & x)913 std::optional<BaseObject> GetBaseObject(const Expr<T> &x) {
914   return common::visit([](const auto &y) { return GetBaseObject(y); }, x.u);
915 }
916 template <typename A>
GetBaseObject(const std::optional<A> & x)917 std::optional<BaseObject> GetBaseObject(const std::optional<A> &x) {
918   if (x) {
919     return GetBaseObject(*x);
920   } else {
921     return std::nullopt;
922   }
923 }
924 
925 // Predicate: IsAllocatableOrPointer()
IsAllocatableOrPointer(const A & x)926 template <typename A> bool IsAllocatableOrPointer(const A &x) {
927   return GetAttrs(x).HasAny(
928       semantics::Attrs{semantics::Attr::POINTER, semantics::Attr::ALLOCATABLE});
929 }
930 
931 // Like IsAllocatableOrPointer, but accepts pointer function results as being
932 // pointers.
933 bool IsAllocatableOrPointerObject(const Expr<SomeType> &, FoldingContext &);
934 
935 bool IsAllocatableDesignator(const Expr<SomeType> &);
936 
937 // Procedure and pointer detection predicates
938 bool IsProcedure(const Expr<SomeType> &);
939 bool IsFunction(const Expr<SomeType> &);
940 bool IsProcedurePointerTarget(const Expr<SomeType> &);
941 bool IsBareNullPointer(const Expr<SomeType> *); // NULL() w/o MOLD=
942 bool IsNullPointer(const Expr<SomeType> &);
943 bool IsObjectPointer(const Expr<SomeType> &, FoldingContext &);
944 
945 // Can Expr be passed as absent to an optional dummy argument.
946 // See 15.5.2.12 point 1 for more details.
947 bool MayBePassedAsAbsentOptional(const Expr<SomeType> &, FoldingContext &);
948 
949 // Extracts the chain of symbols from a designator, which has perhaps been
950 // wrapped in an Expr<>, removing all of the (co)subscripts.  The
951 // base object will be the first symbol in the result vector.
952 struct GetSymbolVectorHelper
953     : public Traverse<GetSymbolVectorHelper, SymbolVector> {
954   using Result = SymbolVector;
955   using Base = Traverse<GetSymbolVectorHelper, Result>;
956   using Base::operator();
GetSymbolVectorHelperGetSymbolVectorHelper957   GetSymbolVectorHelper() : Base{*this} {}
DefaultGetSymbolVectorHelper958   Result Default() { return {}; }
CombineGetSymbolVectorHelper959   Result Combine(Result &&a, Result &&b) {
960     a.insert(a.end(), b.begin(), b.end());
961     return std::move(a);
962   }
963   Result operator()(const Symbol &) const;
964   Result operator()(const Component &) const;
965   Result operator()(const ArrayRef &) const;
966   Result operator()(const CoarrayRef &) const;
967 };
GetSymbolVector(const A & x)968 template <typename A> SymbolVector GetSymbolVector(const A &x) {
969   return GetSymbolVectorHelper{}(x);
970 }
971 
972 // GetLastTarget() returns the rightmost symbol in an object designator's
973 // SymbolVector that has the POINTER or TARGET attribute, or a null pointer
974 // when none is found.
975 const Symbol *GetLastTarget(const SymbolVector &);
976 
977 // Collects all of the Symbols in an expression
978 template <typename A> semantics::UnorderedSymbolSet CollectSymbols(const A &);
979 extern template semantics::UnorderedSymbolSet CollectSymbols(
980     const Expr<SomeType> &);
981 extern template semantics::UnorderedSymbolSet CollectSymbols(
982     const Expr<SomeInteger> &);
983 extern template semantics::UnorderedSymbolSet CollectSymbols(
984     const Expr<SubscriptInteger> &);
985 
986 // Predicate: does a variable contain a vector-valued subscript (not a triplet)?
987 bool HasVectorSubscript(const Expr<SomeType> &);
988 
989 // Utilities for attaching the location of the declaration of a symbol
990 // of interest to a message, if both pointers are non-null.  Handles
991 // the case of USE association gracefully.
992 parser::Message *AttachDeclaration(parser::Message &, const Symbol &);
993 parser::Message *AttachDeclaration(parser::Message *, const Symbol &);
994 template <typename MESSAGES, typename... A>
SayWithDeclaration(MESSAGES & messages,const Symbol & symbol,A &&...x)995 parser::Message *SayWithDeclaration(
996     MESSAGES &messages, const Symbol &symbol, A &&...x) {
997   return AttachDeclaration(messages.Say(std::forward<A>(x)...), symbol);
998 }
999 
1000 // Check for references to impure procedures; returns the name
1001 // of one to complain about, if any exist.
1002 std::optional<std::string> FindImpureCall(
1003     FoldingContext &, const Expr<SomeType> &);
1004 std::optional<std::string> FindImpureCall(
1005     FoldingContext &, const ProcedureRef &);
1006 
1007 // Predicate: is a scalar expression suitable for naive scalar expansion
1008 // in the flattening of an array expression?
1009 // TODO: capture such scalar expansions in temporaries, flatten everything
1010 struct UnexpandabilityFindingVisitor
1011     : public AnyTraverse<UnexpandabilityFindingVisitor> {
1012   using Base = AnyTraverse<UnexpandabilityFindingVisitor>;
1013   using Base::operator();
UnexpandabilityFindingVisitorUnexpandabilityFindingVisitor1014   UnexpandabilityFindingVisitor() : Base{*this} {}
operatorUnexpandabilityFindingVisitor1015   template <typename T> bool operator()(const FunctionRef<T> &) { return true; }
operatorUnexpandabilityFindingVisitor1016   bool operator()(const CoarrayRef &) { return true; }
1017 };
1018 
IsExpandableScalar(const Expr<T> & expr)1019 template <typename T> bool IsExpandableScalar(const Expr<T> &expr) {
1020   return !UnexpandabilityFindingVisitor{}(expr);
1021 }
1022 
1023 // Common handling for procedure pointer compatibility of left- and right-hand
1024 // sides.  Returns nullopt if they're compatible.  Otherwise, it returns a
1025 // message that needs to be augmented by the names of the left and right sides
1026 std::optional<parser::MessageFixedText> CheckProcCompatibility(bool isCall,
1027     const std::optional<characteristics::Procedure> &lhsProcedure,
1028     const characteristics::Procedure *rhsProcedure,
1029     const SpecificIntrinsic *specificIntrinsic, std::string &whyNotCompatible);
1030 
1031 // Scalar constant expansion
1032 class ScalarConstantExpander {
1033 public:
ScalarConstantExpander(ConstantSubscripts && extents)1034   explicit ScalarConstantExpander(ConstantSubscripts &&extents)
1035       : extents_{std::move(extents)} {}
ScalarConstantExpander(ConstantSubscripts && extents,std::optional<ConstantSubscripts> && lbounds)1036   ScalarConstantExpander(
1037       ConstantSubscripts &&extents, std::optional<ConstantSubscripts> &&lbounds)
1038       : extents_{std::move(extents)}, lbounds_{std::move(lbounds)} {}
ScalarConstantExpander(ConstantSubscripts && extents,ConstantSubscripts && lbounds)1039   ScalarConstantExpander(
1040       ConstantSubscripts &&extents, ConstantSubscripts &&lbounds)
1041       : extents_{std::move(extents)}, lbounds_{std::move(lbounds)} {}
1042 
Expand(A && x)1043   template <typename A> A Expand(A &&x) const {
1044     return std::move(x); // default case
1045   }
Expand(Constant<T> && x)1046   template <typename T> Constant<T> Expand(Constant<T> &&x) {
1047     auto expanded{x.Reshape(std::move(extents_))};
1048     if (lbounds_) {
1049       expanded.set_lbounds(std::move(*lbounds_));
1050     }
1051     return expanded;
1052   }
Expand(Parentheses<T> && x)1053   template <typename T> Expr<T> Expand(Parentheses<T> &&x) {
1054     return Expand(std::move(x.left())); // Constant<> can be parenthesized
1055   }
Expand(Expr<T> && x)1056   template <typename T> Expr<T> Expand(Expr<T> &&x) {
1057     return common::visit(
1058         [&](auto &&x) { return Expr<T>{Expand(std::move(x))}; },
1059         std::move(x.u));
1060   }
1061 
1062 private:
1063   ConstantSubscripts extents_;
1064   std::optional<ConstantSubscripts> lbounds_;
1065 };
1066 
1067 // Given a collection of element values, package them as a Constant.
1068 // If the type is Character or a derived type, take the length or type
1069 // (resp.) from a another Constant.
1070 template <typename T>
PackageConstant(std::vector<Scalar<T>> && elements,const Constant<T> & reference,const ConstantSubscripts & shape)1071 Constant<T> PackageConstant(std::vector<Scalar<T>> &&elements,
1072     const Constant<T> &reference, const ConstantSubscripts &shape) {
1073   if constexpr (T::category == TypeCategory::Character) {
1074     return Constant<T>{
1075         reference.LEN(), std::move(elements), ConstantSubscripts{shape}};
1076   } else if constexpr (T::category == TypeCategory::Derived) {
1077     return Constant<T>{reference.GetType().GetDerivedTypeSpec(),
1078         std::move(elements), ConstantSubscripts{shape}};
1079   } else {
1080     return Constant<T>{std::move(elements), ConstantSubscripts{shape}};
1081   }
1082 }
1083 
1084 // Nonstandard conversions of constants (integer->logical, logical->integer)
1085 // that can appear in DATA statements as an extension.
1086 std::optional<Expr<SomeType>> DataConstantConversionExtension(
1087     FoldingContext &, const DynamicType &, const Expr<SomeType> &);
1088 
1089 // Convert Hollerith or short character to a another type as if the
1090 // Hollerith data had been BOZ.
1091 std::optional<Expr<SomeType>> HollerithToBOZ(
1092     FoldingContext &, const Expr<SomeType> &, const DynamicType &);
1093 
1094 } // namespace Fortran::evaluate
1095 
1096 namespace Fortran::semantics {
1097 
1098 class Scope;
1099 
1100 // If a symbol represents an ENTRY, return the symbol of the main entry
1101 // point to its subprogram.
1102 const Symbol *GetMainEntry(const Symbol *);
1103 
1104 // These functions are used in Evaluate so they are defined here rather than in
1105 // Semantics to avoid a link-time dependency on Semantics.
1106 // All of these apply GetUltimate() or ResolveAssociations() to their arguments.
1107 bool IsVariableName(const Symbol &);
1108 bool IsPureProcedure(const Symbol &);
1109 bool IsPureProcedure(const Scope &);
1110 bool IsElementalProcedure(const Symbol &);
1111 bool IsFunction(const Symbol &);
1112 bool IsFunction(const Scope &);
1113 bool IsProcedure(const Symbol &);
1114 bool IsProcedure(const Scope &);
1115 bool IsProcedurePointer(const Symbol &);
1116 bool IsAutomatic(const Symbol &);
1117 bool IsSaved(const Symbol &); // saved implicitly or explicitly
1118 bool IsDummy(const Symbol &);
1119 bool IsAssumedShape(const Symbol &);
1120 bool IsDeferredShape(const Symbol &);
1121 bool IsFunctionResult(const Symbol &);
1122 bool IsKindTypeParameter(const Symbol &);
1123 bool IsLenTypeParameter(const Symbol &);
1124 bool IsExtensibleType(const DerivedTypeSpec *);
1125 bool IsBuiltinDerivedType(const DerivedTypeSpec *derived, const char *name);
1126 // Is this derived type TEAM_TYPE from module ISO_FORTRAN_ENV?
1127 bool IsTeamType(const DerivedTypeSpec *);
1128 // Is this derived type TEAM_TYPE, C_PTR, or C_FUNPTR?
1129 bool IsBadCoarrayType(const DerivedTypeSpec *);
1130 // Is this derived type either C_PTR or C_FUNPTR from module ISO_C_BINDING
1131 bool IsIsoCType(const DerivedTypeSpec *);
1132 bool IsEventTypeOrLockType(const DerivedTypeSpec *);
1133 
1134 // ResolveAssociations() traverses use associations and host associations
1135 // like GetUltimate(), but also resolves through whole variable associations
1136 // with ASSOCIATE(x => y) and related constructs.  GetAssociationRoot()
1137 // applies ResolveAssociations() and then, in the case of resolution to
1138 // a construct association with part of a variable that does not involve a
1139 // vector subscript, returns the first symbol of that variable instead
1140 // of the construct entity.
1141 // (E.g., for ASSOCIATE(x => y%z), ResolveAssociations(x) returns x,
1142 // while GetAssociationRoot(x) returns y.)
1143 const Symbol &ResolveAssociations(const Symbol &);
1144 const Symbol &GetAssociationRoot(const Symbol &);
1145 
1146 const Symbol *FindCommonBlockContaining(const Symbol &);
1147 int CountLenParameters(const DerivedTypeSpec &);
1148 int CountNonConstantLenParameters(const DerivedTypeSpec &);
1149 
1150 // 15.5.2.4(4), type compatibility for dummy and actual arguments.
1151 // Also used for assignment compatibility checking
1152 bool AreTypeParamCompatible(
1153     const semantics::DerivedTypeSpec &, const semantics::DerivedTypeSpec &);
1154 
1155 const Symbol &GetUsedModule(const UseDetails &);
1156 const Symbol *FindFunctionResult(const Symbol &);
1157 
1158 // Type compatibility predicate: are x and y effectively the same type?
1159 // Uses DynamicType::IsTkCompatible(), which handles the case of distinct
1160 // but identical derived types.
1161 bool AreTkCompatibleTypes(const DeclTypeSpec *x, const DeclTypeSpec *y);
1162 
1163 } // namespace Fortran::semantics
1164 
1165 #endif // FORTRAN_EVALUATE_TOOLS_H_
1166