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 <string>
17
18 namespace Fortran::semantics {
19
20 template <typename T>
DumpOptional(llvm::raw_ostream & os,const char * label,const T & x)21 static void DumpOptional(llvm::raw_ostream &os, const char *label, const T &x) {
22 if (x) {
23 os << ' ' << label << ':' << *x;
24 }
25 }
26 template <typename T>
DumpExpr(llvm::raw_ostream & os,const char * label,const std::optional<evaluate::Expr<T>> & x)27 static void DumpExpr(llvm::raw_ostream &os, const char *label,
28 const std::optional<evaluate::Expr<T>> &x) {
29 if (x) {
30 x->AsFortran(os << ' ' << label << ':');
31 }
32 }
33
DumpBool(llvm::raw_ostream & os,const char * label,bool x)34 static void DumpBool(llvm::raw_ostream &os, const char *label, bool x) {
35 if (x) {
36 os << ' ' << label;
37 }
38 }
39
DumpSymbolVector(llvm::raw_ostream & os,const SymbolVector & list)40 static void DumpSymbolVector(llvm::raw_ostream &os, const SymbolVector &list) {
41 char sep{' '};
42 for (const Symbol &elem : list) {
43 os << sep << elem.name();
44 sep = ',';
45 }
46 }
47
DumpType(llvm::raw_ostream & os,const Symbol & symbol)48 static void DumpType(llvm::raw_ostream &os, const Symbol &symbol) {
49 if (const auto *type{symbol.GetType()}) {
50 os << *type << ' ';
51 }
52 }
DumpType(llvm::raw_ostream & os,const DeclTypeSpec * type)53 static void DumpType(llvm::raw_ostream &os, const DeclTypeSpec *type) {
54 if (type) {
55 os << ' ' << *type;
56 }
57 }
58
59 template <typename T>
DumpList(llvm::raw_ostream & os,const char * label,const T & list)60 static void DumpList(llvm::raw_ostream &os, const char *label, const T &list) {
61 if (!list.empty()) {
62 os << ' ' << label << ':';
63 char sep{' '};
64 for (const auto &elem : list) {
65 os << sep << elem;
66 sep = ',';
67 }
68 }
69 }
70
parent() const71 const Scope *ModuleDetails::parent() const {
72 return isSubmodule_ && scope_ ? &scope_->parent() : nullptr;
73 }
ancestor() const74 const Scope *ModuleDetails::ancestor() const {
75 return isSubmodule_ && scope_ ? FindModuleContaining(*scope_) : nullptr;
76 }
set_scope(const Scope * scope)77 void ModuleDetails::set_scope(const Scope *scope) {
78 CHECK(!scope_);
79 bool scopeIsSubmodule{scope->parent().kind() == Scope::Kind::Module};
80 CHECK(isSubmodule_ == scopeIsSubmodule);
81 scope_ = scope;
82 }
83
operator <<(llvm::raw_ostream & os,const SubprogramDetails & x)84 llvm::raw_ostream &operator<<(
85 llvm::raw_ostream &os, const SubprogramDetails &x) {
86 DumpBool(os, "isInterface", x.isInterface_);
87 DumpExpr(os, "bindName", x.bindName_);
88 if (x.result_) {
89 DumpType(os << " result:", x.result());
90 os << x.result_->name();
91 if (!x.result_->attrs().empty()) {
92 os << ", " << x.result_->attrs();
93 }
94 }
95 if (x.entryScope_) {
96 os << " entry";
97 if (x.entryScope_->symbol()) {
98 os << " in " << x.entryScope_->symbol()->name();
99 }
100 }
101 char sep{'('};
102 os << ' ';
103 for (const Symbol *arg : x.dummyArgs_) {
104 os << sep;
105 sep = ',';
106 if (arg) {
107 DumpType(os, *arg);
108 os << arg->name();
109 } else {
110 os << '*';
111 }
112 }
113 os << (sep == '(' ? "()" : ")");
114 if (x.stmtFunction_) {
115 os << " -> " << x.stmtFunction_->AsFortran();
116 }
117 return os;
118 }
119
set_type(const DeclTypeSpec & type)120 void EntityDetails::set_type(const DeclTypeSpec &type) {
121 CHECK(!type_);
122 type_ = &type;
123 }
124
set_rank(int rank)125 void AssocEntityDetails::set_rank(int rank) { rank_ = rank; }
ReplaceType(const DeclTypeSpec & type)126 void EntityDetails::ReplaceType(const DeclTypeSpec &type) { type_ = &type; }
127
set_shape(const ArraySpec & shape)128 void ObjectEntityDetails::set_shape(const ArraySpec &shape) {
129 CHECK(shape_.empty());
130 for (const auto &shapeSpec : shape) {
131 shape_.push_back(shapeSpec);
132 }
133 }
set_coshape(const ArraySpec & coshape)134 void ObjectEntityDetails::set_coshape(const ArraySpec &coshape) {
135 CHECK(coshape_.empty());
136 for (const auto &shapeSpec : coshape) {
137 coshape_.push_back(shapeSpec);
138 }
139 }
140
ProcEntityDetails(EntityDetails && d)141 ProcEntityDetails::ProcEntityDetails(EntityDetails &&d) : EntityDetails(d) {
142 if (type()) {
143 interface_.set_type(*type());
144 }
145 }
146
UseErrorDetails(const UseDetails & useDetails)147 UseErrorDetails::UseErrorDetails(const UseDetails &useDetails) {
148 add_occurrence(useDetails.location(), *GetUsedModule(useDetails).scope());
149 }
add_occurrence(const SourceName & location,const Scope & module)150 UseErrorDetails &UseErrorDetails::add_occurrence(
151 const SourceName &location, const Scope &module) {
152 occurrences_.push_back(std::make_pair(location, &module));
153 return *this;
154 }
155
AddSpecificProc(const Symbol & proc,SourceName bindingName)156 void GenericDetails::AddSpecificProc(
157 const Symbol &proc, SourceName bindingName) {
158 specificProcs_.push_back(proc);
159 bindingNames_.push_back(bindingName);
160 }
set_specific(Symbol & specific)161 void GenericDetails::set_specific(Symbol &specific) {
162 CHECK(!specific_);
163 CHECK(!derivedType_);
164 specific_ = &specific;
165 }
set_derivedType(Symbol & derivedType)166 void GenericDetails::set_derivedType(Symbol &derivedType) {
167 CHECK(!specific_);
168 CHECK(!derivedType_);
169 derivedType_ = &derivedType;
170 }
AddUse(const Symbol & use)171 void GenericDetails::AddUse(const Symbol &use) {
172 CHECK(use.has<UseDetails>());
173 uses_.push_back(use);
174 }
175
CheckSpecific() const176 const Symbol *GenericDetails::CheckSpecific() const {
177 return const_cast<GenericDetails *>(this)->CheckSpecific();
178 }
CheckSpecific()179 Symbol *GenericDetails::CheckSpecific() {
180 if (specific_) {
181 for (const Symbol &proc : specificProcs_) {
182 if (&proc == specific_) {
183 return nullptr;
184 }
185 }
186 return specific_;
187 } else {
188 return nullptr;
189 }
190 }
191
CopyFrom(const GenericDetails & from)192 void GenericDetails::CopyFrom(const GenericDetails &from) {
193 CHECK(specificProcs_.size() == bindingNames_.size());
194 CHECK(from.specificProcs_.size() == from.bindingNames_.size());
195 kind_ = from.kind_;
196 if (from.derivedType_) {
197 CHECK(!derivedType_ || derivedType_ == from.derivedType_);
198 derivedType_ = from.derivedType_;
199 }
200 for (std::size_t i{0}; i < from.specificProcs_.size(); ++i) {
201 if (std::find_if(specificProcs_.begin(), specificProcs_.end(),
202 [&](const Symbol &mySymbol) {
203 return &mySymbol == &*from.specificProcs_[i];
204 }) == specificProcs_.end()) {
205 specificProcs_.push_back(from.specificProcs_[i]);
206 bindingNames_.push_back(from.bindingNames_[i]);
207 }
208 }
209 }
210
211 // The name of the kind of details for this symbol.
212 // This is primarily for debugging.
DetailsToString(const Details & details)213 std::string DetailsToString(const Details &details) {
214 return std::visit(
215 common::visitors{
216 [](const UnknownDetails &) { return "Unknown"; },
217 [](const MainProgramDetails &) { return "MainProgram"; },
218 [](const ModuleDetails &) { return "Module"; },
219 [](const SubprogramDetails &) { return "Subprogram"; },
220 [](const SubprogramNameDetails &) { return "SubprogramName"; },
221 [](const EntityDetails &) { return "Entity"; },
222 [](const ObjectEntityDetails &) { return "ObjectEntity"; },
223 [](const ProcEntityDetails &) { return "ProcEntity"; },
224 [](const DerivedTypeDetails &) { return "DerivedType"; },
225 [](const UseDetails &) { return "Use"; },
226 [](const UseErrorDetails &) { return "UseError"; },
227 [](const HostAssocDetails &) { return "HostAssoc"; },
228 [](const GenericDetails &) { return "Generic"; },
229 [](const ProcBindingDetails &) { return "ProcBinding"; },
230 [](const NamelistDetails &) { return "Namelist"; },
231 [](const CommonBlockDetails &) { return "CommonBlockDetails"; },
232 [](const TypeParamDetails &) { return "TypeParam"; },
233 [](const MiscDetails &) { return "Misc"; },
234 [](const AssocEntityDetails &) { return "AssocEntity"; },
235 },
236 details);
237 }
238
GetDetailsName() const239 const std::string Symbol::GetDetailsName() const {
240 return DetailsToString(details_);
241 }
242
set_details(Details && details)243 void Symbol::set_details(Details &&details) {
244 CHECK(CanReplaceDetails(details));
245 details_ = std::move(details);
246 }
247
CanReplaceDetails(const Details & details) const248 bool Symbol::CanReplaceDetails(const Details &details) const {
249 if (has<UnknownDetails>()) {
250 return true; // can always replace UnknownDetails
251 } else {
252 return std::visit(
253 common::visitors{
254 [](const UseErrorDetails &) { return true; },
255 [&](const ObjectEntityDetails &) { return has<EntityDetails>(); },
256 [&](const ProcEntityDetails &) { return has<EntityDetails>(); },
257 [&](const SubprogramDetails &) {
258 return has<SubprogramNameDetails>() || has<EntityDetails>();
259 },
260 [&](const DerivedTypeDetails &) {
261 const auto *derived{detailsIf<DerivedTypeDetails>()};
262 return derived && derived->isForwardReferenced();
263 },
264 [&](const UseDetails &x) {
265 const auto *use{detailsIf<UseDetails>()};
266 return use && use->symbol() == x.symbol();
267 },
268 [](const auto &) { return false; },
269 },
270 details);
271 }
272 }
273
274 // Usually a symbol's name is the first occurrence in the source, but sometimes
275 // we want to replace it with one at a different location (but same characters).
ReplaceName(const SourceName & name)276 void Symbol::ReplaceName(const SourceName &name) {
277 CHECK(name == name_);
278 name_ = name;
279 }
280
SetType(const DeclTypeSpec & type)281 void Symbol::SetType(const DeclTypeSpec &type) {
282 std::visit(common::visitors{
283 [&](EntityDetails &x) { x.set_type(type); },
284 [&](ObjectEntityDetails &x) { x.set_type(type); },
285 [&](AssocEntityDetails &x) { x.set_type(type); },
286 [&](ProcEntityDetails &x) { x.interface().set_type(type); },
287 [&](TypeParamDetails &x) { x.set_type(type); },
288 [](auto &) {},
289 },
290 details_);
291 }
292
IsFuncResult() const293 bool Symbol::IsFuncResult() const {
294 return std::visit(
295 common::visitors{[](const EntityDetails &x) { return x.isFuncResult(); },
296 [](const ObjectEntityDetails &x) { return x.isFuncResult(); },
297 [](const ProcEntityDetails &x) { return x.isFuncResult(); },
298 [](const HostAssocDetails &x) { return x.symbol().IsFuncResult(); },
299 [](const auto &) { return false; }},
300 details_);
301 }
302
IsObjectArray() const303 bool Symbol::IsObjectArray() const {
304 const auto *details{std::get_if<ObjectEntityDetails>(&details_)};
305 return details && details->IsArray();
306 }
307
IsSubprogram() const308 bool Symbol::IsSubprogram() const {
309 return std::visit(
310 common::visitors{
311 [](const SubprogramDetails &) { return true; },
312 [](const SubprogramNameDetails &) { return true; },
313 [](const GenericDetails &) { return true; },
314 [](const UseDetails &x) { return x.symbol().IsSubprogram(); },
315 [](const auto &) { return false; },
316 },
317 details_);
318 }
319
IsFromModFile() const320 bool Symbol::IsFromModFile() const {
321 return test(Flag::ModFile) ||
322 (!owner_->IsGlobal() && owner_->symbol()->IsFromModFile());
323 }
324
ObjectEntityDetails(EntityDetails && d)325 ObjectEntityDetails::ObjectEntityDetails(EntityDetails &&d)
326 : EntityDetails(d) {}
327
operator <<(llvm::raw_ostream & os,const EntityDetails & x)328 llvm::raw_ostream &operator<<(llvm::raw_ostream &os, const EntityDetails &x) {
329 DumpBool(os, "dummy", x.isDummy());
330 DumpBool(os, "funcResult", x.isFuncResult());
331 if (x.type()) {
332 os << " type: " << *x.type();
333 }
334 DumpExpr(os, "bindName", x.bindName_);
335 return os;
336 }
337
operator <<(llvm::raw_ostream & os,const ObjectEntityDetails & x)338 llvm::raw_ostream &operator<<(
339 llvm::raw_ostream &os, const ObjectEntityDetails &x) {
340 os << *static_cast<const EntityDetails *>(&x);
341 DumpList(os, "shape", x.shape());
342 DumpList(os, "coshape", x.coshape());
343 DumpExpr(os, "init", x.init_);
344 return os;
345 }
346
operator <<(llvm::raw_ostream & os,const AssocEntityDetails & x)347 llvm::raw_ostream &operator<<(
348 llvm::raw_ostream &os, const AssocEntityDetails &x) {
349 os << *static_cast<const EntityDetails *>(&x);
350 if (auto assocRank{x.rank()}) {
351 os << " rank: " << *assocRank;
352 }
353 DumpExpr(os, "expr", x.expr());
354 return os;
355 }
356
operator <<(llvm::raw_ostream & os,const ProcEntityDetails & x)357 llvm::raw_ostream &operator<<(
358 llvm::raw_ostream &os, const ProcEntityDetails &x) {
359 if (auto *symbol{x.interface_.symbol()}) {
360 os << ' ' << symbol->name();
361 } else {
362 DumpType(os, x.interface_.type());
363 }
364 DumpExpr(os, "bindName", x.bindName());
365 DumpOptional(os, "passName", x.passName());
366 if (x.init()) {
367 if (const Symbol * target{*x.init()}) {
368 os << " => " << target->name();
369 } else {
370 os << " => NULL()";
371 }
372 }
373 return os;
374 }
375
operator <<(llvm::raw_ostream & os,const DerivedTypeDetails & x)376 llvm::raw_ostream &operator<<(
377 llvm::raw_ostream &os, const DerivedTypeDetails &x) {
378 DumpBool(os, "sequence", x.sequence_);
379 DumpList(os, "components", x.componentNames_);
380 return os;
381 }
382
operator <<(llvm::raw_ostream & os,const GenericDetails & x)383 llvm::raw_ostream &operator<<(llvm::raw_ostream &os, const GenericDetails &x) {
384 os << ' ' << x.kind().ToString();
385 DumpBool(os, "(specific)", x.specific() != nullptr);
386 DumpBool(os, "(derivedType)", x.derivedType() != nullptr);
387 if (const auto &uses{x.uses()}; !uses.empty()) {
388 os << " (uses:";
389 char sep{' '};
390 for (const Symbol &use : uses) {
391 const Symbol &ultimate{use.GetUltimate()};
392 os << sep << ultimate.name() << "->"
393 << ultimate.owner().GetName().value();
394 sep = ',';
395 }
396 os << ')';
397 }
398 os << " procs:";
399 DumpSymbolVector(os, x.specificProcs());
400 return os;
401 }
402
operator <<(llvm::raw_ostream & os,const Details & details)403 llvm::raw_ostream &operator<<(llvm::raw_ostream &os, const Details &details) {
404 os << DetailsToString(details);
405 std::visit( //
406 common::visitors{
407 [&](const UnknownDetails &) {},
408 [&](const MainProgramDetails &) {},
409 [&](const ModuleDetails &x) {
410 if (x.isSubmodule()) {
411 os << " (";
412 if (x.ancestor()) {
413 auto ancestor{x.ancestor()->GetName().value()};
414 os << ancestor;
415 if (x.parent()) {
416 auto parent{x.parent()->GetName().value()};
417 if (ancestor != parent) {
418 os << ':' << parent;
419 }
420 }
421 }
422 os << ")";
423 }
424 },
425 [&](const SubprogramNameDetails &x) {
426 os << ' ' << EnumToString(x.kind());
427 },
428 [&](const UseDetails &x) {
429 os << " from " << x.symbol().name() << " in "
430 << GetUsedModule(x).name();
431 },
432 [&](const UseErrorDetails &x) {
433 os << " uses:";
434 for (const auto &[location, module] : x.occurrences()) {
435 os << " from " << module->GetName().value() << " at " << location;
436 }
437 },
438 [](const HostAssocDetails &) {},
439 [&](const ProcBindingDetails &x) {
440 os << " => " << x.symbol().name();
441 DumpOptional(os, "passName", x.passName());
442 },
443 [&](const NamelistDetails &x) {
444 os << ':';
445 DumpSymbolVector(os, x.objects());
446 },
447 [&](const CommonBlockDetails &x) {
448 if (x.alignment()) {
449 os << " alignment=" << x.alignment();
450 }
451 os << ':';
452 for (const auto &object : x.objects()) {
453 os << ' ' << object->name();
454 }
455 },
456 [&](const TypeParamDetails &x) {
457 DumpOptional(os, "type", x.type());
458 os << ' ' << common::EnumToString(x.attr());
459 DumpExpr(os, "init", x.init());
460 },
461 [&](const MiscDetails &x) {
462 os << ' ' << MiscDetails::EnumToString(x.kind());
463 },
464 [&](const auto &x) { os << x; },
465 },
466 details);
467 return os;
468 }
469
operator <<(llvm::raw_ostream & o,Symbol::Flag flag)470 llvm::raw_ostream &operator<<(llvm::raw_ostream &o, Symbol::Flag flag) {
471 return o << Symbol::EnumToString(flag);
472 }
473
operator <<(llvm::raw_ostream & o,const Symbol::Flags & flags)474 llvm::raw_ostream &operator<<(
475 llvm::raw_ostream &o, const Symbol::Flags &flags) {
476 std::size_t n{flags.count()};
477 std::size_t seen{0};
478 for (std::size_t j{0}; seen < n; ++j) {
479 Symbol::Flag flag{static_cast<Symbol::Flag>(j)};
480 if (flags.test(flag)) {
481 if (seen++ > 0) {
482 o << ", ";
483 }
484 o << flag;
485 }
486 }
487 return o;
488 }
489
operator <<(llvm::raw_ostream & os,const Symbol & symbol)490 llvm::raw_ostream &operator<<(llvm::raw_ostream &os, const Symbol &symbol) {
491 os << symbol.name();
492 if (!symbol.attrs().empty()) {
493 os << ", " << symbol.attrs();
494 }
495 if (!symbol.flags().empty()) {
496 os << " (" << symbol.flags() << ')';
497 }
498 if (symbol.size_) {
499 os << " size=" << symbol.size_ << " offset=" << symbol.offset_;
500 }
501 os << ": " << symbol.details_;
502 return os;
503 }
504
505 // Output a unique name for a scope by qualifying it with the names of
506 // parent scopes. For scopes without corresponding symbols, use the kind
507 // with an index (e.g. Block1, Block2, etc.).
DumpUniqueName(llvm::raw_ostream & os,const Scope & scope)508 static void DumpUniqueName(llvm::raw_ostream &os, const Scope &scope) {
509 if (!scope.IsGlobal()) {
510 DumpUniqueName(os, scope.parent());
511 os << '/';
512 if (auto *scopeSymbol{scope.symbol()};
513 scopeSymbol && !scopeSymbol->name().empty()) {
514 os << scopeSymbol->name();
515 } else {
516 int index{1};
517 for (auto &child : scope.parent().children()) {
518 if (child == scope) {
519 break;
520 }
521 if (child.kind() == scope.kind()) {
522 ++index;
523 }
524 }
525 os << Scope::EnumToString(scope.kind()) << index;
526 }
527 }
528 }
529
530 // Dump a symbol for UnparseWithSymbols. This will be used for tests so the
531 // format should be reasonably stable.
DumpForUnparse(llvm::raw_ostream & os,const Symbol & symbol,bool isDef)532 llvm::raw_ostream &DumpForUnparse(
533 llvm::raw_ostream &os, const Symbol &symbol, bool isDef) {
534 DumpUniqueName(os, symbol.owner());
535 os << '/' << symbol.name();
536 if (isDef) {
537 if (!symbol.attrs().empty()) {
538 os << ' ' << symbol.attrs();
539 }
540 if (!symbol.flags().empty()) {
541 os << " (" << symbol.flags() << ')';
542 }
543 os << ' ' << symbol.GetDetailsName();
544 DumpType(os, symbol.GetType());
545 }
546 return os;
547 }
548
GetParentTypeSpec(const Scope * scope) const549 const DerivedTypeSpec *Symbol::GetParentTypeSpec(const Scope *scope) const {
550 if (const Symbol * parentComponent{GetParentComponent(scope)}) {
551 const auto &object{parentComponent->get<ObjectEntityDetails>()};
552 return &object.type()->derivedTypeSpec();
553 } else {
554 return nullptr;
555 }
556 }
557
GetParentComponent(const Scope * scope) const558 const Symbol *Symbol::GetParentComponent(const Scope *scope) const {
559 if (const auto *dtDetails{detailsIf<DerivedTypeDetails>()}) {
560 if (const Scope * localScope{scope ? scope : scope_}) {
561 return dtDetails->GetParentComponent(DEREF(localScope));
562 }
563 }
564 return nullptr;
565 }
566
add_component(const Symbol & symbol)567 void DerivedTypeDetails::add_component(const Symbol &symbol) {
568 if (symbol.test(Symbol::Flag::ParentComp)) {
569 CHECK(componentNames_.empty());
570 }
571 componentNames_.push_back(symbol.name());
572 }
573
GetParentComponent(const Scope & scope) const574 const Symbol *DerivedTypeDetails::GetParentComponent(const Scope &scope) const {
575 if (auto extends{GetParentComponentName()}) {
576 if (auto iter{scope.find(*extends)}; iter != scope.cend()) {
577 if (const Symbol & symbol{*iter->second};
578 symbol.test(Symbol::Flag::ParentComp)) {
579 return &symbol;
580 }
581 }
582 }
583 return nullptr;
584 }
585
GetFinalForRank(int rank) const586 const Symbol *DerivedTypeDetails::GetFinalForRank(int rank) const {
587 for (const auto &pair : finals_) {
588 const Symbol &symbol{*pair.second};
589 if (const auto *details{symbol.detailsIf<SubprogramDetails>()}) {
590 if (details->dummyArgs().size() == 1) {
591 if (const Symbol * arg{details->dummyArgs().at(0)}) {
592 if (const auto *object{arg->detailsIf<ObjectEntityDetails>()}) {
593 if (rank == object->shape().Rank() || object->IsAssumedRank() ||
594 symbol.attrs().test(Attr::ELEMENTAL)) {
595 return &symbol;
596 }
597 }
598 }
599 }
600 }
601 }
602 return nullptr;
603 }
604
set_type(const DeclTypeSpec & type)605 void TypeParamDetails::set_type(const DeclTypeSpec &type) {
606 CHECK(!type_);
607 type_ = &type;
608 }
609
IsIntrinsicOperator() const610 bool GenericKind::IsIntrinsicOperator() const {
611 return Is(OtherKind::Concat) || Has<common::LogicalOperator>() ||
612 Has<common::NumericOperator>() || Has<common::RelationalOperator>();
613 }
614
IsOperator() const615 bool GenericKind::IsOperator() const {
616 return IsDefinedOperator() || IsIntrinsicOperator();
617 }
618
ToString() const619 std::string GenericKind::ToString() const {
620 return std::visit(
621 common::visitors {
622 [](const OtherKind &x) { return EnumToString(x); },
623 [](const DefinedIo &x) { return EnumToString(x); },
624 #if !__clang__ && __GNUC__ == 7 && __GNUC_MINOR__ == 2
625 [](const common::NumericOperator &x) {
626 return common::EnumToString(x);
627 },
628 [](const common::LogicalOperator &x) {
629 return common::EnumToString(x);
630 },
631 [](const common::RelationalOperator &x) {
632 return common::EnumToString(x);
633 },
634 #else
635 [](const auto &x) { return common::EnumToString(x); },
636 #endif
637 },
638 u);
639 }
640
Is(GenericKind::OtherKind x) const641 bool GenericKind::Is(GenericKind::OtherKind x) const {
642 const OtherKind *y{std::get_if<OtherKind>(&u)};
643 return y && *y == x;
644 }
645
646 } // namespace Fortran::semantics
647