1 //===-- include/flang/Evaluate/tools.h --------------------------*- C++ -*-===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8
9 #ifndef FORTRAN_EVALUATE_TOOLS_H_
10 #define FORTRAN_EVALUATE_TOOLS_H_
11
12 #include "traverse.h"
13 #include "flang/Common/idioms.h"
14 #include "flang/Common/template.h"
15 #include "flang/Common/unwrap.h"
16 #include "flang/Evaluate/constant.h"
17 #include "flang/Evaluate/expression.h"
18 #include "flang/Parser/message.h"
19 #include "flang/Semantics/attr.h"
20 #include "flang/Semantics/symbol.h"
21 #include <array>
22 #include <optional>
23 #include <set>
24 #include <type_traits>
25 #include <utility>
26
27 namespace Fortran::evaluate {
28
29 // Some expression predicates and extractors.
30
31 // Predicate: true when an expression is a variable reference, not an
32 // operation. Be advised: a call to a function that returns an object
33 // pointer is a "variable" in Fortran (it can be the left-hand side of
34 // an assignment).
35 struct IsVariableHelper
36 : public AnyTraverse<IsVariableHelper, std::optional<bool>> {
37 using Result = std::optional<bool>; // effectively tri-state
38 using Base = AnyTraverse<IsVariableHelper, Result>;
IsVariableHelperIsVariableHelper39 IsVariableHelper() : Base{*this} {}
40 using Base::operator();
operatorIsVariableHelper41 Result operator()(const StaticDataObject &) const { return false; }
42 Result operator()(const Symbol &) const;
43 Result operator()(const Component &) const;
44 Result operator()(const ArrayRef &) const;
45 Result operator()(const Substring &) const;
operatorIsVariableHelper46 Result operator()(const CoarrayRef &) const { return true; }
operatorIsVariableHelper47 Result operator()(const ComplexPart &) const { return true; }
48 Result operator()(const ProcedureDesignator &) const;
operatorIsVariableHelper49 template <typename T> Result operator()(const Expr<T> &x) const {
50 if constexpr (common::HasMember<T, AllIntrinsicTypes> ||
51 std::is_same_v<T, SomeDerived>) {
52 // Expression with a specific type
53 if (std::holds_alternative<Designator<T>>(x.u) ||
54 std::holds_alternative<FunctionRef<T>>(x.u)) {
55 if (auto known{(*this)(x.u)}) {
56 return known;
57 }
58 }
59 return false;
60 } else {
61 return (*this)(x.u);
62 }
63 }
64 };
65
IsVariable(const A & x)66 template <typename A> bool IsVariable(const A &x) {
67 if (auto known{IsVariableHelper{}(x)}) {
68 return *known;
69 } else {
70 return false;
71 }
72 }
73
74 // Predicate: true when an expression is assumed-rank
75 bool IsAssumedRank(const Symbol &);
76 bool IsAssumedRank(const ActualArgument &);
IsAssumedRank(const A &)77 template <typename A> bool IsAssumedRank(const A &) { return false; }
IsAssumedRank(const Designator<A> & designator)78 template <typename A> bool IsAssumedRank(const Designator<A> &designator) {
79 if (const auto *symbol{std::get_if<SymbolRef>(&designator.u)}) {
80 return IsAssumedRank(symbol->get());
81 } else {
82 return false;
83 }
84 }
IsAssumedRank(const Expr<T> & expr)85 template <typename T> bool IsAssumedRank(const Expr<T> &expr) {
86 return common::visit([](const auto &x) { return IsAssumedRank(x); }, expr.u);
87 }
IsAssumedRank(const std::optional<A> & x)88 template <typename A> bool IsAssumedRank(const std::optional<A> &x) {
89 return x && IsAssumedRank(*x);
90 }
91
92 // Predicate: true when an expression is a coarray (corank > 0)
93 bool IsCoarray(const ActualArgument &);
94 bool IsCoarray(const Symbol &);
IsCoarray(const A &)95 template <typename A> bool IsCoarray(const A &) { return false; }
IsCoarray(const Designator<A> & designator)96 template <typename A> bool IsCoarray(const Designator<A> &designator) {
97 if (const auto *symbol{std::get_if<SymbolRef>(&designator.u)}) {
98 return IsCoarray(**symbol);
99 }
100 return false;
101 }
IsCoarray(const Expr<T> & expr)102 template <typename T> bool IsCoarray(const Expr<T> &expr) {
103 return common::visit([](const auto &x) { return IsCoarray(x); }, expr.u);
104 }
IsCoarray(const std::optional<A> & x)105 template <typename A> bool IsCoarray(const std::optional<A> &x) {
106 return x && IsCoarray(*x);
107 }
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
AsExpr(A && x)112 template <typename A> common::IfNoLvalue<Expr<ResultType<A>>, A> AsExpr(A &&x) {
113 return Expr<ResultType<A>>{std::move(x)};
114 }
115
AsExpr(Expr<T> && x)116 template <typename T> Expr<T> AsExpr(Expr<T> &&x) {
117 static_assert(IsSpecificIntrinsicType<T>);
118 return std::move(x);
119 }
120
121 template <TypeCategory CATEGORY>
AsCategoryExpr(Expr<SomeKind<CATEGORY>> && x)122 Expr<SomeKind<CATEGORY>> AsCategoryExpr(Expr<SomeKind<CATEGORY>> &&x) {
123 return std::move(x);
124 }
125
126 template <typename A>
AsGenericExpr(A && x)127 common::IfNoLvalue<Expr<SomeType>, A> AsGenericExpr(A &&x) {
128 if constexpr (common::HasMember<A, TypelessExpression>) {
129 return Expr<SomeType>{std::move(x)};
130 } else {
131 return Expr<SomeType>{AsCategoryExpr(std::move(x))};
132 }
133 }
134
AsGenericExpr(Expr<SomeType> && x)135 inline Expr<SomeType> AsGenericExpr(Expr<SomeType> &&x) { return std::move(x); }
136
137 // These overloads wrap DataRefs and simple whole variables up into
138 // generic expressions if they have a known type.
139 std::optional<Expr<SomeType>> AsGenericExpr(DataRef &&);
140 std::optional<Expr<SomeType>> AsGenericExpr(const Symbol &);
141
142 template <typename A>
AsCategoryExpr(A && x)143 common::IfNoLvalue<Expr<SomeKind<ResultType<A>::category>>, A> AsCategoryExpr(
144 A &&x) {
145 return Expr<SomeKind<ResultType<A>::category>>{AsExpr(std::move(x))};
146 }
147
148 Expr<SomeType> Parenthesize(Expr<SomeType> &&);
149
150 Expr<SomeReal> GetComplexPart(
151 const Expr<SomeComplex> &, bool isImaginary = false);
152 Expr<SomeReal> GetComplexPart(Expr<SomeComplex> &&, bool isImaginary = false);
153
154 template <int KIND>
MakeComplex(Expr<Type<TypeCategory::Real,KIND>> && re,Expr<Type<TypeCategory::Real,KIND>> && im)155 Expr<SomeComplex> MakeComplex(Expr<Type<TypeCategory::Real, KIND>> &&re,
156 Expr<Type<TypeCategory::Real, KIND>> &&im) {
157 return AsCategoryExpr(ComplexConstructor<KIND>{std::move(re), std::move(im)});
158 }
159
IsNumericCategoryExpr()160 template <typename A> constexpr bool IsNumericCategoryExpr() {
161 if constexpr (common::HasMember<A, TypelessExpression>) {
162 return false;
163 } else {
164 return common::HasMember<ResultType<A>, NumericCategoryTypes>;
165 }
166 }
167
168 // Specializing extractor. If an Expr wraps some type of object, perhaps
169 // in several layers, return a pointer to it; otherwise null. Also works
170 // with expressions contained in ActualArgument.
171 template <typename A, typename B>
172 auto UnwrapExpr(B &x) -> common::Constify<A, B> * {
173 using Ty = std::decay_t<B>;
174 if constexpr (std::is_same_v<A, Ty>) {
175 return &x;
176 } else if constexpr (std::is_same_v<Ty, ActualArgument>) {
177 if (auto *expr{x.UnwrapExpr()}) {
178 return UnwrapExpr<A>(*expr);
179 }
180 } else if constexpr (std::is_same_v<Ty, Expr<SomeType>>) {
181 return common::visit([](auto &x) { return UnwrapExpr<A>(x); }, x.u);
182 } else if constexpr (!common::HasMember<A, TypelessExpression>) {
183 if constexpr (std::is_same_v<Ty, Expr<ResultType<A>>> ||
184 std::is_same_v<Ty, Expr<SomeKind<ResultType<A>::category>>>) {
185 return common::visit([](auto &x) { return UnwrapExpr<A>(x); }, x.u);
186 }
187 }
188 return nullptr;
189 }
190
191 template <typename A, typename B>
UnwrapExpr(const std::optional<B> & x)192 const A *UnwrapExpr(const std::optional<B> &x) {
193 if (x) {
194 return UnwrapExpr<A>(*x);
195 } else {
196 return nullptr;
197 }
198 }
199
UnwrapExpr(std::optional<B> & x)200 template <typename A, typename B> A *UnwrapExpr(std::optional<B> &x) {
201 if (x) {
202 return UnwrapExpr<A>(*x);
203 } else {
204 return nullptr;
205 }
206 }
207
208 // A variant of UnwrapExpr above that also skips through (parentheses)
209 // and conversions of kinds within a category. Useful for extracting LEN
210 // type parameter inquiries, at least.
211 template <typename A, typename B>
212 auto UnwrapConvertedExpr(B &x) -> common::Constify<A, B> * {
213 using Ty = std::decay_t<B>;
214 if constexpr (std::is_same_v<A, Ty>) {
215 return &x;
216 } else if constexpr (std::is_same_v<Ty, ActualArgument>) {
217 if (auto *expr{x.UnwrapExpr()}) {
218 return UnwrapConvertedExpr<A>(*expr);
219 }
220 } else if constexpr (std::is_same_v<Ty, Expr<SomeType>>) {
221 return common::visit(
222 [](auto &x) { return UnwrapConvertedExpr<A>(x); }, x.u);
223 } else {
224 using DesiredResult = ResultType<A>;
225 if constexpr (std::is_same_v<Ty, Expr<DesiredResult>> ||
226 std::is_same_v<Ty, Expr<SomeKind<DesiredResult::category>>>) {
227 return common::visit(
228 [](auto &x) { return UnwrapConvertedExpr<A>(x); }, x.u);
229 } else {
230 using ThisResult = ResultType<B>;
231 if constexpr (std::is_same_v<Ty, Expr<ThisResult>>) {
232 return common::visit(
233 [](auto &x) { return UnwrapConvertedExpr<A>(x); }, x.u);
234 } else if constexpr (std::is_same_v<Ty, Parentheses<ThisResult>> ||
235 std::is_same_v<Ty, Convert<ThisResult, DesiredResult::category>>) {
236 return common::visit(
237 [](auto &x) { return UnwrapConvertedExpr<A>(x); }, x.left().u);
238 }
239 }
240 }
241 return nullptr;
242 }
243
244 // When an expression is a "bare" LEN= derived type parameter inquiry,
245 // possibly wrapped in integer kind conversions &/or parentheses, return
246 // a pointer to the Symbol with TypeParamDetails.
ExtractBareLenParameter(const A & expr)247 template <typename A> const Symbol *ExtractBareLenParameter(const A &expr) {
248 if (const auto *typeParam{
249 UnwrapConvertedExpr<evaluate::TypeParamInquiry>(expr)}) {
250 if (!typeParam->base()) {
251 const Symbol &symbol{typeParam->parameter()};
252 if (const auto *tpd{symbol.detailsIf<semantics::TypeParamDetails>()}) {
253 if (tpd->attr() == common::TypeParamAttr::Len) {
254 return &symbol;
255 }
256 }
257 }
258 }
259 return nullptr;
260 }
261
262 // If an expression simply wraps a DataRef, extract and return it.
263 // The Boolean argument controls the handling of Substring and ComplexPart
264 // references: when true (not default), it extracts the base DataRef
265 // of a substring or complex part, if it has one.
266 template <typename A>
ExtractDataRef(const A &,bool intoSubstring,bool intoComplexPart)267 common::IfNoLvalue<std::optional<DataRef>, A> ExtractDataRef(
268 const A &, bool intoSubstring, bool intoComplexPart) {
269 return std::nullopt; // default base case
270 }
271 template <typename T>
272 std::optional<DataRef> ExtractDataRef(const Designator<T> &d,
273 bool intoSubstring = false, bool intoComplexPart = false) {
274 return common::visit(
275 [=](const auto &x) -> std::optional<DataRef> {
276 if constexpr (common::HasMember<decltype(x), decltype(DataRef::u)>) {
277 return DataRef{x};
278 }
279 if constexpr (std::is_same_v<std::decay_t<decltype(x)>, Substring>) {
280 if (intoSubstring) {
281 return ExtractSubstringBase(x);
282 }
283 }
284 if constexpr (std::is_same_v<std::decay_t<decltype(x)>, ComplexPart>) {
285 if (intoComplexPart) {
286 return x.complex();
287 }
288 }
289 return std::nullopt; // w/o "else" to dodge bogus g++ 8.1 warning
290 },
291 d.u);
292 }
293 template <typename T>
294 std::optional<DataRef> ExtractDataRef(const Expr<T> &expr,
295 bool intoSubstring = false, bool intoComplexPart = false) {
296 return common::visit(
297 [=](const auto &x) {
298 return ExtractDataRef(x, intoSubstring, intoComplexPart);
299 },
300 expr.u);
301 }
302 template <typename A>
303 std::optional<DataRef> ExtractDataRef(const std::optional<A> &x,
304 bool intoSubstring = false, bool intoComplexPart = false) {
305 if (x) {
306 return ExtractDataRef(*x, intoSubstring, intoComplexPart);
307 } else {
308 return std::nullopt;
309 }
310 }
311 template <typename A>
312 std::optional<DataRef> ExtractDataRef(
313 const A *p, bool intoSubstring = false, bool intoComplexPart = false) {
314 if (p) {
315 return ExtractDataRef(*p, intoSubstring, intoComplexPart);
316 } else {
317 return std::nullopt;
318 }
319 }
320 std::optional<DataRef> ExtractDataRef(
321 const ActualArgument &, bool intoSubstring = false);
322
323 std::optional<DataRef> ExtractSubstringBase(const Substring &);
324
325 // Predicate: is an expression is an array element reference?
326 template <typename T>
327 bool IsArrayElement(const Expr<T> &expr, bool intoSubstring = true,
328 bool skipComponents = false) {
329 if (auto dataRef{ExtractDataRef(expr, intoSubstring)}) {
330 const DataRef *ref{&*dataRef};
331 if (skipComponents) {
332 while (const Component * component{std::get_if<Component>(&ref->u)}) {
333 ref = &component->base();
334 }
335 }
336 if (const auto *coarrayRef{std::get_if<CoarrayRef>(&ref->u)}) {
337 return !coarrayRef->subscript().empty();
338 } else {
339 return std::holds_alternative<ArrayRef>(ref->u);
340 }
341 } else {
342 return false;
343 }
344 }
345
346 template <typename A>
ExtractNamedEntity(const A & x)347 std::optional<NamedEntity> ExtractNamedEntity(const A &x) {
348 if (auto dataRef{ExtractDataRef(x)}) {
349 return common::visit(
350 common::visitors{
351 [](SymbolRef &&symbol) -> std::optional<NamedEntity> {
352 return NamedEntity{symbol};
353 },
354 [](Component &&component) -> std::optional<NamedEntity> {
355 return NamedEntity{std::move(component)};
356 },
357 [](CoarrayRef &&co) -> std::optional<NamedEntity> {
358 return co.GetBase();
359 },
360 [](auto &&) { return std::optional<NamedEntity>{}; },
361 },
362 std::move(dataRef->u));
363 } else {
364 return std::nullopt;
365 }
366 }
367
368 struct ExtractCoindexedObjectHelper {
operatorExtractCoindexedObjectHelper369 template <typename A> std::optional<CoarrayRef> operator()(const A &) const {
370 return std::nullopt;
371 }
operatorExtractCoindexedObjectHelper372 std::optional<CoarrayRef> operator()(const CoarrayRef &x) const { return x; }
373 template <typename A>
operatorExtractCoindexedObjectHelper374 std::optional<CoarrayRef> operator()(const Expr<A> &expr) const {
375 return common::visit(*this, expr.u);
376 }
operatorExtractCoindexedObjectHelper377 std::optional<CoarrayRef> operator()(const DataRef &dataRef) const {
378 return common::visit(*this, dataRef.u);
379 }
operatorExtractCoindexedObjectHelper380 std::optional<CoarrayRef> operator()(const NamedEntity &named) const {
381 if (const Component * component{named.UnwrapComponent()}) {
382 return (*this)(*component);
383 } else {
384 return std::nullopt;
385 }
386 }
operatorExtractCoindexedObjectHelper387 std::optional<CoarrayRef> operator()(const ProcedureDesignator &des) const {
388 if (const auto *component{
389 std::get_if<common::CopyableIndirection<Component>>(&des.u)}) {
390 return (*this)(component->value());
391 } else {
392 return std::nullopt;
393 }
394 }
operatorExtractCoindexedObjectHelper395 std::optional<CoarrayRef> operator()(const Component &component) const {
396 return (*this)(component.base());
397 }
operatorExtractCoindexedObjectHelper398 std::optional<CoarrayRef> operator()(const ArrayRef &arrayRef) const {
399 return (*this)(arrayRef.base());
400 }
401 };
402
ExtractCoarrayRef(const A & x)403 template <typename A> std::optional<CoarrayRef> ExtractCoarrayRef(const A &x) {
404 if (auto dataRef{ExtractDataRef(x, true)}) {
405 return ExtractCoindexedObjectHelper{}(*dataRef);
406 } else {
407 return ExtractCoindexedObjectHelper{}(x);
408 }
409 }
410
411 // If an expression is simply a whole symbol data designator,
412 // extract and return that symbol, else null.
UnwrapWholeSymbolDataRef(const A & x)413 template <typename A> const Symbol *UnwrapWholeSymbolDataRef(const A &x) {
414 if (auto dataRef{ExtractDataRef(x)}) {
415 if (const SymbolRef * p{std::get_if<SymbolRef>(&dataRef->u)}) {
416 return &p->get();
417 }
418 }
419 return nullptr;
420 }
421
422 // If an expression is a whole symbol or a whole component desginator,
423 // extract and return that symbol, else null.
424 template <typename A>
UnwrapWholeSymbolOrComponentDataRef(const A & x)425 const Symbol *UnwrapWholeSymbolOrComponentDataRef(const A &x) {
426 if (auto dataRef{ExtractDataRef(x)}) {
427 if (const SymbolRef * p{std::get_if<SymbolRef>(&dataRef->u)}) {
428 return &p->get();
429 } else if (const Component * c{std::get_if<Component>(&dataRef->u)}) {
430 if (c->base().Rank() == 0) {
431 return &c->GetLastSymbol();
432 }
433 }
434 }
435 return nullptr;
436 }
437
438 // If an expression is a whole symbol or a whole component designator,
439 // potentially followed by an image selector, extract and return that symbol,
440 // else null.
441 template <typename A>
UnwrapWholeSymbolOrComponentOrCoarrayRef(const A & x)442 const Symbol *UnwrapWholeSymbolOrComponentOrCoarrayRef(const A &x) {
443 if (auto dataRef{ExtractDataRef(x)}) {
444 if (const SymbolRef * p{std::get_if<SymbolRef>(&dataRef->u)}) {
445 return &p->get();
446 } else if (const Component * c{std::get_if<Component>(&dataRef->u)}) {
447 if (c->base().Rank() == 0) {
448 return &c->GetLastSymbol();
449 }
450 } else if (const CoarrayRef * c{std::get_if<CoarrayRef>(&dataRef->u)}) {
451 if (c->subscript().empty()) {
452 return &c->GetLastSymbol();
453 }
454 }
455 }
456 return nullptr;
457 }
458
459 // GetFirstSymbol(A%B%C[I]%D) -> A
GetFirstSymbol(const A & x)460 template <typename A> const Symbol *GetFirstSymbol(const A &x) {
461 if (auto dataRef{ExtractDataRef(x, true)}) {
462 return &dataRef->GetFirstSymbol();
463 } else {
464 return nullptr;
465 }
466 }
467
468 // GetLastPointerSymbol(A%PTR1%B%PTR2%C) -> PTR2
469 const Symbol *GetLastPointerSymbol(const evaluate::DataRef &);
470
471 // Creation of conversion expressions can be done to either a known
472 // specific intrinsic type with ConvertToType<T>(x) or by converting
473 // one arbitrary expression to the type of another with ConvertTo(to, from).
474
475 template <typename TO, TypeCategory FROMCAT>
ConvertToType(Expr<SomeKind<FROMCAT>> && x)476 Expr<TO> ConvertToType(Expr<SomeKind<FROMCAT>> &&x) {
477 static_assert(IsSpecificIntrinsicType<TO>);
478 if constexpr (FROMCAT == TO::category) {
479 if (auto *already{std::get_if<Expr<TO>>(&x.u)}) {
480 return std::move(*already);
481 } else {
482 return Expr<TO>{Convert<TO, FROMCAT>{std::move(x)}};
483 }
484 } else if constexpr (TO::category == TypeCategory::Complex) {
485 using Part = typename TO::Part;
486 Scalar<Part> zero;
487 return Expr<TO>{ComplexConstructor<TO::kind>{
488 ConvertToType<Part>(std::move(x)), Expr<Part>{Constant<Part>{zero}}}};
489 } else if constexpr (FROMCAT == TypeCategory::Complex) {
490 // Extract and convert the real component of a complex value
491 return common::visit(
492 [&](auto &&z) {
493 using ZType = ResultType<decltype(z)>;
494 using Part = typename ZType::Part;
495 return ConvertToType<TO, TypeCategory::Real>(Expr<SomeReal>{
496 Expr<Part>{ComplexComponent<Part::kind>{false, std::move(z)}}});
497 },
498 std::move(x.u));
499 } else {
500 return Expr<TO>{Convert<TO, FROMCAT>{std::move(x)}};
501 }
502 }
503
504 template <typename TO, TypeCategory FROMCAT, int FROMKIND>
ConvertToType(Expr<Type<FROMCAT,FROMKIND>> && x)505 Expr<TO> ConvertToType(Expr<Type<FROMCAT, FROMKIND>> &&x) {
506 return ConvertToType<TO, FROMCAT>(Expr<SomeKind<FROMCAT>>{std::move(x)});
507 }
508
ConvertToType(BOZLiteralConstant && x)509 template <typename TO> Expr<TO> ConvertToType(BOZLiteralConstant &&x) {
510 static_assert(IsSpecificIntrinsicType<TO>);
511 if constexpr (TO::category == TypeCategory::Integer) {
512 return Expr<TO>{
513 Constant<TO>{Scalar<TO>::ConvertUnsigned(std::move(x)).value}};
514 } else {
515 static_assert(TO::category == TypeCategory::Real);
516 using Word = typename Scalar<TO>::Word;
517 return Expr<TO>{
518 Constant<TO>{Scalar<TO>{Word::ConvertUnsigned(std::move(x)).value}}};
519 }
520 }
521
IsBOZLiteral(const Expr<T> & expr)522 template <typename T> bool IsBOZLiteral(const Expr<T> &expr) {
523 return std::holds_alternative<BOZLiteralConstant>(expr.u);
524 }
525
526 // Conversions to dynamic types
527 std::optional<Expr<SomeType>> ConvertToType(
528 const DynamicType &, Expr<SomeType> &&);
529 std::optional<Expr<SomeType>> ConvertToType(
530 const DynamicType &, std::optional<Expr<SomeType>> &&);
531 std::optional<Expr<SomeType>> ConvertToType(const Symbol &, Expr<SomeType> &&);
532 std::optional<Expr<SomeType>> ConvertToType(
533 const Symbol &, std::optional<Expr<SomeType>> &&);
534
535 // Conversions to the type of another expression
536 template <TypeCategory TC, int TK, typename FROM>
ConvertTo(const Expr<Type<TC,TK>> &,FROM && x)537 common::IfNoLvalue<Expr<Type<TC, TK>>, FROM> ConvertTo(
538 const Expr<Type<TC, TK>> &, FROM &&x) {
539 return ConvertToType<Type<TC, TK>>(std::move(x));
540 }
541
542 template <TypeCategory TC, typename FROM>
ConvertTo(const Expr<SomeKind<TC>> & to,FROM && from)543 common::IfNoLvalue<Expr<SomeKind<TC>>, FROM> ConvertTo(
544 const Expr<SomeKind<TC>> &to, FROM &&from) {
545 return common::visit(
546 [&](const auto &toKindExpr) {
547 using KindExpr = std::decay_t<decltype(toKindExpr)>;
548 return AsCategoryExpr(
549 ConvertToType<ResultType<KindExpr>>(std::move(from)));
550 },
551 to.u);
552 }
553
554 template <typename FROM>
ConvertTo(const Expr<SomeType> & to,FROM && from)555 common::IfNoLvalue<Expr<SomeType>, FROM> ConvertTo(
556 const Expr<SomeType> &to, FROM &&from) {
557 return common::visit(
558 [&](const auto &toCatExpr) {
559 return AsGenericExpr(ConvertTo(toCatExpr, std::move(from)));
560 },
561 to.u);
562 }
563
564 // Convert an expression of some known category to a dynamically chosen
565 // kind of some category (usually but not necessarily distinct).
566 template <TypeCategory TOCAT, typename VALUE> struct ConvertToKindHelper {
567 using Result = std::optional<Expr<SomeKind<TOCAT>>>;
568 using Types = CategoryTypes<TOCAT>;
ConvertToKindHelperConvertToKindHelper569 ConvertToKindHelper(int k, VALUE &&x) : kind{k}, value{std::move(x)} {}
TestConvertToKindHelper570 template <typename T> Result Test() {
571 if (kind == T::kind) {
572 return std::make_optional(
573 AsCategoryExpr(ConvertToType<T>(std::move(value))));
574 }
575 return std::nullopt;
576 }
577 int kind;
578 VALUE value;
579 };
580
581 template <TypeCategory TOCAT, typename VALUE>
ConvertToKind(int kind,VALUE && x)582 common::IfNoLvalue<Expr<SomeKind<TOCAT>>, VALUE> ConvertToKind(
583 int kind, VALUE &&x) {
584 return common::SearchTypes(
585 ConvertToKindHelper<TOCAT, VALUE>{kind, std::move(x)})
586 .value();
587 }
588
589 // Given a type category CAT, SameKindExprs<CAT, N> is a variant that
590 // holds an arrays of expressions of the same supported kind in that
591 // category.
592 template <typename A, int N = 2> using SameExprs = std::array<Expr<A>, N>;
593 template <int N = 2> struct SameKindExprsHelper {
594 template <typename A> using SameExprs = std::array<Expr<A>, N>;
595 };
596 template <TypeCategory CAT, int N = 2>
597 using SameKindExprs =
598 common::MapTemplate<SameKindExprsHelper<N>::template SameExprs,
599 CategoryTypes<CAT>>;
600
601 // Given references to two expressions of arbitrary kind in the same type
602 // category, convert one to the kind of the other when it has the smaller kind,
603 // then return them in a type-safe package.
604 template <TypeCategory CAT>
AsSameKindExprs(Expr<SomeKind<CAT>> && x,Expr<SomeKind<CAT>> && y)605 SameKindExprs<CAT, 2> AsSameKindExprs(
606 Expr<SomeKind<CAT>> &&x, Expr<SomeKind<CAT>> &&y) {
607 return common::visit(
608 [&](auto &&kx, auto &&ky) -> SameKindExprs<CAT, 2> {
609 using XTy = ResultType<decltype(kx)>;
610 using YTy = ResultType<decltype(ky)>;
611 if constexpr (std::is_same_v<XTy, YTy>) {
612 return {SameExprs<XTy>{std::move(kx), std::move(ky)}};
613 } else if constexpr (XTy::kind < YTy::kind) {
614 return {SameExprs<YTy>{ConvertTo(ky, std::move(kx)), std::move(ky)}};
615 } else {
616 return {SameExprs<XTy>{std::move(kx), ConvertTo(kx, std::move(ky))}};
617 }
618 #if !__clang__ && 100 * __GNUC__ + __GNUC_MINOR__ == 801
619 // Silence a bogus warning about a missing return with G++ 8.1.0.
620 // Doesn't execute, but must be correctly typed.
621 CHECK(!"can't happen");
622 return {SameExprs<XTy>{std::move(kx), std::move(kx)}};
623 #endif
624 },
625 std::move(x.u), std::move(y.u));
626 }
627
628 // Ensure that both operands of an intrinsic REAL operation (or CMPLX()
629 // constructor) are INTEGER or REAL, then convert them as necessary to the
630 // same kind of REAL.
631 using ConvertRealOperandsResult =
632 std::optional<SameKindExprs<TypeCategory::Real, 2>>;
633 ConvertRealOperandsResult ConvertRealOperands(parser::ContextualMessages &,
634 Expr<SomeType> &&, Expr<SomeType> &&, int defaultRealKind);
635
636 // Per F'2018 R718, if both components are INTEGER, they are both converted
637 // to default REAL and the result is default COMPLEX. Otherwise, the
638 // kind of the result is the kind of most precise REAL component, and the other
639 // component is converted if necessary to its type.
640 std::optional<Expr<SomeComplex>> ConstructComplex(parser::ContextualMessages &,
641 Expr<SomeType> &&, Expr<SomeType> &&, int defaultRealKind);
642 std::optional<Expr<SomeComplex>> ConstructComplex(parser::ContextualMessages &,
643 std::optional<Expr<SomeType>> &&, std::optional<Expr<SomeType>> &&,
644 int defaultRealKind);
645
ScalarConstantToExpr(const A & x)646 template <typename A> Expr<TypeOf<A>> ScalarConstantToExpr(const A &x) {
647 using Ty = TypeOf<A>;
648 static_assert(
649 std::is_same_v<Scalar<Ty>, std::decay_t<A>>, "TypeOf<> is broken");
650 return Expr<TypeOf<A>>{Constant<Ty>{x}};
651 }
652
653 // Combine two expressions of the same specific numeric type with an operation
654 // to produce a new expression.
655 template <template <typename> class OPR, typename SPECIFIC>
Combine(Expr<SPECIFIC> && x,Expr<SPECIFIC> && y)656 Expr<SPECIFIC> Combine(Expr<SPECIFIC> &&x, Expr<SPECIFIC> &&y) {
657 static_assert(IsSpecificIntrinsicType<SPECIFIC>);
658 return AsExpr(OPR<SPECIFIC>{std::move(x), std::move(y)});
659 }
660
661 // Given two expressions of arbitrary kind in the same intrinsic type
662 // category, convert one of them if necessary to the larger kind of the
663 // other, then combine the resulting homogenized operands with a given
664 // operation, returning a new expression in the same type category.
665 template <template <typename> class OPR, TypeCategory CAT>
PromoteAndCombine(Expr<SomeKind<CAT>> && x,Expr<SomeKind<CAT>> && y)666 Expr<SomeKind<CAT>> PromoteAndCombine(
667 Expr<SomeKind<CAT>> &&x, Expr<SomeKind<CAT>> &&y) {
668 return common::visit(
669 [](auto &&xy) {
670 using Ty = ResultType<decltype(xy[0])>;
671 return AsCategoryExpr(
672 Combine<OPR, Ty>(std::move(xy[0]), std::move(xy[1])));
673 },
674 AsSameKindExprs(std::move(x), std::move(y)));
675 }
676
677 // Given two expressions of arbitrary type, try to combine them with a
678 // binary numeric operation (e.g., Add), possibly with data type conversion of
679 // one of the operands to the type of the other. Handles special cases with
680 // typeless literal operands and with REAL/COMPLEX exponentiation to INTEGER
681 // powers.
682 template <template <typename> class OPR>
683 std::optional<Expr<SomeType>> NumericOperation(parser::ContextualMessages &,
684 Expr<SomeType> &&, Expr<SomeType> &&, int defaultRealKind);
685
686 extern template std::optional<Expr<SomeType>> NumericOperation<Power>(
687 parser::ContextualMessages &, Expr<SomeType> &&, Expr<SomeType> &&,
688 int defaultRealKind);
689 extern template std::optional<Expr<SomeType>> NumericOperation<Multiply>(
690 parser::ContextualMessages &, Expr<SomeType> &&, Expr<SomeType> &&,
691 int defaultRealKind);
692 extern template std::optional<Expr<SomeType>> NumericOperation<Divide>(
693 parser::ContextualMessages &, Expr<SomeType> &&, Expr<SomeType> &&,
694 int defaultRealKind);
695 extern template std::optional<Expr<SomeType>> NumericOperation<Add>(
696 parser::ContextualMessages &, Expr<SomeType> &&, Expr<SomeType> &&,
697 int defaultRealKind);
698 extern template std::optional<Expr<SomeType>> NumericOperation<Subtract>(
699 parser::ContextualMessages &, Expr<SomeType> &&, Expr<SomeType> &&,
700 int defaultRealKind);
701
702 std::optional<Expr<SomeType>> Negation(
703 parser::ContextualMessages &, Expr<SomeType> &&);
704
705 // Given two expressions of arbitrary type, try to combine them with a
706 // relational operator (e.g., .LT.), possibly with data type conversion.
707 std::optional<Expr<LogicalResult>> Relate(parser::ContextualMessages &,
708 RelationalOperator, Expr<SomeType> &&, Expr<SomeType> &&);
709
710 // Create a relational operation between two identically-typed operands
711 // and wrap it up in an Expr<LogicalResult>.
712 template <typename T>
PackageRelation(RelationalOperator opr,Expr<T> && x,Expr<T> && y)713 Expr<LogicalResult> PackageRelation(
714 RelationalOperator opr, Expr<T> &&x, Expr<T> &&y) {
715 static_assert(IsSpecificIntrinsicType<T>);
716 return Expr<LogicalResult>{
717 Relational<SomeType>{Relational<T>{opr, std::move(x), std::move(y)}}};
718 }
719
720 template <int K>
LogicalNegation(Expr<Type<TypeCategory::Logical,K>> && x)721 Expr<Type<TypeCategory::Logical, K>> LogicalNegation(
722 Expr<Type<TypeCategory::Logical, K>> &&x) {
723 return AsExpr(Not<K>{std::move(x)});
724 }
725
726 Expr<SomeLogical> LogicalNegation(Expr<SomeLogical> &&);
727
728 template <int K>
BinaryLogicalOperation(LogicalOperator opr,Expr<Type<TypeCategory::Logical,K>> && x,Expr<Type<TypeCategory::Logical,K>> && y)729 Expr<Type<TypeCategory::Logical, K>> BinaryLogicalOperation(LogicalOperator opr,
730 Expr<Type<TypeCategory::Logical, K>> &&x,
731 Expr<Type<TypeCategory::Logical, K>> &&y) {
732 return AsExpr(LogicalOperation<K>{opr, std::move(x), std::move(y)});
733 }
734
735 Expr<SomeLogical> BinaryLogicalOperation(
736 LogicalOperator, Expr<SomeLogical> &&, Expr<SomeLogical> &&);
737
738 // Convenience functions and operator overloadings for expression construction.
739 // These interfaces are defined only for those situations that can never
740 // emit any message. Use the more general templates (above) in other
741 // situations.
742
743 template <TypeCategory C, int K>
744 Expr<Type<C, K>> operator-(Expr<Type<C, K>> &&x) {
745 return AsExpr(Negate<Type<C, K>>{std::move(x)});
746 }
747
748 template <TypeCategory C, int K>
749 Expr<Type<C, K>> operator+(Expr<Type<C, K>> &&x, Expr<Type<C, K>> &&y) {
750 return AsExpr(Combine<Add, Type<C, K>>(std::move(x), std::move(y)));
751 }
752
753 template <TypeCategory C, int K>
754 Expr<Type<C, K>> operator-(Expr<Type<C, K>> &&x, Expr<Type<C, K>> &&y) {
755 return AsExpr(Combine<Subtract, Type<C, K>>(std::move(x), std::move(y)));
756 }
757
758 template <TypeCategory C, int K>
759 Expr<Type<C, K>> operator*(Expr<Type<C, K>> &&x, Expr<Type<C, K>> &&y) {
760 return AsExpr(Combine<Multiply, Type<C, K>>(std::move(x), std::move(y)));
761 }
762
763 template <TypeCategory C, int K>
764 Expr<Type<C, K>> operator/(Expr<Type<C, K>> &&x, Expr<Type<C, K>> &&y) {
765 return AsExpr(Combine<Divide, Type<C, K>>(std::move(x), std::move(y)));
766 }
767
768 template <TypeCategory C> Expr<SomeKind<C>> operator-(Expr<SomeKind<C>> &&x) {
769 return common::visit(
770 [](auto &xk) { return Expr<SomeKind<C>>{-std::move(xk)}; }, x.u);
771 }
772
773 template <TypeCategory CAT>
774 Expr<SomeKind<CAT>> operator+(
775 Expr<SomeKind<CAT>> &&x, Expr<SomeKind<CAT>> &&y) {
776 return PromoteAndCombine<Add, CAT>(std::move(x), std::move(y));
777 }
778
779 template <TypeCategory CAT>
780 Expr<SomeKind<CAT>> operator-(
781 Expr<SomeKind<CAT>> &&x, Expr<SomeKind<CAT>> &&y) {
782 return PromoteAndCombine<Subtract, CAT>(std::move(x), std::move(y));
783 }
784
785 template <TypeCategory CAT>
786 Expr<SomeKind<CAT>> operator*(
787 Expr<SomeKind<CAT>> &&x, Expr<SomeKind<CAT>> &&y) {
788 return PromoteAndCombine<Multiply, CAT>(std::move(x), std::move(y));
789 }
790
791 template <TypeCategory CAT>
792 Expr<SomeKind<CAT>> operator/(
793 Expr<SomeKind<CAT>> &&x, Expr<SomeKind<CAT>> &&y) {
794 return PromoteAndCombine<Divide, CAT>(std::move(x), std::move(y));
795 }
796
797 // A utility for use with common::SearchTypes to create generic expressions
798 // when an intrinsic type category for (say) a variable is known
799 // but the kind parameter value is not.
800 template <TypeCategory CAT, template <typename> class TEMPLATE, typename VALUE>
801 struct TypeKindVisitor {
802 using Result = std::optional<Expr<SomeType>>;
803 using Types = CategoryTypes<CAT>;
804
TypeKindVisitorTypeKindVisitor805 TypeKindVisitor(int k, VALUE &&x) : kind{k}, value{std::move(x)} {}
TypeKindVisitorTypeKindVisitor806 TypeKindVisitor(int k, const VALUE &x) : kind{k}, value{x} {}
807
TestTypeKindVisitor808 template <typename T> Result Test() {
809 if (kind == T::kind) {
810 return AsGenericExpr(TEMPLATE<T>{std::move(value)});
811 }
812 return std::nullopt;
813 }
814
815 int kind;
816 VALUE value;
817 };
818
819 // TypedWrapper() wraps a object in an explicitly typed representation
820 // (e.g., Designator<> or FunctionRef<>) that has been instantiated on
821 // a dynamically chosen Fortran type.
822 template <TypeCategory CATEGORY, template <typename> typename WRAPPER,
823 typename WRAPPED>
WrapperHelper(int kind,WRAPPED && x)824 common::IfNoLvalue<std::optional<Expr<SomeType>>, WRAPPED> WrapperHelper(
825 int kind, WRAPPED &&x) {
826 return common::SearchTypes(
827 TypeKindVisitor<CATEGORY, WRAPPER, WRAPPED>{kind, std::move(x)});
828 }
829
830 template <template <typename> typename WRAPPER, typename WRAPPED>
TypedWrapper(const DynamicType & dyType,WRAPPED && x)831 common::IfNoLvalue<std::optional<Expr<SomeType>>, WRAPPED> TypedWrapper(
832 const DynamicType &dyType, WRAPPED &&x) {
833 switch (dyType.category()) {
834 SWITCH_COVERS_ALL_CASES
835 case TypeCategory::Integer:
836 return WrapperHelper<TypeCategory::Integer, WRAPPER, WRAPPED>(
837 dyType.kind(), std::move(x));
838 case TypeCategory::Real:
839 return WrapperHelper<TypeCategory::Real, WRAPPER, WRAPPED>(
840 dyType.kind(), std::move(x));
841 case TypeCategory::Complex:
842 return WrapperHelper<TypeCategory::Complex, WRAPPER, WRAPPED>(
843 dyType.kind(), std::move(x));
844 case TypeCategory::Character:
845 return WrapperHelper<TypeCategory::Character, WRAPPER, WRAPPED>(
846 dyType.kind(), std::move(x));
847 case TypeCategory::Logical:
848 return WrapperHelper<TypeCategory::Logical, WRAPPER, WRAPPED>(
849 dyType.kind(), std::move(x));
850 case TypeCategory::Derived:
851 return AsGenericExpr(Expr<SomeDerived>{WRAPPER<SomeDerived>{std::move(x)}});
852 }
853 }
854
855 // GetLastSymbol() returns the rightmost symbol in an object or procedure
856 // designator (which has perhaps been wrapped in an Expr<>), or a null pointer
857 // when none is found. It will return an ASSOCIATE construct entity's symbol
858 // rather than descending into its expression.
859 struct GetLastSymbolHelper
860 : public AnyTraverse<GetLastSymbolHelper, std::optional<const Symbol *>> {
861 using Result = std::optional<const Symbol *>;
862 using Base = AnyTraverse<GetLastSymbolHelper, Result>;
GetLastSymbolHelperGetLastSymbolHelper863 GetLastSymbolHelper() : Base{*this} {}
864 using Base::operator();
operatorGetLastSymbolHelper865 Result operator()(const Symbol &x) const { return &x; }
operatorGetLastSymbolHelper866 Result operator()(const Component &x) const { return &x.GetLastSymbol(); }
operatorGetLastSymbolHelper867 Result operator()(const NamedEntity &x) const { return &x.GetLastSymbol(); }
operatorGetLastSymbolHelper868 Result operator()(const ProcedureDesignator &x) const {
869 return x.GetSymbol();
870 }
operatorGetLastSymbolHelper871 template <typename T> Result operator()(const Expr<T> &x) const {
872 if constexpr (common::HasMember<T, AllIntrinsicTypes> ||
873 std::is_same_v<T, SomeDerived>) {
874 if (const auto *designator{std::get_if<Designator<T>>(&x.u)}) {
875 if (auto known{(*this)(*designator)}) {
876 return known;
877 }
878 }
879 return nullptr;
880 } else {
881 return (*this)(x.u);
882 }
883 }
884 };
885
GetLastSymbol(const A & x)886 template <typename A> const Symbol *GetLastSymbol(const A &x) {
887 if (auto known{GetLastSymbolHelper{}(x)}) {
888 return *known;
889 } else {
890 return nullptr;
891 }
892 }
893
894 // Convenience: If GetLastSymbol() succeeds on the argument, return its
895 // set of attributes, otherwise the empty set.
GetAttrs(const A & x)896 template <typename A> semantics::Attrs GetAttrs(const A &x) {
897 if (const Symbol * symbol{GetLastSymbol(x)}) {
898 return symbol->attrs();
899 } else {
900 return {};
901 }
902 }
903
904 // GetBaseObject()
GetBaseObject(const A &)905 template <typename A> std::optional<BaseObject> GetBaseObject(const A &) {
906 return std::nullopt;
907 }
908 template <typename T>
GetBaseObject(const Designator<T> & x)909 std::optional<BaseObject> GetBaseObject(const Designator<T> &x) {
910 return x.GetBaseObject();
911 }
912 template <typename T>
GetBaseObject(const Expr<T> & x)913 std::optional<BaseObject> GetBaseObject(const Expr<T> &x) {
914 return common::visit([](const auto &y) { return GetBaseObject(y); }, x.u);
915 }
916 template <typename A>
GetBaseObject(const std::optional<A> & x)917 std::optional<BaseObject> GetBaseObject(const std::optional<A> &x) {
918 if (x) {
919 return GetBaseObject(*x);
920 } else {
921 return std::nullopt;
922 }
923 }
924
925 // Predicate: IsAllocatableOrPointer()
IsAllocatableOrPointer(const A & x)926 template <typename A> bool IsAllocatableOrPointer(const A &x) {
927 return GetAttrs(x).HasAny(
928 semantics::Attrs{semantics::Attr::POINTER, semantics::Attr::ALLOCATABLE});
929 }
930
931 // Like IsAllocatableOrPointer, but accepts pointer function results as being
932 // pointers.
933 bool IsAllocatableOrPointerObject(const Expr<SomeType> &, FoldingContext &);
934
935 bool IsAllocatableDesignator(const Expr<SomeType> &);
936
937 // Procedure and pointer detection predicates
938 bool IsProcedure(const Expr<SomeType> &);
939 bool IsFunction(const Expr<SomeType> &);
940 bool IsProcedurePointerTarget(const Expr<SomeType> &);
941 bool IsBareNullPointer(const Expr<SomeType> *); // NULL() w/o MOLD=
942 bool IsNullPointer(const Expr<SomeType> &);
943 bool IsObjectPointer(const Expr<SomeType> &, FoldingContext &);
944
945 // Can Expr be passed as absent to an optional dummy argument.
946 // See 15.5.2.12 point 1 for more details.
947 bool MayBePassedAsAbsentOptional(const Expr<SomeType> &, FoldingContext &);
948
949 // Extracts the chain of symbols from a designator, which has perhaps been
950 // wrapped in an Expr<>, removing all of the (co)subscripts. The
951 // base object will be the first symbol in the result vector.
952 struct GetSymbolVectorHelper
953 : public Traverse<GetSymbolVectorHelper, SymbolVector> {
954 using Result = SymbolVector;
955 using Base = Traverse<GetSymbolVectorHelper, Result>;
956 using Base::operator();
GetSymbolVectorHelperGetSymbolVectorHelper957 GetSymbolVectorHelper() : Base{*this} {}
DefaultGetSymbolVectorHelper958 Result Default() { return {}; }
CombineGetSymbolVectorHelper959 Result Combine(Result &&a, Result &&b) {
960 a.insert(a.end(), b.begin(), b.end());
961 return std::move(a);
962 }
963 Result operator()(const Symbol &) const;
964 Result operator()(const Component &) const;
965 Result operator()(const ArrayRef &) const;
966 Result operator()(const CoarrayRef &) const;
967 };
GetSymbolVector(const A & x)968 template <typename A> SymbolVector GetSymbolVector(const A &x) {
969 return GetSymbolVectorHelper{}(x);
970 }
971
972 // GetLastTarget() returns the rightmost symbol in an object designator's
973 // SymbolVector that has the POINTER or TARGET attribute, or a null pointer
974 // when none is found.
975 const Symbol *GetLastTarget(const SymbolVector &);
976
977 // Collects all of the Symbols in an expression
978 template <typename A> semantics::UnorderedSymbolSet CollectSymbols(const A &);
979 extern template semantics::UnorderedSymbolSet CollectSymbols(
980 const Expr<SomeType> &);
981 extern template semantics::UnorderedSymbolSet CollectSymbols(
982 const Expr<SomeInteger> &);
983 extern template semantics::UnorderedSymbolSet CollectSymbols(
984 const Expr<SubscriptInteger> &);
985
986 // Predicate: does a variable contain a vector-valued subscript (not a triplet)?
987 bool HasVectorSubscript(const Expr<SomeType> &);
988
989 // Utilities for attaching the location of the declaration of a symbol
990 // of interest to a message, if both pointers are non-null. Handles
991 // the case of USE association gracefully.
992 parser::Message *AttachDeclaration(parser::Message &, const Symbol &);
993 parser::Message *AttachDeclaration(parser::Message *, const Symbol &);
994 template <typename MESSAGES, typename... A>
SayWithDeclaration(MESSAGES & messages,const Symbol & symbol,A &&...x)995 parser::Message *SayWithDeclaration(
996 MESSAGES &messages, const Symbol &symbol, A &&...x) {
997 return AttachDeclaration(messages.Say(std::forward<A>(x)...), symbol);
998 }
999
1000 // Check for references to impure procedures; returns the name
1001 // of one to complain about, if any exist.
1002 std::optional<std::string> FindImpureCall(
1003 FoldingContext &, const Expr<SomeType> &);
1004 std::optional<std::string> FindImpureCall(
1005 FoldingContext &, const ProcedureRef &);
1006
1007 // Predicate: is a scalar expression suitable for naive scalar expansion
1008 // in the flattening of an array expression?
1009 // TODO: capture such scalar expansions in temporaries, flatten everything
1010 struct UnexpandabilityFindingVisitor
1011 : public AnyTraverse<UnexpandabilityFindingVisitor> {
1012 using Base = AnyTraverse<UnexpandabilityFindingVisitor>;
1013 using Base::operator();
UnexpandabilityFindingVisitorUnexpandabilityFindingVisitor1014 UnexpandabilityFindingVisitor() : Base{*this} {}
operatorUnexpandabilityFindingVisitor1015 template <typename T> bool operator()(const FunctionRef<T> &) { return true; }
operatorUnexpandabilityFindingVisitor1016 bool operator()(const CoarrayRef &) { return true; }
1017 };
1018
IsExpandableScalar(const Expr<T> & expr)1019 template <typename T> bool IsExpandableScalar(const Expr<T> &expr) {
1020 return !UnexpandabilityFindingVisitor{}(expr);
1021 }
1022
1023 // Common handling for procedure pointer compatibility of left- and right-hand
1024 // sides. Returns nullopt if they're compatible. Otherwise, it returns a
1025 // message that needs to be augmented by the names of the left and right sides
1026 std::optional<parser::MessageFixedText> CheckProcCompatibility(bool isCall,
1027 const std::optional<characteristics::Procedure> &lhsProcedure,
1028 const characteristics::Procedure *rhsProcedure,
1029 const SpecificIntrinsic *specificIntrinsic, std::string &whyNotCompatible);
1030
1031 // Scalar constant expansion
1032 class ScalarConstantExpander {
1033 public:
ScalarConstantExpander(ConstantSubscripts && extents)1034 explicit ScalarConstantExpander(ConstantSubscripts &&extents)
1035 : extents_{std::move(extents)} {}
ScalarConstantExpander(ConstantSubscripts && extents,std::optional<ConstantSubscripts> && lbounds)1036 ScalarConstantExpander(
1037 ConstantSubscripts &&extents, std::optional<ConstantSubscripts> &&lbounds)
1038 : extents_{std::move(extents)}, lbounds_{std::move(lbounds)} {}
ScalarConstantExpander(ConstantSubscripts && extents,ConstantSubscripts && lbounds)1039 ScalarConstantExpander(
1040 ConstantSubscripts &&extents, ConstantSubscripts &&lbounds)
1041 : extents_{std::move(extents)}, lbounds_{std::move(lbounds)} {}
1042
Expand(A && x)1043 template <typename A> A Expand(A &&x) const {
1044 return std::move(x); // default case
1045 }
Expand(Constant<T> && x)1046 template <typename T> Constant<T> Expand(Constant<T> &&x) {
1047 auto expanded{x.Reshape(std::move(extents_))};
1048 if (lbounds_) {
1049 expanded.set_lbounds(std::move(*lbounds_));
1050 }
1051 return expanded;
1052 }
Expand(Parentheses<T> && x)1053 template <typename T> Expr<T> Expand(Parentheses<T> &&x) {
1054 return Expand(std::move(x.left())); // Constant<> can be parenthesized
1055 }
Expand(Expr<T> && x)1056 template <typename T> Expr<T> Expand(Expr<T> &&x) {
1057 return common::visit(
1058 [&](auto &&x) { return Expr<T>{Expand(std::move(x))}; },
1059 std::move(x.u));
1060 }
1061
1062 private:
1063 ConstantSubscripts extents_;
1064 std::optional<ConstantSubscripts> lbounds_;
1065 };
1066
1067 // Given a collection of element values, package them as a Constant.
1068 // If the type is Character or a derived type, take the length or type
1069 // (resp.) from a another Constant.
1070 template <typename T>
PackageConstant(std::vector<Scalar<T>> && elements,const Constant<T> & reference,const ConstantSubscripts & shape)1071 Constant<T> PackageConstant(std::vector<Scalar<T>> &&elements,
1072 const Constant<T> &reference, const ConstantSubscripts &shape) {
1073 if constexpr (T::category == TypeCategory::Character) {
1074 return Constant<T>{
1075 reference.LEN(), std::move(elements), ConstantSubscripts{shape}};
1076 } else if constexpr (T::category == TypeCategory::Derived) {
1077 return Constant<T>{reference.GetType().GetDerivedTypeSpec(),
1078 std::move(elements), ConstantSubscripts{shape}};
1079 } else {
1080 return Constant<T>{std::move(elements), ConstantSubscripts{shape}};
1081 }
1082 }
1083
1084 // Nonstandard conversions of constants (integer->logical, logical->integer)
1085 // that can appear in DATA statements as an extension.
1086 std::optional<Expr<SomeType>> DataConstantConversionExtension(
1087 FoldingContext &, const DynamicType &, const Expr<SomeType> &);
1088
1089 // Convert Hollerith or short character to a another type as if the
1090 // Hollerith data had been BOZ.
1091 std::optional<Expr<SomeType>> HollerithToBOZ(
1092 FoldingContext &, const Expr<SomeType> &, const DynamicType &);
1093
1094 } // namespace Fortran::evaluate
1095
1096 namespace Fortran::semantics {
1097
1098 class Scope;
1099
1100 // If a symbol represents an ENTRY, return the symbol of the main entry
1101 // point to its subprogram.
1102 const Symbol *GetMainEntry(const Symbol *);
1103
1104 // These functions are used in Evaluate so they are defined here rather than in
1105 // Semantics to avoid a link-time dependency on Semantics.
1106 // All of these apply GetUltimate() or ResolveAssociations() to their arguments.
1107 bool IsVariableName(const Symbol &);
1108 bool IsPureProcedure(const Symbol &);
1109 bool IsPureProcedure(const Scope &);
1110 bool IsElementalProcedure(const Symbol &);
1111 bool IsFunction(const Symbol &);
1112 bool IsFunction(const Scope &);
1113 bool IsProcedure(const Symbol &);
1114 bool IsProcedure(const Scope &);
1115 bool IsProcedurePointer(const Symbol &);
1116 bool IsAutomatic(const Symbol &);
1117 bool IsSaved(const Symbol &); // saved implicitly or explicitly
1118 bool IsDummy(const Symbol &);
1119 bool IsAssumedShape(const Symbol &);
1120 bool IsDeferredShape(const Symbol &);
1121 bool IsFunctionResult(const Symbol &);
1122 bool IsKindTypeParameter(const Symbol &);
1123 bool IsLenTypeParameter(const Symbol &);
1124 bool IsExtensibleType(const DerivedTypeSpec *);
1125 bool IsBuiltinDerivedType(const DerivedTypeSpec *derived, const char *name);
1126 // Is this derived type TEAM_TYPE from module ISO_FORTRAN_ENV?
1127 bool IsTeamType(const DerivedTypeSpec *);
1128 // Is this derived type TEAM_TYPE, C_PTR, or C_FUNPTR?
1129 bool IsBadCoarrayType(const DerivedTypeSpec *);
1130 // Is this derived type either C_PTR or C_FUNPTR from module ISO_C_BINDING
1131 bool IsIsoCType(const DerivedTypeSpec *);
1132 bool IsEventTypeOrLockType(const DerivedTypeSpec *);
1133
1134 // ResolveAssociations() traverses use associations and host associations
1135 // like GetUltimate(), but also resolves through whole variable associations
1136 // with ASSOCIATE(x => y) and related constructs. GetAssociationRoot()
1137 // applies ResolveAssociations() and then, in the case of resolution to
1138 // a construct association with part of a variable that does not involve a
1139 // vector subscript, returns the first symbol of that variable instead
1140 // of the construct entity.
1141 // (E.g., for ASSOCIATE(x => y%z), ResolveAssociations(x) returns x,
1142 // while GetAssociationRoot(x) returns y.)
1143 const Symbol &ResolveAssociations(const Symbol &);
1144 const Symbol &GetAssociationRoot(const Symbol &);
1145
1146 const Symbol *FindCommonBlockContaining(const Symbol &);
1147 int CountLenParameters(const DerivedTypeSpec &);
1148 int CountNonConstantLenParameters(const DerivedTypeSpec &);
1149
1150 // 15.5.2.4(4), type compatibility for dummy and actual arguments.
1151 // Also used for assignment compatibility checking
1152 bool AreTypeParamCompatible(
1153 const semantics::DerivedTypeSpec &, const semantics::DerivedTypeSpec &);
1154
1155 const Symbol &GetUsedModule(const UseDetails &);
1156 const Symbol *FindFunctionResult(const Symbol &);
1157
1158 // Type compatibility predicate: are x and y effectively the same type?
1159 // Uses DynamicType::IsTkCompatible(), which handles the case of distinct
1160 // but identical derived types.
1161 bool AreTkCompatibleTypes(const DeclTypeSpec *x, const DeclTypeSpec *y);
1162
1163 } // namespace Fortran::semantics
1164
1165 #endif // FORTRAN_EVALUATE_TOOLS_H_
1166