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