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// Predicate: does an expression contain a procedure reference?
1124bool HasProcedureRef(const Expr<SomeType> &expr);
1125
1126// Predicate: does an expression contain a VOLATILE or ASYNCHRONOUS symbol?
1127bool HasVolatileOrAsynchronousSymbol(const Expr<SomeType> &expr);
1128
1129// Can a scalar real RHS expression in an assignment be rewritten as a split
1130// sum expression tree?
1131bool CanBuildSplitSumExpressionTree(
1132 const Expr<SomeType> &lhs, const Expr<SomeType> &rhs);
1133
1134// Try to rewrite a scalar real sum as a split sum expression tree.
1135std::optional<Expr<SomeType>> TryBuildSplitSumExpressionTree(
1136 const Expr<SomeType> &expr);
1137
1138// Utilities for attaching the location of the declaration of a symbol
1139// of interest to a message. Handles the case of USE association gracefully.
1140parser::Message *AttachDeclaration(parser::Message &, const Symbol &);
1141parser::Message *AttachDeclaration(parser::Message *, const Symbol &);
1142template <typename MESSAGES, typename... A>
1143parser::Message *SayWithDeclaration(
1144 MESSAGES &messages, const Symbol &symbol, A &&...x) {
1145 return AttachDeclaration(messages.Say(std::forward<A>(x)...), symbol);
1146}
1147template <typename... A>
1148parser::Message *WarnWithDeclaration(FoldingContext context,
1149 const Symbol &symbol, common::LanguageFeature feature, A &&...x) {
1150 return AttachDeclaration(
1151 context.Warn(feature, std::forward<A>(x)...), symbol);
1152}
1153template <typename... A>
1154parser::Message *WarnWithDeclaration(FoldingContext &context,
1155 const Symbol &symbol, common::UsageWarning warning, A &&...x) {
1156 return AttachDeclaration(
1157 context.Warn(warning, std::forward<A>(x)...), symbol);
1158}
1159
1160// Check for references to impure procedures; returns the name
1161// of one to complain about, if any exist.
1162std::optional<std::string> FindImpureCall(
1163 FoldingContext &, const Expr<SomeType> &);
1164std::optional<std::string> FindImpureCall(
1165 FoldingContext &, const ProcedureRef &);
1166
1167// Predicate: does an expression contain anything that would prevent it from
1168// being duplicated so that two instances of it then appear in the same
1169// expression?
1170class UnsafeToCopyVisitor : public AnyTraverse<UnsafeToCopyVisitor> {
1171public:
1172 using Base = AnyTraverse<UnsafeToCopyVisitor>;
1173 using Base::operator();
1174 explicit UnsafeToCopyVisitor(bool admitPureCall)
1175 : Base{*this}, admitPureCall_{admitPureCall} {}
1176 template <typename T> bool operator()(const FunctionRef<T> &procRef) {
1177 return !admitPureCall_ || !procRef.proc().IsPure();
1178 }
1179 bool operator()(const CoarrayRef &) { return true; }
1180
1181private:
1182 bool admitPureCall_{false};
1183};
1184
1185template <typename A>
1186bool IsSafelyCopyable(const A &x, bool admitPureCall = false) {
1187 return !UnsafeToCopyVisitor{admitPureCall}(x);
1188}
1189
1190// Predicate: is a scalar expression suitable for naive scalar expansion
1191// in the flattening of an array expression?
1192// TODO: capture such scalar expansions in temporaries, flatten everything
1193template <typename T>
1194bool IsExpandableScalar(const Expr<T> &expr, FoldingContext &context,
1195 const Shape &shape, bool admitPureCall = false) {
1196 if (IsSafelyCopyable(expr, admitPureCall)) {
1197 return true;
1198 } else {
1199 auto extents{AsConstantExtents(context, shape)};
1200 return extents && !HasNegativeExtent(*extents) && GetSize(*extents) == 1;
1201 }
1202}
1203
1204// Common handling for procedure pointer compatibility of left- and right-hand
1205// sides. Returns nullopt if they're compatible. Otherwise, it returns a
1206// message that needs to be augmented by the names of the left and right sides.
1207std::optional<parser::MessageFixedText> CheckProcCompatibility(bool isCall,
1208 const std::optional<characteristics::Procedure> &lhsProcedure,
1209 const characteristics::Procedure *rhsProcedure,
1210 const SpecificIntrinsic *specificIntrinsic, std::string &whyNotCompatible,
1211 std::optional<std::string> &warning, bool ignoreImplicitVsExplicit);
1212
1213// Scalar constant expansion
1214class ScalarConstantExpander {
1215public:
1216 explicit ScalarConstantExpander(ConstantSubscripts &&extents)
1217 : extents_{std::move(extents)} {}
1218 ScalarConstantExpander(
1219 ConstantSubscripts &&extents, std::optional<ConstantSubscripts> &&lbounds)
1220 : extents_{std::move(extents)}, lbounds_{std::move(lbounds)} {}
1221 ScalarConstantExpander(
1222 ConstantSubscripts &&extents, ConstantSubscripts &&lbounds)
1223 : extents_{std::move(extents)}, lbounds_{std::move(lbounds)} {}
1224
1225 template <typename A> A Expand(A &&x) const {
1226 return std::move(x); // default case
1227 }
1228 template <typename T> Constant<T> Expand(Constant<T> &&x) {
1229 auto expanded{x.Reshape(std::move(extents_))};
1230 if (lbounds_) {
1231 expanded.set_lbounds(std::move(*lbounds_));
1232 }
1233 return expanded;
1234 }
1235 template <typename T> Expr<T> Expand(Parentheses<T> &&x) {
1236 return Expand(std::move(x.left())); // Constant<> can be parenthesized
1237 }
1238 template <typename T> Expr<T> Expand(Expr<T> &&x) {
1239 return common::visit(
1240 [&](auto &&x) { return Expr<T>{Expand(std::move(x))}; },
1241 std::move(x.u));
1242 }
1243
1244private:
1245 ConstantSubscripts extents_;
1246 std::optional<ConstantSubscripts> lbounds_;
1247};
1248
1249// Given a collection of element values, package them as a Constant.
1250// If the type is Character or a derived type, take the length or type
1251// (resp.) from a another Constant.
1252template <typename T>
1253Constant<T> PackageConstant(std::vector<Scalar<T>> &&elements,
1254 const Constant<T> &reference, const ConstantSubscripts &shape) {
1255 if constexpr (T::category == TypeCategory::Character) {
1256 return Constant<T>{
1257 reference.LEN(), std::move(elements), ConstantSubscripts{shape}};
1258 } else if constexpr (T::category == TypeCategory::Derived) {
1259 return Constant<T>{reference.GetType().GetDerivedTypeSpec(),
1260 std::move(elements), ConstantSubscripts{shape}};
1261 } else {
1262 return Constant<T>{std::move(elements), ConstantSubscripts{shape}};
1263 }
1264}
1265
1266// Nonstandard conversions of constants (integer->logical, logical->integer)
1267// that can appear in DATA statements as an extension.
1268std::optional<Expr<SomeType>> DataConstantConversionExtension(
1269 FoldingContext &, const DynamicType &, const Expr<SomeType> &);
1270
1271// Convert Hollerith or short character to a another type as if the
1272// Hollerith data had been BOZ.
1273std::optional<Expr<SomeType>> HollerithToBOZ(
1274 FoldingContext &, const Expr<SomeType> &, const DynamicType &);
1275
1276// Set explicit lower bounds on a constant array.
1277class ArrayConstantBoundChanger {
1278public:
1279 explicit ArrayConstantBoundChanger(ConstantSubscripts &&lbounds)
1280 : lbounds_{std::move(lbounds)} {}
1281
1282 template <typename A> A ChangeLbounds(A &&x) const {
1283 return std::move(x); // default case
1284 }
1285 template <typename T> Constant<T> ChangeLbounds(Constant<T> &&x) {
1286 x.set_lbounds(std::move(lbounds_));
1287 return std::move(x);
1288 }
1289 template <typename T> Expr<T> ChangeLbounds(Parentheses<T> &&x) {
1290 return ChangeLbounds(
1291 std::move(x.left())); // Constant<> can be parenthesized
1292 }
1293 template <typename T> Expr<T> ChangeLbounds(Expr<T> &&x) {
1294 return common::visit(
1295 [&](auto &&x) { return Expr<T>{ChangeLbounds(std::move(x))}; },
1296 std::move(x.u)); // recurse until we hit a constant
1297 }
1298
1299private:
1300 ConstantSubscripts &&lbounds_;
1301};
1302
1303// Predicate: should two expressions be considered identical for the purposes
1304// of determining whether two procedure interfaces are compatible, modulo
1305// naming of corresponding dummy arguments?
1306template <typename T>
1307std::optional<bool> AreEquivalentInInterface(const Expr<T> &, const Expr<T> &);
1308extern template std::optional<bool> AreEquivalentInInterface<SubscriptInteger>(
1310extern template std::optional<bool> AreEquivalentInInterface<SomeInteger>(
1311 const Expr<SomeInteger> &, const Expr<SomeInteger> &);
1312
1313bool CheckForCoindexedObject(parser::ContextualMessages &,
1314 const std::optional<ActualArgument> &, const std::string &procName,
1315 const std::string &argName);
1316
1317// Get the symbol vectors of the expression where symbols are grouped together
1318// if they are part of the same component expression.
1319//
1320// Example: a%b + c%d
1321// Will be grouped as: [(a, b), (c, d)]
1322std::vector<SymbolVector> GetSymbolVectors(const Expr<SomeType> &expr);
1323
1324bool IsCUDADeviceSymbol(const Symbol &sym);
1325bool IsCUDADeviceOnlySymbol(const Symbol &sym);
1326
1327inline bool IsCUDAManagedOrUnifiedSymbol(const Symbol &sym) {
1328 if (const auto *details =
1329 sym.GetUltimate().detailsIf<semantics::ObjectEntityDetails>()) {
1330 if (details->cudaDataAttr() &&
1331 (*details->cudaDataAttr() == common::CUDADataAttr::Managed ||
1332 *details->cudaDataAttr() == common::CUDADataAttr::Unified)) {
1333 return true;
1334 }
1335 }
1336 return false;
1337}
1338
1339inline bool IsCUDADataAttrSymbol(const Symbol &sym, common::CUDADataAttr attr) {
1340 if (const auto *details =
1341 sym.GetUltimate().detailsIf<semantics::ObjectEntityDetails>()) {
1342 return details->cudaDataAttr() && *details->cudaDataAttr() == attr;
1343 }
1344 return false;
1345}
1346
1347inline bool IsCUDAManagedSymbol(const Symbol &sym) {
1348 return IsCUDADataAttrSymbol(sym, common::CUDADataAttr::Managed);
1349}
1350
1351inline bool IsCUDAUnifiedSymbol(const Symbol &sym) {
1352 return IsCUDADataAttrSymbol(sym, common::CUDADataAttr::Unified);
1353}
1354
1355// Non-allocatable module-level managed/unified variables use pointer
1356// indirection through a companion global in __nv_managed_data__.
1357// Explicit data transfers (cudaMemcpy) must be avoided for these
1358// variables since they would target the shadow address rather than
1359// the actual unified memory address.
1360inline bool IsNonAllocatableModuleCUDAManagedSymbol(const Symbol &sym) {
1361 const Symbol &ultimate = sym.GetUltimate();
1362 if (!IsCUDAManagedOrUnifiedSymbol(ultimate))
1363 return false;
1364 if (ultimate.attrs().test(semantics::Attr::ALLOCATABLE))
1365 return false;
1366 return ultimate.owner().IsModule();
1367}
1368
1369template <typename A>
1370inline bool HasNonAllocatableModuleCUDAManagedSymbols(const A &expr) {
1371 for (const Symbol &sym : CollectCudaSymbols(expr))
1372 if (IsNonAllocatableModuleCUDAManagedSymbol(sym))
1373 return true;
1374 return false;
1375}
1376
1377// Get the number of distinct symbols with CUDA device
1378// attribute in the expression.
1379template <typename A> inline int GetNbOfCUDADeviceSymbols(const A &expr) {
1380 semantics::UnorderedSymbolSet symbols;
1381 for (const Symbol &sym : CollectCudaSymbols(expr)) {
1382 if (IsCUDADeviceSymbol(sym)) {
1383 symbols.insert(sym);
1384 }
1385 }
1386 return symbols.size();
1387}
1388
1389// Get the number of unique symbols with CUDA device attribute.
1390int GetNbOfUniqueCUDADeviceSymbols(const Expr<SomeType> &expr);
1391
1392// Get the number of distinct symbols with CUDA managed or unified
1393// attribute in the expression.
1394template <typename A>
1395inline int GetNbOfCUDAManagedOrUnifiedSymbols(const A &expr) {
1396 semantics::UnorderedSymbolSet symbols;
1397 for (const Symbol &sym : CollectCudaSymbols(expr)) {
1398 if (IsCUDAManagedOrUnifiedSymbol(sym)) {
1399 symbols.insert(sym);
1400 }
1401 }
1402 return symbols.size();
1403}
1404
1405// Get the number of distinct symbols with the CUDA managed attribute in the
1406// expression.
1407template <typename A> inline int GetNbOfCUDAManagedSymbols(const A &expr) {
1408 semantics::UnorderedSymbolSet symbols;
1409 for (const Symbol &sym : CollectCudaSymbols(expr)) {
1410 if (IsCUDAManagedSymbol(sym)) {
1411 symbols.insert(sym);
1412 }
1413 }
1414 return symbols.size();
1415}
1416
1417// Get the number of distinct symbols with a CUDA device attribute other than
1418// unified in the expression.
1419template <typename A> inline int GetNbOfCUDANonUnifiedSymbols(const A &expr) {
1420 semantics::UnorderedSymbolSet symbols;
1421 for (const Symbol &sym : CollectCudaSymbols(expr)) {
1422 if (IsCUDADeviceSymbol(sym) && !IsCUDAUnifiedSymbol(sym)) {
1423 symbols.insert(sym);
1424 }
1425 }
1426 return symbols.size();
1427}
1428
1429// Check if any of the symbols part of the expression has a CUDA device
1430// attribute.
1431template <typename A> inline bool HasCUDADeviceAttrs(const A &expr) {
1432 return GetNbOfCUDADeviceSymbols(expr) > 0;
1433}
1434
1435// Check if any of the symbols part of the expression has the CUDA managed
1436// attribute.
1437template <typename A> inline bool HasCUDAManagedSymbols(const A &expr) {
1438 return GetNbOfCUDAManagedSymbols(expr) > 0;
1439}
1440
1441// Check if any of the symbols part of the expression has a CUDA device
1442// attribute other than unified.
1443template <typename A> inline bool HasCUDANonUnifiedSymbols(const A &expr) {
1444 return GetNbOfCUDANonUnifiedSymbols(expr) > 0;
1445}
1446
1447// True for a whole reference to a managed array: a whole array variable, or a
1448// whole array component that itself has the managed attribute (a%b where b is
1449// managed). An array section, an array element, a component of a managed object
1450// and a computed value are all false.
1451template <typename A> inline bool IsWholeManagedArray(const A &expr) {
1452 const Symbol *sym{UnwrapWholeSymbolOrComponentDataRef(expr)};
1453 return expr.Rank() > 0 && sym && IsCUDAManagedSymbol(*sym);
1454}
1455
1456// CUDA Fortran Programming Guide 3.4.1 defines which assignments in host code
1457// are copies. A copy that reads or writes device, managed or constant data runs
1458// on stream zero, so it waits for previously launched kernels.
1459// - Device or constant data on one side and host data on the other is a copy,
1460// and so is device data on both sides.
1461// - A whole managed variable or array is copied when the other side is a
1462// constant, a host variable, a host array or a host array section.
1463// - A managed array section is assigned by host code when the other side is
1464// host or managed data.
1465// - A managed variable, array or array section is copied when the other side is
1466// device data, in both directions.
1467// One difference from the guide is that a managed array section is copied when
1468// the other side is a whole managed array, as the reference compiler does.
1469// Unified data is host memory that the device can also access, so it takes the
1470// place of host data in the rules above and an assignment between unified sides
1471// is host code.
1472// Return true if the assignment is one of the copies above.
1473template <typename A, typename B>
1474inline bool IsCUDADataTransfer(const A &lhs, const B &rhs) {
1475 // Unified data is left out of these counts and checks so that it is handled
1476 // as host data.
1477 bool lhsHasManaged{HasCUDAManagedSymbols(lhs)};
1478 bool lhsIsHost{!HasCUDANonUnifiedSymbols(lhs)};
1479 int rhsNbManagedSymbols{GetNbOfCUDAManagedSymbols(rhs)};
1480 int rhsNbSymbols{GetNbOfCUDANonUnifiedSymbols(rhs)};
1481
1482 if (HasNonAllocatableModuleCUDAManagedSymbols(lhs))
1483 return false;
1484
1485 // The host can read and write managed data in place, and copying one section
1486 // at a time in a loop is slow, so only whole arrays are copied.
1487 bool wholeLhs{IsWholeManagedArray(lhs)};
1488 bool wholeRhs{IsWholeManagedArray(rhs)};
1489
1490 if (wholeLhs && rhsNbSymbols == 0 && rhsNbManagedSymbols == 0 &&
1491 (IsVariable(rhs) || IsConstantExpr(rhs))) {
1492 return true; // Whole managed array copied from constant or host data.
1493 }
1494
1495 // The host cannot reach device or constant data, unlike managed and unified
1496 // data, so an assignment with such a side is a copy, sections included.
1497 bool lhsIsDeviceOnly{!lhsHasManaged && !lhsIsHost};
1498 // The right-hand side can be an expression, so one device operand is enough.
1499 bool rhsHasDeviceOnly{rhsNbSymbols > rhsNbManagedSymbols};
1500
1501 // Assignments done on the host, with no copy.
1502 // - A whole allocatable left-hand side with no device data. The assignment
1503 // may reallocate it, which is done on the host.
1504 // - A managed left-hand side with no whole managed array on either side. Only
1505 // sections and elements are involved, and the host reads and writes them in
1506 // place.
1507 // - A host left-hand side assigned from a managed section or element.
1508 // - An expression involving managed data. Evaluating it on the host avoids a
1509 // temporary.
1510 // - A managed left-hand side assigned from host data. Whole arrays are copied
1511 // by the early return above.
1512 if ((IsAllocatableDesignator(lhs) && !lhsIsDeviceOnly && !rhsHasDeviceOnly &&
1513 (lhsHasManaged || rhsNbManagedSymbols >= 1)) ||
1514 (lhsHasManaged && !rhsHasDeviceOnly && !(wholeLhs || wholeRhs)) ||
1515 (lhsIsHost && rhsNbManagedSymbols >= 1 && !rhsHasDeviceOnly &&
1516 !wholeRhs) ||
1517 (rhsNbManagedSymbols >= 1 && !IsVariable(rhs) && !lhsIsDeviceOnly) ||
1518 (lhsHasManaged && rhsNbSymbols == 0)) {
1519 return false;
1520 }
1521 return !lhsIsHost || rhsNbSymbols > 0;
1522}
1523
1526bool HasCUDAImplicitTransfer(const Expr<SomeType> &expr);
1527
1530
1531// Checks whether the symbol on the LHS is present in the RHS expression.
1532bool CheckForSymbolMatch(const Expr<SomeType> *lhs, const Expr<SomeType> *rhs);
1533
1534namespace operation {
1535
1536enum class Operator {
1537 Unknown,
1538 Add,
1539 And,
1540 Associated,
1541 Call,
1542 Constant,
1543 Convert,
1544 Conditional,
1545 Div,
1546 Eq,
1547 Eqv,
1548 False,
1549 Ge,
1550 Gt,
1551 Identity,
1552 Intrinsic,
1553 Le,
1554 Lt,
1555 Max,
1556 Min,
1557 Mul,
1558 Ne,
1559 Neqv,
1560 Not,
1561 Or,
1562 Pow,
1563 Resize, // Convert within the same TypeCategory
1564 Sub,
1565 True,
1566};
1567
1568using OperatorSet = common::EnumSet<Operator, 32>;
1569
1570std::string ToString(Operator op);
1571
1572template <int Kind> Operator OperationCode(const LogicalOperation<Kind> &op) {
1573 switch (op.logicalOperator) {
1574 case common::LogicalOperator::And:
1575 return Operator::And;
1576 case common::LogicalOperator::Or:
1577 return Operator::Or;
1578 case common::LogicalOperator::Eqv:
1579 return Operator::Eqv;
1580 case common::LogicalOperator::Neqv:
1581 return Operator::Neqv;
1582 case common::LogicalOperator::Not:
1583 return Operator::Not;
1584 }
1585 return Operator::Unknown;
1586}
1587
1588Operator OperationCode(const Relational<SomeType> &op);
1589
1590template <typename T> Operator OperationCode(const Relational<T> &op) {
1591 switch (op.opr) {
1592 case common::RelationalOperator::LT:
1593 return Operator::Lt;
1594 case common::RelationalOperator::LE:
1595 return Operator::Le;
1596 case common::RelationalOperator::EQ:
1597 return Operator::Eq;
1598 case common::RelationalOperator::NE:
1599 return Operator::Ne;
1600 case common::RelationalOperator::GE:
1601 return Operator::Ge;
1602 case common::RelationalOperator::GT:
1603 return Operator::Gt;
1604 }
1605 return Operator::Unknown;
1606}
1607
1608template <typename T> Operator OperationCode(const Add<T> &op) {
1609 return Operator::Add;
1610}
1611
1612template <typename T> Operator OperationCode(const Subtract<T> &op) {
1613 return Operator::Sub;
1614}
1615
1616template <typename T> Operator OperationCode(const Multiply<T> &op) {
1617 return Operator::Mul;
1618}
1619
1620template <typename T> Operator OperationCode(const Divide<T> &op) {
1621 return Operator::Div;
1622}
1623
1624template <typename T> Operator OperationCode(const Power<T> &op) {
1625 return Operator::Pow;
1626}
1627
1628template <typename T> Operator OperationCode(const RealToIntPower<T> &op) {
1629 return Operator::Pow;
1630}
1631
1632template <typename T, common::TypeCategory C>
1633Operator OperationCode(const Convert<T, C> &op) {
1634 if constexpr (C == T::category) {
1635 return Operator::Resize;
1636 } else {
1637 return Operator::Convert;
1638 }
1639}
1640
1641template <typename T> Operator OperationCode(const Extremum<T> &op) {
1642 if (op.ordering == Ordering::Greater) {
1643 return Operator::Max;
1644 } else {
1645 return Operator::Min;
1646 }
1647}
1648
1649template <typename T> Operator OperationCode(const Constant<T> &x) {
1650 return Operator::Constant;
1651}
1652
1653template <typename T> Operator OperationCode(const Designator<T> &x) {
1654 return Operator::Identity;
1655}
1656
1657template <typename T> Operator OperationCode(const T &) {
1658 return Operator::Unknown;
1659}
1660
1661Operator OperationCode(const ProcedureDesignator &proc);
1662
1663} // namespace operation
1664
1665// Return information about the top-level operation (ignoring parentheses):
1666// the operation code and the list of arguments.
1667std::pair<operation::Operator, std::vector<Expr<SomeType>>>
1668GetTopLevelOperation(const Expr<SomeType> &expr);
1669
1670// Return information about the top-level operation (ignoring parentheses, and
1671// resizing converts)
1672std::pair<operation::Operator, std::vector<Expr<SomeType>>>
1673GetTopLevelOperationIgnoreResizing(const Expr<SomeType> &expr);
1674
1675// Check if expr is same as x, or a sequence of Convert operations on x.
1676bool IsSameOrConvertOf(const Expr<SomeType> &expr, const Expr<SomeType> &x);
1677
1678// Check if the Variable appears as a subexpression of the expression.
1679bool IsVarSubexpressionOf(
1680 const Expr<SomeType> &var, const Expr<SomeType> &super);
1681
1682// Strip away any top-level Convert operations (if any exist) and return
1683// the input value. A ComplexConstructor(x, 0) is also considered as a
1684// convert operation.
1685// If the input is not Operation, Designator, FunctionRef or Constant,
1686// it returns std::nullopt.
1687std::optional<Expr<SomeType>> GetConvertInput(const Expr<SomeType> &x);
1688
1689// How many ancestors does have a derived type have?
1690std::optional<int> CountDerivedTypeAncestors(const semantics::Scope &);
1691
1692// For an expression of enumeration type, extract the value of the hidden
1693// __ordinal component. Returns std::nullopt if the expression is not a
1694// constant or structure constructor of an enumeration-type value.
1695std::optional<Expr<SomeType>> GetEnumerationOrdinal(Expr<SomeDerived> &);
1696
1697} // namespace Fortran::evaluate
1698
1699namespace Fortran::semantics {
1700
1701class Scope;
1702
1703// If a symbol represents an ENTRY, return the symbol of the main entry
1704// point to its subprogram.
1705const Symbol *GetMainEntry(const Symbol *);
1706
1707inline bool IsAlternateEntry(const Symbol *symbol) {
1708 // If symbol is not alternate entry symbol, GetMainEntry() returns the same
1709 // symbol.
1710 return symbol && GetMainEntry(symbol) != symbol;
1711}
1712
1713// These functions are used in Evaluate so they are defined here rather than in
1714// Semantics to avoid a link-time dependency on Semantics.
1715// All of these apply GetUltimate() or ResolveAssociations() to their arguments.
1716bool IsVariableName(const Symbol &);
1717bool IsPureProcedure(const Symbol &);
1718bool IsPureProcedure(const Scope &);
1719bool IsSimpleProcedure(const Symbol &);
1720bool IsSimpleProcedure(const Scope &);
1721bool IsExplicitlyImpureProcedure(const Symbol &);
1722bool IsElementalProcedure(const Symbol &);
1723bool IsFunction(const Symbol &);
1724bool IsFunction(const Scope &);
1725bool IsProcedure(const Symbol &);
1726bool IsProcedure(const Scope &);
1727bool IsProcedurePointer(const Symbol *);
1728bool IsProcedurePointer(const Symbol &);
1729bool IsObjectPointer(const Symbol *);
1730bool IsAllocatableOrObjectPointer(const Symbol *);
1731bool IsAutomatic(const Symbol &);
1732bool IsSaved(const Symbol &); // saved implicitly or explicitly
1733bool IsDummy(const Symbol &);
1734
1735bool IsAssumedRank(const Symbol &);
1736template <typename A> bool IsAssumedRank(const A &x) {
1737 auto *symbol{UnwrapWholeSymbolDataRef(x)};
1738 return symbol && IsAssumedRank(*symbol);
1739}
1740
1741bool IsAssumedShape(const Symbol &);
1742template <typename A> bool IsAssumedShape(const A &x) {
1743 auto *symbol{UnwrapWholeSymbolDataRef(x)};
1744 return symbol && IsAssumedShape(*symbol);
1745}
1746
1747bool IsDeferredShape(const Symbol &);
1748bool IsFunctionResult(const Symbol &);
1749bool IsKindTypeParameter(const Symbol &);
1750bool IsLenTypeParameter(const Symbol &);
1751bool IsExtensibleType(const DerivedTypeSpec *);
1752bool IsSequenceOrBindCType(const DerivedTypeSpec *);
1753bool IsBuiltinDerivedType(const DerivedTypeSpec *derived, const char *name);
1754bool IsBuiltinCPtr(const Symbol &);
1755bool IsFromBuiltinModule(const Symbol &);
1756bool IsEventType(const DerivedTypeSpec *);
1757bool IsLockType(const DerivedTypeSpec *);
1758bool IsNotifyType(const DerivedTypeSpec *);
1759// Is this derived type IEEE_FLAG_TYPE from module ISO_IEEE_EXCEPTIONS?
1760bool IsIeeeFlagType(const DerivedTypeSpec *);
1761// Is this derived type IEEE_ROUND_TYPE from module ISO_IEEE_ARITHMETIC?
1762bool IsIeeeRoundType(const DerivedTypeSpec *);
1763// Is this derived type TEAM_TYPE from module ISO_FORTRAN_ENV?
1764bool IsTeamType(const DerivedTypeSpec *);
1765// Is this derived type TEAM_TYPE, C_PTR, or C_FUNPTR?
1766bool IsBadCoarrayType(const DerivedTypeSpec *);
1767// Is this derived type either C_PTR or C_FUNPTR from module ISO_C_BINDING
1768bool IsIsoCType(const DerivedTypeSpec *);
1769bool IsEventTypeOrLockType(const DerivedTypeSpec *);
1770inline bool IsAssumedSizeArray(const Symbol &symbol) {
1771 if (const auto *object{symbol.detailsIf<ObjectEntityDetails>()}) {
1772 return (object->isDummy() || symbol.test(Symbol::Flag::CrayPointee)) &&
1773 object->shape().CanBeAssumedSize();
1774 } else if (const auto *assoc{symbol.detailsIf<AssocEntityDetails>()}) {
1775 return assoc->IsAssumedSize();
1776 } else {
1777 return false;
1778 }
1779}
1780
1781// ResolveAssociations() traverses use associations and host associations
1782// like GetUltimate(), but also resolves through whole variable associations
1783// with ASSOCIATE(x => y) and related constructs. GetAssociationRoot()
1784// applies ResolveAssociations() and then, in the case of resolution to
1785// a construct association with part of a variable that does not involve a
1786// vector subscript, returns the first symbol of that variable instead
1787// of the construct entity.
1788// (E.g., for ASSOCIATE(x => y%z), ResolveAssociations(x) returns x,
1789// while GetAssociationRoot(x) returns y.)
1790// In a SELECT RANK construct, ResolveAssociations() stops at a
1791// RANK(n) or RANK(*) case symbol, but traverses the selector for
1792// RANK DEFAULT.
1793const Symbol &ResolveAssociations(const Symbol &, bool stopAtTypeGuard = false);
1794const Symbol &GetAssociationRoot(const Symbol &, bool stopAtTypeGuard = false);
1795
1796const Symbol *FindCommonBlockContaining(const Symbol &);
1797int CountLenParameters(const DerivedTypeSpec &);
1798int CountNonConstantLenParameters(const DerivedTypeSpec &);
1799
1800const Symbol &GetUsedModule(const UseDetails &);
1801const Symbol *FindFunctionResult(const Symbol &);
1802
1803// Type compatibility predicate: are x and y effectively the same type?
1804// Uses DynamicType::IsTkCompatible(), which handles the case of distinct
1805// but identical derived types.
1806bool AreTkCompatibleTypes(const DeclTypeSpec *x, const DeclTypeSpec *y);
1807
1808common::IgnoreTKRSet GetIgnoreTKR(const Symbol &);
1809
1810std::optional<int> GetDummyArgumentNumber(const Symbol *);
1811
1812const Symbol *FindAncestorModuleProcedure(const Symbol *symInSubmodule);
1813
1814// Given a Cray pointee symbol, returns the related Cray pointer symbol.
1815const Symbol &GetCrayPointer(const Symbol &crayPointee);
1816
1817} // namespace Fortran::semantics
1818
1819#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:697
Definition static-data.h:29
Definition variable.h:304
Definition type.h:56
Definition message.h:397
Definition scope.h:68
Definition symbol.h:896
Definition symbol.h:707
Definition call.h:34
bool HasCUDAImplicitTransfer(const Expr< SomeType > &expr)
Definition tools.cpp:1279
bool HasOnlyCUDAConstntImplicitTransfer(const Expr< SomeType > &expr)
Check if the expression is a mix of host and constant variables.
Definition tools.cpp:1285
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:399
Definition expression.h:302
Definition characteristics.h:367