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