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   return os;
129 }
130 
131 void EntityDetails::set_type(const DeclTypeSpec &type) {
132   CHECK(!type_);
133   type_ = &type;
134 }
135 
136 void AssocEntityDetails::set_rank(int rank) { rank_ = rank; }
137 void EntityDetails::ReplaceType(const DeclTypeSpec &type) { type_ = &type; }
138 
139 void ObjectEntityDetails::set_shape(const ArraySpec &shape) {
140   CHECK(shape_.empty());
141   for (const auto &shapeSpec : shape) {
142     shape_.push_back(shapeSpec);
143   }
144 }
145 void ObjectEntityDetails::set_coshape(const ArraySpec &coshape) {
146   CHECK(coshape_.empty());
147   for (const auto &shapeSpec : coshape) {
148     coshape_.push_back(shapeSpec);
149   }
150 }
151 
152 ProcEntityDetails::ProcEntityDetails(EntityDetails &&d) : EntityDetails(d) {
153   if (type()) {
154     interface_.set_type(*type());
155   }
156 }
157 
158 UseErrorDetails::UseErrorDetails(const UseDetails &useDetails) {
159   add_occurrence(useDetails.location(), *GetUsedModule(useDetails).scope());
160 }
161 UseErrorDetails &UseErrorDetails::add_occurrence(
162     const SourceName &location, const Scope &module) {
163   occurrences_.push_back(std::make_pair(location, &module));
164   return *this;
165 }
166 
167 void GenericDetails::AddSpecificProc(
168     const Symbol &proc, SourceName bindingName) {
169   specificProcs_.push_back(proc);
170   bindingNames_.push_back(bindingName);
171 }
172 void GenericDetails::set_specific(Symbol &specific) {
173   CHECK(!specific_);
174   CHECK(!derivedType_);
175   specific_ = &specific;
176 }
177 void GenericDetails::set_derivedType(Symbol &derivedType) {
178   CHECK(!specific_);
179   CHECK(!derivedType_);
180   derivedType_ = &derivedType;
181 }
182 void GenericDetails::AddUse(const Symbol &use) {
183   CHECK(use.has<UseDetails>());
184   uses_.push_back(use);
185 }
186 
187 const Symbol *GenericDetails::CheckSpecific() const {
188   return const_cast<GenericDetails *>(this)->CheckSpecific();
189 }
190 Symbol *GenericDetails::CheckSpecific() {
191   if (specific_) {
192     for (const Symbol &proc : specificProcs_) {
193       if (&proc == specific_) {
194         return nullptr;
195       }
196     }
197     return specific_;
198   } else {
199     return nullptr;
200   }
201 }
202 
203 void GenericDetails::CopyFrom(const GenericDetails &from) {
204   CHECK(specificProcs_.size() == bindingNames_.size());
205   CHECK(from.specificProcs_.size() == from.bindingNames_.size());
206   kind_ = from.kind_;
207   if (from.derivedType_) {
208     CHECK(!derivedType_ || derivedType_ == from.derivedType_);
209     derivedType_ = from.derivedType_;
210   }
211   for (std::size_t i{0}; i < from.specificProcs_.size(); ++i) {
212     if (std::find_if(specificProcs_.begin(), specificProcs_.end(),
213             [&](const Symbol &mySymbol) {
214               return &mySymbol.GetUltimate() ==
215                   &from.specificProcs_[i]->GetUltimate();
216             }) == specificProcs_.end()) {
217       specificProcs_.push_back(from.specificProcs_[i]);
218       bindingNames_.push_back(from.bindingNames_[i]);
219     }
220   }
221 }
222 
223 // The name of the kind of details for this symbol.
224 // This is primarily for debugging.
225 std::string DetailsToString(const Details &details) {
226   return std::visit(
227       common::visitors{
228           [](const UnknownDetails &) { return "Unknown"; },
229           [](const MainProgramDetails &) { return "MainProgram"; },
230           [](const ModuleDetails &) { return "Module"; },
231           [](const SubprogramDetails &) { return "Subprogram"; },
232           [](const SubprogramNameDetails &) { return "SubprogramName"; },
233           [](const EntityDetails &) { return "Entity"; },
234           [](const ObjectEntityDetails &) { return "ObjectEntity"; },
235           [](const ProcEntityDetails &) { return "ProcEntity"; },
236           [](const DerivedTypeDetails &) { return "DerivedType"; },
237           [](const UseDetails &) { return "Use"; },
238           [](const UseErrorDetails &) { return "UseError"; },
239           [](const HostAssocDetails &) { return "HostAssoc"; },
240           [](const GenericDetails &) { return "Generic"; },
241           [](const ProcBindingDetails &) { return "ProcBinding"; },
242           [](const NamelistDetails &) { return "Namelist"; },
243           [](const CommonBlockDetails &) { return "CommonBlockDetails"; },
244           [](const TypeParamDetails &) { return "TypeParam"; },
245           [](const MiscDetails &) { return "Misc"; },
246           [](const AssocEntityDetails &) { return "AssocEntity"; },
247       },
248       details);
249 }
250 
251 const std::string Symbol::GetDetailsName() const {
252   return DetailsToString(details_);
253 }
254 
255 void Symbol::set_details(Details &&details) {
256   CHECK(CanReplaceDetails(details));
257   details_ = std::move(details);
258 }
259 
260 bool Symbol::CanReplaceDetails(const Details &details) const {
261   if (has<UnknownDetails>()) {
262     return true; // can always replace UnknownDetails
263   } else {
264     return std::visit(
265         common::visitors{
266             [](const UseErrorDetails &) { return true; },
267             [&](const ObjectEntityDetails &) { return has<EntityDetails>(); },
268             [&](const ProcEntityDetails &) { return has<EntityDetails>(); },
269             [&](const SubprogramDetails &) {
270               return has<SubprogramNameDetails>() || has<EntityDetails>();
271             },
272             [&](const DerivedTypeDetails &) {
273               const auto *derived{this->detailsIf<DerivedTypeDetails>()};
274               return derived && derived->isForwardReferenced();
275             },
276             [&](const UseDetails &x) {
277               const auto *use{this->detailsIf<UseDetails>()};
278               return use && use->symbol() == x.symbol();
279             },
280             [](const auto &) { return false; },
281         },
282         details);
283   }
284 }
285 
286 // Usually a symbol's name is the first occurrence in the source, but sometimes
287 // we want to replace it with one at a different location (but same characters).
288 void Symbol::ReplaceName(const SourceName &name) {
289   CHECK(name == name_);
290   name_ = name;
291 }
292 
293 void Symbol::SetType(const DeclTypeSpec &type) {
294   std::visit(common::visitors{
295                  [&](EntityDetails &x) { x.set_type(type); },
296                  [&](ObjectEntityDetails &x) { x.set_type(type); },
297                  [&](AssocEntityDetails &x) { x.set_type(type); },
298                  [&](ProcEntityDetails &x) { x.interface().set_type(type); },
299                  [&](TypeParamDetails &x) { x.set_type(type); },
300                  [](auto &) {},
301              },
302       details_);
303 }
304 
305 template <typename T>
306 constexpr bool HasBindName{std::is_convertible_v<T, const WithBindName *>};
307 
308 const std::string *Symbol::GetBindName() const {
309   return std::visit(
310       [&](auto &x) -> const std::string * {
311         if constexpr (HasBindName<decltype(&x)>) {
312           return x.bindName();
313         } else {
314           return nullptr;
315         }
316       },
317       details_);
318 }
319 
320 void Symbol::SetBindName(std::string &&name) {
321   std::visit(
322       [&](auto &x) {
323         if constexpr (HasBindName<decltype(&x)>) {
324           x.set_bindName(std::move(name));
325         } else {
326           DIE("bind name not allowed on this kind of symbol");
327         }
328       },
329       details_);
330 }
331 
332 bool Symbol::IsFuncResult() const {
333   return std::visit(
334       common::visitors{[](const EntityDetails &x) { return x.isFuncResult(); },
335           [](const ObjectEntityDetails &x) { return x.isFuncResult(); },
336           [](const ProcEntityDetails &x) { return x.isFuncResult(); },
337           [](const HostAssocDetails &x) { return x.symbol().IsFuncResult(); },
338           [](const auto &) { return false; }},
339       details_);
340 }
341 
342 bool Symbol::IsObjectArray() const {
343   const auto *details{std::get_if<ObjectEntityDetails>(&details_)};
344   return details && details->IsArray();
345 }
346 
347 bool Symbol::IsSubprogram() const {
348   return std::visit(
349       common::visitors{
350           [](const SubprogramDetails &) { return true; },
351           [](const SubprogramNameDetails &) { return true; },
352           [](const GenericDetails &) { return true; },
353           [](const UseDetails &x) { return x.symbol().IsSubprogram(); },
354           [](const auto &) { return false; },
355       },
356       details_);
357 }
358 
359 bool Symbol::IsFromModFile() const {
360   return test(Flag::ModFile) ||
361       (!owner_->IsTopLevel() && owner_->symbol()->IsFromModFile());
362 }
363 
364 ObjectEntityDetails::ObjectEntityDetails(EntityDetails &&d)
365     : EntityDetails(d) {}
366 
367 llvm::raw_ostream &operator<<(llvm::raw_ostream &os, const EntityDetails &x) {
368   DumpBool(os, "dummy", x.isDummy());
369   DumpBool(os, "funcResult", x.isFuncResult());
370   if (x.type()) {
371     os << " type: " << *x.type();
372   }
373   DumpOptional(os, "bindName", x.bindName());
374   return os;
375 }
376 
377 llvm::raw_ostream &operator<<(
378     llvm::raw_ostream &os, const ObjectEntityDetails &x) {
379   os << *static_cast<const EntityDetails *>(&x);
380   DumpList(os, "shape", x.shape());
381   DumpList(os, "coshape", x.coshape());
382   DumpExpr(os, "init", x.init_);
383   return os;
384 }
385 
386 llvm::raw_ostream &operator<<(
387     llvm::raw_ostream &os, const AssocEntityDetails &x) {
388   os << *static_cast<const EntityDetails *>(&x);
389   if (auto assocRank{x.rank()}) {
390     os << " rank: " << *assocRank;
391   }
392   DumpExpr(os, "expr", x.expr());
393   return os;
394 }
395 
396 llvm::raw_ostream &operator<<(
397     llvm::raw_ostream &os, const ProcEntityDetails &x) {
398   if (auto *symbol{x.interface_.symbol()}) {
399     os << ' ' << symbol->name();
400   } else {
401     DumpType(os, x.interface_.type());
402   }
403   DumpOptional(os, "bindName", x.bindName());
404   DumpOptional(os, "passName", x.passName());
405   if (x.init()) {
406     if (const Symbol * target{*x.init()}) {
407       os << " => " << target->name();
408     } else {
409       os << " => NULL()";
410     }
411   }
412   return os;
413 }
414 
415 llvm::raw_ostream &operator<<(
416     llvm::raw_ostream &os, const DerivedTypeDetails &x) {
417   DumpBool(os, "sequence", x.sequence_);
418   DumpList(os, "components", x.componentNames_);
419   return os;
420 }
421 
422 llvm::raw_ostream &operator<<(llvm::raw_ostream &os, const GenericDetails &x) {
423   os << ' ' << x.kind().ToString();
424   DumpBool(os, "(specific)", x.specific() != nullptr);
425   DumpBool(os, "(derivedType)", x.derivedType() != nullptr);
426   if (const auto &uses{x.uses()}; !uses.empty()) {
427     os << " (uses:";
428     char sep{' '};
429     for (const Symbol &use : uses) {
430       const Symbol &ultimate{use.GetUltimate()};
431       os << sep << ultimate.name() << "->"
432          << ultimate.owner().GetName().value();
433       sep = ',';
434     }
435     os << ')';
436   }
437   os << " procs:";
438   DumpSymbolVector(os, x.specificProcs());
439   return os;
440 }
441 
442 llvm::raw_ostream &operator<<(llvm::raw_ostream &os, const Details &details) {
443   os << DetailsToString(details);
444   std::visit( //
445       common::visitors{
446           [&](const UnknownDetails &) {},
447           [&](const MainProgramDetails &) {},
448           [&](const ModuleDetails &x) {
449             if (x.isSubmodule()) {
450               os << " (";
451               if (x.ancestor()) {
452                 auto ancestor{x.ancestor()->GetName().value()};
453                 os << ancestor;
454                 if (x.parent()) {
455                   auto parent{x.parent()->GetName().value()};
456                   if (ancestor != parent) {
457                     os << ':' << parent;
458                   }
459                 }
460               }
461               os << ")";
462             }
463           },
464           [&](const SubprogramNameDetails &x) {
465             os << ' ' << EnumToString(x.kind());
466           },
467           [&](const UseDetails &x) {
468             os << " from " << x.symbol().name() << " in "
469                << GetUsedModule(x).name();
470           },
471           [&](const UseErrorDetails &x) {
472             os << " uses:";
473             char sep{':'};
474             for (const auto &[location, module] : x.occurrences()) {
475               os << sep << " from " << module->GetName().value() << " at "
476                  << location;
477               sep = ',';
478             }
479           },
480           [](const HostAssocDetails &) {},
481           [&](const ProcBindingDetails &x) {
482             os << " => " << x.symbol().name();
483             DumpOptional(os, "passName", x.passName());
484           },
485           [&](const NamelistDetails &x) {
486             os << ':';
487             DumpSymbolVector(os, x.objects());
488           },
489           [&](const CommonBlockDetails &x) {
490             DumpOptional(os, "bindName", x.bindName());
491             if (x.alignment()) {
492               os << " alignment=" << x.alignment();
493             }
494             os << ':';
495             for (const auto &object : x.objects()) {
496               os << ' ' << object->name();
497             }
498           },
499           [&](const TypeParamDetails &x) {
500             DumpOptional(os, "type", x.type());
501             os << ' ' << common::EnumToString(x.attr());
502             DumpExpr(os, "init", x.init());
503           },
504           [&](const MiscDetails &x) {
505             os << ' ' << MiscDetails::EnumToString(x.kind());
506           },
507           [&](const auto &x) { os << x; },
508       },
509       details);
510   return os;
511 }
512 
513 llvm::raw_ostream &operator<<(llvm::raw_ostream &o, Symbol::Flag flag) {
514   return o << Symbol::EnumToString(flag);
515 }
516 
517 llvm::raw_ostream &operator<<(
518     llvm::raw_ostream &o, const Symbol::Flags &flags) {
519   std::size_t n{flags.count()};
520   std::size_t seen{0};
521   for (std::size_t j{0}; seen < n; ++j) {
522     Symbol::Flag flag{static_cast<Symbol::Flag>(j)};
523     if (flags.test(flag)) {
524       if (seen++ > 0) {
525         o << ", ";
526       }
527       o << flag;
528     }
529   }
530   return o;
531 }
532 
533 llvm::raw_ostream &operator<<(llvm::raw_ostream &os, const Symbol &symbol) {
534   os << symbol.name();
535   if (!symbol.attrs().empty()) {
536     os << ", " << symbol.attrs();
537   }
538   if (!symbol.flags().empty()) {
539     os << " (" << symbol.flags() << ')';
540   }
541   if (symbol.size_) {
542     os << " size=" << symbol.size_ << " offset=" << symbol.offset_;
543   }
544   os << ": " << symbol.details_;
545   return os;
546 }
547 
548 #if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
549 void Symbol::dump() const { llvm::errs() << *this << '\n'; }
550 #endif
551 
552 // Output a unique name for a scope by qualifying it with the names of
553 // parent scopes. For scopes without corresponding symbols, use the kind
554 // with an index (e.g. Block1, Block2, etc.).
555 static void DumpUniqueName(llvm::raw_ostream &os, const Scope &scope) {
556   if (!scope.IsTopLevel()) {
557     DumpUniqueName(os, scope.parent());
558     os << '/';
559     if (auto *scopeSymbol{scope.symbol()};
560         scopeSymbol && !scopeSymbol->name().empty()) {
561       os << scopeSymbol->name();
562     } else {
563       int index{1};
564       for (auto &child : scope.parent().children()) {
565         if (child == scope) {
566           break;
567         }
568         if (child.kind() == scope.kind()) {
569           ++index;
570         }
571       }
572       os << Scope::EnumToString(scope.kind()) << index;
573     }
574   }
575 }
576 
577 // Dump a symbol for UnparseWithSymbols. This will be used for tests so the
578 // format should be reasonably stable.
579 llvm::raw_ostream &DumpForUnparse(
580     llvm::raw_ostream &os, const Symbol &symbol, bool isDef) {
581   DumpUniqueName(os, symbol.owner());
582   os << '/' << symbol.name();
583   if (isDef) {
584     if (!symbol.attrs().empty()) {
585       os << ' ' << symbol.attrs();
586     }
587     if (!symbol.flags().empty()) {
588       os << " (" << symbol.flags() << ')';
589     }
590     os << ' ' << symbol.GetDetailsName();
591     DumpType(os, symbol.GetType());
592   }
593   return os;
594 }
595 
596 const DerivedTypeSpec *Symbol::GetParentTypeSpec(const Scope *scope) const {
597   if (const Symbol * parentComponent{GetParentComponent(scope)}) {
598     const auto &object{parentComponent->get<ObjectEntityDetails>()};
599     return &object.type()->derivedTypeSpec();
600   } else {
601     return nullptr;
602   }
603 }
604 
605 const Symbol *Symbol::GetParentComponent(const Scope *scope) const {
606   if (const auto *dtDetails{detailsIf<DerivedTypeDetails>()}) {
607     if (const Scope * localScope{scope ? scope : scope_}) {
608       return dtDetails->GetParentComponent(DEREF(localScope));
609     }
610   }
611   return nullptr;
612 }
613 
614 void DerivedTypeDetails::add_component(const Symbol &symbol) {
615   if (symbol.test(Symbol::Flag::ParentComp)) {
616     CHECK(componentNames_.empty());
617   }
618   componentNames_.push_back(symbol.name());
619 }
620 
621 const Symbol *DerivedTypeDetails::GetParentComponent(const Scope &scope) const {
622   if (auto extends{GetParentComponentName()}) {
623     if (auto iter{scope.find(*extends)}; iter != scope.cend()) {
624       if (const Symbol & symbol{*iter->second};
625           symbol.test(Symbol::Flag::ParentComp)) {
626         return &symbol;
627       }
628     }
629   }
630   return nullptr;
631 }
632 
633 const Symbol *DerivedTypeDetails::GetFinalForRank(int rank) const {
634   for (const auto &pair : finals_) {
635     const Symbol &symbol{*pair.second};
636     if (const auto *details{symbol.detailsIf<SubprogramDetails>()}) {
637       if (details->dummyArgs().size() == 1) {
638         if (const Symbol * arg{details->dummyArgs().at(0)}) {
639           if (const auto *object{arg->detailsIf<ObjectEntityDetails>()}) {
640             if (rank == object->shape().Rank() || object->IsAssumedRank() ||
641                 symbol.attrs().test(Attr::ELEMENTAL)) {
642               return &symbol;
643             }
644           }
645         }
646       }
647     }
648   }
649   return nullptr;
650 }
651 
652 void TypeParamDetails::set_type(const DeclTypeSpec &type) {
653   CHECK(!type_);
654   type_ = &type;
655 }
656 
657 bool GenericKind::IsIntrinsicOperator() const {
658   return Is(OtherKind::Concat) || Has<common::LogicalOperator>() ||
659       Has<common::NumericOperator>() || Has<common::RelationalOperator>();
660 }
661 
662 bool GenericKind::IsOperator() const {
663   return IsDefinedOperator() || IsIntrinsicOperator();
664 }
665 
666 std::string GenericKind::ToString() const {
667   return std::visit(
668       common::visitors {
669         [](const OtherKind &x) { return EnumToString(x); },
670             [](const DefinedIo &x) { return AsFortran(x).ToString(); },
671 #if !__clang__ && __GNUC__ == 7 && __GNUC_MINOR__ == 2
672             [](const common::NumericOperator &x) {
673               return common::EnumToString(x);
674             },
675             [](const common::LogicalOperator &x) {
676               return common::EnumToString(x);
677             },
678             [](const common::RelationalOperator &x) {
679               return common::EnumToString(x);
680             },
681 #else
682             [](const auto &x) { return common::EnumToString(x); },
683 #endif
684       },
685       u);
686 }
687 
688 SourceName GenericKind::AsFortran(DefinedIo x) {
689   const char *name{nullptr};
690   switch (x) {
691     SWITCH_COVERS_ALL_CASES
692   case DefinedIo::ReadFormatted:
693     name = "read(formatted)";
694     break;
695   case DefinedIo::ReadUnformatted:
696     name = "read(unformatted)";
697     break;
698   case DefinedIo::WriteFormatted:
699     name = "write(formatted)";
700     break;
701   case DefinedIo::WriteUnformatted:
702     name = "write(unformatted)";
703     break;
704   }
705   return {name, std::strlen(name)};
706 }
707 
708 bool GenericKind::Is(GenericKind::OtherKind x) const {
709   const OtherKind *y{std::get_if<OtherKind>(&u)};
710   return y && *y == x;
711 }
712 
713 bool SymbolOffsetCompare::operator()(const SymbolRef &x, const SymbolRef &y) const {
714   const Symbol *xCommon{FindCommonBlockContaining(*x)};
715   const Symbol *yCommon{FindCommonBlockContaining(*y)};
716   if (xCommon) {
717     if (yCommon) {
718       const SymbolSourcePositionCompare sourceCmp;
719       if (sourceCmp(*xCommon, *yCommon)) {
720         return true;
721       } else if (sourceCmp(*yCommon, *xCommon)) {
722         return false;
723       } else if (x->offset() == y->offset()) {
724         return x->size() > y->size();
725       } else {
726         return x->offset() < y->offset();
727       }
728     } else {
729       return false;
730     }
731   } else if (yCommon) {
732     return true;
733   } else if (x->offset() == y->offset()) {
734     return x->size() > y->size();
735   } else {
736     return x->offset() < y->offset();
737   }
738   return x->GetSemanticsContext().allCookedSources().Precedes(
739       x->name(), y->name());
740 }
741 
742 bool SymbolOffsetCompare::operator()(
743     const MutableSymbolRef &x, const MutableSymbolRef &y) const {
744   return (*this)(SymbolRef{*x}, SymbolRef{*y});
745 }
746 
747 } // namespace Fortran::semantics
748