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