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