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