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