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