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