1 // Copyright (c) 2019, NVIDIA CORPORATION.  All rights reserved.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //     http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #include "formatting.h"
16 #include "call.h"
17 #include "constant.h"
18 #include "expression.h"
19 #include "fold.h"
20 #include "tools.h"
21 #include "../parser/characters.h"
22 #include "../semantics/symbol.h"
23 
24 namespace Fortran::evaluate {
25 
26 bool formatForPGF90{false};
27 
ShapeAsFortran(std::ostream & o,const ConstantSubscripts & shape)28 static void ShapeAsFortran(std::ostream &o, const ConstantSubscripts &shape) {
29   if (GetRank(shape) > 1) {
30     o << ",shape=";
31     char ch{'['};
32     for (auto dim : shape) {
33       o << ch << dim;
34       ch = ',';
35     }
36     o << "])";
37   }
38 }
39 
40 template<typename RESULT, typename VALUE>
AsFortran(std::ostream & o) const41 std::ostream &ConstantBase<RESULT, VALUE>::AsFortran(std::ostream &o) const {
42   if (Rank() > 1) {
43     o << "reshape(";
44   }
45   if (Rank() > 0) {
46     o << '[' << GetType().AsFortran() << "::";
47   }
48   bool first{true};
49   for (const auto &value : values_) {
50     if (first) {
51       first = false;
52     } else {
53       o << ',';
54     }
55     if constexpr (Result::category == TypeCategory::Integer) {
56       o << value.SignedDecimal() << '_' << Result::kind;
57     } else if constexpr (Result::category == TypeCategory::Real ||
58         Result::category == TypeCategory::Complex) {
59       value.AsFortran(o, Result::kind);
60     } else if constexpr (Result::category == TypeCategory::Character) {
61       o << Result::kind << '_' << parser::QuoteCharacterLiteral(value, true);
62     } else if constexpr (Result::category == TypeCategory::Logical) {
63       if (value.IsTrue()) {
64         o << ".true.";
65       } else {
66         o << ".false.";
67       }
68       o << '_' << Result::kind;
69     } else {
70       StructureConstructor{result_.derivedTypeSpec(), value}.AsFortran(o);
71     }
72   }
73   if (Rank() > 0) {
74     o << ']';
75   }
76   ShapeAsFortran(o, shape());
77   return o;
78 }
79 
80 template<int KIND>
AsFortran(std::ostream & o) const81 std::ostream &Constant<Type<TypeCategory::Character, KIND>>::AsFortran(
82     std::ostream &o) const {
83   if (Rank() > 1) {
84     o << "reshape(";
85   }
86   if (Rank() > 0) {
87     o << '[' << GetType().AsFortran(std::to_string(length_)) << "::";
88   }
89   auto total{static_cast<ConstantSubscript>(size())};
90   for (ConstantSubscript j{0}; j < total; ++j) {
91     Scalar<Result> value{values_.substr(j * length_, length_)};
92     if (j > 0) {
93       o << ',';
94     }
95     if (Result::kind != 1 || !formatForPGF90) {
96       o << Result::kind << '_';
97     }
98     o << parser::QuoteCharacterLiteral(value);
99   }
100   if (Rank() > 0) {
101     o << ']';
102   }
103   ShapeAsFortran(o, shape());
104   return o;
105 }
106 
AsFortran(std::ostream & o) const107 std::ostream &ActualArgument::AssumedType::AsFortran(std::ostream &o) const {
108   return o << symbol_->name().ToString();
109 }
110 
AsFortran(std::ostream & o) const111 std::ostream &ActualArgument::AsFortran(std::ostream &o) const {
112   if (keyword.has_value()) {
113     o << keyword->ToString() << '=';
114   }
115   if (isAlternateReturn) {
116     o << '*';
117   }
118   if (const auto *expr{UnwrapExpr()}) {
119     return expr->AsFortran(o);
120   } else {
121     return std::get<AssumedType>(u_).AsFortran(o);
122   }
123 }
124 
AsFortran(std::ostream & o) const125 std::ostream &SpecificIntrinsic::AsFortran(std::ostream &o) const {
126   return o << name;
127 }
128 
AsFortran(std::ostream & o) const129 std::ostream &ProcedureRef::AsFortran(std::ostream &o) const {
130   proc_.AsFortran(o);
131   char separator{'('};
132   for (const auto &arg : arguments_) {
133     if (arg.has_value()) {
134       arg->AsFortran(o << separator);
135       separator = ',';
136     }
137   }
138   if (separator == '(') {
139     o << '(';
140   }
141   return o << ')';
142 }
143 
144 // Operator precedence formatting; insert parentheses around operands
145 // only when necessary.
146 
147 enum class Precedence {  // in increasing order for sane comparisons
148   DefinedBinary,
149   Or,
150   And,
151   Equivalence,  // .EQV., .NEQV.
152   Not,  // which binds *less* tightly in Fortran than relations
153   Relational,
154   Additive,  // +, -, and (arbitrarily) //
155   Negate,  // which binds *less* tightly than *, /, **
156   Multiplicative,  // *, /
157   Power,  // **, which is right-associative unlike the other dyadic operators
158   DefinedUnary,
159   Parenthesize,  // (x), (real, imaginary)
160   Constant,  // parenthesize if negative integer/real operand
161   Primary,  // don't parenthesize
162 };
163 
164 template<typename A> constexpr Precedence ToPrecedence{Precedence::Primary};
165 
166 template<int KIND>
167 constexpr Precedence ToPrecedence<LogicalOperation<KIND>>{Precedence::Or};
168 template<int KIND>
169 constexpr Precedence ToPrecedence<Not<KIND>>{Precedence::Not};
170 template<typename T>
171 constexpr Precedence ToPrecedence<Relational<T>>{Precedence::Relational};
172 template<typename T>
173 constexpr Precedence ToPrecedence<Add<T>>{Precedence::Additive};
174 template<typename T>
175 constexpr Precedence ToPrecedence<Subtract<T>>{Precedence::Additive};
176 template<int KIND>
177 constexpr Precedence ToPrecedence<Concat<KIND>>{Precedence::Additive};
178 template<typename T>
179 constexpr Precedence ToPrecedence<Negate<T>>{Precedence::Negate};
180 template<typename T>
181 constexpr Precedence ToPrecedence<Multiply<T>>{Precedence::Multiplicative};
182 template<typename T>
183 constexpr Precedence ToPrecedence<Divide<T>>{Precedence::Multiplicative};
184 template<typename T>
185 constexpr Precedence ToPrecedence<Power<T>>{Precedence::Power};
186 template<typename T>
187 constexpr Precedence ToPrecedence<RealToIntPower<T>>{Precedence::Power};
188 template<typename T>
189 constexpr Precedence ToPrecedence<Constant<T>>{Precedence::Constant};
190 template<int KIND>
191 constexpr Precedence ToPrecedence<SetLength<KIND>>{Precedence::Constant};
192 template<typename T>
193 constexpr Precedence ToPrecedence<Parentheses<T>>{Precedence::Parenthesize};
194 template<int KIND>
195 constexpr Precedence ToPrecedence<ComplexConstructor<KIND>>{
196     Precedence::Parenthesize};
197 
198 template<typename T>
GetPrecedence(const Expr<T> & expr)199 static constexpr Precedence GetPrecedence(const Expr<T> &expr) {
200   return std::visit(
201       [](const auto &x) {
202         static constexpr Precedence prec{
203             ToPrecedence<std::decay_t<decltype(x)>>};
204         if constexpr (prec == Precedence::Or) {
205           // Distinguish the four logical binary operations.
206           switch (x.logicalOperator) {
207           case LogicalOperator::And: return Precedence::And;
208           case LogicalOperator::Or: return Precedence::Or;
209           case LogicalOperator::Eqv:
210           case LogicalOperator::Neqv:
211             return Precedence::Equivalence;
212             CRASH_NO_CASE;
213           }
214         }
215         return prec;
216       },
217       expr.u);
218 }
219 template<TypeCategory CAT>
GetPrecedence(const Expr<SomeKind<CAT>> & expr)220 static constexpr Precedence GetPrecedence(const Expr<SomeKind<CAT>> &expr) {
221   return std::visit([](const auto &x) { return GetPrecedence(x); }, expr.u);
222 }
223 
IsNegatedScalarConstant(const Expr<T> & expr)224 template<typename T> static bool IsNegatedScalarConstant(const Expr<T> &expr) {
225   static constexpr TypeCategory cat{T::category};
226   if constexpr (cat == TypeCategory::Integer || cat == TypeCategory::Real) {
227     if (auto n{GetScalarConstantValue<T>(expr)}) {
228       return n->IsNegative();
229     }
230   }
231   return false;
232 }
233 
234 template<TypeCategory CAT>
IsNegatedScalarConstant(const Expr<SomeKind<CAT>> & expr)235 static bool IsNegatedScalarConstant(const Expr<SomeKind<CAT>> &expr) {
236   return std::visit(
237       [](const auto &x) { return IsNegatedScalarConstant(x); }, expr.u);
238 }
239 
240 template<typename D, typename R, typename... O>
AsFortran(std::ostream & o) const241 std::ostream &Operation<D, R, O...>::AsFortran(std::ostream &o) const {
242   Precedence lhsPrec{GetPrecedence(left())};
243   o << derived().Prefix();
244   static constexpr Precedence thisPrec{ToPrecedence<D>};
245   if constexpr (operands == 1) {
246     bool parens{lhsPrec < Precedence::Constant &&
247         !(thisPrec == Precedence::Not && lhsPrec == Precedence::Relational)};
248     o << (parens ? "(" : "") << left() << (parens ? ")" : "");
249   } else {
250     bool lhsParens{lhsPrec == Precedence::Parenthesize || lhsPrec < thisPrec ||
251         (lhsPrec == thisPrec && lhsPrec == Precedence::Power) ||
252         (thisPrec != Precedence::Additive && lhsPrec == Precedence::Constant &&
253             IsNegatedScalarConstant(left()))};
254     o << (lhsParens ? "(" : "") << left() << (lhsParens ? ")" : "");
255     o << derived().Infix();
256     Precedence rhsPrec{GetPrecedence(right())};
257     bool rhsParens{rhsPrec == Precedence::Parenthesize ||
258         rhsPrec == Precedence::Negate || rhsPrec < thisPrec ||
259         (rhsPrec == Precedence::Constant && IsNegatedScalarConstant(right()))};
260     o << (rhsParens ? "(" : "") << right() << (rhsParens ? ")" : "");
261   }
262   return o << derived().Suffix();
263 }
264 
265 template<typename TO, TypeCategory FROMCAT>
AsFortran(std::ostream & o) const266 std::ostream &Convert<TO, FROMCAT>::AsFortran(std::ostream &o) const {
267   static_assert(TO::category == TypeCategory::Integer ||
268           TO::category == TypeCategory::Real ||
269           TO::category == TypeCategory::Character ||
270           TO::category == TypeCategory::Logical,
271       "Convert<> to bad category!");
272   if constexpr (TO::category == TypeCategory::Character) {
273     this->left().AsFortran(o << "achar(iachar(") << ')';
274   } else if constexpr (TO::category == TypeCategory::Integer) {
275     this->left().AsFortran(o << "int(");
276   } else if constexpr (TO::category == TypeCategory::Real) {
277     this->left().AsFortran(o << "real(");
278   } else {
279     this->left().AsFortran(o << "logical(");
280   }
281   return o << ",kind=" << TO::kind << ')';
282 }
283 
Infix() const284 template<typename A> const char *Relational<A>::Infix() const {
285   switch (opr) {
286   case RelationalOperator::LT: return "<";
287   case RelationalOperator::LE: return "<=";
288   case RelationalOperator::EQ: return "==";
289   case RelationalOperator::NE: return "/=";
290   case RelationalOperator::GE: return ">=";
291   case RelationalOperator::GT: return ">";
292   }
293   return nullptr;
294 }
295 
AsFortran(std::ostream & o) const296 std::ostream &Relational<SomeType>::AsFortran(std::ostream &o) const {
297   std::visit([&](const auto &rel) { rel.AsFortran(o); }, u);
298   return o;
299 }
300 
Infix() const301 template<int KIND> const char *LogicalOperation<KIND>::Infix() const {
302   switch (logicalOperator) {
303   case LogicalOperator::And: return ".and.";
304   case LogicalOperator::Or: return ".or.";
305   case LogicalOperator::Eqv: return ".eqv.";
306   case LogicalOperator::Neqv: return ".neqv.";
307   }
308   return nullptr;
309 }
310 
311 template<typename T>
EmitArray(std::ostream & o,const Expr<T> & expr)312 std::ostream &EmitArray(std::ostream &o, const Expr<T> &expr) {
313   return expr.AsFortran(o);
314 }
315 
316 template<typename T>
317 std::ostream &EmitArray(std::ostream &, const ArrayConstructorValues<T> &);
318 
319 template<typename T>
EmitArray(std::ostream & o,const ImpliedDo<T> & implDo)320 std::ostream &EmitArray(std::ostream &o, const ImpliedDo<T> &implDo) {
321   o << '(';
322   EmitArray(o, implDo.values());
323   o << ',' << ImpliedDoIndex::Result::AsFortran()
324     << "::" << implDo.name().ToString() << '=';
325   implDo.lower().AsFortran(o) << ',';
326   implDo.upper().AsFortran(o) << ',';
327   implDo.stride().AsFortran(o) << ')';
328   return o;
329 }
330 
331 template<typename T>
EmitArray(std::ostream & o,const ArrayConstructorValues<T> & values)332 std::ostream &EmitArray(
333     std::ostream &o, const ArrayConstructorValues<T> &values) {
334   const char *sep{""};
335   for (const auto &value : values) {
336     o << sep;
337     std::visit([&](const auto &x) { EmitArray(o, x); }, value.u);
338     sep = ",";
339   }
340   return o;
341 }
342 
343 template<typename T>
AsFortran(std::ostream & o) const344 std::ostream &ArrayConstructor<T>::AsFortran(std::ostream &o) const {
345   o << '[' << GetType().AsFortran() << "::";
346   EmitArray(o, *this);
347   return o << ']';
348 }
349 
350 template<int KIND>
AsFortran(std::ostream & o) const351 std::ostream &ArrayConstructor<Type<TypeCategory::Character, KIND>>::AsFortran(
352     std::ostream &o) const {
353   std::stringstream len;
354   LEN().AsFortran(len);
355   o << '[' << GetType().AsFortran(len.str()) << "::";
356   EmitArray(o, *this);
357   return o << ']';
358 }
359 
AsFortran(std::ostream & o) const360 std::ostream &ArrayConstructor<SomeDerived>::AsFortran(std::ostream &o) const {
361   o << '[' << GetType().AsFortran() << "::";
362   EmitArray(o, *this);
363   return o << ']';
364 }
365 
366 template<typename RESULT>
AsFortran(std::ostream & o) const367 std::ostream &ExpressionBase<RESULT>::AsFortran(std::ostream &o) const {
368   std::visit(
369       common::visitors{
370           [&](const BOZLiteralConstant &x) {
371             o << "z'" << x.Hexadecimal() << "'";
372           },
373           [&](const NullPointer &) { o << "NULL()"; },
374           [&](const common::CopyableIndirection<Substring> &s) {
375             s.value().AsFortran(o);
376           },
377           [&](const ImpliedDoIndex &i) { o << i.name.ToString(); },
378           [&](const auto &x) { x.AsFortran(o); },
379       },
380       derived().u);
381   return o;
382 }
383 
AsFortran(std::ostream & o) const384 std::ostream &StructureConstructor::AsFortran(std::ostream &o) const {
385   o << DerivedTypeSpecAsFortran(result_.derivedTypeSpec());
386   if (values_.empty()) {
387     o << '(';
388   } else {
389     char ch{'('};
390     for (const auto &[symbol, value] : values_) {
391       value.value().AsFortran(o << ch << symbol->name().ToString() << '=');
392       ch = ',';
393     }
394   }
395   return o << ')';
396 }
397 
AsFortran() const398 std::string DynamicType::AsFortran() const {
399   if (derived_ != nullptr) {
400     CHECK(category_ == TypeCategory::Derived);
401     return DerivedTypeSpecAsFortran(*derived_);
402   } else if (charLength_ != nullptr) {
403     std::string result{"CHARACTER(KIND="s + std::to_string(kind_) + ",LEN="};
404     if (charLength_->isAssumed()) {
405       result += '*';
406     } else if (charLength_->isDeferred()) {
407       result += ':';
408     } else if (const auto &length{charLength_->GetExplicit()}) {
409       std::stringstream ss;
410       length->AsFortran(ss);
411       result += ss.str();
412     }
413     return result + ')';
414   } else if (IsUnlimitedPolymorphic()) {
415     return "CLASS(*)";
416   } else if (IsAssumedType()) {
417     return "TYPE(*)";
418   } else if (kind_ == 0) {
419     return "(typeless intrinsic function argument)";
420   } else {
421     return EnumToString(category_) + '(' + std::to_string(kind_) + ')';
422   }
423 }
424 
AsFortran(std::string && charLenExpr) const425 std::string DynamicType::AsFortran(std::string &&charLenExpr) const {
426   if (!charLenExpr.empty() && category_ == TypeCategory::Character) {
427     return "CHARACTER(KIND=" + std::to_string(kind_) +
428         ",LEN=" + std::move(charLenExpr) + ')';
429   } else {
430     return AsFortran();
431   }
432 }
433 
AsFortran() const434 std::string SomeDerived::AsFortran() const {
435   if (IsUnlimitedPolymorphic()) {
436     return "CLASS(*)";
437   } else {
438     return "TYPE("s + DerivedTypeSpecAsFortran(derivedTypeSpec()) + ')';
439   }
440 }
441 
DerivedTypeSpecAsFortran(const semantics::DerivedTypeSpec & spec)442 std::string DerivedTypeSpecAsFortran(const semantics::DerivedTypeSpec &spec) {
443   std::stringstream ss;
444   ss << spec.name().ToString();
445   char ch{'('};
446   for (const auto &[name, value] : spec.parameters()) {
447     ss << ch << name.ToString() << '=';
448     ch = ',';
449     if (value.isAssumed()) {
450       ss << '*';
451     } else if (value.isDeferred()) {
452       ss << ':';
453     } else {
454       value.GetExplicit()->AsFortran(ss);
455     }
456   }
457   if (ch != '(') {
458     ss << ')';
459   }
460   return ss.str();
461 }
462 
EmitVar(std::ostream & o,const Symbol & symbol)463 std::ostream &EmitVar(std::ostream &o, const Symbol &symbol) {
464   return o << symbol.name().ToString();
465 }
466 
EmitVar(std::ostream & o,const std::string & lit)467 std::ostream &EmitVar(std::ostream &o, const std::string &lit) {
468   return o << parser::QuoteCharacterLiteral(lit);
469 }
470 
EmitVar(std::ostream & o,const std::u16string & lit)471 std::ostream &EmitVar(std::ostream &o, const std::u16string &lit) {
472   return o << parser::QuoteCharacterLiteral(lit);
473 }
474 
EmitVar(std::ostream & o,const std::u32string & lit)475 std::ostream &EmitVar(std::ostream &o, const std::u32string &lit) {
476   return o << parser::QuoteCharacterLiteral(lit);
477 }
478 
EmitVar(std::ostream & o,const A & x)479 template<typename A> std::ostream &EmitVar(std::ostream &o, const A &x) {
480   return x.AsFortran(o);
481 }
482 
483 template<typename A>
EmitVar(std::ostream & o,const A * p,const char * kw=nullptr)484 std::ostream &EmitVar(std::ostream &o, const A *p, const char *kw = nullptr) {
485   if (p != nullptr) {
486     if (kw != nullptr) {
487       o << kw;
488     }
489     EmitVar(o, *p);
490   }
491   return o;
492 }
493 
494 template<typename A>
EmitVar(std::ostream & o,const std::optional<A> & x,const char * kw=nullptr)495 std::ostream &EmitVar(
496     std::ostream &o, const std::optional<A> &x, const char *kw = nullptr) {
497   if (x.has_value()) {
498     if (kw != nullptr) {
499       o << kw;
500     }
501     EmitVar(o, *x);
502   }
503   return o;
504 }
505 
506 template<typename A, bool COPY>
EmitVar(std::ostream & o,const common::Indirection<A,COPY> & p,const char * kw=nullptr)507 std::ostream &EmitVar(std::ostream &o, const common::Indirection<A, COPY> &p,
508     const char *kw = nullptr) {
509   if (kw != nullptr) {
510     o << kw;
511   }
512   EmitVar(o, p.value());
513   return o;
514 }
515 
516 template<typename A>
EmitVar(std::ostream & o,const std::shared_ptr<A> & p)517 std::ostream &EmitVar(std::ostream &o, const std::shared_ptr<A> &p) {
518   CHECK(p != nullptr);
519   return EmitVar(o, *p);
520 }
521 
522 template<typename... A>
EmitVar(std::ostream & o,const std::variant<A...> & u)523 std::ostream &EmitVar(std::ostream &o, const std::variant<A...> &u) {
524   std::visit([&](const auto &x) { EmitVar(o, x); }, u);
525   return o;
526 }
527 
AsFortran(std::ostream & o) const528 std::ostream &BaseObject::AsFortran(std::ostream &o) const {
529   return EmitVar(o, u);
530 }
531 
532 template<int KIND>
AsFortran(std::ostream & o) const533 std::ostream &TypeParamInquiry<KIND>::AsFortran(std::ostream &o) const {
534   if (base_.has_value()) {
535     return base_->AsFortran(o) << '%';
536   }
537   return EmitVar(o, *parameter_);
538 }
539 
AsFortran(std::ostream & o) const540 std::ostream &Component::AsFortran(std::ostream &o) const {
541   base_.value().AsFortran(o);
542   return EmitVar(o << '%', *symbol_);
543 }
544 
AsFortran(std::ostream & o) const545 std::ostream &NamedEntity::AsFortran(std::ostream &o) const {
546   std::visit(
547       common::visitors{
548           [&](const Symbol *s) { EmitVar(o, *s); },
549           [&](const Component &c) { c.AsFortran(o); },
550       },
551       u_);
552   return o;
553 }
554 
AsFortran(std::ostream & o) const555 std::ostream &Triplet::AsFortran(std::ostream &o) const {
556   EmitVar(o, lower_) << ':';
557   EmitVar(o, upper_);
558   EmitVar(o << ':', stride_.value());
559   return o;
560 }
561 
AsFortran(std::ostream & o) const562 std::ostream &Subscript::AsFortran(std::ostream &o) const {
563   return EmitVar(o, u);
564 }
565 
AsFortran(std::ostream & o) const566 std::ostream &ArrayRef::AsFortran(std::ostream &o) const {
567   base_.AsFortran(o);
568   char separator{'('};
569   for (const Subscript &ss : subscript_) {
570     ss.AsFortran(o << separator);
571     separator = ',';
572   }
573   return o << ')';
574 }
575 
AsFortran(std::ostream & o) const576 std::ostream &CoarrayRef::AsFortran(std::ostream &o) const {
577   bool first{true};
578   for (const Symbol *part : base_) {
579     if (first) {
580       first = false;
581     } else {
582       o << '%';
583     }
584     EmitVar(o, *part);
585   }
586   char separator{'('};
587   for (const auto &sscript : subscript_) {
588     EmitVar(o << separator, sscript);
589     separator = ',';
590   }
591   if (separator == ',') {
592     o << ')';
593   }
594   separator = '[';
595   for (const auto &css : cosubscript_) {
596     EmitVar(o << separator, css);
597     separator = ',';
598   }
599   if (stat_.has_value()) {
600     EmitVar(o << separator, stat_, "STAT=");
601     separator = ',';
602   }
603   if (team_.has_value()) {
604     EmitVar(
605         o << separator, team_, teamIsTeamNumber_ ? "TEAM_NUMBER=" : "TEAM=");
606   }
607   return o << ']';
608 }
609 
AsFortran(std::ostream & o) const610 std::ostream &DataRef::AsFortran(std::ostream &o) const {
611   return EmitVar(o, u);
612 }
613 
AsFortran(std::ostream & o) const614 std::ostream &Substring::AsFortran(std::ostream &o) const {
615   EmitVar(o, parent_) << '(';
616   EmitVar(o, lower_) << ':';
617   return EmitVar(o, upper_) << ')';
618 }
619 
AsFortran(std::ostream & o) const620 std::ostream &ComplexPart::AsFortran(std::ostream &o) const {
621   return complex_.AsFortran(o) << '%' << EnumToString(part_);
622 }
623 
AsFortran(std::ostream & o) const624 std::ostream &ProcedureDesignator::AsFortran(std::ostream &o) const {
625   return EmitVar(o, u);
626 }
627 
628 template<typename T>
AsFortran(std::ostream & o) const629 std::ostream &Designator<T>::AsFortran(std::ostream &o) const {
630   std::visit(
631       common::visitors{
632           [&](const Symbol *sym) { EmitVar(o, *sym); },
633           [&](const auto &x) { x.AsFortran(o); },
634       },
635       u);
636   return o;
637 }
638 
AsFortran(std::ostream & o) const639 std::ostream &DescriptorInquiry::AsFortran(std::ostream &o) const {
640   switch (field_) {
641   case Field::LowerBound: o << "lbound("; break;
642   case Field::Extent: o << "size("; break;
643   case Field::Stride: o << "%STRIDE("; break;
644   case Field::Rank: o << "rank("; break;
645   }
646   base_.AsFortran(o);
647   if (dimension_ >= 0) {
648     o << ",dim=" << (dimension_ + 1);
649   }
650   return o << ')';
651 }
652 
653 INSTANTIATE_CONSTANT_TEMPLATES
654 INSTANTIATE_EXPRESSION_TEMPLATES
655 INSTANTIATE_VARIABLE_TEMPLATES
656 }
657