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