xref: /llvm-project/flang/lib/Evaluate/formatting.cpp (revision 01688ee9df5ead4dcff3c460d7f038f307f1ee5d)
1 //===-- lib/Evaluate/formatting.cpp ---------------------------------------===//
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 #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/symbol.h"
18 #include "llvm/Support/raw_ostream.h"
19 
20 namespace Fortran::evaluate {
21 
22 static void ShapeAsFortran(
23     llvm::raw_ostream &o, const ConstantSubscripts &shape) {
24   if (GetRank(shape) > 1) {
25     o << ",shape=";
26     char ch{'['};
27     for (auto dim : shape) {
28       o << ch << dim;
29       ch = ',';
30     }
31     o << "])";
32   }
33 }
34 
35 template <typename RESULT, typename VALUE>
36 llvm::raw_ostream &ConstantBase<RESULT, VALUE>::AsFortran(
37     llvm::raw_ostream &o) const {
38   if (Rank() > 1) {
39     o << "reshape(";
40   }
41   if (Rank() > 0) {
42     o << '[' << GetType().AsFortran() << "::";
43   }
44   bool first{true};
45   for (const auto &value : values_) {
46     if (first) {
47       first = false;
48     } else {
49       o << ',';
50     }
51     if constexpr (Result::category == TypeCategory::Integer) {
52       o << value.SignedDecimal() << '_' << Result::kind;
53     } else if constexpr (Result::category == TypeCategory::Real ||
54         Result::category == TypeCategory::Complex) {
55       value.AsFortran(o, Result::kind);
56     } else if constexpr (Result::category == TypeCategory::Character) {
57       o << Result::kind << '_' << parser::QuoteCharacterLiteral(value, true);
58     } else if constexpr (Result::category == TypeCategory::Logical) {
59       if (!value.IsCanonical()) {
60         o << "transfer(" << value.word().ToInt64() << "_8,.false._"
61           << Result::kind << ')';
62       } else if (value.IsTrue()) {
63         o << ".true." << '_' << Result::kind;
64       } else {
65         o << ".false." << '_' << Result::kind;
66       }
67     } else {
68       StructureConstructor{result_.derivedTypeSpec(), value}.AsFortran(o);
69     }
70   }
71   if (Rank() > 0) {
72     o << ']';
73   }
74   ShapeAsFortran(o, shape());
75   return o;
76 }
77 
78 template <int KIND>
79 llvm::raw_ostream &Constant<Type<TypeCategory::Character, KIND>>::AsFortran(
80     llvm::raw_ostream &o) const {
81   if (Rank() > 1) {
82     o << "reshape(";
83   }
84   if (Rank() > 0) {
85     o << '[' << GetType().AsFortran(std::to_string(length_)) << "::";
86   }
87   auto total{static_cast<ConstantSubscript>(size())};
88   for (ConstantSubscript j{0}; j < total; ++j) {
89     Scalar<Result> value{values_.substr(j * length_, length_)};
90     if (j > 0) {
91       o << ',';
92     }
93     if (Result::kind != 1) {
94       o << Result::kind << '_';
95     }
96     o << parser::QuoteCharacterLiteral(value);
97   }
98   if (Rank() > 0) {
99     o << ']';
100   }
101   ShapeAsFortran(o, shape());
102   return o;
103 }
104 
105 llvm::raw_ostream &ActualArgument::AssumedType::AsFortran(
106     llvm::raw_ostream &o) const {
107   return o << symbol_->name().ToString();
108 }
109 
110 llvm::raw_ostream &ActualArgument::AsFortran(llvm::raw_ostream &o) const {
111   if (keyword_) {
112     o << keyword_->ToString() << '=';
113   }
114   common::visit(
115       common::visitors{
116           [&](const common::CopyableIndirection<Expr<SomeType>> &expr) {
117             expr.value().AsFortran(o);
118           },
119           [&](const AssumedType &assumedType) { assumedType.AsFortran(o); },
120           [&](const common::Label &label) { o << '*' << label; },
121       },
122       u_);
123   return o;
124 }
125 
126 llvm::raw_ostream &SpecificIntrinsic::AsFortran(llvm::raw_ostream &o) const {
127   return o << name;
128 }
129 
130 llvm::raw_ostream &ProcedureRef::AsFortran(llvm::raw_ostream &o) const {
131   for (const auto &arg : arguments_) {
132     if (arg && arg->isPassedObject()) {
133       arg->AsFortran(o) << '%';
134       break;
135     }
136   }
137   proc_.AsFortran(o);
138   char separator{'('};
139   for (const auto &arg : arguments_) {
140     if (arg && !arg->isPassedObject()) {
141       arg->AsFortran(o << separator);
142       separator = ',';
143     }
144   }
145   if (separator == '(') {
146     o << '(';
147   }
148   return o << ')';
149 }
150 
151 // Operator precedence formatting; insert parentheses around operands
152 // only when necessary.
153 
154 enum class Precedence { // in increasing order for sane comparisons
155   DefinedBinary,
156   Or,
157   And,
158   Equivalence, // .EQV., .NEQV.
159   Not, // which binds *less* tightly in Fortran than relations
160   Relational,
161   Additive, // +, -, and (arbitrarily) //
162   Negate, // which binds *less* tightly than *, /, **
163   Multiplicative, // *, /
164   Power, // **, which is right-associative unlike the other dyadic operators
165   DefinedUnary,
166   Top,
167 };
168 
169 template <typename A> constexpr Precedence ToPrecedence(const A &) {
170   return Precedence::Top;
171 }
172 template <int KIND>
173 static Precedence ToPrecedence(const LogicalOperation<KIND> &x) {
174   switch (x.logicalOperator) {
175     SWITCH_COVERS_ALL_CASES
176   case LogicalOperator::And:
177     return Precedence::And;
178   case LogicalOperator::Or:
179     return Precedence::Or;
180   case LogicalOperator::Not:
181     return Precedence::Not;
182   case LogicalOperator::Eqv:
183   case LogicalOperator::Neqv:
184     return Precedence::Equivalence;
185   }
186 }
187 template <int KIND> constexpr Precedence ToPrecedence(const Not<KIND> &) {
188   return Precedence::Not;
189 }
190 template <typename T> constexpr Precedence ToPrecedence(const Relational<T> &) {
191   return Precedence::Relational;
192 }
193 template <typename T> constexpr Precedence ToPrecedence(const Add<T> &) {
194   return Precedence::Additive;
195 }
196 template <typename T> constexpr Precedence ToPrecedence(const Subtract<T> &) {
197   return Precedence::Additive;
198 }
199 template <int KIND> constexpr Precedence ToPrecedence(const Concat<KIND> &) {
200   return Precedence::Additive;
201 }
202 template <typename T> constexpr Precedence ToPrecedence(const Negate<T> &) {
203   return Precedence::Negate;
204 }
205 template <typename T> constexpr Precedence ToPrecedence(const Multiply<T> &) {
206   return Precedence::Multiplicative;
207 }
208 template <typename T> constexpr Precedence ToPrecedence(const Divide<T> &) {
209   return Precedence::Multiplicative;
210 }
211 template <typename T> constexpr Precedence ToPrecedence(const Power<T> &) {
212   return Precedence::Power;
213 }
214 template <typename T>
215 constexpr Precedence ToPrecedence(const RealToIntPower<T> &) {
216   return Precedence::Power;
217 }
218 template <typename T> static Precedence ToPrecedence(const Constant<T> &x) {
219   static constexpr TypeCategory cat{T::category};
220   if constexpr (cat == TypeCategory::Integer || cat == TypeCategory::Real) {
221     if (auto n{GetScalarConstantValue<T>(x)}) {
222       if (n->IsNegative()) {
223         return Precedence::Negate;
224       }
225     }
226   }
227   return Precedence::Top;
228 }
229 template <typename T> static Precedence ToPrecedence(const Expr<T> &expr) {
230   return common::visit([](const auto &x) { return ToPrecedence(x); }, expr.u);
231 }
232 
233 template <typename T> static bool IsNegatedScalarConstant(const Expr<T> &expr) {
234   static constexpr TypeCategory cat{T::category};
235   if constexpr (cat == TypeCategory::Integer || cat == TypeCategory::Real) {
236     if (auto n{GetScalarConstantValue<T>(expr)}) {
237       return n->IsNegative();
238     }
239   }
240   return false;
241 }
242 
243 template <TypeCategory CAT>
244 static bool IsNegatedScalarConstant(const Expr<SomeKind<CAT>> &expr) {
245   return common::visit(
246       [](const auto &x) { return IsNegatedScalarConstant(x); }, expr.u);
247 }
248 
249 struct OperatorSpelling {
250   const char *prefix{""}, *infix{","}, *suffix{""};
251 };
252 
253 template <typename A> constexpr OperatorSpelling SpellOperator(const A &) {
254   return OperatorSpelling{};
255 }
256 template <typename A>
257 constexpr OperatorSpelling SpellOperator(const Negate<A> &) {
258   return OperatorSpelling{"-", "", ""};
259 }
260 template <typename A>
261 constexpr OperatorSpelling SpellOperator(const Parentheses<A> &) {
262   return OperatorSpelling{"(", "", ")"};
263 }
264 template <int KIND>
265 static OperatorSpelling SpellOperator(const ComplexComponent<KIND> &x) {
266   return {x.isImaginaryPart ? "aimag(" : "real(", "", ")"};
267 }
268 template <int KIND>
269 constexpr OperatorSpelling SpellOperator(const Not<KIND> &) {
270   return OperatorSpelling{".NOT.", "", ""};
271 }
272 template <int KIND>
273 constexpr OperatorSpelling SpellOperator(const SetLength<KIND> &) {
274   return OperatorSpelling{"%SET_LENGTH(", ",", ")"};
275 }
276 template <int KIND>
277 constexpr OperatorSpelling SpellOperator(const ComplexConstructor<KIND> &) {
278   return OperatorSpelling{"(", ",", ")"};
279 }
280 template <typename A> constexpr OperatorSpelling SpellOperator(const Add<A> &) {
281   return OperatorSpelling{"", "+", ""};
282 }
283 template <typename A>
284 constexpr OperatorSpelling SpellOperator(const Subtract<A> &) {
285   return OperatorSpelling{"", "-", ""};
286 }
287 template <typename A>
288 constexpr OperatorSpelling SpellOperator(const Multiply<A> &) {
289   return OperatorSpelling{"", "*", ""};
290 }
291 template <typename A>
292 constexpr OperatorSpelling SpellOperator(const Divide<A> &) {
293   return OperatorSpelling{"", "/", ""};
294 }
295 template <typename A>
296 constexpr OperatorSpelling SpellOperator(const Power<A> &) {
297   return OperatorSpelling{"", "**", ""};
298 }
299 template <typename A>
300 constexpr OperatorSpelling SpellOperator(const RealToIntPower<A> &) {
301   return OperatorSpelling{"", "**", ""};
302 }
303 template <typename A>
304 static OperatorSpelling SpellOperator(const Extremum<A> &x) {
305   return OperatorSpelling{
306       x.ordering == Ordering::Less ? "min(" : "max(", ",", ")"};
307 }
308 template <int KIND>
309 constexpr OperatorSpelling SpellOperator(const Concat<KIND> &) {
310   return OperatorSpelling{"", "//", ""};
311 }
312 template <int KIND>
313 static OperatorSpelling SpellOperator(const LogicalOperation<KIND> &x) {
314   return OperatorSpelling{"", AsFortran(x.logicalOperator), ""};
315 }
316 template <typename T>
317 static OperatorSpelling SpellOperator(const Relational<T> &x) {
318   return OperatorSpelling{"", AsFortran(x.opr), ""};
319 }
320 
321 template <typename D, typename R, typename... O>
322 llvm::raw_ostream &Operation<D, R, O...>::AsFortran(
323     llvm::raw_ostream &o) const {
324   Precedence lhsPrec{ToPrecedence(left())};
325   OperatorSpelling spelling{SpellOperator(derived())};
326   o << spelling.prefix;
327   Precedence thisPrec{ToPrecedence(derived())};
328   if constexpr (operands == 1) {
329     if (thisPrec != Precedence::Top && lhsPrec < thisPrec) {
330       left().AsFortran(o << '(') << ')';
331     } else {
332       left().AsFortran(o);
333     }
334   } else {
335     if (thisPrec != Precedence::Top &&
336         (lhsPrec < thisPrec ||
337             (lhsPrec == Precedence::Power && thisPrec == Precedence::Power))) {
338       left().AsFortran(o << '(') << ')';
339     } else {
340       left().AsFortran(o);
341     }
342     o << spelling.infix;
343     Precedence rhsPrec{ToPrecedence(right())};
344     if (thisPrec != Precedence::Top && rhsPrec < thisPrec) {
345       right().AsFortran(o << '(') << ')';
346     } else {
347       right().AsFortran(o);
348     }
349   }
350   return o << spelling.suffix;
351 }
352 
353 template <typename TO, TypeCategory FROMCAT>
354 llvm::raw_ostream &Convert<TO, FROMCAT>::AsFortran(llvm::raw_ostream &o) const {
355   static_assert(TO::category == TypeCategory::Integer ||
356           TO::category == TypeCategory::Real ||
357           TO::category == TypeCategory::Complex ||
358           TO::category == TypeCategory::Character ||
359           TO::category == TypeCategory::Logical,
360       "Convert<> to bad category!");
361   if constexpr (TO::category == TypeCategory::Character) {
362     this->left().AsFortran(o << "achar(iachar(") << ')';
363   } else if constexpr (TO::category == TypeCategory::Integer) {
364     this->left().AsFortran(o << "int(");
365   } else if constexpr (TO::category == TypeCategory::Real) {
366     this->left().AsFortran(o << "real(");
367   } else if constexpr (TO::category == TypeCategory::Complex) {
368     this->left().AsFortran(o << "cmplx(");
369   } else {
370     this->left().AsFortran(o << "logical(");
371   }
372   return o << ",kind=" << TO::kind << ')';
373 }
374 
375 llvm::raw_ostream &Relational<SomeType>::AsFortran(llvm::raw_ostream &o) const {
376   common::visit([&](const auto &rel) { rel.AsFortran(o); }, u);
377   return o;
378 }
379 
380 template <typename T>
381 llvm::raw_ostream &EmitArray(llvm::raw_ostream &o, const Expr<T> &expr) {
382   return expr.AsFortran(o);
383 }
384 
385 template <typename T>
386 llvm::raw_ostream &EmitArray(
387     llvm::raw_ostream &, const ArrayConstructorValues<T> &);
388 
389 template <typename T>
390 llvm::raw_ostream &EmitArray(llvm::raw_ostream &o, const ImpliedDo<T> &implDo) {
391   o << '(';
392   EmitArray(o, implDo.values());
393   o << ',' << ImpliedDoIndex::Result::AsFortran()
394     << "::" << implDo.name().ToString() << '=';
395   implDo.lower().AsFortran(o) << ',';
396   implDo.upper().AsFortran(o) << ',';
397   implDo.stride().AsFortran(o) << ')';
398   return o;
399 }
400 
401 template <typename T>
402 llvm::raw_ostream &EmitArray(
403     llvm::raw_ostream &o, const ArrayConstructorValues<T> &values) {
404   const char *sep{""};
405   for (const auto &value : values) {
406     o << sep;
407     common::visit([&](const auto &x) { EmitArray(o, x); }, value.u);
408     sep = ",";
409   }
410   return o;
411 }
412 
413 template <typename T>
414 llvm::raw_ostream &ArrayConstructor<T>::AsFortran(llvm::raw_ostream &o) const {
415   o << '[' << GetType().AsFortran() << "::";
416   EmitArray(o, *this);
417   return o << ']';
418 }
419 
420 template <int KIND>
421 llvm::raw_ostream &
422 ArrayConstructor<Type<TypeCategory::Character, KIND>>::AsFortran(
423     llvm::raw_ostream &o) const {
424   o << '[';
425   if (const auto *len{LEN()}) {
426     o << GetType().AsFortran(len->AsFortran()) << "::";
427   }
428   EmitArray(o, *this);
429   return o << ']';
430 }
431 
432 llvm::raw_ostream &ArrayConstructor<SomeDerived>::AsFortran(
433     llvm::raw_ostream &o) const {
434   o << '[' << GetType().AsFortran() << "::";
435   EmitArray(o, *this);
436   return o << ']';
437 }
438 
439 template <typename RESULT>
440 std::string ExpressionBase<RESULT>::AsFortran() const {
441   std::string buf;
442   llvm::raw_string_ostream ss{buf};
443   AsFortran(ss);
444   return ss.str();
445 }
446 
447 template <typename RESULT>
448 llvm::raw_ostream &ExpressionBase<RESULT>::AsFortran(
449     llvm::raw_ostream &o) const {
450   common::visit(common::visitors{
451                     [&](const BOZLiteralConstant &x) {
452                       o << "z'" << x.Hexadecimal() << "'";
453                     },
454                     [&](const NullPointer &) { o << "NULL()"; },
455                     [&](const common::CopyableIndirection<Substring> &s) {
456                       s.value().AsFortran(o);
457                     },
458                     [&](const ImpliedDoIndex &i) { o << i.name.ToString(); },
459                     [&](const auto &x) { x.AsFortran(o); },
460                 },
461       derived().u);
462   return o;
463 }
464 
465 llvm::raw_ostream &StructureConstructor::AsFortran(llvm::raw_ostream &o) const {
466   o << DerivedTypeSpecAsFortran(result_.derivedTypeSpec());
467   if (values_.empty()) {
468     o << '(';
469   } else {
470     char ch{'('};
471     for (const auto &[symbol, value] : values_) {
472       value.value().AsFortran(o << ch << symbol->name().ToString() << '=');
473       ch = ',';
474     }
475   }
476   return o << ')';
477 }
478 
479 std::string DynamicType::AsFortran() const {
480   if (derived_) {
481     CHECK(category_ == TypeCategory::Derived);
482     return DerivedTypeSpecAsFortran(*derived_);
483   } else if (charLengthParamValue_ || knownLength()) {
484     std::string result{"CHARACTER(KIND="s + std::to_string(kind_) + ",LEN="};
485     if (knownLength()) {
486       result += std::to_string(*knownLength()) + "_8";
487     } else if (charLengthParamValue_->isAssumed()) {
488       result += '*';
489     } else if (charLengthParamValue_->isDeferred()) {
490       result += ':';
491     } else if (const auto &length{charLengthParamValue_->GetExplicit()}) {
492       result += length->AsFortran();
493     }
494     return result + ')';
495   } else if (IsUnlimitedPolymorphic()) {
496     return "CLASS(*)";
497   } else if (IsAssumedType()) {
498     return "TYPE(*)";
499   } else if (IsTypelessIntrinsicArgument()) {
500     return "(typeless intrinsic function argument)";
501   } else {
502     return parser::ToUpperCaseLetters(EnumToString(category_)) + '(' +
503         std::to_string(kind_) + ')';
504   }
505 }
506 
507 std::string DynamicType::AsFortran(std::string &&charLenExpr) const {
508   if (!charLenExpr.empty() && category_ == TypeCategory::Character) {
509     return "CHARACTER(KIND=" + std::to_string(kind_) +
510         ",LEN=" + std::move(charLenExpr) + ')';
511   } else {
512     return AsFortran();
513   }
514 }
515 
516 std::string SomeDerived::AsFortran() const {
517   if (IsUnlimitedPolymorphic()) {
518     return "CLASS(*)";
519   } else {
520     return "TYPE("s + DerivedTypeSpecAsFortran(derivedTypeSpec()) + ')';
521   }
522 }
523 
524 std::string DerivedTypeSpecAsFortran(const semantics::DerivedTypeSpec &spec) {
525   std::string buf;
526   llvm::raw_string_ostream ss{buf};
527   ss << spec.name().ToString();
528   char ch{'('};
529   for (const auto &[name, value] : spec.parameters()) {
530     ss << ch << name.ToString() << '=';
531     ch = ',';
532     if (value.isAssumed()) {
533       ss << '*';
534     } else if (value.isDeferred()) {
535       ss << ':';
536     } else {
537       value.GetExplicit()->AsFortran(ss);
538     }
539   }
540   if (ch != '(') {
541     ss << ')';
542   }
543   return ss.str();
544 }
545 
546 llvm::raw_ostream &EmitVar(llvm::raw_ostream &o, const Symbol &symbol) {
547   return o << symbol.name().ToString();
548 }
549 
550 llvm::raw_ostream &EmitVar(llvm::raw_ostream &o, const std::string &lit) {
551   return o << parser::QuoteCharacterLiteral(lit);
552 }
553 
554 llvm::raw_ostream &EmitVar(llvm::raw_ostream &o, const std::u16string &lit) {
555   return o << parser::QuoteCharacterLiteral(lit);
556 }
557 
558 llvm::raw_ostream &EmitVar(llvm::raw_ostream &o, const std::u32string &lit) {
559   return o << parser::QuoteCharacterLiteral(lit);
560 }
561 
562 template <typename A>
563 llvm::raw_ostream &EmitVar(llvm::raw_ostream &o, const A &x) {
564   return x.AsFortran(o);
565 }
566 
567 template <typename A>
568 llvm::raw_ostream &EmitVar(llvm::raw_ostream &o, common::Reference<A> x) {
569   return EmitVar(o, *x);
570 }
571 
572 template <typename A>
573 llvm::raw_ostream &EmitVar(
574     llvm::raw_ostream &o, const A *p, const char *kw = nullptr) {
575   if (p) {
576     if (kw) {
577       o << kw;
578     }
579     EmitVar(o, *p);
580   }
581   return o;
582 }
583 
584 template <typename A>
585 llvm::raw_ostream &EmitVar(
586     llvm::raw_ostream &o, const std::optional<A> &x, const char *kw = nullptr) {
587   if (x) {
588     if (kw) {
589       o << kw;
590     }
591     EmitVar(o, *x);
592   }
593   return o;
594 }
595 
596 template <typename A, bool COPY>
597 llvm::raw_ostream &EmitVar(llvm::raw_ostream &o,
598     const common::Indirection<A, COPY> &p, const char *kw = nullptr) {
599   if (kw) {
600     o << kw;
601   }
602   EmitVar(o, p.value());
603   return o;
604 }
605 
606 template <typename A>
607 llvm::raw_ostream &EmitVar(llvm::raw_ostream &o, const std::shared_ptr<A> &p) {
608   CHECK(p);
609   return EmitVar(o, *p);
610 }
611 
612 template <typename... A>
613 llvm::raw_ostream &EmitVar(llvm::raw_ostream &o, const std::variant<A...> &u) {
614   common::visit([&](const auto &x) { EmitVar(o, x); }, u);
615   return o;
616 }
617 
618 llvm::raw_ostream &BaseObject::AsFortran(llvm::raw_ostream &o) const {
619   return EmitVar(o, u);
620 }
621 
622 llvm::raw_ostream &TypeParamInquiry::AsFortran(llvm::raw_ostream &o) const {
623   if (base_) {
624     base_.value().AsFortran(o) << '%';
625   }
626   return EmitVar(o, parameter_);
627 }
628 
629 llvm::raw_ostream &Component::AsFortran(llvm::raw_ostream &o) const {
630   base_.value().AsFortran(o);
631   return EmitVar(o << '%', symbol_);
632 }
633 
634 llvm::raw_ostream &NamedEntity::AsFortran(llvm::raw_ostream &o) const {
635   common::visit(common::visitors{
636                     [&](SymbolRef s) { EmitVar(o, s); },
637                     [&](const Component &c) { c.AsFortran(o); },
638                 },
639       u_);
640   return o;
641 }
642 
643 llvm::raw_ostream &Triplet::AsFortran(llvm::raw_ostream &o) const {
644   EmitVar(o, lower_) << ':';
645   EmitVar(o, upper_);
646   EmitVar(o << ':', stride_.value());
647   return o;
648 }
649 
650 llvm::raw_ostream &Subscript::AsFortran(llvm::raw_ostream &o) const {
651   return EmitVar(o, u);
652 }
653 
654 llvm::raw_ostream &ArrayRef::AsFortran(llvm::raw_ostream &o) const {
655   base_.AsFortran(o);
656   char separator{'('};
657   for (const Subscript &ss : subscript_) {
658     ss.AsFortran(o << separator);
659     separator = ',';
660   }
661   return o << ')';
662 }
663 
664 llvm::raw_ostream &CoarrayRef::AsFortran(llvm::raw_ostream &o) const {
665   bool first{true};
666   for (const Symbol &part : base_) {
667     if (first) {
668       first = false;
669     } else {
670       o << '%';
671     }
672     EmitVar(o, part);
673   }
674   char separator{'('};
675   for (const auto &sscript : subscript_) {
676     EmitVar(o << separator, sscript);
677     separator = ',';
678   }
679   if (separator == ',') {
680     o << ')';
681   }
682   separator = '[';
683   for (const auto &css : cosubscript_) {
684     EmitVar(o << separator, css);
685     separator = ',';
686   }
687   if (stat_) {
688     EmitVar(o << separator, stat_, "STAT=");
689     separator = ',';
690   }
691   if (team_) {
692     EmitVar(
693         o << separator, team_, teamIsTeamNumber_ ? "TEAM_NUMBER=" : "TEAM=");
694   }
695   return o << ']';
696 }
697 
698 llvm::raw_ostream &DataRef::AsFortran(llvm::raw_ostream &o) const {
699   return EmitVar(o, u);
700 }
701 
702 llvm::raw_ostream &Substring::AsFortran(llvm::raw_ostream &o) const {
703   EmitVar(o, parent_) << '(';
704   EmitVar(o, lower_) << ':';
705   return EmitVar(o, upper_) << ')';
706 }
707 
708 llvm::raw_ostream &ComplexPart::AsFortran(llvm::raw_ostream &o) const {
709   return complex_.AsFortran(o) << '%' << EnumToString(part_);
710 }
711 
712 llvm::raw_ostream &ProcedureDesignator::AsFortran(llvm::raw_ostream &o) const {
713   return EmitVar(o, u);
714 }
715 
716 template <typename T>
717 llvm::raw_ostream &Designator<T>::AsFortran(llvm::raw_ostream &o) const {
718   common::visit(common::visitors{
719                     [&](SymbolRef symbol) { EmitVar(o, symbol); },
720                     [&](const auto &x) { x.AsFortran(o); },
721                 },
722       u);
723   return o;
724 }
725 
726 llvm::raw_ostream &DescriptorInquiry::AsFortran(llvm::raw_ostream &o) const {
727   switch (field_) {
728   case Field::LowerBound:
729     o << "lbound(";
730     break;
731   case Field::Extent:
732     o << "size(";
733     break;
734   case Field::Stride:
735     o << "%STRIDE(";
736     break;
737   case Field::Rank:
738     o << "int(rank(";
739     break;
740   case Field::Len:
741     o << "int(";
742     break;
743   }
744   base_.AsFortran(o);
745   if (field_ == Field::Len) {
746     o << "%len";
747   } else if (field_ == Field::Rank) {
748     o << ")";
749   } else {
750     if (dimension_ >= 0) {
751       o << ",dim=" << (dimension_ + 1);
752     }
753   }
754   return o << ",kind=" << DescriptorInquiry::Result::kind << ")";
755 }
756 
757 llvm::raw_ostream &Assignment::AsFortran(llvm::raw_ostream &o) const {
758   common::visit(
759       common::visitors{
760           [&](const Assignment::Intrinsic &) {
761             rhs.AsFortran(lhs.AsFortran(o) << '=');
762           },
763           [&](const ProcedureRef &proc) { proc.AsFortran(o << "CALL "); },
764           [&](const BoundsSpec &bounds) {
765             lhs.AsFortran(o);
766             if (!bounds.empty()) {
767               char sep{'('};
768               for (const auto &bound : bounds) {
769                 bound.AsFortran(o << sep) << ':';
770                 sep = ',';
771               }
772               o << ')';
773             }
774             rhs.AsFortran(o << " => ");
775           },
776           [&](const BoundsRemapping &bounds) {
777             lhs.AsFortran(o);
778             if (!bounds.empty()) {
779               char sep{'('};
780               for (const auto &bound : bounds) {
781                 bound.first.AsFortran(o << sep) << ':';
782                 bound.second.AsFortran(o);
783                 sep = ',';
784               }
785               o << ')';
786             }
787             rhs.AsFortran(o << " => ");
788           },
789       },
790       u);
791   return o;
792 }
793 
794 #ifdef _MSC_VER // disable bogus warning about missing definitions
795 #pragma warning(disable : 4661)
796 #endif
797 INSTANTIATE_CONSTANT_TEMPLATES
798 INSTANTIATE_EXPRESSION_TEMPLATES
799 INSTANTIATE_VARIABLE_TEMPLATES
800 } // namespace Fortran::evaluate
801