1 //===-- lib/Evaluate/formatting.cpp ---------------------------------------===//
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
7 //===----------------------------------------------------------------------===//
9 #include "flang/Evaluate/formatting.h"
10 #include "flang/Common/Fortran.h"
11 #include "flang/Evaluate/call.h"
12 #include "flang/Evaluate/constant.h"
13 #include "flang/Evaluate/expression.h"
14 #include "flang/Evaluate/fold.h"
15 #include "flang/Evaluate/tools.h"
16 #include "flang/Parser/characters.h"
17 #include "flang/Semantics/semantics.h"
18 #include "flang/Semantics/symbol.h"
19 #include "llvm/Support/raw_ostream.h"
21 namespace Fortran::evaluate
{
23 // Constant arrays can have non-default lower bounds, but this can't be
24 // expressed in Fortran syntax directly, only implied through the use of
25 // named constant (PARAMETER) definitions. For debugging, setting this flag
26 // enables a non-standard %LBOUND=[...] argument to the RESHAPE intrinsic
27 // calls used to dumy constants. It's off by default so that this syntax
28 // doesn't show up in module files.
29 static const bool printLbounds
{false};
31 static void ShapeAsFortran(llvm::raw_ostream
&o
,
32 const ConstantSubscripts
&shape
, const ConstantSubscripts
&lbounds
,
33 bool hasNonDefaultLowerBound
) {
34 if (GetRank(shape
) > 1 || hasNonDefaultLowerBound
) {
37 for (auto dim
: shape
) {
42 if (hasNonDefaultLowerBound
) {
45 for (auto lb
: lbounds
) {
55 template <typename RESULT
, typename VALUE
>
56 llvm::raw_ostream
&ConstantBase
<RESULT
, VALUE
>::AsFortran(
57 llvm::raw_ostream
&o
) const {
58 bool hasNonDefaultLowerBound
{printLbounds
&& HasNonDefaultLowerBound()};
59 if (Rank() > 1 || hasNonDefaultLowerBound
) {
63 o
<< '[' << GetType().AsFortran() << "::";
66 for (const auto &value
: values_
) {
72 if constexpr (Result::category
== TypeCategory::Integer
) {
73 o
<< value
.SignedDecimal() << '_' << Result::kind
;
74 } else if constexpr (Result::category
== TypeCategory::Real
||
75 Result::category
== TypeCategory::Complex
) {
76 value
.AsFortran(o
, Result::kind
);
77 } else if constexpr (Result::category
== TypeCategory::Character
) {
78 o
<< Result::kind
<< '_' << parser::QuoteCharacterLiteral(value
, true);
79 } else if constexpr (Result::category
== TypeCategory::Logical
) {
80 if (!value
.IsCanonical()) {
81 o
<< "transfer(" << value
.word().ToInt64() << "_8,.false._"
82 << Result::kind
<< ')';
83 } else if (value
.IsTrue()) {
84 o
<< ".true." << '_' << Result::kind
;
86 o
<< ".false." << '_' << Result::kind
;
89 StructureConstructor
{result_
.derivedTypeSpec(), value
}.AsFortran(o
);
95 ShapeAsFortran(o
, shape(), lbounds(), hasNonDefaultLowerBound
);
100 llvm::raw_ostream
&Constant
<Type
<TypeCategory::Character
, KIND
>>::AsFortran(
101 llvm::raw_ostream
&o
) const {
102 bool hasNonDefaultLowerBound
{printLbounds
&& HasNonDefaultLowerBound()};
103 if (Rank() > 1 || hasNonDefaultLowerBound
) {
107 o
<< '[' << GetType().AsFortran(std::to_string(length_
)) << "::";
109 auto total
{static_cast<ConstantSubscript
>(size())};
110 for (ConstantSubscript j
{0}; j
< total
; ++j
) {
111 Scalar
<Result
> value
{values_
.substr(j
* length_
, length_
)};
115 if (Result::kind
!= 1) {
116 o
<< Result::kind
<< '_';
118 o
<< parser::QuoteCharacterLiteral(value
);
123 ShapeAsFortran(o
, shape(), lbounds(), hasNonDefaultLowerBound
);
127 llvm::raw_ostream
&EmitVar(llvm::raw_ostream
&o
, const Symbol
&symbol
,
128 std::optional
<parser::CharBlock
> name
= std::nullopt
) {
129 const auto &renamings
{symbol
.owner().context().moduleFileOutputRenamings()};
130 if (auto iter
{renamings
.find(&symbol
)}; iter
!= renamings
.end()) {
131 return o
<< iter
->second
.ToString();
133 return o
<< name
->ToString();
135 return o
<< symbol
.name().ToString();
139 llvm::raw_ostream
&EmitVar(llvm::raw_ostream
&o
, const std::string
&lit
) {
140 return o
<< parser::QuoteCharacterLiteral(lit
);
143 llvm::raw_ostream
&EmitVar(llvm::raw_ostream
&o
, const std::u16string
&lit
) {
144 return o
<< parser::QuoteCharacterLiteral(lit
);
147 llvm::raw_ostream
&EmitVar(llvm::raw_ostream
&o
, const std::u32string
&lit
) {
148 return o
<< parser::QuoteCharacterLiteral(lit
);
151 template <typename A
>
152 llvm::raw_ostream
&EmitVar(llvm::raw_ostream
&o
, const A
&x
) {
153 return x
.AsFortran(o
);
156 template <typename A
>
157 llvm::raw_ostream
&EmitVar(llvm::raw_ostream
&o
, common::Reference
<A
> x
) {
158 return EmitVar(o
, *x
);
161 template <typename A
>
162 llvm::raw_ostream
&EmitVar(
163 llvm::raw_ostream
&o
, const A
*p
, const char *kw
= nullptr) {
173 template <typename A
>
174 llvm::raw_ostream
&EmitVar(
175 llvm::raw_ostream
&o
, const std::optional
<A
> &x
, const char *kw
= nullptr) {
185 template <typename A
, bool COPY
>
186 llvm::raw_ostream
&EmitVar(llvm::raw_ostream
&o
,
187 const common::Indirection
<A
, COPY
> &p
, const char *kw
= nullptr) {
191 EmitVar(o
, p
.value());
195 template <typename A
>
196 llvm::raw_ostream
&EmitVar(llvm::raw_ostream
&o
, const std::shared_ptr
<A
> &p
) {
198 return EmitVar(o
, *p
);
201 template <typename
... A
>
202 llvm::raw_ostream
&EmitVar(llvm::raw_ostream
&o
, const std::variant
<A
...> &u
) {
203 common::visit([&](const auto &x
) { EmitVar(o
, x
); }, u
);
207 llvm::raw_ostream
&ActualArgument::AssumedType::AsFortran(
208 llvm::raw_ostream
&o
) const {
209 return EmitVar(o
, *symbol_
);
212 llvm::raw_ostream
&ActualArgument::AsFortran(llvm::raw_ostream
&o
) const {
214 o
<< keyword_
->ToString() << '=';
216 if (isPercentVal()) {
218 } else if (isPercentRef()) {
223 [&](const common::CopyableIndirection
<Expr
<SomeType
>> &expr
) {
224 expr
.value().AsFortran(o
);
226 [&](const AssumedType
&assumedType
) { assumedType
.AsFortran(o
); },
227 [&](const common::Label
&label
) { o
<< '*' << label
; },
230 if (isPercentVal() || isPercentRef()) {
236 llvm::raw_ostream
&SpecificIntrinsic::AsFortran(llvm::raw_ostream
&o
) const {
240 llvm::raw_ostream
&ProcedureRef::AsFortran(llvm::raw_ostream
&o
) const {
241 for (const auto &arg
: arguments_
) {
242 if (arg
&& arg
->isPassedObject()) {
243 arg
->AsFortran(o
) << '%';
248 if (!chevrons_
.empty()) {
250 for (const auto &expr
: chevrons_
) {
252 expr
.AsFortran(o
<< "<<<");
255 expr
.AsFortran(o
<< ",");
261 for (const auto &arg
: arguments_
) {
262 if (arg
&& !arg
->isPassedObject()) {
263 arg
->AsFortran(o
<< separator
);
267 if (separator
== '(') {
273 // Operator precedence formatting; insert parentheses around operands
274 // only when necessary.
276 enum class Precedence
{ // in increasing order for sane comparisons
280 Equivalence
, // .EQV., .NEQV.
281 Not
, // which binds *less* tightly in Fortran than relations
283 Additive
, // +, -, and (arbitrarily) //
284 Negate
, // which binds *less* tightly than *, /, **
285 Multiplicative
, // *, /
286 Power
, // **, which is right-associative unlike the other dyadic operators
291 template <typename A
> constexpr Precedence
ToPrecedence(const A
&) {
292 return Precedence::Top
;
295 static Precedence
ToPrecedence(const LogicalOperation
<KIND
> &x
) {
296 switch (x
.logicalOperator
) {
297 SWITCH_COVERS_ALL_CASES
298 case LogicalOperator::And
:
299 return Precedence::And
;
300 case LogicalOperator::Or
:
301 return Precedence::Or
;
302 case LogicalOperator::Not
:
303 return Precedence::Not
;
304 case LogicalOperator::Eqv
:
305 case LogicalOperator::Neqv
:
306 return Precedence::Equivalence
;
309 template <int KIND
> constexpr Precedence
ToPrecedence(const Not
<KIND
> &) {
310 return Precedence::Not
;
312 template <typename T
> constexpr Precedence
ToPrecedence(const Relational
<T
> &) {
313 return Precedence::Relational
;
315 template <typename T
> constexpr Precedence
ToPrecedence(const Add
<T
> &) {
316 return Precedence::Additive
;
318 template <typename T
> constexpr Precedence
ToPrecedence(const Subtract
<T
> &) {
319 return Precedence::Additive
;
321 template <int KIND
> constexpr Precedence
ToPrecedence(const Concat
<KIND
> &) {
322 return Precedence::Additive
;
324 template <typename T
> constexpr Precedence
ToPrecedence(const Negate
<T
> &) {
325 return Precedence::Negate
;
327 template <typename T
> constexpr Precedence
ToPrecedence(const Multiply
<T
> &) {
328 return Precedence::Multiplicative
;
330 template <typename T
> constexpr Precedence
ToPrecedence(const Divide
<T
> &) {
331 return Precedence::Multiplicative
;
333 template <typename T
> constexpr Precedence
ToPrecedence(const Power
<T
> &) {
334 return Precedence::Power
;
336 template <typename T
>
337 constexpr Precedence
ToPrecedence(const RealToIntPower
<T
> &) {
338 return Precedence::Power
;
340 template <typename T
> static Precedence
ToPrecedence(const Constant
<T
> &x
) {
341 static constexpr TypeCategory cat
{T::category
};
342 if constexpr (cat
== TypeCategory::Integer
|| cat
== TypeCategory::Real
) {
343 if (auto n
{GetScalarConstantValue
<T
>(x
)}) {
344 if (n
->IsNegative()) {
345 return Precedence::Negate
;
349 return Precedence::Top
;
351 template <typename T
> static Precedence
ToPrecedence(const Expr
<T
> &expr
) {
352 return common::visit([](const auto &x
) { return ToPrecedence(x
); }, expr
.u
);
355 template <typename T
> static bool IsNegatedScalarConstant(const Expr
<T
> &expr
) {
356 static constexpr TypeCategory cat
{T::category
};
357 if constexpr (cat
== TypeCategory::Integer
|| cat
== TypeCategory::Real
) {
358 if (auto n
{GetScalarConstantValue
<T
>(expr
)}) {
359 return n
->IsNegative();
365 template <TypeCategory CAT
>
366 static bool IsNegatedScalarConstant(const Expr
<SomeKind
<CAT
>> &expr
) {
367 return common::visit(
368 [](const auto &x
) { return IsNegatedScalarConstant(x
); }, expr
.u
);
371 struct OperatorSpelling
{
372 const char *prefix
{""}, *infix
{","}, *suffix
{""};
375 template <typename A
> constexpr OperatorSpelling
SpellOperator(const A
&) {
376 return OperatorSpelling
{};
378 template <typename A
>
379 constexpr OperatorSpelling
SpellOperator(const Negate
<A
> &) {
380 return OperatorSpelling
{"-", "", ""};
382 template <typename A
>
383 constexpr OperatorSpelling
SpellOperator(const Parentheses
<A
> &) {
384 return OperatorSpelling
{"(", "", ")"};
387 static OperatorSpelling
SpellOperator(const ComplexComponent
<KIND
> &x
) {
388 return {x
.isImaginaryPart
? "aimag(" : "real(", "", ")"};
391 constexpr OperatorSpelling
SpellOperator(const Not
<KIND
> &) {
392 return OperatorSpelling
{".NOT.", "", ""};
395 constexpr OperatorSpelling
SpellOperator(const SetLength
<KIND
> &) {
396 return OperatorSpelling
{"%SET_LENGTH(", ",", ")"};
399 constexpr OperatorSpelling
SpellOperator(const ComplexConstructor
<KIND
> &) {
400 return OperatorSpelling
{"(", ",", ")"};
402 template <typename A
> constexpr OperatorSpelling
SpellOperator(const Add
<A
> &) {
403 return OperatorSpelling
{"", "+", ""};
405 template <typename A
>
406 constexpr OperatorSpelling
SpellOperator(const Subtract
<A
> &) {
407 return OperatorSpelling
{"", "-", ""};
409 template <typename A
>
410 constexpr OperatorSpelling
SpellOperator(const Multiply
<A
> &) {
411 return OperatorSpelling
{"", "*", ""};
413 template <typename A
>
414 constexpr OperatorSpelling
SpellOperator(const Divide
<A
> &) {
415 return OperatorSpelling
{"", "/", ""};
417 template <typename A
>
418 constexpr OperatorSpelling
SpellOperator(const Power
<A
> &) {
419 return OperatorSpelling
{"", "**", ""};
421 template <typename A
>
422 constexpr OperatorSpelling
SpellOperator(const RealToIntPower
<A
> &) {
423 return OperatorSpelling
{"", "**", ""};
425 template <typename A
>
426 static OperatorSpelling
SpellOperator(const Extremum
<A
> &x
) {
427 return OperatorSpelling
{
428 x
.ordering
== Ordering::Less
? "min(" : "max(", ",", ")"};
431 constexpr OperatorSpelling
SpellOperator(const Concat
<KIND
> &) {
432 return OperatorSpelling
{"", "//", ""};
435 static OperatorSpelling
SpellOperator(const LogicalOperation
<KIND
> &x
) {
436 return OperatorSpelling
{"", AsFortran(x
.logicalOperator
), ""};
438 template <typename T
>
439 static OperatorSpelling
SpellOperator(const Relational
<T
> &x
) {
440 return OperatorSpelling
{"", AsFortran(x
.opr
), ""};
443 template <typename D
, typename R
, typename
... O
>
444 llvm::raw_ostream
&Operation
<D
, R
, O
...>::AsFortran(
445 llvm::raw_ostream
&o
) const {
446 Precedence lhsPrec
{ToPrecedence(left())};
447 OperatorSpelling spelling
{SpellOperator(derived())};
448 o
<< spelling
.prefix
;
449 Precedence thisPrec
{ToPrecedence(derived())};
450 if constexpr (operands
== 1) {
451 if (thisPrec
!= Precedence::Top
&& lhsPrec
< thisPrec
) {
452 left().AsFortran(o
<< '(') << ')';
457 if (thisPrec
!= Precedence::Top
&&
458 (lhsPrec
< thisPrec
||
459 (lhsPrec
== Precedence::Power
&& thisPrec
== Precedence::Power
))) {
460 left().AsFortran(o
<< '(') << ')';
465 Precedence rhsPrec
{ToPrecedence(right())};
466 if (thisPrec
!= Precedence::Top
&& rhsPrec
< thisPrec
) {
467 right().AsFortran(o
<< '(') << ')';
469 right().AsFortran(o
);
472 return o
<< spelling
.suffix
;
475 template <typename TO
, TypeCategory FROMCAT
>
476 llvm::raw_ostream
&Convert
<TO
, FROMCAT
>::AsFortran(llvm::raw_ostream
&o
) const {
477 static_assert(TO::category
== TypeCategory::Integer
||
478 TO::category
== TypeCategory::Real
||
479 TO::category
== TypeCategory::Complex
||
480 TO::category
== TypeCategory::Character
||
481 TO::category
== TypeCategory::Logical
,
482 "Convert<> to bad category!");
483 if constexpr (TO::category
== TypeCategory::Character
) {
484 this->left().AsFortran(o
<< "achar(iachar(") << ')';
485 } else if constexpr (TO::category
== TypeCategory::Integer
) {
486 this->left().AsFortran(o
<< "int(");
487 } else if constexpr (TO::category
== TypeCategory::Real
) {
488 this->left().AsFortran(o
<< "real(");
489 } else if constexpr (TO::category
== TypeCategory::Complex
) {
490 this->left().AsFortran(o
<< "cmplx(");
492 this->left().AsFortran(o
<< "logical(");
494 return o
<< ",kind=" << TO::kind
<< ')';
497 llvm::raw_ostream
&Relational
<SomeType
>::AsFortran(llvm::raw_ostream
&o
) const {
498 common::visit([&](const auto &rel
) { rel
.AsFortran(o
); }, u
);
502 template <typename T
>
503 llvm::raw_ostream
&EmitArray(llvm::raw_ostream
&o
, const Expr
<T
> &expr
) {
504 return expr
.AsFortran(o
);
507 template <typename T
>
508 llvm::raw_ostream
&EmitArray(
509 llvm::raw_ostream
&, const ArrayConstructorValues
<T
> &);
511 template <typename T
>
512 llvm::raw_ostream
&EmitArray(llvm::raw_ostream
&o
, const ImpliedDo
<T
> &implDo
) {
514 EmitArray(o
, implDo
.values());
515 o
<< ',' << ImpliedDoIndex::Result::AsFortran()
516 << "::" << implDo
.name().ToString() << '=';
517 implDo
.lower().AsFortran(o
) << ',';
518 implDo
.upper().AsFortran(o
) << ',';
519 implDo
.stride().AsFortran(o
) << ')';
523 template <typename T
>
524 llvm::raw_ostream
&EmitArray(
525 llvm::raw_ostream
&o
, const ArrayConstructorValues
<T
> &values
) {
527 for (const auto &value
: values
) {
529 common::visit([&](const auto &x
) { EmitArray(o
, x
); }, value
.u
);
535 template <typename T
>
536 llvm::raw_ostream
&ArrayConstructor
<T
>::AsFortran(llvm::raw_ostream
&o
) const {
537 o
<< '[' << GetType().AsFortran() << "::";
544 ArrayConstructor
<Type
<TypeCategory::Character
, KIND
>>::AsFortran(
545 llvm::raw_ostream
&o
) const {
547 if (const auto *len
{LEN()}) {
548 o
<< GetType().AsFortran(len
->AsFortran()) << "::";
554 llvm::raw_ostream
&ArrayConstructor
<SomeDerived
>::AsFortran(
555 llvm::raw_ostream
&o
) const {
556 o
<< '[' << GetType().AsFortran() << "::";
561 template <typename RESULT
>
562 std::string ExpressionBase
<RESULT
>::AsFortran() const {
564 llvm::raw_string_ostream ss
{buf
};
569 template <typename RESULT
>
570 llvm::raw_ostream
&ExpressionBase
<RESULT
>::AsFortran(
571 llvm::raw_ostream
&o
) const {
572 common::visit(common::visitors
{
573 [&](const BOZLiteralConstant
&x
) {
574 o
<< "z'" << x
.Hexadecimal() << "'";
576 [&](const NullPointer
&) { o
<< "NULL()"; },
577 [&](const common::CopyableIndirection
<Substring
> &s
) {
578 s
.value().AsFortran(o
);
580 [&](const ImpliedDoIndex
&i
) { o
<< i
.name
.ToString(); },
581 [&](const auto &x
) { x
.AsFortran(o
); },
587 static std::string
DerivedTypeSpecAsFortran(
588 const semantics::DerivedTypeSpec
&spec
) {
590 llvm::raw_string_ostream ss
{buf
};
591 EmitVar(ss
, spec
.typeSymbol(), spec
.name());
593 for (const auto &[name
, value
] : spec
.parameters()) {
594 ss
<< ch
<< name
.ToString() << '=';
596 if (value
.isAssumed()) {
598 } else if (value
.isDeferred()) {
601 value
.GetExplicit()->AsFortran(ss
);
610 llvm::raw_ostream
&StructureConstructor::AsFortran(llvm::raw_ostream
&o
) const {
611 o
<< DerivedTypeSpecAsFortran(result_
.derivedTypeSpec());
612 if (values_
.empty()) {
616 for (const auto &[symbol
, value
] : values_
) {
617 value
.value().AsFortran(EmitVar(o
<< ch
, *symbol
) << '=');
624 std::string
DynamicType::AsFortran() const {
626 CHECK(category_
== TypeCategory::Derived
);
627 std::string result
{DerivedTypeSpecAsFortran(*derived_
)};
628 if (IsPolymorphic()) {
629 result
= "CLASS("s
+ result
+ ')';
632 } else if (charLengthParamValue_
|| knownLength()) {
633 std::string result
{"CHARACTER(KIND="s
+ std::to_string(kind_
) + ",LEN="};
635 result
+= std::to_string(*knownLength()) + "_8";
636 } else if (charLengthParamValue_
->isAssumed()) {
638 } else if (charLengthParamValue_
->isDeferred()) {
640 } else if (const auto &length
{charLengthParamValue_
->GetExplicit()}) {
641 result
+= length
->AsFortran();
644 } else if (IsAssumedType()) {
646 } else if (IsUnlimitedPolymorphic()) {
648 } else if (IsTypelessIntrinsicArgument()) {
649 return "(typeless intrinsic function argument)";
651 return parser::ToUpperCaseLetters(EnumToString(category_
)) + '(' +
652 std::to_string(kind_
) + ')';
656 std::string
DynamicType::AsFortran(std::string
&&charLenExpr
) const {
657 if (!charLenExpr
.empty() && category_
== TypeCategory::Character
) {
658 return "CHARACTER(KIND=" + std::to_string(kind_
) +
659 ",LEN=" + std::move(charLenExpr
) + ')';
665 std::string
SomeDerived::AsFortran() const {
666 if (IsUnlimitedPolymorphic()) {
669 return "TYPE("s
+ DerivedTypeSpecAsFortran(derivedTypeSpec()) + ')';
673 llvm::raw_ostream
&BaseObject::AsFortran(llvm::raw_ostream
&o
) const {
674 return EmitVar(o
, u
);
677 llvm::raw_ostream
&TypeParamInquiry::AsFortran(llvm::raw_ostream
&o
) const {
679 base_
.value().AsFortran(o
) << '%';
681 return EmitVar(o
, parameter_
);
684 llvm::raw_ostream
&Component::AsFortran(llvm::raw_ostream
&o
) const {
685 base_
.value().AsFortran(o
);
686 return EmitVar(o
<< '%', symbol_
);
689 llvm::raw_ostream
&NamedEntity::AsFortran(llvm::raw_ostream
&o
) const {
690 common::visit(common::visitors
{
691 [&](SymbolRef s
) { EmitVar(o
, s
); },
692 [&](const Component
&c
) { c
.AsFortran(o
); },
698 llvm::raw_ostream
&Triplet::AsFortran(llvm::raw_ostream
&o
) const {
699 EmitVar(o
, lower_
) << ':';
701 EmitVar(o
<< ':', stride_
.value());
705 llvm::raw_ostream
&Subscript::AsFortran(llvm::raw_ostream
&o
) const {
706 return EmitVar(o
, u
);
709 llvm::raw_ostream
&ArrayRef::AsFortran(llvm::raw_ostream
&o
) const {
712 for (const Subscript
&ss
: subscript_
) {
713 ss
.AsFortran(o
<< separator
);
719 llvm::raw_ostream
&CoarrayRef::AsFortran(llvm::raw_ostream
&o
) const {
721 for (const Symbol
&part
: base_
) {
730 for (const auto &sscript
: subscript_
) {
731 EmitVar(o
<< separator
, sscript
);
734 if (separator
== ',') {
738 for (const auto &css
: cosubscript_
) {
739 EmitVar(o
<< separator
, css
);
743 EmitVar(o
<< separator
, stat_
, "STAT=");
748 o
<< separator
, team_
, teamIsTeamNumber_
? "TEAM_NUMBER=" : "TEAM=");
753 llvm::raw_ostream
&DataRef::AsFortran(llvm::raw_ostream
&o
) const {
754 return EmitVar(o
, u
);
757 llvm::raw_ostream
&Substring::AsFortran(llvm::raw_ostream
&o
) const {
758 EmitVar(o
, parent_
) << '(';
759 EmitVar(o
, lower_
) << ':';
760 return EmitVar(o
, upper_
) << ')';
763 llvm::raw_ostream
&ComplexPart::AsFortran(llvm::raw_ostream
&o
) const {
764 return complex_
.AsFortran(o
) << '%' << EnumToString(part_
);
767 llvm::raw_ostream
&ProcedureDesignator::AsFortran(llvm::raw_ostream
&o
) const {
768 return EmitVar(o
, u
);
771 template <typename T
>
772 llvm::raw_ostream
&Designator
<T
>::AsFortran(llvm::raw_ostream
&o
) const {
773 common::visit(common::visitors
{
774 [&](SymbolRef symbol
) { EmitVar(o
, symbol
); },
775 [&](const auto &x
) { x
.AsFortran(o
); },
781 llvm::raw_ostream
&DescriptorInquiry::AsFortran(llvm::raw_ostream
&o
) const {
783 case Field::LowerBound
:
800 if (field_
== Field::Len
) {
802 } else if (field_
== Field::Rank
) {
805 if (dimension_
>= 0) {
806 o
<< ",dim=" << (dimension_
+ 1);
809 return o
<< ",kind=" << DescriptorInquiry::Result::kind
<< ")";
812 llvm::raw_ostream
&Assignment::AsFortran(llvm::raw_ostream
&o
) const {
815 [&](const Assignment::Intrinsic
&) {
816 rhs
.AsFortran(lhs
.AsFortran(o
) << '=');
818 [&](const ProcedureRef
&proc
) { proc
.AsFortran(o
<< "CALL "); },
819 [&](const BoundsSpec
&bounds
) {
821 if (!bounds
.empty()) {
823 for (const auto &bound
: bounds
) {
824 bound
.AsFortran(o
<< sep
) << ':';
829 rhs
.AsFortran(o
<< " => ");
831 [&](const BoundsRemapping
&bounds
) {
833 if (!bounds
.empty()) {
835 for (const auto &bound
: bounds
) {
836 bound
.first
.AsFortran(o
<< sep
) << ':';
837 bound
.second
.AsFortran(o
);
842 rhs
.AsFortran(o
<< " => ");
849 #ifdef _MSC_VER // disable bogus warning about missing definitions
850 #pragma warning(disable : 4661)
852 INSTANTIATE_CONSTANT_TEMPLATES
853 INSTANTIATE_EXPRESSION_TEMPLATES
854 INSTANTIATE_VARIABLE_TEMPLATES
855 } // namespace Fortran::evaluate