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