xref: /llvm-project/flang/lib/Semantics/tools.cpp (revision fc97d2e68b03bc2979395e84b645e5b3ba35aecd)
1 //===-- lib/Semantics/tools.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/Parser/tools.h"
10 #include "flang/Common/Fortran.h"
11 #include "flang/Common/indirection.h"
12 #include "flang/Parser/dump-parse-tree.h"
13 #include "flang/Parser/message.h"
14 #include "flang/Parser/parse-tree.h"
15 #include "flang/Semantics/scope.h"
16 #include "flang/Semantics/semantics.h"
17 #include "flang/Semantics/symbol.h"
18 #include "flang/Semantics/tools.h"
19 #include "flang/Semantics/type.h"
20 #include "llvm/Support/raw_ostream.h"
21 #include <algorithm>
22 #include <set>
23 #include <variant>
24 
25 namespace Fortran::semantics {
26 
27 // Find this or containing scope that matches predicate
28 static const Scope *FindScopeContaining(
29     const Scope &start, std::function<bool(const Scope &)> predicate) {
30   for (const Scope *scope{&start};; scope = &scope->parent()) {
31     if (predicate(*scope)) {
32       return scope;
33     }
34     if (scope->IsTopLevel()) {
35       return nullptr;
36     }
37   }
38 }
39 
40 const Scope &GetTopLevelUnitContaining(const Scope &start) {
41   CHECK(!start.IsTopLevel());
42   return DEREF(FindScopeContaining(
43       start, [](const Scope &scope) { return scope.parent().IsTopLevel(); }));
44 }
45 
46 const Scope &GetTopLevelUnitContaining(const Symbol &symbol) {
47   return GetTopLevelUnitContaining(symbol.owner());
48 }
49 
50 const Scope *FindModuleContaining(const Scope &start) {
51   return FindScopeContaining(
52       start, [](const Scope &scope) { return scope.IsModule(); });
53 }
54 
55 const Scope *FindModuleFileContaining(const Scope &start) {
56   return FindScopeContaining(
57       start, [](const Scope &scope) { return scope.IsModuleFile(); });
58 }
59 
60 const Scope &GetProgramUnitContaining(const Scope &start) {
61   CHECK(!start.IsTopLevel());
62   return DEREF(FindScopeContaining(start, [](const Scope &scope) {
63     switch (scope.kind()) {
64     case Scope::Kind::Module:
65     case Scope::Kind::MainProgram:
66     case Scope::Kind::Subprogram:
67     case Scope::Kind::BlockData:
68       return true;
69     default:
70       return false;
71     }
72   }));
73 }
74 
75 const Scope &GetProgramUnitContaining(const Symbol &symbol) {
76   return GetProgramUnitContaining(symbol.owner());
77 }
78 
79 const Scope &GetProgramUnitOrBlockConstructContaining(const Scope &start) {
80   CHECK(!start.IsTopLevel());
81   return DEREF(FindScopeContaining(start, [](const Scope &scope) {
82     switch (scope.kind()) {
83     case Scope::Kind::Module:
84     case Scope::Kind::MainProgram:
85     case Scope::Kind::Subprogram:
86     case Scope::Kind::BlockData:
87     case Scope::Kind::BlockConstruct:
88       return true;
89     default:
90       return false;
91     }
92   }));
93 }
94 
95 const Scope &GetProgramUnitOrBlockConstructContaining(const Symbol &symbol) {
96   return GetProgramUnitOrBlockConstructContaining(symbol.owner());
97 }
98 
99 const Scope *FindPureProcedureContaining(const Scope &start) {
100   // N.B. We only need to examine the innermost containing program unit
101   // because an internal subprogram of a pure subprogram must also
102   // be pure (C1592).
103   if (start.IsTopLevel()) {
104     return nullptr;
105   } else {
106     const Scope &scope{GetProgramUnitContaining(start)};
107     return IsPureProcedure(scope) ? &scope : nullptr;
108   }
109 }
110 
111 const Scope *FindOpenACCConstructContaining(const Scope *scope) {
112   return scope ? FindScopeContaining(*scope,
113                      [](const Scope &s) {
114                        return s.kind() == Scope::Kind::OpenACCConstruct;
115                      })
116                : nullptr;
117 }
118 
119 // 7.5.2.4 "same derived type" test -- rely on IsTkCompatibleWith() and its
120 // infrastructure to detect and handle comparisons on distinct (but "same")
121 // sequence/bind(C) derived types
122 static bool MightBeSameDerivedType(
123     const std::optional<evaluate::DynamicType> &lhsType,
124     const std::optional<evaluate::DynamicType> &rhsType) {
125   return lhsType && rhsType && lhsType->IsTkCompatibleWith(*rhsType);
126 }
127 
128 Tristate IsDefinedAssignment(
129     const std::optional<evaluate::DynamicType> &lhsType, int lhsRank,
130     const std::optional<evaluate::DynamicType> &rhsType, int rhsRank) {
131   if (!lhsType || !rhsType) {
132     return Tristate::No; // error or rhs is untyped
133   }
134   if (lhsType->IsUnlimitedPolymorphic()) {
135     return Tristate::No;
136   }
137   if (rhsType->IsUnlimitedPolymorphic()) {
138     return Tristate::Maybe;
139   }
140   TypeCategory lhsCat{lhsType->category()};
141   TypeCategory rhsCat{rhsType->category()};
142   if (rhsRank > 0 && lhsRank != rhsRank) {
143     return Tristate::Yes;
144   } else if (lhsCat != TypeCategory::Derived) {
145     return ToTristate(lhsCat != rhsCat &&
146         (!IsNumericTypeCategory(lhsCat) || !IsNumericTypeCategory(rhsCat) ||
147             lhsCat == TypeCategory::Unsigned ||
148             rhsCat == TypeCategory::Unsigned));
149   } else if (MightBeSameDerivedType(lhsType, rhsType)) {
150     return Tristate::Maybe; // TYPE(t) = TYPE(t) can be defined or intrinsic
151   } else {
152     return Tristate::Yes;
153   }
154 }
155 
156 bool IsIntrinsicRelational(common::RelationalOperator opr,
157     const evaluate::DynamicType &type0, int rank0,
158     const evaluate::DynamicType &type1, int rank1) {
159   if (!evaluate::AreConformable(rank0, rank1)) {
160     return false;
161   } else {
162     auto cat0{type0.category()};
163     auto cat1{type1.category()};
164     if (cat0 == TypeCategory::Unsigned || cat1 == TypeCategory::Unsigned) {
165       return cat0 == cat1;
166     } else if (IsNumericTypeCategory(cat0) && IsNumericTypeCategory(cat1)) {
167       // numeric types: EQ/NE always ok, others ok for non-complex
168       return opr == common::RelationalOperator::EQ ||
169           opr == common::RelationalOperator::NE ||
170           (cat0 != TypeCategory::Complex && cat1 != TypeCategory::Complex);
171     } else {
172       // not both numeric: only Character is ok
173       return cat0 == TypeCategory::Character && cat1 == TypeCategory::Character;
174     }
175   }
176 }
177 
178 bool IsIntrinsicNumeric(const evaluate::DynamicType &type0) {
179   return IsNumericTypeCategory(type0.category());
180 }
181 bool IsIntrinsicNumeric(const evaluate::DynamicType &type0, int rank0,
182     const evaluate::DynamicType &type1, int rank1) {
183   return evaluate::AreConformable(rank0, rank1) &&
184       IsNumericTypeCategory(type0.category()) &&
185       IsNumericTypeCategory(type1.category());
186 }
187 
188 bool IsIntrinsicLogical(const evaluate::DynamicType &type0) {
189   return type0.category() == TypeCategory::Logical;
190 }
191 bool IsIntrinsicLogical(const evaluate::DynamicType &type0, int rank0,
192     const evaluate::DynamicType &type1, int rank1) {
193   return evaluate::AreConformable(rank0, rank1) &&
194       type0.category() == TypeCategory::Logical &&
195       type1.category() == TypeCategory::Logical;
196 }
197 
198 bool IsIntrinsicConcat(const evaluate::DynamicType &type0, int rank0,
199     const evaluate::DynamicType &type1, int rank1) {
200   return evaluate::AreConformable(rank0, rank1) &&
201       type0.category() == TypeCategory::Character &&
202       type1.category() == TypeCategory::Character &&
203       type0.kind() == type1.kind();
204 }
205 
206 bool IsGenericDefinedOp(const Symbol &symbol) {
207   const Symbol &ultimate{symbol.GetUltimate()};
208   if (const auto *generic{ultimate.detailsIf<GenericDetails>()}) {
209     return generic->kind().IsDefinedOperator();
210   } else if (const auto *misc{ultimate.detailsIf<MiscDetails>()}) {
211     return misc->kind() == MiscDetails::Kind::TypeBoundDefinedOp;
212   } else {
213     return false;
214   }
215 }
216 
217 bool IsDefinedOperator(SourceName name) {
218   const char *begin{name.begin()};
219   const char *end{name.end()};
220   return begin != end && begin[0] == '.' && end[-1] == '.';
221 }
222 
223 std::string MakeOpName(SourceName name) {
224   std::string result{name.ToString()};
225   return IsDefinedOperator(name)         ? "OPERATOR(" + result + ")"
226       : result.find("operator(", 0) == 0 ? parser::ToUpperCaseLetters(result)
227                                          : result;
228 }
229 
230 bool IsCommonBlockContaining(const Symbol &block, const Symbol &object) {
231   const auto &objects{block.get<CommonBlockDetails>().objects()};
232   return llvm::is_contained(objects, object);
233 }
234 
235 bool IsUseAssociated(const Symbol &symbol, const Scope &scope) {
236   const Scope &owner{GetTopLevelUnitContaining(symbol.GetUltimate().owner())};
237   return owner.kind() == Scope::Kind::Module &&
238       owner != GetTopLevelUnitContaining(scope);
239 }
240 
241 bool DoesScopeContain(
242     const Scope *maybeAncestor, const Scope &maybeDescendent) {
243   return maybeAncestor && !maybeDescendent.IsTopLevel() &&
244       FindScopeContaining(maybeDescendent.parent(),
245           [&](const Scope &scope) { return &scope == maybeAncestor; });
246 }
247 
248 bool DoesScopeContain(const Scope *maybeAncestor, const Symbol &symbol) {
249   return DoesScopeContain(maybeAncestor, symbol.owner());
250 }
251 
252 static const Symbol &FollowHostAssoc(const Symbol &symbol) {
253   for (const Symbol *s{&symbol};;) {
254     const auto *details{s->detailsIf<HostAssocDetails>()};
255     if (!details) {
256       return *s;
257     }
258     s = &details->symbol();
259   }
260 }
261 
262 bool IsHostAssociated(const Symbol &symbol, const Scope &scope) {
263   const Symbol &base{FollowHostAssoc(symbol)};
264   return base.owner().IsTopLevel() ||
265       DoesScopeContain(&GetProgramUnitOrBlockConstructContaining(base),
266           GetProgramUnitOrBlockConstructContaining(scope));
267 }
268 
269 bool IsHostAssociatedIntoSubprogram(const Symbol &symbol, const Scope &scope) {
270   const Symbol &base{FollowHostAssoc(symbol)};
271   return base.owner().IsTopLevel() ||
272       DoesScopeContain(&GetProgramUnitOrBlockConstructContaining(base),
273           GetProgramUnitContaining(scope));
274 }
275 
276 bool IsInStmtFunction(const Symbol &symbol) {
277   if (const Symbol * function{symbol.owner().symbol()}) {
278     return IsStmtFunction(*function);
279   }
280   return false;
281 }
282 
283 bool IsStmtFunctionDummy(const Symbol &symbol) {
284   return IsDummy(symbol) && IsInStmtFunction(symbol);
285 }
286 
287 bool IsStmtFunctionResult(const Symbol &symbol) {
288   return IsFunctionResult(symbol) && IsInStmtFunction(symbol);
289 }
290 
291 bool IsPointerDummy(const Symbol &symbol) {
292   return IsPointer(symbol) && IsDummy(symbol);
293 }
294 
295 bool IsBindCProcedure(const Symbol &original) {
296   const Symbol &symbol{original.GetUltimate()};
297   if (const auto *procDetails{symbol.detailsIf<ProcEntityDetails>()}) {
298     if (procDetails->procInterface()) {
299       // procedure component with a BIND(C) interface
300       return IsBindCProcedure(*procDetails->procInterface());
301     }
302   }
303   return symbol.attrs().test(Attr::BIND_C) && IsProcedure(symbol);
304 }
305 
306 bool IsBindCProcedure(const Scope &scope) {
307   if (const Symbol * symbol{scope.GetSymbol()}) {
308     return IsBindCProcedure(*symbol);
309   } else {
310     return false;
311   }
312 }
313 
314 static const Symbol *FindPointerComponent(
315     const Scope &scope, std::set<const Scope *> &visited) {
316   if (!scope.IsDerivedType()) {
317     return nullptr;
318   }
319   if (!visited.insert(&scope).second) {
320     return nullptr;
321   }
322   // If there's a top-level pointer component, return it for clearer error
323   // messaging.
324   for (const auto &pair : scope) {
325     const Symbol &symbol{*pair.second};
326     if (IsPointer(symbol)) {
327       return &symbol;
328     }
329   }
330   for (const auto &pair : scope) {
331     const Symbol &symbol{*pair.second};
332     if (const auto *details{symbol.detailsIf<ObjectEntityDetails>()}) {
333       if (const DeclTypeSpec * type{details->type()}) {
334         if (const DerivedTypeSpec * derived{type->AsDerived()}) {
335           if (const Scope * nested{derived->scope()}) {
336             if (const Symbol *
337                 pointer{FindPointerComponent(*nested, visited)}) {
338               return pointer;
339             }
340           }
341         }
342       }
343     }
344   }
345   return nullptr;
346 }
347 
348 const Symbol *FindPointerComponent(const Scope &scope) {
349   std::set<const Scope *> visited;
350   return FindPointerComponent(scope, visited);
351 }
352 
353 const Symbol *FindPointerComponent(const DerivedTypeSpec &derived) {
354   if (const Scope * scope{derived.scope()}) {
355     return FindPointerComponent(*scope);
356   } else {
357     return nullptr;
358   }
359 }
360 
361 const Symbol *FindPointerComponent(const DeclTypeSpec &type) {
362   if (const DerivedTypeSpec * derived{type.AsDerived()}) {
363     return FindPointerComponent(*derived);
364   } else {
365     return nullptr;
366   }
367 }
368 
369 const Symbol *FindPointerComponent(const DeclTypeSpec *type) {
370   return type ? FindPointerComponent(*type) : nullptr;
371 }
372 
373 const Symbol *FindPointerComponent(const Symbol &symbol) {
374   return IsPointer(symbol) ? &symbol : FindPointerComponent(symbol.GetType());
375 }
376 
377 // C1594 specifies several ways by which an object might be globally visible.
378 const Symbol *FindExternallyVisibleObject(
379     const Symbol &object, const Scope &scope, bool isPointerDefinition) {
380   // TODO: Storage association with any object for which this predicate holds,
381   // once EQUIVALENCE is supported.
382   const Symbol &ultimate{GetAssociationRoot(object)};
383   if (IsDummy(ultimate)) {
384     if (IsIntentIn(ultimate)) {
385       return &ultimate;
386     }
387     if (!isPointerDefinition && IsPointer(ultimate) &&
388         IsPureProcedure(ultimate.owner()) && IsFunction(ultimate.owner())) {
389       return &ultimate;
390     }
391   } else if (ultimate.owner().IsDerivedType()) {
392     return nullptr;
393   } else if (&GetProgramUnitContaining(ultimate) !=
394       &GetProgramUnitContaining(scope)) {
395     return &object;
396   } else if (const Symbol * block{FindCommonBlockContaining(ultimate)}) {
397     return block;
398   }
399   return nullptr;
400 }
401 
402 const Symbol &BypassGeneric(const Symbol &symbol) {
403   const Symbol &ultimate{symbol.GetUltimate()};
404   if (const auto *generic{ultimate.detailsIf<GenericDetails>()}) {
405     if (const Symbol * specific{generic->specific()}) {
406       return *specific;
407     }
408   }
409   return symbol;
410 }
411 
412 const Symbol &GetCrayPointer(const Symbol &crayPointee) {
413   const Symbol *found{nullptr};
414   for (const auto &[pointee, pointer] :
415       crayPointee.GetUltimate().owner().crayPointers()) {
416     if (pointee == crayPointee.name()) {
417       found = &pointer.get();
418       break;
419     }
420   }
421   return DEREF(found);
422 }
423 
424 bool ExprHasTypeCategory(
425     const SomeExpr &expr, const common::TypeCategory &type) {
426   auto dynamicType{expr.GetType()};
427   return dynamicType && dynamicType->category() == type;
428 }
429 
430 bool ExprTypeKindIsDefault(
431     const SomeExpr &expr, const SemanticsContext &context) {
432   auto dynamicType{expr.GetType()};
433   return dynamicType &&
434       dynamicType->category() != common::TypeCategory::Derived &&
435       dynamicType->kind() == context.GetDefaultKind(dynamicType->category());
436 }
437 
438 // If an analyzed expr or assignment is missing, dump the node and die.
439 template <typename T>
440 static void CheckMissingAnalysis(
441     bool crash, SemanticsContext *context, const T &x) {
442   if (crash && !(context && context->AnyFatalError())) {
443     std::string buf;
444     llvm::raw_string_ostream ss{buf};
445     ss << "node has not been analyzed:\n";
446     parser::DumpTree(ss, x);
447     common::die(buf.c_str());
448   }
449 }
450 
451 const SomeExpr *GetExprHelper::Get(const parser::Expr &x) {
452   CheckMissingAnalysis(crashIfNoExpr_ && !x.typedExpr, context_, x);
453   return x.typedExpr ? common::GetPtrFromOptional(x.typedExpr->v) : nullptr;
454 }
455 const SomeExpr *GetExprHelper::Get(const parser::Variable &x) {
456   CheckMissingAnalysis(crashIfNoExpr_ && !x.typedExpr, context_, x);
457   return x.typedExpr ? common::GetPtrFromOptional(x.typedExpr->v) : nullptr;
458 }
459 const SomeExpr *GetExprHelper::Get(const parser::DataStmtConstant &x) {
460   CheckMissingAnalysis(crashIfNoExpr_ && !x.typedExpr, context_, x);
461   return x.typedExpr ? common::GetPtrFromOptional(x.typedExpr->v) : nullptr;
462 }
463 const SomeExpr *GetExprHelper::Get(const parser::AllocateObject &x) {
464   CheckMissingAnalysis(crashIfNoExpr_ && !x.typedExpr, context_, x);
465   return x.typedExpr ? common::GetPtrFromOptional(x.typedExpr->v) : nullptr;
466 }
467 const SomeExpr *GetExprHelper::Get(const parser::PointerObject &x) {
468   CheckMissingAnalysis(crashIfNoExpr_ && !x.typedExpr, context_, x);
469   return x.typedExpr ? common::GetPtrFromOptional(x.typedExpr->v) : nullptr;
470 }
471 
472 const evaluate::Assignment *GetAssignment(const parser::AssignmentStmt &x) {
473   return x.typedAssignment ? common::GetPtrFromOptional(x.typedAssignment->v)
474                            : nullptr;
475 }
476 const evaluate::Assignment *GetAssignment(
477     const parser::PointerAssignmentStmt &x) {
478   return x.typedAssignment ? common::GetPtrFromOptional(x.typedAssignment->v)
479                            : nullptr;
480 }
481 
482 const Symbol *FindInterface(const Symbol &symbol) {
483   return common::visit(
484       common::visitors{
485           [](const ProcEntityDetails &details) {
486             const Symbol *interface{details.procInterface()};
487             return interface ? FindInterface(*interface) : nullptr;
488           },
489           [](const ProcBindingDetails &details) {
490             return FindInterface(details.symbol());
491           },
492           [&](const SubprogramDetails &) { return &symbol; },
493           [](const UseDetails &details) {
494             return FindInterface(details.symbol());
495           },
496           [](const HostAssocDetails &details) {
497             return FindInterface(details.symbol());
498           },
499           [](const GenericDetails &details) {
500             return details.specific() ? FindInterface(*details.specific())
501                                       : nullptr;
502           },
503           [](const auto &) -> const Symbol * { return nullptr; },
504       },
505       symbol.details());
506 }
507 
508 const Symbol *FindSubprogram(const Symbol &symbol) {
509   return common::visit(
510       common::visitors{
511           [&](const ProcEntityDetails &details) -> const Symbol * {
512             if (details.procInterface()) {
513               return FindSubprogram(*details.procInterface());
514             } else {
515               return &symbol;
516             }
517           },
518           [](const ProcBindingDetails &details) {
519             return FindSubprogram(details.symbol());
520           },
521           [&](const SubprogramDetails &) { return &symbol; },
522           [](const UseDetails &details) {
523             return FindSubprogram(details.symbol());
524           },
525           [](const HostAssocDetails &details) {
526             return FindSubprogram(details.symbol());
527           },
528           [](const GenericDetails &details) {
529             return details.specific() ? FindSubprogram(*details.specific())
530                                       : nullptr;
531           },
532           [](const auto &) -> const Symbol * { return nullptr; },
533       },
534       symbol.details());
535 }
536 
537 const Symbol *FindOverriddenBinding(
538     const Symbol &symbol, bool &isInaccessibleDeferred) {
539   isInaccessibleDeferred = false;
540   if (symbol.has<ProcBindingDetails>()) {
541     if (const DeclTypeSpec * parentType{FindParentTypeSpec(symbol.owner())}) {
542       if (const DerivedTypeSpec * parentDerived{parentType->AsDerived()}) {
543         if (const Scope * parentScope{parentDerived->typeSymbol().scope()}) {
544           if (const Symbol *
545               overridden{parentScope->FindComponent(symbol.name())}) {
546             // 7.5.7.3 p1: only accessible bindings are overridden
547             if (!overridden->attrs().test(Attr::PRIVATE) ||
548                 FindModuleContaining(overridden->owner()) ==
549                     FindModuleContaining(symbol.owner())) {
550               return overridden;
551             } else if (overridden->attrs().test(Attr::DEFERRED)) {
552               isInaccessibleDeferred = true;
553               return overridden;
554             }
555           }
556         }
557       }
558     }
559   }
560   return nullptr;
561 }
562 
563 const Symbol *FindGlobal(const Symbol &original) {
564   const Symbol &ultimate{original.GetUltimate()};
565   if (ultimate.owner().IsGlobal()) {
566     return &ultimate;
567   }
568   bool isLocal{false};
569   if (IsDummy(ultimate)) {
570   } else if (IsPointer(ultimate)) {
571   } else if (ultimate.has<ProcEntityDetails>()) {
572     isLocal = IsExternal(ultimate);
573   } else if (const auto *subp{ultimate.detailsIf<SubprogramDetails>()}) {
574     isLocal = subp->isInterface();
575   }
576   if (isLocal) {
577     const std::string *bind{ultimate.GetBindName()};
578     if (!bind || ultimate.name() == *bind) {
579       const Scope &globalScope{ultimate.owner().context().globalScope()};
580       if (auto iter{globalScope.find(ultimate.name())};
581           iter != globalScope.end()) {
582         const Symbol &global{*iter->second};
583         const std::string *globalBind{global.GetBindName()};
584         if (!globalBind || global.name() == *globalBind) {
585           return &global;
586         }
587       }
588     }
589   }
590   return nullptr;
591 }
592 
593 const DeclTypeSpec *FindParentTypeSpec(const DerivedTypeSpec &derived) {
594   return FindParentTypeSpec(derived.typeSymbol());
595 }
596 
597 const DeclTypeSpec *FindParentTypeSpec(const DeclTypeSpec &decl) {
598   if (const DerivedTypeSpec * derived{decl.AsDerived()}) {
599     return FindParentTypeSpec(*derived);
600   } else {
601     return nullptr;
602   }
603 }
604 
605 const DeclTypeSpec *FindParentTypeSpec(const Scope &scope) {
606   if (scope.kind() == Scope::Kind::DerivedType) {
607     if (const auto *symbol{scope.symbol()}) {
608       return FindParentTypeSpec(*symbol);
609     }
610   }
611   return nullptr;
612 }
613 
614 const DeclTypeSpec *FindParentTypeSpec(const Symbol &symbol) {
615   if (const Scope * scope{symbol.scope()}) {
616     if (const auto *details{symbol.detailsIf<DerivedTypeDetails>()}) {
617       if (const Symbol * parent{details->GetParentComponent(*scope)}) {
618         return parent->GetType();
619       }
620     }
621   }
622   return nullptr;
623 }
624 
625 const EquivalenceSet *FindEquivalenceSet(const Symbol &symbol) {
626   const Symbol &ultimate{symbol.GetUltimate()};
627   for (const EquivalenceSet &set : ultimate.owner().equivalenceSets()) {
628     for (const EquivalenceObject &object : set) {
629       if (object.symbol == ultimate) {
630         return &set;
631       }
632     }
633   }
634   return nullptr;
635 }
636 
637 bool IsOrContainsEventOrLockComponent(const Symbol &original) {
638   const Symbol &symbol{ResolveAssociations(original)};
639   if (const auto *details{symbol.detailsIf<ObjectEntityDetails>()}) {
640     if (const DeclTypeSpec * type{details->type()}) {
641       if (const DerivedTypeSpec * derived{type->AsDerived()}) {
642         return IsEventTypeOrLockType(derived) ||
643             FindEventOrLockPotentialComponent(*derived);
644       }
645     }
646   }
647   return false;
648 }
649 
650 // Check this symbol suitable as a type-bound procedure - C769
651 bool CanBeTypeBoundProc(const Symbol &symbol) {
652   if (IsDummy(symbol) || IsProcedurePointer(symbol)) {
653     return false;
654   } else if (symbol.has<SubprogramNameDetails>()) {
655     return symbol.owner().kind() == Scope::Kind::Module;
656   } else if (auto *details{symbol.detailsIf<SubprogramDetails>()}) {
657     if (details->isInterface()) {
658       return !symbol.attrs().test(Attr::ABSTRACT);
659     } else {
660       return symbol.owner().kind() == Scope::Kind::Module;
661     }
662   } else if (const auto *proc{symbol.detailsIf<ProcEntityDetails>()}) {
663     return !symbol.attrs().test(Attr::INTRINSIC) &&
664         proc->HasExplicitInterface();
665   } else {
666     return false;
667   }
668 }
669 
670 bool HasDeclarationInitializer(const Symbol &symbol) {
671   if (IsNamedConstant(symbol)) {
672     return false;
673   } else if (const auto *object{symbol.detailsIf<ObjectEntityDetails>()}) {
674     return object->init().has_value();
675   } else if (const auto *proc{symbol.detailsIf<ProcEntityDetails>()}) {
676     return proc->init().has_value();
677   } else {
678     return false;
679   }
680 }
681 
682 bool IsInitialized(const Symbol &symbol, bool ignoreDataStatements,
683     bool ignoreAllocatable, bool ignorePointer) {
684   if (!ignoreAllocatable && IsAllocatable(symbol)) {
685     return true;
686   } else if (!ignoreDataStatements && symbol.test(Symbol::Flag::InDataStmt)) {
687     return true;
688   } else if (HasDeclarationInitializer(symbol)) {
689     return true;
690   } else if (IsPointer(symbol)) {
691     return !ignorePointer;
692   } else if (IsNamedConstant(symbol)) {
693     return false;
694   } else if (const auto *object{symbol.detailsIf<ObjectEntityDetails>()}) {
695     if ((!object->isDummy() || IsIntentOut(symbol)) && object->type()) {
696       if (const auto *derived{object->type()->AsDerived()}) {
697         return derived->HasDefaultInitialization(
698             ignoreAllocatable, ignorePointer);
699       }
700     }
701   }
702   return false;
703 }
704 
705 bool IsDestructible(const Symbol &symbol, const Symbol *derivedTypeSymbol) {
706   if (IsAllocatable(symbol) || IsAutomatic(symbol)) {
707     return true;
708   } else if (IsNamedConstant(symbol) || IsFunctionResult(symbol) ||
709       IsPointer(symbol)) {
710     return false;
711   } else if (const auto *object{symbol.detailsIf<ObjectEntityDetails>()}) {
712     if ((!object->isDummy() || IsIntentOut(symbol)) && object->type()) {
713       if (const auto *derived{object->type()->AsDerived()}) {
714         return &derived->typeSymbol() != derivedTypeSymbol &&
715             derived->HasDestruction();
716       }
717     }
718   }
719   return false;
720 }
721 
722 bool HasIntrinsicTypeName(const Symbol &symbol) {
723   std::string name{symbol.name().ToString()};
724   if (name == "doubleprecision") {
725     return true;
726   } else if (name == "derived") {
727     return false;
728   } else {
729     for (int i{0}; i != common::TypeCategory_enumSize; ++i) {
730       if (name == parser::ToLowerCaseLetters(EnumToString(TypeCategory{i}))) {
731         return true;
732       }
733     }
734     return false;
735   }
736 }
737 
738 bool IsSeparateModuleProcedureInterface(const Symbol *symbol) {
739   if (symbol && symbol->attrs().test(Attr::MODULE)) {
740     if (auto *details{symbol->detailsIf<SubprogramDetails>()}) {
741       return details->isInterface();
742     }
743   }
744   return false;
745 }
746 
747 SymbolVector FinalsForDerivedTypeInstantiation(const DerivedTypeSpec &spec) {
748   SymbolVector result;
749   const Symbol &typeSymbol{spec.typeSymbol()};
750   if (const auto *derived{typeSymbol.detailsIf<DerivedTypeDetails>()}) {
751     for (const auto &pair : derived->finals()) {
752       const Symbol &subr{*pair.second};
753       // Errors in FINAL subroutines are caught in CheckFinal
754       // in check-declarations.cpp.
755       if (const auto *subprog{subr.detailsIf<SubprogramDetails>()};
756           subprog && subprog->dummyArgs().size() == 1) {
757         if (const Symbol * arg{subprog->dummyArgs()[0]}) {
758           if (const DeclTypeSpec * type{arg->GetType()}) {
759             if (type->category() == DeclTypeSpec::TypeDerived &&
760                 evaluate::AreSameDerivedType(spec, type->derivedTypeSpec())) {
761               result.emplace_back(subr);
762             }
763           }
764         }
765       }
766     }
767   }
768   return result;
769 }
770 
771 const Symbol *IsFinalizable(const Symbol &symbol,
772     std::set<const DerivedTypeSpec *> *inProgress, bool withImpureFinalizer) {
773   if (IsPointer(symbol) || evaluate::IsAssumedRank(symbol)) {
774     return nullptr;
775   }
776   if (const auto *object{symbol.detailsIf<ObjectEntityDetails>()}) {
777     if (object->isDummy() && !IsIntentOut(symbol)) {
778       return nullptr;
779     }
780     const DeclTypeSpec *type{object->type()};
781     if (const DerivedTypeSpec * typeSpec{type ? type->AsDerived() : nullptr}) {
782       return IsFinalizable(
783           *typeSpec, inProgress, withImpureFinalizer, symbol.Rank());
784     }
785   }
786   return nullptr;
787 }
788 
789 const Symbol *IsFinalizable(const DerivedTypeSpec &derived,
790     std::set<const DerivedTypeSpec *> *inProgress, bool withImpureFinalizer,
791     std::optional<int> rank) {
792   const Symbol *elemental{nullptr};
793   for (auto ref : FinalsForDerivedTypeInstantiation(derived)) {
794     const Symbol *symbol{&ref->GetUltimate()};
795     if (const auto *binding{symbol->detailsIf<ProcBindingDetails>()}) {
796       symbol = &binding->symbol();
797     }
798     if (const auto *proc{symbol->detailsIf<ProcEntityDetails>()}) {
799       symbol = proc->procInterface();
800     }
801     if (!symbol) {
802     } else if (IsElementalProcedure(*symbol)) {
803       elemental = symbol;
804     } else {
805       if (rank) {
806         if (const SubprogramDetails *
807             subp{symbol->detailsIf<SubprogramDetails>()}) {
808           if (const auto &args{subp->dummyArgs()}; !args.empty() &&
809               args.at(0) && !evaluate::IsAssumedRank(*args.at(0)) &&
810               args.at(0)->Rank() != *rank) {
811             continue; // not a finalizer for this rank
812           }
813         }
814       }
815       if (!withImpureFinalizer || !IsPureProcedure(*symbol)) {
816         return symbol;
817       }
818       // Found non-elemental pure finalizer of matching rank, but still
819       // need to check components for an impure finalizer.
820       elemental = nullptr;
821       break;
822     }
823   }
824   if (elemental && (!withImpureFinalizer || !IsPureProcedure(*elemental))) {
825     return elemental;
826   }
827   // Check components (including ancestors)
828   std::set<const DerivedTypeSpec *> basis;
829   if (inProgress) {
830     if (inProgress->find(&derived) != inProgress->end()) {
831       return nullptr; // don't loop on recursive type
832     }
833   } else {
834     inProgress = &basis;
835   }
836   auto iterator{inProgress->insert(&derived).first};
837   const Symbol *result{nullptr};
838   for (const Symbol &component : PotentialComponentIterator{derived}) {
839     result = IsFinalizable(component, inProgress, withImpureFinalizer);
840     if (result) {
841       break;
842     }
843   }
844   inProgress->erase(iterator);
845   return result;
846 }
847 
848 static const Symbol *HasImpureFinal(
849     const DerivedTypeSpec &derived, std::optional<int> rank) {
850   return IsFinalizable(derived, nullptr, /*withImpureFinalizer=*/true, rank);
851 }
852 
853 const Symbol *HasImpureFinal(const Symbol &original, std::optional<int> rank) {
854   const Symbol &symbol{ResolveAssociations(original)};
855   if (symbol.has<ObjectEntityDetails>()) {
856     if (const DeclTypeSpec * symType{symbol.GetType()}) {
857       if (const DerivedTypeSpec * derived{symType->AsDerived()}) {
858         if (evaluate::IsAssumedRank(symbol)) {
859           // finalizable assumed-rank not allowed (C839)
860           return nullptr;
861         } else {
862           int actualRank{rank.value_or(symbol.Rank())};
863           return HasImpureFinal(*derived, actualRank);
864         }
865       }
866     }
867   }
868   return nullptr;
869 }
870 
871 bool MayRequireFinalization(const DerivedTypeSpec &derived) {
872   return IsFinalizable(derived) ||
873       FindPolymorphicAllocatablePotentialComponent(derived);
874 }
875 
876 bool HasAllocatableDirectComponent(const DerivedTypeSpec &derived) {
877   DirectComponentIterator directs{derived};
878   return std::any_of(directs.begin(), directs.end(), IsAllocatable);
879 }
880 
881 bool IsAssumedLengthCharacter(const Symbol &symbol) {
882   if (const DeclTypeSpec * type{symbol.GetType()}) {
883     return type->category() == DeclTypeSpec::Character &&
884         type->characterTypeSpec().length().isAssumed();
885   } else {
886     return false;
887   }
888 }
889 
890 bool IsInBlankCommon(const Symbol &symbol) {
891   const Symbol *block{FindCommonBlockContaining(symbol)};
892   return block && block->name().empty();
893 }
894 
895 // C722 and C723:  For a function to be assumed length, it must be external and
896 // of CHARACTER type
897 bool IsExternal(const Symbol &symbol) {
898   return ClassifyProcedure(symbol) == ProcedureDefinitionClass::External;
899 }
900 
901 // Most scopes have no EQUIVALENCE, and this function is a fast no-op for them.
902 std::list<std::list<SymbolRef>> GetStorageAssociations(const Scope &scope) {
903   UnorderedSymbolSet distinct;
904   for (const EquivalenceSet &set : scope.equivalenceSets()) {
905     for (const EquivalenceObject &object : set) {
906       distinct.emplace(object.symbol);
907     }
908   }
909   // This set is ordered by ascending offsets, with ties broken by greatest
910   // size.  A multiset is used here because multiple symbols may have the
911   // same offset and size; the symbols in the set, however, are distinct.
912   std::multiset<SymbolRef, SymbolOffsetCompare> associated;
913   for (SymbolRef ref : distinct) {
914     associated.emplace(*ref);
915   }
916   std::list<std::list<SymbolRef>> result;
917   std::size_t limit{0};
918   const Symbol *currentCommon{nullptr};
919   for (const Symbol &symbol : associated) {
920     const Symbol *thisCommon{FindCommonBlockContaining(symbol)};
921     if (result.empty() || symbol.offset() >= limit ||
922         thisCommon != currentCommon) {
923       // Start a new group
924       result.emplace_back(std::list<SymbolRef>{});
925       limit = 0;
926       currentCommon = thisCommon;
927     }
928     result.back().emplace_back(symbol);
929     limit = std::max(limit, symbol.offset() + symbol.size());
930   }
931   return result;
932 }
933 
934 bool IsModuleProcedure(const Symbol &symbol) {
935   return ClassifyProcedure(symbol) == ProcedureDefinitionClass::Module;
936 }
937 
938 class ImageControlStmtHelper {
939   using ImageControlStmts =
940       std::variant<parser::ChangeTeamConstruct, parser::CriticalConstruct,
941           parser::EventPostStmt, parser::EventWaitStmt, parser::FormTeamStmt,
942           parser::LockStmt, parser::SyncAllStmt, parser::SyncImagesStmt,
943           parser::SyncMemoryStmt, parser::SyncTeamStmt, parser::UnlockStmt>;
944 
945 public:
946   template <typename T> bool operator()(const T &) {
947     return common::HasMember<T, ImageControlStmts>;
948   }
949   template <typename T> bool operator()(const common::Indirection<T> &x) {
950     return (*this)(x.value());
951   }
952   template <typename A> bool operator()(const parser::Statement<A> &x) {
953     return (*this)(x.statement);
954   }
955   bool operator()(const parser::AllocateStmt &stmt) {
956     const auto &allocationList{std::get<std::list<parser::Allocation>>(stmt.t)};
957     for (const auto &allocation : allocationList) {
958       const auto &allocateObject{
959           std::get<parser::AllocateObject>(allocation.t)};
960       if (IsCoarrayObject(allocateObject)) {
961         return true;
962       }
963     }
964     return false;
965   }
966   bool operator()(const parser::DeallocateStmt &stmt) {
967     const auto &allocateObjectList{
968         std::get<std::list<parser::AllocateObject>>(stmt.t)};
969     for (const auto &allocateObject : allocateObjectList) {
970       if (IsCoarrayObject(allocateObject)) {
971         return true;
972       }
973     }
974     return false;
975   }
976   bool operator()(const parser::CallStmt &stmt) {
977     const auto &procedureDesignator{
978         std::get<parser::ProcedureDesignator>(stmt.call.t)};
979     if (auto *name{std::get_if<parser::Name>(&procedureDesignator.u)}) {
980       // TODO: also ensure that the procedure is, in fact, an intrinsic
981       if (name->source == "move_alloc") {
982         const auto &args{
983             std::get<std::list<parser::ActualArgSpec>>(stmt.call.t)};
984         if (!args.empty()) {
985           const parser::ActualArg &actualArg{
986               std::get<parser::ActualArg>(args.front().t)};
987           if (const auto *argExpr{
988                   std::get_if<common::Indirection<parser::Expr>>(
989                       &actualArg.u)}) {
990             return HasCoarray(argExpr->value());
991           }
992         }
993       }
994     }
995     return false;
996   }
997   bool operator()(const parser::StopStmt &stmt) {
998     // STOP is an image control statement; ERROR STOP is not
999     return std::get<parser::StopStmt::Kind>(stmt.t) ==
1000         parser::StopStmt::Kind::Stop;
1001   }
1002   bool operator()(const parser::IfStmt &stmt) {
1003     return (*this)(
1004         std::get<parser::UnlabeledStatement<parser::ActionStmt>>(stmt.t)
1005             .statement);
1006   }
1007   bool operator()(const parser::ActionStmt &stmt) {
1008     return common::visit(*this, stmt.u);
1009   }
1010 
1011 private:
1012   bool IsCoarrayObject(const parser::AllocateObject &allocateObject) {
1013     const parser::Name &name{GetLastName(allocateObject)};
1014     return name.symbol && evaluate::IsCoarray(*name.symbol);
1015   }
1016 };
1017 
1018 bool IsImageControlStmt(const parser::ExecutableConstruct &construct) {
1019   return common::visit(ImageControlStmtHelper{}, construct.u);
1020 }
1021 
1022 std::optional<parser::MessageFixedText> GetImageControlStmtCoarrayMsg(
1023     const parser::ExecutableConstruct &construct) {
1024   if (const auto *actionStmt{
1025           std::get_if<parser::Statement<parser::ActionStmt>>(&construct.u)}) {
1026     return common::visit(
1027         common::visitors{
1028             [](const common::Indirection<parser::AllocateStmt> &)
1029                 -> std::optional<parser::MessageFixedText> {
1030               return "ALLOCATE of a coarray is an image control"
1031                      " statement"_en_US;
1032             },
1033             [](const common::Indirection<parser::DeallocateStmt> &)
1034                 -> std::optional<parser::MessageFixedText> {
1035               return "DEALLOCATE of a coarray is an image control"
1036                      " statement"_en_US;
1037             },
1038             [](const common::Indirection<parser::CallStmt> &)
1039                 -> std::optional<parser::MessageFixedText> {
1040               return "MOVE_ALLOC of a coarray is an image control"
1041                      " statement "_en_US;
1042             },
1043             [](const auto &) -> std::optional<parser::MessageFixedText> {
1044               return std::nullopt;
1045             },
1046         },
1047         actionStmt->statement.u);
1048   }
1049   return std::nullopt;
1050 }
1051 
1052 parser::CharBlock GetImageControlStmtLocation(
1053     const parser::ExecutableConstruct &executableConstruct) {
1054   return common::visit(
1055       common::visitors{
1056           [](const common::Indirection<parser::ChangeTeamConstruct>
1057                   &construct) {
1058             return std::get<parser::Statement<parser::ChangeTeamStmt>>(
1059                 construct.value().t)
1060                 .source;
1061           },
1062           [](const common::Indirection<parser::CriticalConstruct> &construct) {
1063             return std::get<parser::Statement<parser::CriticalStmt>>(
1064                 construct.value().t)
1065                 .source;
1066           },
1067           [](const parser::Statement<parser::ActionStmt> &actionStmt) {
1068             return actionStmt.source;
1069           },
1070           [](const auto &) { return parser::CharBlock{}; },
1071       },
1072       executableConstruct.u);
1073 }
1074 
1075 bool HasCoarray(const parser::Expr &expression) {
1076   if (const auto *expr{GetExpr(nullptr, expression)}) {
1077     for (const Symbol &symbol : evaluate::CollectSymbols(*expr)) {
1078       if (evaluate::IsCoarray(symbol)) {
1079         return true;
1080       }
1081     }
1082   }
1083   return false;
1084 }
1085 
1086 bool IsAssumedType(const Symbol &symbol) {
1087   if (const DeclTypeSpec * type{symbol.GetType()}) {
1088     return type->IsAssumedType();
1089   }
1090   return false;
1091 }
1092 
1093 bool IsPolymorphic(const Symbol &symbol) {
1094   if (const DeclTypeSpec * type{symbol.GetType()}) {
1095     return type->IsPolymorphic();
1096   }
1097   return false;
1098 }
1099 
1100 bool IsUnlimitedPolymorphic(const Symbol &symbol) {
1101   if (const DeclTypeSpec * type{symbol.GetType()}) {
1102     return type->IsUnlimitedPolymorphic();
1103   }
1104   return false;
1105 }
1106 
1107 bool IsPolymorphicAllocatable(const Symbol &symbol) {
1108   return IsAllocatable(symbol) && IsPolymorphic(symbol);
1109 }
1110 
1111 const Scope *FindCUDADeviceContext(const Scope *scope) {
1112   return !scope ? nullptr : FindScopeContaining(*scope, [](const Scope &s) {
1113     return IsCUDADeviceContext(&s);
1114   });
1115 }
1116 
1117 std::optional<common::CUDADataAttr> GetCUDADataAttr(const Symbol *symbol) {
1118   const auto *object{
1119       symbol ? symbol->detailsIf<ObjectEntityDetails>() : nullptr};
1120   return object ? object->cudaDataAttr() : std::nullopt;
1121 }
1122 
1123 std::optional<parser::MessageFormattedText> CheckAccessibleSymbol(
1124     const Scope &scope, const Symbol &symbol) {
1125   if (symbol.attrs().test(Attr::PRIVATE)) {
1126     if (FindModuleFileContaining(scope)) {
1127       // Don't enforce component accessibility checks in module files;
1128       // there may be forward-substituted named constants of derived type
1129       // whose structure constructors reference private components.
1130     } else if (const Scope *
1131         moduleScope{FindModuleContaining(symbol.owner())}) {
1132       if (!moduleScope->Contains(scope)) {
1133         return parser::MessageFormattedText{
1134             "PRIVATE name '%s' is only accessible within module '%s'"_err_en_US,
1135             symbol.name(), moduleScope->GetName().value()};
1136       }
1137     }
1138   }
1139   return std::nullopt;
1140 }
1141 
1142 SymbolVector OrderParameterNames(const Symbol &typeSymbol) {
1143   SymbolVector result;
1144   if (const DerivedTypeSpec * spec{typeSymbol.GetParentTypeSpec()}) {
1145     result = OrderParameterNames(spec->typeSymbol());
1146   }
1147   const auto &paramNames{typeSymbol.get<DerivedTypeDetails>().paramNameOrder()};
1148   result.insert(result.end(), paramNames.begin(), paramNames.end());
1149   return result;
1150 }
1151 
1152 SymbolVector OrderParameterDeclarations(const Symbol &typeSymbol) {
1153   SymbolVector result;
1154   if (const DerivedTypeSpec * spec{typeSymbol.GetParentTypeSpec()}) {
1155     result = OrderParameterDeclarations(spec->typeSymbol());
1156   }
1157   const auto &paramDecls{typeSymbol.get<DerivedTypeDetails>().paramDeclOrder()};
1158   result.insert(result.end(), paramDecls.begin(), paramDecls.end());
1159   return result;
1160 }
1161 
1162 const DeclTypeSpec &FindOrInstantiateDerivedType(
1163     Scope &scope, DerivedTypeSpec &&spec, DeclTypeSpec::Category category) {
1164   spec.EvaluateParameters(scope.context());
1165   if (const DeclTypeSpec *
1166       type{scope.FindInstantiatedDerivedType(spec, category)}) {
1167     return *type;
1168   }
1169   // Create a new instantiation of this parameterized derived type
1170   // for this particular distinct set of actual parameter values.
1171   DeclTypeSpec &type{scope.MakeDerivedType(category, std::move(spec))};
1172   type.derivedTypeSpec().Instantiate(scope);
1173   return type;
1174 }
1175 
1176 const Symbol *FindSeparateModuleSubprogramInterface(const Symbol *proc) {
1177   if (proc) {
1178     if (const auto *subprogram{proc->detailsIf<SubprogramDetails>()}) {
1179       if (const Symbol * iface{subprogram->moduleInterface()}) {
1180         return iface;
1181       }
1182     }
1183   }
1184   return nullptr;
1185 }
1186 
1187 ProcedureDefinitionClass ClassifyProcedure(const Symbol &symbol) { // 15.2.2
1188   const Symbol &ultimate{symbol.GetUltimate()};
1189   if (!IsProcedure(ultimate)) {
1190     return ProcedureDefinitionClass::None;
1191   } else if (ultimate.attrs().test(Attr::INTRINSIC)) {
1192     return ProcedureDefinitionClass::Intrinsic;
1193   } else if (IsDummy(ultimate)) {
1194     return ProcedureDefinitionClass::Dummy;
1195   } else if (IsProcedurePointer(symbol)) {
1196     return ProcedureDefinitionClass::Pointer;
1197   } else if (ultimate.attrs().test(Attr::EXTERNAL)) {
1198     return ProcedureDefinitionClass::External;
1199   } else if (const auto *nameDetails{
1200                  ultimate.detailsIf<SubprogramNameDetails>()}) {
1201     switch (nameDetails->kind()) {
1202     case SubprogramKind::Module:
1203       return ProcedureDefinitionClass::Module;
1204     case SubprogramKind::Internal:
1205       return ProcedureDefinitionClass::Internal;
1206     }
1207   } else if (const Symbol * subp{FindSubprogram(symbol)}) {
1208     if (const auto *subpDetails{subp->detailsIf<SubprogramDetails>()}) {
1209       if (subpDetails->stmtFunction()) {
1210         return ProcedureDefinitionClass::StatementFunction;
1211       }
1212     }
1213     switch (ultimate.owner().kind()) {
1214     case Scope::Kind::Global:
1215     case Scope::Kind::IntrinsicModules:
1216       return ProcedureDefinitionClass::External;
1217     case Scope::Kind::Module:
1218       return ProcedureDefinitionClass::Module;
1219     case Scope::Kind::MainProgram:
1220     case Scope::Kind::Subprogram:
1221       return ProcedureDefinitionClass::Internal;
1222     default:
1223       break;
1224     }
1225   }
1226   return ProcedureDefinitionClass::None;
1227 }
1228 
1229 // ComponentIterator implementation
1230 
1231 template <ComponentKind componentKind>
1232 typename ComponentIterator<componentKind>::const_iterator
1233 ComponentIterator<componentKind>::const_iterator::Create(
1234     const DerivedTypeSpec &derived) {
1235   const_iterator it{};
1236   it.componentPath_.emplace_back(derived);
1237   it.Increment(); // cue up first relevant component, if any
1238   return it;
1239 }
1240 
1241 template <ComponentKind componentKind>
1242 const DerivedTypeSpec *
1243 ComponentIterator<componentKind>::const_iterator::PlanComponentTraversal(
1244     const Symbol &component) const {
1245   if (const auto *details{component.detailsIf<ObjectEntityDetails>()}) {
1246     if (const DeclTypeSpec * type{details->type()}) {
1247       if (const auto *derived{type->AsDerived()}) {
1248         bool traverse{false};
1249         if constexpr (componentKind == ComponentKind::Ordered) {
1250           // Order Component (only visit parents)
1251           traverse = component.test(Symbol::Flag::ParentComp);
1252         } else if constexpr (componentKind == ComponentKind::Direct) {
1253           traverse = !IsAllocatableOrObjectPointer(&component);
1254         } else if constexpr (componentKind == ComponentKind::Ultimate) {
1255           traverse = !IsAllocatableOrObjectPointer(&component);
1256         } else if constexpr (componentKind == ComponentKind::Potential) {
1257           traverse = !IsPointer(component);
1258         } else if constexpr (componentKind == ComponentKind::Scope) {
1259           traverse = !IsAllocatableOrObjectPointer(&component);
1260         } else if constexpr (componentKind ==
1261             ComponentKind::PotentialAndPointer) {
1262           traverse = !IsPointer(component);
1263         }
1264         if (traverse) {
1265           const Symbol &newTypeSymbol{derived->typeSymbol()};
1266           // Avoid infinite loop if the type is already part of the types
1267           // being visited. It is possible to have "loops in type" because
1268           // C744 does not forbid to use not yet declared type for
1269           // ALLOCATABLE or POINTER components.
1270           for (const auto &node : componentPath_) {
1271             if (&newTypeSymbol == &node.GetTypeSymbol()) {
1272               return nullptr;
1273             }
1274           }
1275           return derived;
1276         }
1277       }
1278     } // intrinsic & unlimited polymorphic not traversable
1279   }
1280   return nullptr;
1281 }
1282 
1283 template <ComponentKind componentKind>
1284 static bool StopAtComponentPre(const Symbol &component) {
1285   if constexpr (componentKind == ComponentKind::Ordered) {
1286     // Parent components need to be iterated upon after their
1287     // sub-components in structure constructor analysis.
1288     return !component.test(Symbol::Flag::ParentComp);
1289   } else if constexpr (componentKind == ComponentKind::Direct) {
1290     return true;
1291   } else if constexpr (componentKind == ComponentKind::Ultimate) {
1292     return component.has<ProcEntityDetails>() ||
1293         IsAllocatableOrObjectPointer(&component) ||
1294         (component.has<ObjectEntityDetails>() &&
1295             component.get<ObjectEntityDetails>().type() &&
1296             component.get<ObjectEntityDetails>().type()->AsIntrinsic());
1297   } else if constexpr (componentKind == ComponentKind::Potential) {
1298     return !IsPointer(component);
1299   } else if constexpr (componentKind == ComponentKind::PotentialAndPointer) {
1300     return true;
1301   } else {
1302     DIE("unexpected ComponentKind");
1303   }
1304 }
1305 
1306 template <ComponentKind componentKind>
1307 static bool StopAtComponentPost(const Symbol &component) {
1308   return componentKind == ComponentKind::Ordered &&
1309       component.test(Symbol::Flag::ParentComp);
1310 }
1311 
1312 template <ComponentKind componentKind>
1313 void ComponentIterator<componentKind>::const_iterator::Increment() {
1314   while (!componentPath_.empty()) {
1315     ComponentPathNode &deepest{componentPath_.back()};
1316     if (deepest.component()) {
1317       if (!deepest.descended()) {
1318         deepest.set_descended(true);
1319         if (const DerivedTypeSpec *
1320             derived{PlanComponentTraversal(*deepest.component())}) {
1321           componentPath_.emplace_back(*derived);
1322           continue;
1323         }
1324       } else if (!deepest.visited()) {
1325         deepest.set_visited(true);
1326         return; // this is the next component to visit, after descending
1327       }
1328     }
1329     auto &nameIterator{deepest.nameIterator()};
1330     if (nameIterator == deepest.nameEnd()) {
1331       componentPath_.pop_back();
1332     } else if constexpr (componentKind == ComponentKind::Scope) {
1333       deepest.set_component(*nameIterator++->second);
1334       deepest.set_descended(false);
1335       deepest.set_visited(true);
1336       return; // this is the next component to visit, before descending
1337     } else {
1338       const Scope &scope{deepest.GetScope()};
1339       auto scopeIter{scope.find(*nameIterator++)};
1340       if (scopeIter != scope.cend()) {
1341         const Symbol &component{*scopeIter->second};
1342         deepest.set_component(component);
1343         deepest.set_descended(false);
1344         if (StopAtComponentPre<componentKind>(component)) {
1345           deepest.set_visited(true);
1346           return; // this is the next component to visit, before descending
1347         } else {
1348           deepest.set_visited(!StopAtComponentPost<componentKind>(component));
1349         }
1350       }
1351     }
1352   }
1353 }
1354 
1355 template <ComponentKind componentKind>
1356 std::string
1357 ComponentIterator<componentKind>::const_iterator::BuildResultDesignatorName()
1358     const {
1359   std::string designator;
1360   for (const auto &node : componentPath_) {
1361     designator += "%"s + DEREF(node.component()).name().ToString();
1362   }
1363   return designator;
1364 }
1365 
1366 template class ComponentIterator<ComponentKind::Ordered>;
1367 template class ComponentIterator<ComponentKind::Direct>;
1368 template class ComponentIterator<ComponentKind::Ultimate>;
1369 template class ComponentIterator<ComponentKind::Potential>;
1370 template class ComponentIterator<ComponentKind::Scope>;
1371 template class ComponentIterator<ComponentKind::PotentialAndPointer>;
1372 
1373 UltimateComponentIterator::const_iterator FindCoarrayUltimateComponent(
1374     const DerivedTypeSpec &derived) {
1375   UltimateComponentIterator ultimates{derived};
1376   return std::find_if(ultimates.begin(), ultimates.end(),
1377       [](const Symbol &symbol) { return evaluate::IsCoarray(symbol); });
1378 }
1379 
1380 UltimateComponentIterator::const_iterator FindPointerUltimateComponent(
1381     const DerivedTypeSpec &derived) {
1382   UltimateComponentIterator ultimates{derived};
1383   return std::find_if(ultimates.begin(), ultimates.end(), IsPointer);
1384 }
1385 
1386 PotentialComponentIterator::const_iterator FindEventOrLockPotentialComponent(
1387     const DerivedTypeSpec &derived) {
1388   PotentialComponentIterator potentials{derived};
1389   return std::find_if(
1390       potentials.begin(), potentials.end(), [](const Symbol &component) {
1391         if (const auto *details{component.detailsIf<ObjectEntityDetails>()}) {
1392           const DeclTypeSpec *type{details->type()};
1393           return type && IsEventTypeOrLockType(type->AsDerived());
1394         }
1395         return false;
1396       });
1397 }
1398 
1399 UltimateComponentIterator::const_iterator FindAllocatableUltimateComponent(
1400     const DerivedTypeSpec &derived) {
1401   UltimateComponentIterator ultimates{derived};
1402   return std::find_if(ultimates.begin(), ultimates.end(), IsAllocatable);
1403 }
1404 
1405 DirectComponentIterator::const_iterator FindAllocatableOrPointerDirectComponent(
1406     const DerivedTypeSpec &derived) {
1407   DirectComponentIterator directs{derived};
1408   return std::find_if(directs.begin(), directs.end(), IsAllocatableOrPointer);
1409 }
1410 
1411 PotentialComponentIterator::const_iterator
1412 FindPolymorphicAllocatablePotentialComponent(const DerivedTypeSpec &derived) {
1413   PotentialComponentIterator potentials{derived};
1414   return std::find_if(
1415       potentials.begin(), potentials.end(), IsPolymorphicAllocatable);
1416 }
1417 
1418 const Symbol *FindUltimateComponent(const DerivedTypeSpec &derived,
1419     const std::function<bool(const Symbol &)> &predicate) {
1420   UltimateComponentIterator ultimates{derived};
1421   if (auto it{std::find_if(ultimates.begin(), ultimates.end(),
1422           [&predicate](const Symbol &component) -> bool {
1423             return predicate(component);
1424           })}) {
1425     return &*it;
1426   }
1427   return nullptr;
1428 }
1429 
1430 const Symbol *FindUltimateComponent(const Symbol &symbol,
1431     const std::function<bool(const Symbol &)> &predicate) {
1432   if (predicate(symbol)) {
1433     return &symbol;
1434   } else if (const auto *object{symbol.detailsIf<ObjectEntityDetails>()}) {
1435     if (const auto *type{object->type()}) {
1436       if (const auto *derived{type->AsDerived()}) {
1437         return FindUltimateComponent(*derived, predicate);
1438       }
1439     }
1440   }
1441   return nullptr;
1442 }
1443 
1444 const Symbol *FindImmediateComponent(const DerivedTypeSpec &type,
1445     const std::function<bool(const Symbol &)> &predicate) {
1446   if (const Scope * scope{type.scope()}) {
1447     const Symbol *parent{nullptr};
1448     for (const auto &pair : *scope) {
1449       const Symbol *symbol{&*pair.second};
1450       if (predicate(*symbol)) {
1451         return symbol;
1452       }
1453       if (symbol->test(Symbol::Flag::ParentComp)) {
1454         parent = symbol;
1455       }
1456     }
1457     if (parent) {
1458       if (const auto *object{parent->detailsIf<ObjectEntityDetails>()}) {
1459         if (const auto *type{object->type()}) {
1460           if (const auto *derived{type->AsDerived()}) {
1461             return FindImmediateComponent(*derived, predicate);
1462           }
1463         }
1464       }
1465     }
1466   }
1467   return nullptr;
1468 }
1469 
1470 const Symbol *IsFunctionResultWithSameNameAsFunction(const Symbol &symbol) {
1471   if (IsFunctionResult(symbol)) {
1472     if (const Symbol * function{symbol.owner().symbol()}) {
1473       if (symbol.name() == function->name()) {
1474         return function;
1475       }
1476     }
1477     // Check ENTRY result symbols too
1478     const Scope &outer{symbol.owner().parent()};
1479     auto iter{outer.find(symbol.name())};
1480     if (iter != outer.end()) {
1481       const Symbol &outerSym{*iter->second};
1482       if (const auto *subp{outerSym.detailsIf<SubprogramDetails>()}) {
1483         if (subp->entryScope() == &symbol.owner() &&
1484             symbol.name() == outerSym.name()) {
1485           return &outerSym;
1486         }
1487       }
1488     }
1489   }
1490   return nullptr;
1491 }
1492 
1493 void LabelEnforce::Post(const parser::GotoStmt &gotoStmt) {
1494   CheckLabelUse(gotoStmt.v);
1495 }
1496 void LabelEnforce::Post(const parser::ComputedGotoStmt &computedGotoStmt) {
1497   for (auto &i : std::get<std::list<parser::Label>>(computedGotoStmt.t)) {
1498     CheckLabelUse(i);
1499   }
1500 }
1501 
1502 void LabelEnforce::Post(const parser::ArithmeticIfStmt &arithmeticIfStmt) {
1503   CheckLabelUse(std::get<1>(arithmeticIfStmt.t));
1504   CheckLabelUse(std::get<2>(arithmeticIfStmt.t));
1505   CheckLabelUse(std::get<3>(arithmeticIfStmt.t));
1506 }
1507 
1508 void LabelEnforce::Post(const parser::AssignStmt &assignStmt) {
1509   CheckLabelUse(std::get<parser::Label>(assignStmt.t));
1510 }
1511 
1512 void LabelEnforce::Post(const parser::AssignedGotoStmt &assignedGotoStmt) {
1513   for (auto &i : std::get<std::list<parser::Label>>(assignedGotoStmt.t)) {
1514     CheckLabelUse(i);
1515   }
1516 }
1517 
1518 void LabelEnforce::Post(const parser::AltReturnSpec &altReturnSpec) {
1519   CheckLabelUse(altReturnSpec.v);
1520 }
1521 
1522 void LabelEnforce::Post(const parser::ErrLabel &errLabel) {
1523   CheckLabelUse(errLabel.v);
1524 }
1525 void LabelEnforce::Post(const parser::EndLabel &endLabel) {
1526   CheckLabelUse(endLabel.v);
1527 }
1528 void LabelEnforce::Post(const parser::EorLabel &eorLabel) {
1529   CheckLabelUse(eorLabel.v);
1530 }
1531 
1532 void LabelEnforce::CheckLabelUse(const parser::Label &labelUsed) {
1533   if (labels_.find(labelUsed) == labels_.end()) {
1534     SayWithConstruct(context_, currentStatementSourcePosition_,
1535         parser::MessageFormattedText{
1536             "Control flow escapes from %s"_err_en_US, construct_},
1537         constructSourcePosition_);
1538   }
1539 }
1540 
1541 parser::MessageFormattedText LabelEnforce::GetEnclosingConstructMsg() {
1542   return {"Enclosing %s statement"_en_US, construct_};
1543 }
1544 
1545 void LabelEnforce::SayWithConstruct(SemanticsContext &context,
1546     parser::CharBlock stmtLocation, parser::MessageFormattedText &&message,
1547     parser::CharBlock constructLocation) {
1548   context.Say(stmtLocation, message)
1549       .Attach(constructLocation, GetEnclosingConstructMsg());
1550 }
1551 
1552 bool HasAlternateReturns(const Symbol &subprogram) {
1553   for (const auto *dummyArg : subprogram.get<SubprogramDetails>().dummyArgs()) {
1554     if (!dummyArg) {
1555       return true;
1556     }
1557   }
1558   return false;
1559 }
1560 
1561 bool IsAutomaticallyDestroyed(const Symbol &symbol) {
1562   return symbol.has<ObjectEntityDetails>() &&
1563       (symbol.owner().kind() == Scope::Kind::Subprogram ||
1564           symbol.owner().kind() == Scope::Kind::BlockConstruct) &&
1565       !IsNamedConstant(symbol) && (!IsDummy(symbol) || IsIntentOut(symbol)) &&
1566       !IsPointer(symbol) && !IsSaved(symbol) &&
1567       !FindCommonBlockContaining(symbol);
1568 }
1569 
1570 const std::optional<parser::Name> &MaybeGetNodeName(
1571     const ConstructNode &construct) {
1572   return common::visit(
1573       common::visitors{
1574           [&](const parser::BlockConstruct *blockConstruct)
1575               -> const std::optional<parser::Name> & {
1576             return std::get<0>(blockConstruct->t).statement.v;
1577           },
1578           [&](const auto *a) -> const std::optional<parser::Name> & {
1579             return std::get<0>(std::get<0>(a->t).statement.t);
1580           },
1581       },
1582       construct);
1583 }
1584 
1585 std::optional<ArraySpec> ToArraySpec(
1586     evaluate::FoldingContext &context, const evaluate::Shape &shape) {
1587   if (auto extents{evaluate::AsConstantExtents(context, shape)}) {
1588     ArraySpec result;
1589     for (const auto &extent : *extents) {
1590       result.emplace_back(ShapeSpec::MakeExplicit(Bound{extent}));
1591     }
1592     return {std::move(result)};
1593   } else {
1594     return std::nullopt;
1595   }
1596 }
1597 
1598 std::optional<ArraySpec> ToArraySpec(evaluate::FoldingContext &context,
1599     const std::optional<evaluate::Shape> &shape) {
1600   return shape ? ToArraySpec(context, *shape) : std::nullopt;
1601 }
1602 
1603 static const DeclTypeSpec *GetDtvArgTypeSpec(const Symbol &proc) {
1604   if (const auto *subp{proc.detailsIf<SubprogramDetails>()};
1605       subp && !subp->dummyArgs().empty()) {
1606     if (const auto *arg{subp->dummyArgs()[0]}) {
1607       return arg->GetType();
1608     }
1609   }
1610   return nullptr;
1611 }
1612 
1613 const DerivedTypeSpec *GetDtvArgDerivedType(const Symbol &proc) {
1614   if (const auto *type{GetDtvArgTypeSpec(proc)}) {
1615     return type->AsDerived();
1616   } else {
1617     return nullptr;
1618   }
1619 }
1620 
1621 bool HasDefinedIo(common::DefinedIo which, const DerivedTypeSpec &derived,
1622     const Scope *scope) {
1623   if (const Scope * dtScope{derived.scope()}) {
1624     for (const auto &pair : *dtScope) {
1625       const Symbol &symbol{*pair.second};
1626       if (const auto *generic{symbol.detailsIf<GenericDetails>()}) {
1627         GenericKind kind{generic->kind()};
1628         if (const auto *io{std::get_if<common::DefinedIo>(&kind.u)}) {
1629           if (*io == which) {
1630             return true; // type-bound GENERIC exists
1631           }
1632         }
1633       }
1634     }
1635   }
1636   if (scope) {
1637     SourceName name{GenericKind::AsFortran(which)};
1638     evaluate::DynamicType dyDerived{derived};
1639     for (; scope && !scope->IsGlobal(); scope = &scope->parent()) {
1640       auto iter{scope->find(name)};
1641       if (iter != scope->end()) {
1642         const auto &generic{iter->second->GetUltimate().get<GenericDetails>()};
1643         for (auto ref : generic.specificProcs()) {
1644           const Symbol &procSym{ref->GetUltimate()};
1645           if (const DeclTypeSpec * dtSpec{GetDtvArgTypeSpec(procSym)}) {
1646             if (auto dyDummy{evaluate::DynamicType::From(*dtSpec)}) {
1647               if (dyDummy->IsTkCompatibleWith(dyDerived)) {
1648                 return true; // GENERIC or INTERFACE not in type
1649               }
1650             }
1651           }
1652         }
1653       }
1654     }
1655   }
1656   // Check for inherited defined I/O
1657   const auto *parentType{derived.typeSymbol().GetParentTypeSpec()};
1658   return parentType && HasDefinedIo(which, *parentType, scope);
1659 }
1660 
1661 template <typename E>
1662 std::forward_list<std::string> GetOperatorNames(
1663     const SemanticsContext &context, E opr) {
1664   std::forward_list<std::string> result;
1665   for (const char *name : context.languageFeatures().GetNames(opr)) {
1666     result.emplace_front("operator("s + name + ')');
1667   }
1668   return result;
1669 }
1670 
1671 std::forward_list<std::string> GetAllNames(
1672     const SemanticsContext &context, const SourceName &name) {
1673   std::string str{name.ToString()};
1674   if (!name.empty() && name.end()[-1] == ')' &&
1675       name.ToString().rfind("operator(", 0) == 0) {
1676     for (int i{0}; i != common::LogicalOperator_enumSize; ++i) {
1677       auto names{GetOperatorNames(context, common::LogicalOperator{i})};
1678       if (llvm::is_contained(names, str)) {
1679         return names;
1680       }
1681     }
1682     for (int i{0}; i != common::RelationalOperator_enumSize; ++i) {
1683       auto names{GetOperatorNames(context, common::RelationalOperator{i})};
1684       if (llvm::is_contained(names, str)) {
1685         return names;
1686       }
1687     }
1688   }
1689   return {str};
1690 }
1691 
1692 void WarnOnDeferredLengthCharacterScalar(SemanticsContext &context,
1693     const SomeExpr *expr, parser::CharBlock at, const char *what) {
1694   if (context.languageFeatures().ShouldWarn(
1695           common::UsageWarning::F202XAllocatableBreakingChange)) {
1696     if (const Symbol *
1697         symbol{evaluate::UnwrapWholeSymbolOrComponentDataRef(expr)}) {
1698       const Symbol &ultimate{ResolveAssociations(*symbol)};
1699       if (const DeclTypeSpec * type{ultimate.GetType()}; type &&
1700           type->category() == DeclTypeSpec::Category::Character &&
1701           type->characterTypeSpec().length().isDeferred() &&
1702           IsAllocatable(ultimate) && ultimate.Rank() == 0) {
1703         context.Say(at,
1704             "The deferred length allocatable character scalar variable '%s' may be reallocated to a different length under the new Fortran 202X standard semantics for %s"_port_en_US,
1705             symbol->name(), what);
1706       }
1707     }
1708   }
1709 }
1710 
1711 bool CouldBeDataPointerValuedFunction(const Symbol *original) {
1712   if (original) {
1713     const Symbol &ultimate{original->GetUltimate()};
1714     if (const Symbol * result{FindFunctionResult(ultimate)}) {
1715       return IsPointer(*result) && !IsProcedure(*result);
1716     }
1717     if (const auto *generic{ultimate.detailsIf<GenericDetails>()}) {
1718       for (const SymbolRef &ref : generic->specificProcs()) {
1719         if (CouldBeDataPointerValuedFunction(&*ref)) {
1720           return true;
1721         }
1722       }
1723     }
1724   }
1725   return false;
1726 }
1727 
1728 std::string GetModuleOrSubmoduleName(const Symbol &symbol) {
1729   const auto &details{symbol.get<ModuleDetails>()};
1730   std::string result{symbol.name().ToString()};
1731   if (details.ancestor() && details.ancestor()->symbol()) {
1732     result = details.ancestor()->symbol()->name().ToString() + ':' + result;
1733   }
1734   return result;
1735 }
1736 
1737 std::string GetCommonBlockObjectName(const Symbol &common, bool underscoring) {
1738   if (const std::string * bind{common.GetBindName()}) {
1739     return *bind;
1740   }
1741   if (common.name().empty()) {
1742     return Fortran::common::blankCommonObjectName;
1743   }
1744   return underscoring ? common.name().ToString() + "_"s
1745                       : common.name().ToString();
1746 }
1747 
1748 bool HadUseError(
1749     SemanticsContext &context, SourceName at, const Symbol *symbol) {
1750   if (const auto *details{
1751           symbol ? symbol->detailsIf<UseErrorDetails>() : nullptr}) {
1752     auto &msg{context.Say(
1753         at, "Reference to '%s' is ambiguous"_err_en_US, symbol->name())};
1754     for (const auto &[location, module] : details->occurrences()) {
1755       msg.Attach(location, "'%s' was use-associated from module '%s'"_en_US, at,
1756           module->GetName().value());
1757     }
1758     context.SetError(*symbol);
1759     return true;
1760   } else {
1761     return false;
1762   }
1763 }
1764 
1765 } // namespace Fortran::semantics
1766