xref: /llvm-project/flang/lib/Semantics/symbol.cpp (revision 038b42ba5b47b1aa2d47ef5706a713f6bfbbc37c)
1 //===-- lib/Semantics/symbol.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/Semantics/symbol.h"
10 #include "flang/Common/idioms.h"
11 #include "flang/Evaluate/expression.h"
12 #include "flang/Semantics/scope.h"
13 #include "flang/Semantics/semantics.h"
14 #include "flang/Semantics/tools.h"
15 #include "llvm/Support/raw_ostream.h"
16 #include <cstring>
17 #include <string>
18 #include <type_traits>
19 
20 namespace Fortran::semantics {
21 
22 template <typename T>
23 static void DumpOptional(llvm::raw_ostream &os, const char *label, const T &x) {
24   if (x) {
25     os << ' ' << label << ':' << *x;
26   }
27 }
28 template <typename T>
29 static void DumpExpr(llvm::raw_ostream &os, const char *label,
30     const std::optional<evaluate::Expr<T>> &x) {
31   if (x) {
32     x->AsFortran(os << ' ' << label << ':');
33   }
34 }
35 
36 static void DumpBool(llvm::raw_ostream &os, const char *label, bool x) {
37   if (x) {
38     os << ' ' << label;
39   }
40 }
41 
42 static void DumpSymbolVector(llvm::raw_ostream &os, const SymbolVector &list) {
43   char sep{' '};
44   for (const Symbol &elem : list) {
45     os << sep << elem.name();
46     sep = ',';
47   }
48 }
49 
50 static void DumpType(llvm::raw_ostream &os, const Symbol &symbol) {
51   if (const auto *type{symbol.GetType()}) {
52     os << *type << ' ';
53   }
54 }
55 static void DumpType(llvm::raw_ostream &os, const DeclTypeSpec *type) {
56   if (type) {
57     os << ' ' << *type;
58   }
59 }
60 
61 template <typename T>
62 static void DumpList(llvm::raw_ostream &os, const char *label, const T &list) {
63   if (!list.empty()) {
64     os << ' ' << label << ':';
65     char sep{' '};
66     for (const auto &elem : list) {
67       os << sep << elem;
68       sep = ',';
69     }
70   }
71 }
72 
73 void SubprogramDetails::set_moduleInterface(Symbol &symbol) {
74   CHECK(!moduleInterface_);
75   moduleInterface_ = &symbol;
76 }
77 
78 const Scope *ModuleDetails::parent() const {
79   return isSubmodule_ && scope_ ? &scope_->parent() : nullptr;
80 }
81 const Scope *ModuleDetails::ancestor() const {
82   return isSubmodule_ && scope_ ? FindModuleContaining(*scope_) : nullptr;
83 }
84 void ModuleDetails::set_scope(const Scope *scope) {
85   CHECK(!scope_);
86   bool scopeIsSubmodule{scope->parent().kind() == Scope::Kind::Module};
87   CHECK(isSubmodule_ == scopeIsSubmodule);
88   scope_ = scope;
89 }
90 
91 llvm::raw_ostream &operator<<(
92     llvm::raw_ostream &os, const SubprogramDetails &x) {
93   DumpBool(os, "isInterface", x.isInterface_);
94   DumpBool(os, "dummy", x.isDummy_);
95   DumpOptional(os, "bindName", x.bindName());
96   if (x.result_) {
97     DumpType(os << " result:", x.result());
98     os << x.result_->name();
99     if (!x.result_->attrs().empty()) {
100       os << ", " << x.result_->attrs();
101     }
102   }
103   if (x.entryScope_) {
104     os << " entry";
105     if (x.entryScope_->symbol()) {
106       os << " in " << x.entryScope_->symbol()->name();
107     }
108   }
109   char sep{'('};
110   os << ' ';
111   for (const Symbol *arg : x.dummyArgs_) {
112     os << sep;
113     sep = ',';
114     if (arg) {
115       DumpType(os, *arg);
116       os << arg->name();
117     } else {
118       os << '*';
119     }
120   }
121   os << (sep == '(' ? "()" : ")");
122   if (x.stmtFunction_) {
123     os << " -> " << x.stmtFunction_->AsFortran();
124   }
125   if (x.moduleInterface_) {
126     os << " moduleInterface: " << *x.moduleInterface_;
127   }
128   if (x.defaultIgnoreTKR_) {
129     os << " defaultIgnoreTKR";
130   }
131   if (x.cudaSubprogramAttrs_) {
132     os << " cudaSubprogramAttrs: "
133        << common::EnumToString(*x.cudaSubprogramAttrs_);
134   }
135   if (!x.cudaLaunchBounds_.empty()) {
136     os << " cudaLaunchBounds:";
137     for (auto x : x.cudaLaunchBounds_) {
138       os << ' ' << x;
139     }
140   }
141   if (!x.cudaClusterDims_.empty()) {
142     os << " cudaClusterDims:";
143     for (auto x : x.cudaClusterDims_) {
144       os << ' ' << x;
145     }
146   }
147   return os;
148 }
149 
150 void EntityDetails::set_type(const DeclTypeSpec &type) {
151   CHECK(!type_);
152   type_ = &type;
153 }
154 
155 void AssocEntityDetails::set_rank(int rank) { rank_ = rank; }
156 void AssocEntityDetails::set_IsAssumedSize() { rank_ = isAssumedSize; }
157 void AssocEntityDetails::set_IsAssumedRank() { rank_ = isAssumedRank; }
158 void EntityDetails::ReplaceType(const DeclTypeSpec &type) { type_ = &type; }
159 
160 ObjectEntityDetails::ObjectEntityDetails(EntityDetails &&d)
161     : EntityDetails(std::move(d)) {}
162 
163 void ObjectEntityDetails::set_shape(const ArraySpec &shape) {
164   CHECK(shape_.empty());
165   for (const auto &shapeSpec : shape) {
166     shape_.push_back(shapeSpec);
167   }
168 }
169 void ObjectEntityDetails::set_coshape(const ArraySpec &coshape) {
170   CHECK(coshape_.empty());
171   for (const auto &shapeSpec : coshape) {
172     coshape_.push_back(shapeSpec);
173   }
174 }
175 
176 ProcEntityDetails::ProcEntityDetails(EntityDetails &&d)
177     : EntityDetails(std::move(d)) {}
178 
179 UseErrorDetails::UseErrorDetails(const UseDetails &useDetails) {
180   add_occurrence(useDetails.location(), useDetails.symbol());
181 }
182 UseErrorDetails &UseErrorDetails::add_occurrence(
183     const SourceName &location, const Symbol &used) {
184   occurrences_.push_back(std::make_pair(location, &used));
185   return *this;
186 }
187 
188 void GenericDetails::AddSpecificProc(
189     const Symbol &proc, SourceName bindingName) {
190   specificProcs_.push_back(proc);
191   bindingNames_.push_back(bindingName);
192 }
193 void GenericDetails::set_specific(Symbol &specific) {
194   CHECK(!specific_);
195   specific_ = &specific;
196 }
197 void GenericDetails::clear_specific() { specific_ = nullptr; }
198 void GenericDetails::set_derivedType(Symbol &derivedType) {
199   CHECK(!derivedType_);
200   derivedType_ = &derivedType;
201 }
202 void GenericDetails::clear_derivedType() { derivedType_ = nullptr; }
203 void GenericDetails::AddUse(const Symbol &use) {
204   CHECK(use.has<UseDetails>());
205   uses_.push_back(use);
206 }
207 
208 const Symbol *GenericDetails::CheckSpecific() const {
209   return const_cast<GenericDetails *>(this)->CheckSpecific();
210 }
211 Symbol *GenericDetails::CheckSpecific() {
212   if (specific_ && !specific_->has<UseErrorDetails>()) {
213     const Symbol &ultimate{specific_->GetUltimate()};
214     for (const Symbol &proc : specificProcs_) {
215       if (&proc.GetUltimate() == &ultimate) {
216         return nullptr;
217       }
218     }
219     return specific_;
220   } else {
221     return nullptr;
222   }
223 }
224 
225 void GenericDetails::CopyFrom(const GenericDetails &from) {
226   CHECK(specificProcs_.size() == bindingNames_.size());
227   CHECK(from.specificProcs_.size() == from.bindingNames_.size());
228   kind_ = from.kind_;
229   if (from.derivedType_) {
230     CHECK(!derivedType_ || derivedType_ == from.derivedType_);
231     derivedType_ = from.derivedType_;
232   }
233   for (std::size_t i{0}; i < from.specificProcs_.size(); ++i) {
234     if (llvm::none_of(specificProcs_, [&](const Symbol &mySymbol) {
235           return &mySymbol.GetUltimate() ==
236               &from.specificProcs_[i]->GetUltimate();
237         })) {
238       specificProcs_.push_back(from.specificProcs_[i]);
239       bindingNames_.push_back(from.bindingNames_[i]);
240     }
241   }
242 }
243 
244 // The name of the kind of details for this symbol.
245 // This is primarily for debugging.
246 std::string DetailsToString(const Details &details) {
247   return common::visit(
248       common::visitors{
249           [](const UnknownDetails &) { return "Unknown"; },
250           [](const MainProgramDetails &) { return "MainProgram"; },
251           [](const ModuleDetails &) { return "Module"; },
252           [](const SubprogramDetails &) { return "Subprogram"; },
253           [](const SubprogramNameDetails &) { return "SubprogramName"; },
254           [](const EntityDetails &) { return "Entity"; },
255           [](const ObjectEntityDetails &) { return "ObjectEntity"; },
256           [](const ProcEntityDetails &) { return "ProcEntity"; },
257           [](const DerivedTypeDetails &) { return "DerivedType"; },
258           [](const UseDetails &) { return "Use"; },
259           [](const UseErrorDetails &) { return "UseError"; },
260           [](const HostAssocDetails &) { return "HostAssoc"; },
261           [](const GenericDetails &) { return "Generic"; },
262           [](const ProcBindingDetails &) { return "ProcBinding"; },
263           [](const NamelistDetails &) { return "Namelist"; },
264           [](const CommonBlockDetails &) { return "CommonBlockDetails"; },
265           [](const TypeParamDetails &) { return "TypeParam"; },
266           [](const MiscDetails &) { return "Misc"; },
267           [](const AssocEntityDetails &) { return "AssocEntity"; },
268       },
269       details);
270 }
271 
272 std::string Symbol::GetDetailsName() const { return DetailsToString(details_); }
273 
274 void Symbol::set_details(Details &&details) {
275   CHECK(CanReplaceDetails(details));
276   details_ = std::move(details);
277 }
278 
279 bool Symbol::CanReplaceDetails(const Details &details) const {
280   if (has<UnknownDetails>()) {
281     return true; // can always replace UnknownDetails
282   } else {
283     return common::visit(
284         common::visitors{
285             [](const UseErrorDetails &) { return true; },
286             [&](const ObjectEntityDetails &) { return has<EntityDetails>(); },
287             [&](const ProcEntityDetails &) { return has<EntityDetails>(); },
288             [&](const SubprogramDetails &) {
289               return has<SubprogramNameDetails>() || has<EntityDetails>();
290             },
291             [&](const DerivedTypeDetails &) {
292               const auto *derived{this->detailsIf<DerivedTypeDetails>()};
293               return derived && derived->isForwardReferenced();
294             },
295             [&](const UseDetails &x) {
296               const auto *use{this->detailsIf<UseDetails>()};
297               return use && use->symbol() == x.symbol();
298             },
299             [&](const HostAssocDetails &) {
300               return this->has<HostAssocDetails>();
301             },
302             [](const auto &) { return false; },
303         },
304         details);
305   }
306 }
307 
308 // Usually a symbol's name is the first occurrence in the source, but sometimes
309 // we want to replace it with one at a different location (but same characters).
310 void Symbol::ReplaceName(const SourceName &name) {
311   CHECK(name == name_);
312   name_ = name;
313 }
314 
315 void Symbol::SetType(const DeclTypeSpec &type) {
316   common::visit(common::visitors{
317                     [&](EntityDetails &x) { x.set_type(type); },
318                     [&](ObjectEntityDetails &x) { x.set_type(type); },
319                     [&](AssocEntityDetails &x) { x.set_type(type); },
320                     [&](ProcEntityDetails &x) { x.set_type(type); },
321                     [&](TypeParamDetails &x) { x.set_type(type); },
322                     [](auto &) {},
323                 },
324       details_);
325 }
326 
327 template <typename T>
328 constexpr bool HasBindName{std::is_convertible_v<T, const WithBindName *>};
329 
330 const std::string *Symbol::GetBindName() const {
331   return common::visit(
332       [&](auto &x) -> const std::string * {
333         if constexpr (HasBindName<decltype(&x)>) {
334           return x.bindName();
335         } else {
336           return nullptr;
337         }
338       },
339       details_);
340 }
341 
342 void Symbol::SetBindName(std::string &&name) {
343   common::visit(
344       [&](auto &x) {
345         if constexpr (HasBindName<decltype(&x)>) {
346           x.set_bindName(std::move(name));
347         } else {
348           DIE("bind name not allowed on this kind of symbol");
349         }
350       },
351       details_);
352 }
353 
354 bool Symbol::GetIsExplicitBindName() const {
355   return common::visit(
356       [&](auto &x) -> bool {
357         if constexpr (HasBindName<decltype(&x)>) {
358           return x.isExplicitBindName();
359         } else {
360           return false;
361         }
362       },
363       details_);
364 }
365 
366 void Symbol::SetIsExplicitBindName(bool yes) {
367   common::visit(
368       [&](auto &x) {
369         if constexpr (HasBindName<decltype(&x)>) {
370           x.set_isExplicitBindName(yes);
371         } else {
372           DIE("bind name not allowed on this kind of symbol");
373         }
374       },
375       details_);
376 }
377 
378 void Symbol::SetIsCDefined(bool yes) {
379   common::visit(
380       [&](auto &x) {
381         if constexpr (HasBindName<decltype(&x)>) {
382           x.set_isCDefined(yes);
383         } else {
384           DIE("CDEFINED not allowed on this kind of symbol");
385         }
386       },
387       details_);
388 }
389 
390 bool Symbol::IsFuncResult() const {
391   return common::visit(
392       common::visitors{[](const EntityDetails &x) { return x.isFuncResult(); },
393           [](const ObjectEntityDetails &x) { return x.isFuncResult(); },
394           [](const ProcEntityDetails &x) { return x.isFuncResult(); },
395           [](const HostAssocDetails &x) { return x.symbol().IsFuncResult(); },
396           [](const auto &) { return false; }},
397       details_);
398 }
399 
400 const ArraySpec *Symbol::GetShape() const {
401   if (const auto *details{std::get_if<ObjectEntityDetails>(&details_)}) {
402     return &details->shape();
403   } else {
404     return nullptr;
405   }
406 }
407 
408 bool Symbol::IsObjectArray() const {
409   const ArraySpec *shape{GetShape()};
410   return shape && !shape->empty();
411 }
412 
413 bool Symbol::IsSubprogram() const {
414   return common::visit(
415       common::visitors{
416           [](const SubprogramDetails &) { return true; },
417           [](const SubprogramNameDetails &) { return true; },
418           [](const GenericDetails &) { return true; },
419           [](const UseDetails &x) { return x.symbol().IsSubprogram(); },
420           [](const auto &) { return false; },
421       },
422       details_);
423 }
424 
425 bool Symbol::IsFromModFile() const {
426   return test(Flag::ModFile) ||
427       (!owner_->IsTopLevel() && owner_->symbol()->IsFromModFile());
428 }
429 
430 llvm::raw_ostream &operator<<(llvm::raw_ostream &os, const EntityDetails &x) {
431   DumpBool(os, "dummy", x.isDummy());
432   DumpBool(os, "funcResult", x.isFuncResult());
433   if (x.type()) {
434     os << " type: " << *x.type();
435   }
436   DumpOptional(os, "bindName", x.bindName());
437   DumpBool(os, "CDEFINED", x.isCDefined());
438   return os;
439 }
440 
441 llvm::raw_ostream &operator<<(
442     llvm::raw_ostream &os, const ObjectEntityDetails &x) {
443   os << *static_cast<const EntityDetails *>(&x);
444   DumpList(os, "shape", x.shape());
445   DumpList(os, "coshape", x.coshape());
446   DumpExpr(os, "init", x.init_);
447   if (x.unanalyzedPDTComponentInit()) {
448     os << " (has unanalyzedPDTComponentInit)";
449   }
450   if (!x.ignoreTKR_.empty()) {
451     x.ignoreTKR_.Dump(os << ' ', common::EnumToString);
452   }
453   if (x.cudaDataAttr()) {
454     os << " cudaDataAttr: " << common::EnumToString(*x.cudaDataAttr());
455   }
456   return os;
457 }
458 
459 llvm::raw_ostream &operator<<(
460     llvm::raw_ostream &os, const AssocEntityDetails &x) {
461   os << *static_cast<const EntityDetails *>(&x);
462   if (x.IsAssumedSize()) {
463     os << " RANK(*)";
464   } else if (x.IsAssumedRank()) {
465     os << " RANK DEFAULT";
466   } else if (auto assocRank{x.rank()}) {
467     os << " RANK(" << *assocRank << ')';
468   }
469   DumpExpr(os, "expr", x.expr());
470   return os;
471 }
472 
473 llvm::raw_ostream &operator<<(
474     llvm::raw_ostream &os, const ProcEntityDetails &x) {
475   if (x.procInterface_) {
476     if (x.rawProcInterface_ != x.procInterface_) {
477       os << ' ' << x.rawProcInterface_->name() << " ->";
478     }
479     os << ' ' << x.procInterface_->name();
480   } else {
481     DumpType(os, x.type());
482   }
483   DumpOptional(os, "bindName", x.bindName());
484   DumpOptional(os, "passName", x.passName());
485   if (x.init()) {
486     if (const Symbol * target{*x.init()}) {
487       os << " => " << target->name();
488     } else {
489       os << " => NULL()";
490     }
491   }
492   if (x.isCUDAKernel()) {
493     os << " isCUDAKernel";
494   }
495   return os;
496 }
497 
498 llvm::raw_ostream &operator<<(
499     llvm::raw_ostream &os, const DerivedTypeDetails &x) {
500   DumpBool(os, "sequence", x.sequence_);
501   DumpList(os, "components", x.componentNames_);
502   return os;
503 }
504 
505 llvm::raw_ostream &operator<<(llvm::raw_ostream &os, const GenericDetails &x) {
506   os << ' ' << x.kind().ToString();
507   DumpBool(os, "(specific)", x.specific() != nullptr);
508   DumpBool(os, "(derivedType)", x.derivedType() != nullptr);
509   if (const auto &uses{x.uses()}; !uses.empty()) {
510     os << " (uses:";
511     char sep{' '};
512     for (const Symbol &use : uses) {
513       const Symbol &ultimate{use.GetUltimate()};
514       os << sep << ultimate.name() << "->"
515          << ultimate.owner().GetName().value();
516       sep = ',';
517     }
518     os << ')';
519   }
520   os << " procs:";
521   DumpSymbolVector(os, x.specificProcs());
522   return os;
523 }
524 
525 llvm::raw_ostream &operator<<(llvm::raw_ostream &os, const Details &details) {
526   os << DetailsToString(details);
527   common::visit( //
528       common::visitors{
529           [&](const UnknownDetails &) {},
530           [&](const MainProgramDetails &) {},
531           [&](const ModuleDetails &x) {
532             if (x.isSubmodule()) {
533               os << " (";
534               if (x.ancestor()) {
535                 auto ancestor{x.ancestor()->GetName().value()};
536                 os << ancestor;
537                 if (x.parent()) {
538                   auto parent{x.parent()->GetName().value()};
539                   if (ancestor != parent) {
540                     os << ':' << parent;
541                   }
542                 }
543               }
544               os << ")";
545             }
546             if (x.isDefaultPrivate()) {
547               os << " isDefaultPrivate";
548             }
549           },
550           [&](const SubprogramNameDetails &x) {
551             os << ' ' << EnumToString(x.kind());
552           },
553           [&](const UseDetails &x) {
554             os << " from " << x.symbol().name() << " in "
555                << GetUsedModule(x).name();
556           },
557           [&](const UseErrorDetails &x) {
558             os << " uses:";
559             char sep{':'};
560             for (const auto &[location, sym] : x.occurrences()) {
561               os << sep << " from " << sym->name() << " at " << location;
562               sep = ',';
563             }
564           },
565           [](const HostAssocDetails &) {},
566           [&](const ProcBindingDetails &x) {
567             os << " => " << x.symbol().name();
568             DumpOptional(os, "passName", x.passName());
569             if (x.numPrivatesNotOverridden() > 0) {
570               os << " numPrivatesNotOverridden: "
571                  << x.numPrivatesNotOverridden();
572             }
573           },
574           [&](const NamelistDetails &x) {
575             os << ':';
576             DumpSymbolVector(os, x.objects());
577           },
578           [&](const CommonBlockDetails &x) {
579             DumpOptional(os, "bindName", x.bindName());
580             if (x.alignment()) {
581               os << " alignment=" << x.alignment();
582             }
583             os << ':';
584             for (const auto &object : x.objects()) {
585               os << ' ' << object->name();
586             }
587           },
588           [&](const TypeParamDetails &x) {
589             DumpOptional(os, "type", x.type());
590             if (auto attr{x.attr()}) {
591               os << ' ' << common::EnumToString(*attr);
592             } else {
593               os << " (no attr)";
594             }
595             DumpExpr(os, "init", x.init());
596           },
597           [&](const MiscDetails &x) {
598             os << ' ' << MiscDetails::EnumToString(x.kind());
599           },
600           [&](const auto &x) { os << x; },
601       },
602       details);
603   return os;
604 }
605 
606 llvm::raw_ostream &operator<<(llvm::raw_ostream &o, Symbol::Flag flag) {
607   return o << Symbol::EnumToString(flag);
608 }
609 
610 llvm::raw_ostream &operator<<(
611     llvm::raw_ostream &o, const Symbol::Flags &flags) {
612   std::size_t n{flags.count()};
613   std::size_t seen{0};
614   for (std::size_t j{0}; seen < n; ++j) {
615     Symbol::Flag flag{static_cast<Symbol::Flag>(j)};
616     if (flags.test(flag)) {
617       if (seen++ > 0) {
618         o << ", ";
619       }
620       o << flag;
621     }
622   }
623   return o;
624 }
625 
626 llvm::raw_ostream &operator<<(llvm::raw_ostream &os, const Symbol &symbol) {
627   os << symbol.name();
628   if (!symbol.attrs().empty()) {
629     os << ", " << symbol.attrs();
630   }
631   if (!symbol.flags().empty()) {
632     os << " (" << symbol.flags() << ')';
633   }
634   if (symbol.size_) {
635     os << " size=" << symbol.size_ << " offset=" << symbol.offset_;
636   }
637   os << ": " << symbol.details_;
638   return os;
639 }
640 
641 #if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
642 void Symbol::dump() const { llvm::errs() << *this << '\n'; }
643 #endif
644 
645 // Output a unique name for a scope by qualifying it with the names of
646 // parent scopes. For scopes without corresponding symbols, use the kind
647 // with an index (e.g. Block1, Block2, etc.).
648 static void DumpUniqueName(llvm::raw_ostream &os, const Scope &scope) {
649   if (!scope.IsTopLevel()) {
650     DumpUniqueName(os, scope.parent());
651     os << '/';
652     if (auto *scopeSymbol{scope.symbol()};
653         scopeSymbol && !scopeSymbol->name().empty()) {
654       os << scopeSymbol->name();
655     } else {
656       int index{1};
657       for (auto &child : scope.parent().children()) {
658         if (child == scope) {
659           break;
660         }
661         if (child.kind() == scope.kind()) {
662           ++index;
663         }
664       }
665       os << Scope::EnumToString(scope.kind()) << index;
666     }
667   }
668 }
669 
670 // Dump a symbol for UnparseWithSymbols. This will be used for tests so the
671 // format should be reasonably stable.
672 llvm::raw_ostream &DumpForUnparse(
673     llvm::raw_ostream &os, const Symbol &symbol, bool isDef) {
674   DumpUniqueName(os, symbol.owner());
675   os << '/' << symbol.name();
676   if (isDef) {
677     if (!symbol.attrs().empty()) {
678       os << ' ' << symbol.attrs();
679     }
680     if (!symbol.flags().empty()) {
681       os << " (" << symbol.flags() << ')';
682     }
683     os << ' ' << symbol.GetDetailsName();
684     DumpType(os, symbol.GetType());
685   }
686   return os;
687 }
688 
689 const DerivedTypeSpec *Symbol::GetParentTypeSpec(const Scope *scope) const {
690   if (const Symbol * parentComponent{GetParentComponent(scope)}) {
691     const auto &object{parentComponent->get<ObjectEntityDetails>()};
692     return &object.type()->derivedTypeSpec();
693   } else {
694     return nullptr;
695   }
696 }
697 
698 const Symbol *Symbol::GetParentComponent(const Scope *scope) const {
699   if (const auto *dtDetails{detailsIf<DerivedTypeDetails>()}) {
700     if (const Scope * localScope{scope ? scope : scope_}) {
701       return dtDetails->GetParentComponent(DEREF(localScope));
702     }
703   }
704   return nullptr;
705 }
706 
707 void DerivedTypeDetails::add_component(const Symbol &symbol) {
708   if (symbol.test(Symbol::Flag::ParentComp)) {
709     CHECK(componentNames_.empty());
710   }
711   componentNames_.push_back(symbol.name());
712 }
713 
714 const Symbol *DerivedTypeDetails::GetParentComponent(const Scope &scope) const {
715   if (auto extends{GetParentComponentName()}) {
716     if (auto iter{scope.find(*extends)}; iter != scope.cend()) {
717       if (const Symbol & symbol{*iter->second};
718           symbol.test(Symbol::Flag::ParentComp)) {
719         return &symbol;
720       }
721     }
722   }
723   return nullptr;
724 }
725 
726 const Symbol *DerivedTypeDetails::GetFinalForRank(int rank) const {
727   for (const auto &pair : finals_) {
728     const Symbol &symbol{*pair.second};
729     if (const auto *details{symbol.detailsIf<SubprogramDetails>()}) {
730       if (details->dummyArgs().size() == 1) {
731         if (const Symbol * arg{details->dummyArgs().at(0)}) {
732           if (const auto *object{arg->detailsIf<ObjectEntityDetails>()}) {
733             if (rank == object->shape().Rank() || object->IsAssumedRank() ||
734                 IsElementalProcedure(symbol)) {
735               return &symbol;
736             }
737           }
738         }
739       }
740     }
741   }
742   return nullptr;
743 }
744 
745 TypeParamDetails &TypeParamDetails::set_attr(common::TypeParamAttr attr) {
746   CHECK(!attr_);
747   attr_ = attr;
748   return *this;
749 }
750 
751 TypeParamDetails &TypeParamDetails::set_type(const DeclTypeSpec &type) {
752   CHECK(!type_);
753   type_ = &type;
754   return *this;
755 }
756 
757 bool GenericKind::IsIntrinsicOperator() const {
758   return Is(OtherKind::Concat) || Has<common::LogicalOperator>() ||
759       Has<common::NumericOperator>() || Has<common::RelationalOperator>();
760 }
761 
762 bool GenericKind::IsOperator() const {
763   return IsDefinedOperator() || IsIntrinsicOperator();
764 }
765 
766 std::string GenericKind::ToString() const {
767   return common::visit(
768       common::visitors{
769           [](const OtherKind &x) { return std::string{EnumToString(x)}; },
770           [](const common::DefinedIo &x) { return AsFortran(x).ToString(); },
771           [](const auto &x) { return std::string{common::EnumToString(x)}; },
772       },
773       u);
774 }
775 
776 SourceName GenericKind::AsFortran(common::DefinedIo x) {
777   const char *name{common::AsFortran(x)};
778   return {name, std::strlen(name)};
779 }
780 
781 bool GenericKind::Is(GenericKind::OtherKind x) const {
782   const OtherKind *y{std::get_if<OtherKind>(&u)};
783   return y && *y == x;
784 }
785 
786 std::string Symbol::OmpFlagToClauseName(Symbol::Flag ompFlag) {
787   std::string clauseName;
788   switch (ompFlag) {
789   case Symbol::Flag::OmpShared:
790     clauseName = "SHARED";
791     break;
792   case Symbol::Flag::OmpPrivate:
793     clauseName = "PRIVATE";
794     break;
795   case Symbol::Flag::OmpLinear:
796     clauseName = "LINEAR";
797     break;
798   case Symbol::Flag::OmpFirstPrivate:
799     clauseName = "FIRSTPRIVATE";
800     break;
801   case Symbol::Flag::OmpLastPrivate:
802     clauseName = "LASTPRIVATE";
803     break;
804   case Symbol::Flag::OmpMapTo:
805   case Symbol::Flag::OmpMapFrom:
806   case Symbol::Flag::OmpMapToFrom:
807   case Symbol::Flag::OmpMapAlloc:
808   case Symbol::Flag::OmpMapRelease:
809   case Symbol::Flag::OmpMapDelete:
810     clauseName = "MAP";
811     break;
812   case Symbol::Flag::OmpUseDevicePtr:
813     clauseName = "USE_DEVICE_PTR";
814     break;
815   case Symbol::Flag::OmpUseDeviceAddr:
816     clauseName = "USE_DEVICE_ADDR";
817     break;
818   case Symbol::Flag::OmpCopyIn:
819     clauseName = "COPYIN";
820     break;
821   case Symbol::Flag::OmpCopyPrivate:
822     clauseName = "COPYPRIVATE";
823     break;
824   case Symbol::Flag::OmpIsDevicePtr:
825     clauseName = "IS_DEVICE_PTR";
826     break;
827   case Symbol::Flag::OmpHasDeviceAddr:
828     clauseName = "HAS_DEVICE_ADDR";
829     break;
830   default:
831     clauseName = "";
832     break;
833   }
834   return clauseName;
835 }
836 
837 bool SymbolOffsetCompare::operator()(
838     const SymbolRef &x, const SymbolRef &y) const {
839   const Symbol *xCommon{FindCommonBlockContaining(*x)};
840   const Symbol *yCommon{FindCommonBlockContaining(*y)};
841   if (xCommon) {
842     if (yCommon) {
843       const SymbolSourcePositionCompare sourceCmp;
844       if (sourceCmp(*xCommon, *yCommon)) {
845         return true;
846       } else if (sourceCmp(*yCommon, *xCommon)) {
847         return false;
848       } else if (x->offset() == y->offset()) {
849         return x->size() > y->size();
850       } else {
851         return x->offset() < y->offset();
852       }
853     } else {
854       return false;
855     }
856   } else if (yCommon) {
857     return true;
858   } else if (x->offset() == y->offset()) {
859     return x->size() > y->size();
860   } else {
861     return x->offset() < y->offset();
862   }
863   return x->GetSemanticsContext().allCookedSources().Precedes(
864       x->name(), y->name());
865 }
866 
867 bool SymbolOffsetCompare::operator()(
868     const MutableSymbolRef &x, const MutableSymbolRef &y) const {
869   return (*this)(SymbolRef{*x}, SymbolRef{*y});
870 }
871 
872 } // namespace Fortran::semantics
873