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