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