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