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