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 *FindPureProcedureContaining(const Scope &start) {
80   // N.B. We only need to examine the innermost containing program unit
81   // because an internal subprogram of a pure subprogram must also
82   // be pure (C1592).
83   if (start.IsTopLevel()) {
84     return nullptr;
85   } else {
86     const Scope &scope{GetProgramUnitContaining(start)};
87     return IsPureProcedure(scope) ? &scope : nullptr;
88   }
89 }
90 
91 // 7.5.2.4 "same derived type" test -- rely on IsTkCompatibleWith() and its
92 // infrastructure to detect and handle comparisons on distinct (but "same")
93 // sequence/bind(C) derived types
94 static bool MightBeSameDerivedType(
95     const std::optional<evaluate::DynamicType> &lhsType,
96     const std::optional<evaluate::DynamicType> &rhsType) {
97   return lhsType && rhsType && rhsType->IsTkCompatibleWith(*lhsType);
98 }
99 
100 Tristate IsDefinedAssignment(
101     const std::optional<evaluate::DynamicType> &lhsType, int lhsRank,
102     const std::optional<evaluate::DynamicType> &rhsType, int rhsRank) {
103   if (!lhsType || !rhsType) {
104     return Tristate::No; // error or rhs is untyped
105   }
106   TypeCategory lhsCat{lhsType->category()};
107   TypeCategory rhsCat{rhsType->category()};
108   if (rhsRank > 0 && lhsRank != rhsRank) {
109     return Tristate::Yes;
110   } else if (lhsCat != TypeCategory::Derived) {
111     return ToTristate(lhsCat != rhsCat &&
112         (!IsNumericTypeCategory(lhsCat) || !IsNumericTypeCategory(rhsCat)));
113   } else if (MightBeSameDerivedType(lhsType, rhsType)) {
114     return Tristate::Maybe; // TYPE(t) = TYPE(t) can be defined or intrinsic
115   } else {
116     return Tristate::Yes;
117   }
118 }
119 
120 bool IsIntrinsicRelational(common::RelationalOperator opr,
121     const evaluate::DynamicType &type0, int rank0,
122     const evaluate::DynamicType &type1, int rank1) {
123   if (!evaluate::AreConformable(rank0, rank1)) {
124     return false;
125   } else {
126     auto cat0{type0.category()};
127     auto cat1{type1.category()};
128     if (IsNumericTypeCategory(cat0) && IsNumericTypeCategory(cat1)) {
129       // numeric types: EQ/NE always ok, others ok for non-complex
130       return opr == common::RelationalOperator::EQ ||
131           opr == common::RelationalOperator::NE ||
132           (cat0 != TypeCategory::Complex && cat1 != TypeCategory::Complex);
133     } else {
134       // not both numeric: only Character is ok
135       return cat0 == TypeCategory::Character && cat1 == TypeCategory::Character;
136     }
137   }
138 }
139 
140 bool IsIntrinsicNumeric(const evaluate::DynamicType &type0) {
141   return IsNumericTypeCategory(type0.category());
142 }
143 bool IsIntrinsicNumeric(const evaluate::DynamicType &type0, int rank0,
144     const evaluate::DynamicType &type1, int rank1) {
145   return evaluate::AreConformable(rank0, rank1) &&
146       IsNumericTypeCategory(type0.category()) &&
147       IsNumericTypeCategory(type1.category());
148 }
149 
150 bool IsIntrinsicLogical(const evaluate::DynamicType &type0) {
151   return type0.category() == TypeCategory::Logical;
152 }
153 bool IsIntrinsicLogical(const evaluate::DynamicType &type0, int rank0,
154     const evaluate::DynamicType &type1, int rank1) {
155   return evaluate::AreConformable(rank0, rank1) &&
156       type0.category() == TypeCategory::Logical &&
157       type1.category() == TypeCategory::Logical;
158 }
159 
160 bool IsIntrinsicConcat(const evaluate::DynamicType &type0, int rank0,
161     const evaluate::DynamicType &type1, int rank1) {
162   return evaluate::AreConformable(rank0, rank1) &&
163       type0.category() == TypeCategory::Character &&
164       type1.category() == TypeCategory::Character &&
165       type0.kind() == type1.kind();
166 }
167 
168 bool IsGenericDefinedOp(const Symbol &symbol) {
169   const Symbol &ultimate{symbol.GetUltimate()};
170   if (const auto *generic{ultimate.detailsIf<GenericDetails>()}) {
171     return generic->kind().IsDefinedOperator();
172   } else if (const auto *misc{ultimate.detailsIf<MiscDetails>()}) {
173     return misc->kind() == MiscDetails::Kind::TypeBoundDefinedOp;
174   } else {
175     return false;
176   }
177 }
178 
179 bool IsDefinedOperator(SourceName name) {
180   const char *begin{name.begin()};
181   const char *end{name.end()};
182   return begin != end && begin[0] == '.' && end[-1] == '.';
183 }
184 
185 std::string MakeOpName(SourceName name) {
186   std::string result{name.ToString()};
187   return IsDefinedOperator(name)         ? "OPERATOR(" + result + ")"
188       : result.find("operator(", 0) == 0 ? parser::ToUpperCaseLetters(result)
189                                          : result;
190 }
191 
192 bool IsCommonBlockContaining(const Symbol &block, const Symbol &object) {
193   const auto &objects{block.get<CommonBlockDetails>().objects()};
194   auto found{std::find(objects.begin(), objects.end(), object)};
195   return found != objects.end();
196 }
197 
198 bool IsUseAssociated(const Symbol &symbol, const Scope &scope) {
199   const Scope &owner{GetProgramUnitContaining(symbol.GetUltimate().owner())};
200   return owner.kind() == Scope::Kind::Module &&
201       owner != GetProgramUnitContaining(scope);
202 }
203 
204 bool DoesScopeContain(
205     const Scope *maybeAncestor, const Scope &maybeDescendent) {
206   return maybeAncestor && !maybeDescendent.IsTopLevel() &&
207       FindScopeContaining(maybeDescendent.parent(),
208           [&](const Scope &scope) { return &scope == maybeAncestor; });
209 }
210 
211 bool DoesScopeContain(const Scope *maybeAncestor, const Symbol &symbol) {
212   return DoesScopeContain(maybeAncestor, symbol.owner());
213 }
214 
215 static const Symbol &FollowHostAssoc(const Symbol &symbol) {
216   for (const Symbol *s{&symbol};;) {
217     const auto *details{s->detailsIf<HostAssocDetails>()};
218     if (!details) {
219       return *s;
220     }
221     s = &details->symbol();
222   }
223 }
224 
225 bool IsHostAssociated(const Symbol &symbol, const Scope &scope) {
226   const Scope &subprogram{GetProgramUnitContaining(scope)};
227   return DoesScopeContain(
228       &GetProgramUnitContaining(FollowHostAssoc(symbol)), subprogram);
229 }
230 
231 bool IsInStmtFunction(const Symbol &symbol) {
232   if (const Symbol * function{symbol.owner().symbol()}) {
233     return IsStmtFunction(*function);
234   }
235   return false;
236 }
237 
238 bool IsStmtFunctionDummy(const Symbol &symbol) {
239   return IsDummy(symbol) && IsInStmtFunction(symbol);
240 }
241 
242 bool IsStmtFunctionResult(const Symbol &symbol) {
243   return IsFunctionResult(symbol) && IsInStmtFunction(symbol);
244 }
245 
246 bool IsPointerDummy(const Symbol &symbol) {
247   return IsPointer(symbol) && IsDummy(symbol);
248 }
249 
250 // proc-name
251 bool IsProcName(const Symbol &symbol) {
252   return symbol.GetUltimate().has<ProcEntityDetails>();
253 }
254 
255 bool IsBindCProcedure(const Symbol &symbol) {
256   if (const auto *procDetails{symbol.detailsIf<ProcEntityDetails>()}) {
257     if (const Symbol * procInterface{procDetails->interface().symbol()}) {
258       // procedure component with a BIND(C) interface
259       return IsBindCProcedure(*procInterface);
260     }
261   }
262   return symbol.attrs().test(Attr::BIND_C) && IsProcedure(symbol);
263 }
264 
265 bool IsBindCProcedure(const Scope &scope) {
266   if (const Symbol * symbol{scope.GetSymbol()}) {
267     return IsBindCProcedure(*symbol);
268   } else {
269     return false;
270   }
271 }
272 
273 static const Symbol *FindPointerComponent(
274     const Scope &scope, std::set<const Scope *> &visited) {
275   if (!scope.IsDerivedType()) {
276     return nullptr;
277   }
278   if (!visited.insert(&scope).second) {
279     return nullptr;
280   }
281   // If there's a top-level pointer component, return it for clearer error
282   // messaging.
283   for (const auto &pair : scope) {
284     const Symbol &symbol{*pair.second};
285     if (IsPointer(symbol)) {
286       return &symbol;
287     }
288   }
289   for (const auto &pair : scope) {
290     const Symbol &symbol{*pair.second};
291     if (const auto *details{symbol.detailsIf<ObjectEntityDetails>()}) {
292       if (const DeclTypeSpec * type{details->type()}) {
293         if (const DerivedTypeSpec * derived{type->AsDerived()}) {
294           if (const Scope * nested{derived->scope()}) {
295             if (const Symbol *
296                 pointer{FindPointerComponent(*nested, visited)}) {
297               return pointer;
298             }
299           }
300         }
301       }
302     }
303   }
304   return nullptr;
305 }
306 
307 const Symbol *FindPointerComponent(const Scope &scope) {
308   std::set<const Scope *> visited;
309   return FindPointerComponent(scope, visited);
310 }
311 
312 const Symbol *FindPointerComponent(const DerivedTypeSpec &derived) {
313   if (const Scope * scope{derived.scope()}) {
314     return FindPointerComponent(*scope);
315   } else {
316     return nullptr;
317   }
318 }
319 
320 const Symbol *FindPointerComponent(const DeclTypeSpec &type) {
321   if (const DerivedTypeSpec * derived{type.AsDerived()}) {
322     return FindPointerComponent(*derived);
323   } else {
324     return nullptr;
325   }
326 }
327 
328 const Symbol *FindPointerComponent(const DeclTypeSpec *type) {
329   return type ? FindPointerComponent(*type) : nullptr;
330 }
331 
332 const Symbol *FindPointerComponent(const Symbol &symbol) {
333   return IsPointer(symbol) ? &symbol : FindPointerComponent(symbol.GetType());
334 }
335 
336 // C1594 specifies several ways by which an object might be globally visible.
337 const Symbol *FindExternallyVisibleObject(
338     const Symbol &object, const Scope &scope) {
339   // TODO: Storage association with any object for which this predicate holds,
340   // once EQUIVALENCE is supported.
341   const Symbol &ultimate{GetAssociationRoot(object)};
342   if (IsDummy(ultimate)) {
343     if (IsIntentIn(ultimate)) {
344       return &ultimate;
345     }
346     if (IsPointer(ultimate) && IsPureProcedure(ultimate.owner()) &&
347         IsFunction(ultimate.owner())) {
348       return &ultimate;
349     }
350   } else if (&GetProgramUnitContaining(ultimate) !=
351       &GetProgramUnitContaining(scope)) {
352     return &object;
353   } else if (const Symbol * block{FindCommonBlockContaining(ultimate)}) {
354     return block;
355   }
356   return nullptr;
357 }
358 
359 const Symbol &BypassGeneric(const Symbol &symbol) {
360   const Symbol &ultimate{symbol.GetUltimate()};
361   if (const auto *generic{ultimate.detailsIf<GenericDetails>()}) {
362     if (const Symbol * specific{generic->specific()}) {
363       return *specific;
364     }
365   }
366   return symbol;
367 }
368 
369 bool ExprHasTypeCategory(
370     const SomeExpr &expr, const common::TypeCategory &type) {
371   auto dynamicType{expr.GetType()};
372   return dynamicType && dynamicType->category() == type;
373 }
374 
375 bool ExprTypeKindIsDefault(
376     const SomeExpr &expr, const SemanticsContext &context) {
377   auto dynamicType{expr.GetType()};
378   return dynamicType &&
379       dynamicType->category() != common::TypeCategory::Derived &&
380       dynamicType->kind() == context.GetDefaultKind(dynamicType->category());
381 }
382 
383 // If an analyzed expr or assignment is missing, dump the node and die.
384 template <typename T>
385 static void CheckMissingAnalysis(bool absent, const T &x) {
386   if (absent) {
387     std::string buf;
388     llvm::raw_string_ostream ss{buf};
389     ss << "node has not been analyzed:\n";
390     parser::DumpTree(ss, x);
391     common::die(ss.str().c_str());
392   }
393 }
394 
395 template <typename T> static const SomeExpr *GetTypedExpr(const T &x) {
396   CheckMissingAnalysis(!x.typedExpr, x);
397   return common::GetPtrFromOptional(x.typedExpr->v);
398 }
399 const SomeExpr *GetExprHelper::Get(const parser::Expr &x) {
400   return GetTypedExpr(x);
401 }
402 const SomeExpr *GetExprHelper::Get(const parser::Variable &x) {
403   return GetTypedExpr(x);
404 }
405 const SomeExpr *GetExprHelper::Get(const parser::DataStmtConstant &x) {
406   return GetTypedExpr(x);
407 }
408 const SomeExpr *GetExprHelper::Get(const parser::AllocateObject &x) {
409   return GetTypedExpr(x);
410 }
411 const SomeExpr *GetExprHelper::Get(const parser::PointerObject &x) {
412   return GetTypedExpr(x);
413 }
414 
415 const evaluate::Assignment *GetAssignment(const parser::AssignmentStmt &x) {
416   CheckMissingAnalysis(!x.typedAssignment, x);
417   return common::GetPtrFromOptional(x.typedAssignment->v);
418 }
419 const evaluate::Assignment *GetAssignment(
420     const parser::PointerAssignmentStmt &x) {
421   CheckMissingAnalysis(!x.typedAssignment, x);
422   return common::GetPtrFromOptional(x.typedAssignment->v);
423 }
424 
425 const Symbol *FindInterface(const Symbol &symbol) {
426   return std::visit(
427       common::visitors{
428           [](const ProcEntityDetails &details) {
429             return details.interface().symbol();
430           },
431           [](const ProcBindingDetails &details) { return &details.symbol(); },
432           [](const auto &) -> const Symbol * { return nullptr; },
433       },
434       symbol.details());
435 }
436 
437 const Symbol *FindSubprogram(const Symbol &symbol) {
438   return std::visit(
439       common::visitors{
440           [&](const ProcEntityDetails &details) -> const Symbol * {
441             if (const Symbol * interface{details.interface().symbol()}) {
442               return FindSubprogram(*interface);
443             } else {
444               return &symbol;
445             }
446           },
447           [](const ProcBindingDetails &details) {
448             return FindSubprogram(details.symbol());
449           },
450           [&](const SubprogramDetails &) { return &symbol; },
451           [](const UseDetails &details) {
452             return FindSubprogram(details.symbol());
453           },
454           [](const HostAssocDetails &details) {
455             return FindSubprogram(details.symbol());
456           },
457           [](const auto &) -> const Symbol * { return nullptr; },
458       },
459       symbol.details());
460 }
461 
462 const Symbol *FindOverriddenBinding(const Symbol &symbol) {
463   if (symbol.has<ProcBindingDetails>()) {
464     if (const DeclTypeSpec * parentType{FindParentTypeSpec(symbol.owner())}) {
465       if (const DerivedTypeSpec * parentDerived{parentType->AsDerived()}) {
466         if (const Scope * parentScope{parentDerived->typeSymbol().scope()}) {
467           return parentScope->FindComponent(symbol.name());
468         }
469       }
470     }
471   }
472   return nullptr;
473 }
474 
475 const DeclTypeSpec *FindParentTypeSpec(const DerivedTypeSpec &derived) {
476   return FindParentTypeSpec(derived.typeSymbol());
477 }
478 
479 const DeclTypeSpec *FindParentTypeSpec(const DeclTypeSpec &decl) {
480   if (const DerivedTypeSpec * derived{decl.AsDerived()}) {
481     return FindParentTypeSpec(*derived);
482   } else {
483     return nullptr;
484   }
485 }
486 
487 const DeclTypeSpec *FindParentTypeSpec(const Scope &scope) {
488   if (scope.kind() == Scope::Kind::DerivedType) {
489     if (const auto *symbol{scope.symbol()}) {
490       return FindParentTypeSpec(*symbol);
491     }
492   }
493   return nullptr;
494 }
495 
496 const DeclTypeSpec *FindParentTypeSpec(const Symbol &symbol) {
497   if (const Scope * scope{symbol.scope()}) {
498     if (const auto *details{symbol.detailsIf<DerivedTypeDetails>()}) {
499       if (const Symbol * parent{details->GetParentComponent(*scope)}) {
500         return parent->GetType();
501       }
502     }
503   }
504   return nullptr;
505 }
506 
507 const EquivalenceSet *FindEquivalenceSet(const Symbol &symbol) {
508   const Symbol &ultimate{symbol.GetUltimate()};
509   for (const EquivalenceSet &set : ultimate.owner().equivalenceSets()) {
510     for (const EquivalenceObject &object : set) {
511       if (object.symbol == ultimate) {
512         return &set;
513       }
514     }
515   }
516   return nullptr;
517 }
518 
519 bool IsOrContainsEventOrLockComponent(const Symbol &original) {
520   const Symbol &symbol{ResolveAssociations(original)};
521   if (const auto *details{symbol.detailsIf<ObjectEntityDetails>()}) {
522     if (const DeclTypeSpec * type{details->type()}) {
523       if (const DerivedTypeSpec * derived{type->AsDerived()}) {
524         return IsEventTypeOrLockType(derived) ||
525             FindEventOrLockPotentialComponent(*derived);
526       }
527     }
528   }
529   return false;
530 }
531 
532 // Check this symbol suitable as a type-bound procedure - C769
533 bool CanBeTypeBoundProc(const Symbol *symbol) {
534   if (!symbol || IsDummy(*symbol) || IsProcedurePointer(*symbol)) {
535     return false;
536   } else if (symbol->has<SubprogramNameDetails>()) {
537     return symbol->owner().kind() == Scope::Kind::Module;
538   } else if (auto *details{symbol->detailsIf<SubprogramDetails>()}) {
539     return symbol->owner().kind() == Scope::Kind::Module ||
540         details->isInterface();
541   } else if (const auto *proc{symbol->detailsIf<ProcEntityDetails>()}) {
542     return !symbol->attrs().test(Attr::INTRINSIC) &&
543         proc->HasExplicitInterface();
544   } else {
545     return false;
546   }
547 }
548 
549 bool HasDeclarationInitializer(const Symbol &symbol) {
550   if (IsNamedConstant(symbol)) {
551     return false;
552   } else if (const auto *object{symbol.detailsIf<ObjectEntityDetails>()}) {
553     return object->init().has_value();
554   } else if (const auto *proc{symbol.detailsIf<ProcEntityDetails>()}) {
555     return proc->init().has_value();
556   } else {
557     return false;
558   }
559 }
560 
561 bool IsInitialized(const Symbol &symbol, bool ignoreDataStatements) {
562   if (IsAllocatable(symbol) ||
563       (!ignoreDataStatements && symbol.test(Symbol::Flag::InDataStmt)) ||
564       HasDeclarationInitializer(symbol)) {
565     return true;
566   } else if (IsNamedConstant(symbol) || IsFunctionResult(symbol) ||
567       IsPointer(symbol)) {
568     return false;
569   } else if (const auto *object{symbol.detailsIf<ObjectEntityDetails>()}) {
570     if (!object->isDummy() && object->type()) {
571       if (const auto *derived{object->type()->AsDerived()}) {
572         DirectComponentIterator directs{*derived};
573         return bool{std::find_if(
574             directs.begin(), directs.end(), [](const Symbol &component) {
575               return IsAllocatable(component) ||
576                   HasDeclarationInitializer(component);
577             })};
578       }
579     }
580   }
581   return false;
582 }
583 
584 bool IsDestructible(const Symbol &symbol, const Symbol *derivedTypeSymbol) {
585   if (IsAllocatable(symbol) || IsAutomatic(symbol)) {
586     return true;
587   } else if (IsNamedConstant(symbol) || IsFunctionResult(symbol) ||
588       IsPointer(symbol)) {
589     return false;
590   } else if (const auto *object{symbol.detailsIf<ObjectEntityDetails>()}) {
591     if (!object->isDummy() && object->type()) {
592       if (const auto *derived{object->type()->AsDerived()}) {
593         return &derived->typeSymbol() != derivedTypeSymbol &&
594             derived->HasDestruction();
595       }
596     }
597   }
598   return false;
599 }
600 
601 bool HasIntrinsicTypeName(const Symbol &symbol) {
602   std::string name{symbol.name().ToString()};
603   if (name == "doubleprecision") {
604     return true;
605   } else if (name == "derived") {
606     return false;
607   } else {
608     for (int i{0}; i != common::TypeCategory_enumSize; ++i) {
609       if (name == parser::ToLowerCaseLetters(EnumToString(TypeCategory{i}))) {
610         return true;
611       }
612     }
613     return false;
614   }
615 }
616 
617 bool IsSeparateModuleProcedureInterface(const Symbol *symbol) {
618   if (symbol && symbol->attrs().test(Attr::MODULE)) {
619     if (auto *details{symbol->detailsIf<SubprogramDetails>()}) {
620       return details->isInterface();
621     }
622   }
623   return false;
624 }
625 
626 bool IsFinalizable(
627     const Symbol &symbol, std::set<const DerivedTypeSpec *> *inProgress) {
628   if (IsPointer(symbol)) {
629     return false;
630   }
631   if (const auto *object{symbol.detailsIf<ObjectEntityDetails>()}) {
632     if (object->isDummy() && !IsIntentOut(symbol)) {
633       return false;
634     }
635     const DeclTypeSpec *type{object->type()};
636     const DerivedTypeSpec *typeSpec{type ? type->AsDerived() : nullptr};
637     return typeSpec && IsFinalizable(*typeSpec, inProgress);
638   }
639   return false;
640 }
641 
642 bool IsFinalizable(const DerivedTypeSpec &derived,
643     std::set<const DerivedTypeSpec *> *inProgress) {
644   if (!derived.typeSymbol().get<DerivedTypeDetails>().finals().empty()) {
645     return true;
646   }
647   std::set<const DerivedTypeSpec *> basis;
648   if (inProgress) {
649     if (inProgress->find(&derived) != inProgress->end()) {
650       return false; // don't loop on recursive type
651     }
652   } else {
653     inProgress = &basis;
654   }
655   auto iterator{inProgress->insert(&derived).first};
656   PotentialComponentIterator components{derived};
657   bool result{bool{std::find_if(
658       components.begin(), components.end(), [=](const Symbol &component) {
659         return IsFinalizable(component, inProgress);
660       })}};
661   inProgress->erase(iterator);
662   return result;
663 }
664 
665 bool HasImpureFinal(const DerivedTypeSpec &derived) {
666   if (const auto *details{
667           derived.typeSymbol().detailsIf<DerivedTypeDetails>()}) {
668     const auto &finals{details->finals()};
669     return std::any_of(finals.begin(), finals.end(),
670         [](const auto &x) { return !x.second->attrs().test(Attr::PURE); });
671   } else {
672     return false;
673   }
674 }
675 
676 bool IsAssumedLengthCharacter(const Symbol &symbol) {
677   if (const DeclTypeSpec * type{symbol.GetType()}) {
678     return type->category() == DeclTypeSpec::Character &&
679         type->characterTypeSpec().length().isAssumed();
680   } else {
681     return false;
682   }
683 }
684 
685 bool IsInBlankCommon(const Symbol &symbol) {
686   const Symbol *block{FindCommonBlockContaining(symbol)};
687   return block && block->name().empty();
688 }
689 
690 // C722 and C723:  For a function to be assumed length, it must be external and
691 // of CHARACTER type
692 bool IsExternal(const Symbol &symbol) {
693   return ClassifyProcedure(symbol) == ProcedureDefinitionClass::External;
694 }
695 
696 // Most scopes have no EQUIVALENCE, and this function is a fast no-op for them.
697 std::list<std::list<SymbolRef>> GetStorageAssociations(const Scope &scope) {
698   UnorderedSymbolSet distinct;
699   for (const EquivalenceSet &set : scope.equivalenceSets()) {
700     for (const EquivalenceObject &object : set) {
701       distinct.emplace(object.symbol);
702     }
703   }
704   // This set is ordered by ascending offsets, with ties broken by greatest
705   // size.  A multiset is used here because multiple symbols may have the
706   // same offset and size; the symbols in the set, however, are distinct.
707   std::multiset<SymbolRef, SymbolOffsetCompare> associated;
708   for (SymbolRef ref : distinct) {
709     associated.emplace(*ref);
710   }
711   std::list<std::list<SymbolRef>> result;
712   std::size_t limit{0};
713   const Symbol *currentCommon{nullptr};
714   for (const Symbol &symbol : associated) {
715     const Symbol *thisCommon{FindCommonBlockContaining(symbol)};
716     if (result.empty() || symbol.offset() >= limit ||
717         thisCommon != currentCommon) {
718       // Start a new group
719       result.emplace_back(std::list<SymbolRef>{});
720       limit = 0;
721       currentCommon = thisCommon;
722     }
723     result.back().emplace_back(symbol);
724     limit = std::max(limit, symbol.offset() + symbol.size());
725   }
726   return result;
727 }
728 
729 bool IsModuleProcedure(const Symbol &symbol) {
730   return ClassifyProcedure(symbol) == ProcedureDefinitionClass::Module;
731 }
732 const Symbol *IsExternalInPureContext(
733     const Symbol &symbol, const Scope &scope) {
734   if (const auto *pureProc{FindPureProcedureContaining(scope)}) {
735     return FindExternallyVisibleObject(symbol.GetUltimate(), *pureProc);
736   }
737   return nullptr;
738 }
739 
740 PotentialComponentIterator::const_iterator FindPolymorphicPotentialComponent(
741     const DerivedTypeSpec &derived) {
742   PotentialComponentIterator potentials{derived};
743   return std::find_if(
744       potentials.begin(), potentials.end(), [](const Symbol &component) {
745         if (const auto *details{component.detailsIf<ObjectEntityDetails>()}) {
746           const DeclTypeSpec *type{details->type()};
747           return type && type->IsPolymorphic();
748         }
749         return false;
750       });
751 }
752 
753 bool IsOrContainsPolymorphicComponent(const Symbol &original) {
754   const Symbol &symbol{ResolveAssociations(original)};
755   if (const auto *details{symbol.detailsIf<ObjectEntityDetails>()}) {
756     if (const DeclTypeSpec * type{details->type()}) {
757       if (type->IsPolymorphic()) {
758         return true;
759       }
760       if (const DerivedTypeSpec * derived{type->AsDerived()}) {
761         return (bool)FindPolymorphicPotentialComponent(*derived);
762       }
763     }
764   }
765   return false;
766 }
767 
768 bool InProtectedContext(const Symbol &symbol, const Scope &currentScope) {
769   return IsProtected(symbol) && !IsHostAssociated(symbol, currentScope);
770 }
771 
772 // C1101 and C1158
773 // Modifiability checks on the leftmost symbol ("base object")
774 // of a data-ref
775 std::optional<parser::MessageFixedText> WhyNotModifiableFirst(
776     const Symbol &symbol, const Scope &scope) {
777   if (symbol.has<AssocEntityDetails>()) {
778     return "'%s' is construct associated with an expression"_en_US;
779   } else if (IsExternalInPureContext(symbol, scope)) {
780     return "'%s' is externally visible and referenced in a pure"
781            " procedure"_en_US;
782   } else if (!IsVariableName(symbol)) {
783     return "'%s' is not a variable"_en_US;
784   } else {
785     return std::nullopt;
786   }
787 }
788 
789 // Modifiability checks on the rightmost symbol of a data-ref
790 std::optional<parser::MessageFixedText> WhyNotModifiableLast(
791     const Symbol &symbol, const Scope &scope) {
792   if (IsOrContainsEventOrLockComponent(symbol)) {
793     return "'%s' is an entity with either an EVENT_TYPE or LOCK_TYPE"_en_US;
794   } else {
795     return std::nullopt;
796   }
797 }
798 
799 // Modifiability checks on the leftmost (base) symbol of a data-ref
800 // that apply only when there are no pointer components or a base
801 // that is a pointer.
802 std::optional<parser::MessageFixedText> WhyNotModifiableIfNoPtr(
803     const Symbol &symbol, const Scope &scope) {
804   if (InProtectedContext(symbol, scope)) {
805     return "'%s' is protected in this scope"_en_US;
806   } else if (IsIntentIn(symbol)) {
807     return "'%s' is an INTENT(IN) dummy argument"_en_US;
808   } else {
809     return std::nullopt;
810   }
811 }
812 
813 // Apply all modifiability checks to a single symbol
814 std::optional<parser::MessageFixedText> WhyNotModifiable(
815     const Symbol &original, const Scope &scope) {
816   const Symbol &symbol{GetAssociationRoot(original)};
817   if (auto first{WhyNotModifiableFirst(symbol, scope)}) {
818     return first;
819   } else if (auto last{WhyNotModifiableLast(symbol, scope)}) {
820     return last;
821   } else if (!IsPointer(symbol)) {
822     return WhyNotModifiableIfNoPtr(symbol, scope);
823   } else {
824     return std::nullopt;
825   }
826 }
827 
828 // Modifiability checks for a data-ref
829 std::optional<parser::Message> WhyNotModifiable(parser::CharBlock at,
830     const SomeExpr &expr, const Scope &scope, bool vectorSubscriptIsOk) {
831   if (auto dataRef{evaluate::ExtractDataRef(expr, true)}) {
832     if (!vectorSubscriptIsOk && evaluate::HasVectorSubscript(expr)) {
833       return parser::Message{at, "Variable has a vector subscript"_en_US};
834     }
835     const Symbol &first{GetAssociationRoot(dataRef->GetFirstSymbol())};
836     if (auto maybeWhyFirst{WhyNotModifiableFirst(first, scope)}) {
837       return parser::Message{first.name(),
838           parser::MessageFormattedText{
839               std::move(*maybeWhyFirst), first.name()}};
840     }
841     const Symbol &last{dataRef->GetLastSymbol()};
842     if (auto maybeWhyLast{WhyNotModifiableLast(last, scope)}) {
843       return parser::Message{last.name(),
844           parser::MessageFormattedText{std::move(*maybeWhyLast), last.name()}};
845     }
846     if (!GetLastPointerSymbol(*dataRef)) {
847       if (auto maybeWhyFirst{WhyNotModifiableIfNoPtr(first, scope)}) {
848         return parser::Message{first.name(),
849             parser::MessageFormattedText{
850                 std::move(*maybeWhyFirst), first.name()}};
851       }
852     }
853   } else if (!evaluate::IsVariable(expr)) {
854     return parser::Message{
855         at, "'%s' is not a variable"_en_US, expr.AsFortran()};
856   } else {
857     // reference to function returning POINTER
858   }
859   return std::nullopt;
860 }
861 
862 class ImageControlStmtHelper {
863   using ImageControlStmts = std::variant<parser::ChangeTeamConstruct,
864       parser::CriticalConstruct, parser::EventPostStmt, parser::EventWaitStmt,
865       parser::FormTeamStmt, parser::LockStmt, parser::StopStmt,
866       parser::SyncAllStmt, parser::SyncImagesStmt, parser::SyncMemoryStmt,
867       parser::SyncTeamStmt, parser::UnlockStmt>;
868 
869 public:
870   template <typename T> bool operator()(const T &) {
871     return common::HasMember<T, ImageControlStmts>;
872   }
873   template <typename T> bool operator()(const common::Indirection<T> &x) {
874     return (*this)(x.value());
875   }
876   bool operator()(const parser::AllocateStmt &stmt) {
877     const auto &allocationList{std::get<std::list<parser::Allocation>>(stmt.t)};
878     for (const auto &allocation : allocationList) {
879       const auto &allocateObject{
880           std::get<parser::AllocateObject>(allocation.t)};
881       if (IsCoarrayObject(allocateObject)) {
882         return true;
883       }
884     }
885     return false;
886   }
887   bool operator()(const parser::DeallocateStmt &stmt) {
888     const auto &allocateObjectList{
889         std::get<std::list<parser::AllocateObject>>(stmt.t)};
890     for (const auto &allocateObject : allocateObjectList) {
891       if (IsCoarrayObject(allocateObject)) {
892         return true;
893       }
894     }
895     return false;
896   }
897   bool operator()(const parser::CallStmt &stmt) {
898     const auto &procedureDesignator{
899         std::get<parser::ProcedureDesignator>(stmt.v.t)};
900     if (auto *name{std::get_if<parser::Name>(&procedureDesignator.u)}) {
901       // TODO: also ensure that the procedure is, in fact, an intrinsic
902       if (name->source == "move_alloc") {
903         const auto &args{std::get<std::list<parser::ActualArgSpec>>(stmt.v.t)};
904         if (!args.empty()) {
905           const parser::ActualArg &actualArg{
906               std::get<parser::ActualArg>(args.front().t)};
907           if (const auto *argExpr{
908                   std::get_if<common::Indirection<parser::Expr>>(
909                       &actualArg.u)}) {
910             return HasCoarray(argExpr->value());
911           }
912         }
913       }
914     }
915     return false;
916   }
917   bool operator()(const parser::Statement<parser::ActionStmt> &stmt) {
918     return std::visit(*this, stmt.statement.u);
919   }
920 
921 private:
922   bool IsCoarrayObject(const parser::AllocateObject &allocateObject) {
923     const parser::Name &name{GetLastName(allocateObject)};
924     return name.symbol && evaluate::IsCoarray(*name.symbol);
925   }
926 };
927 
928 bool IsImageControlStmt(const parser::ExecutableConstruct &construct) {
929   return std::visit(ImageControlStmtHelper{}, construct.u);
930 }
931 
932 std::optional<parser::MessageFixedText> GetImageControlStmtCoarrayMsg(
933     const parser::ExecutableConstruct &construct) {
934   if (const auto *actionStmt{
935           std::get_if<parser::Statement<parser::ActionStmt>>(&construct.u)}) {
936     return std::visit(
937         common::visitors{
938             [](const common::Indirection<parser::AllocateStmt> &)
939                 -> std::optional<parser::MessageFixedText> {
940               return "ALLOCATE of a coarray is an image control"
941                      " statement"_en_US;
942             },
943             [](const common::Indirection<parser::DeallocateStmt> &)
944                 -> std::optional<parser::MessageFixedText> {
945               return "DEALLOCATE of a coarray is an image control"
946                      " statement"_en_US;
947             },
948             [](const common::Indirection<parser::CallStmt> &)
949                 -> std::optional<parser::MessageFixedText> {
950               return "MOVE_ALLOC of a coarray is an image control"
951                      " statement "_en_US;
952             },
953             [](const auto &) -> std::optional<parser::MessageFixedText> {
954               return std::nullopt;
955             },
956         },
957         actionStmt->statement.u);
958   }
959   return std::nullopt;
960 }
961 
962 parser::CharBlock GetImageControlStmtLocation(
963     const parser::ExecutableConstruct &executableConstruct) {
964   return std::visit(
965       common::visitors{
966           [](const common::Indirection<parser::ChangeTeamConstruct>
967                   &construct) {
968             return std::get<parser::Statement<parser::ChangeTeamStmt>>(
969                 construct.value().t)
970                 .source;
971           },
972           [](const common::Indirection<parser::CriticalConstruct> &construct) {
973             return std::get<parser::Statement<parser::CriticalStmt>>(
974                 construct.value().t)
975                 .source;
976           },
977           [](const parser::Statement<parser::ActionStmt> &actionStmt) {
978             return actionStmt.source;
979           },
980           [](const auto &) { return parser::CharBlock{}; },
981       },
982       executableConstruct.u);
983 }
984 
985 bool HasCoarray(const parser::Expr &expression) {
986   if (const auto *expr{GetExpr(expression)}) {
987     for (const Symbol &symbol : evaluate::CollectSymbols(*expr)) {
988       if (evaluate::IsCoarray(symbol)) {
989         return true;
990       }
991     }
992   }
993   return false;
994 }
995 
996 bool IsPolymorphic(const Symbol &symbol) {
997   if (const DeclTypeSpec * type{symbol.GetType()}) {
998     return type->IsPolymorphic();
999   }
1000   return false;
1001 }
1002 
1003 bool IsPolymorphicAllocatable(const Symbol &symbol) {
1004   return IsAllocatable(symbol) && IsPolymorphic(symbol);
1005 }
1006 
1007 std::optional<parser::MessageFormattedText> CheckAccessibleComponent(
1008     const Scope &scope, const Symbol &symbol) {
1009   CHECK(symbol.owner().IsDerivedType()); // symbol must be a component
1010   if (symbol.attrs().test(Attr::PRIVATE)) {
1011     if (FindModuleFileContaining(scope)) {
1012       // Don't enforce component accessibility checks in module files;
1013       // there may be forward-substituted named constants of derived type
1014       // whose structure constructors reference private components.
1015     } else if (const Scope *
1016         moduleScope{FindModuleContaining(symbol.owner())}) {
1017       if (!moduleScope->Contains(scope)) {
1018         return parser::MessageFormattedText{
1019             "PRIVATE component '%s' is only accessible within module '%s'"_err_en_US,
1020             symbol.name(), moduleScope->GetName().value()};
1021       }
1022     }
1023   }
1024   return std::nullopt;
1025 }
1026 
1027 std::list<SourceName> OrderParameterNames(const Symbol &typeSymbol) {
1028   std::list<SourceName> result;
1029   if (const DerivedTypeSpec * spec{typeSymbol.GetParentTypeSpec()}) {
1030     result = OrderParameterNames(spec->typeSymbol());
1031   }
1032   const auto &paramNames{typeSymbol.get<DerivedTypeDetails>().paramNames()};
1033   result.insert(result.end(), paramNames.begin(), paramNames.end());
1034   return result;
1035 }
1036 
1037 SymbolVector OrderParameterDeclarations(const Symbol &typeSymbol) {
1038   SymbolVector result;
1039   if (const DerivedTypeSpec * spec{typeSymbol.GetParentTypeSpec()}) {
1040     result = OrderParameterDeclarations(spec->typeSymbol());
1041   }
1042   const auto &paramDecls{typeSymbol.get<DerivedTypeDetails>().paramDecls()};
1043   result.insert(result.end(), paramDecls.begin(), paramDecls.end());
1044   return result;
1045 }
1046 
1047 const DeclTypeSpec &FindOrInstantiateDerivedType(
1048     Scope &scope, DerivedTypeSpec &&spec, DeclTypeSpec::Category category) {
1049   spec.EvaluateParameters(scope.context());
1050   if (const DeclTypeSpec *
1051       type{scope.FindInstantiatedDerivedType(spec, category)}) {
1052     return *type;
1053   }
1054   // Create a new instantiation of this parameterized derived type
1055   // for this particular distinct set of actual parameter values.
1056   DeclTypeSpec &type{scope.MakeDerivedType(category, std::move(spec))};
1057   type.derivedTypeSpec().Instantiate(scope);
1058   return type;
1059 }
1060 
1061 const Symbol *FindSeparateModuleSubprogramInterface(const Symbol *proc) {
1062   if (proc) {
1063     if (const Symbol * submodule{proc->owner().symbol()}) {
1064       if (const auto *details{submodule->detailsIf<ModuleDetails>()}) {
1065         if (const Scope * ancestor{details->ancestor()}) {
1066           const Symbol *iface{ancestor->FindSymbol(proc->name())};
1067           if (IsSeparateModuleProcedureInterface(iface)) {
1068             return iface;
1069           }
1070         }
1071       }
1072     }
1073   }
1074   return nullptr;
1075 }
1076 
1077 ProcedureDefinitionClass ClassifyProcedure(const Symbol &symbol) { // 15.2.2
1078   const Symbol &ultimate{symbol.GetUltimate()};
1079   if (ultimate.attrs().test(Attr::INTRINSIC)) {
1080     return ProcedureDefinitionClass::Intrinsic;
1081   } else if (ultimate.attrs().test(Attr::EXTERNAL)) {
1082     return ProcedureDefinitionClass::External;
1083   } else if (const auto *procDetails{ultimate.detailsIf<ProcEntityDetails>()}) {
1084     if (procDetails->isDummy()) {
1085       return ProcedureDefinitionClass::Dummy;
1086     } else if (IsPointer(ultimate)) {
1087       return ProcedureDefinitionClass::Pointer;
1088     }
1089   } else if (const Symbol * subp{FindSubprogram(symbol)}) {
1090     if (const auto *subpDetails{subp->detailsIf<SubprogramDetails>()}) {
1091       if (subpDetails->stmtFunction()) {
1092         return ProcedureDefinitionClass::StatementFunction;
1093       }
1094     }
1095     switch (ultimate.owner().kind()) {
1096     case Scope::Kind::Global:
1097     case Scope::Kind::IntrinsicModules:
1098       return ProcedureDefinitionClass::External;
1099     case Scope::Kind::Module:
1100       return ProcedureDefinitionClass::Module;
1101     case Scope::Kind::MainProgram:
1102     case Scope::Kind::Subprogram:
1103       return ProcedureDefinitionClass::Internal;
1104     default:
1105       break;
1106     }
1107   }
1108   return ProcedureDefinitionClass::None;
1109 }
1110 
1111 // ComponentIterator implementation
1112 
1113 template <ComponentKind componentKind>
1114 typename ComponentIterator<componentKind>::const_iterator
1115 ComponentIterator<componentKind>::const_iterator::Create(
1116     const DerivedTypeSpec &derived) {
1117   const_iterator it{};
1118   it.componentPath_.emplace_back(derived);
1119   it.Increment(); // cue up first relevant component, if any
1120   return it;
1121 }
1122 
1123 template <ComponentKind componentKind>
1124 const DerivedTypeSpec *
1125 ComponentIterator<componentKind>::const_iterator::PlanComponentTraversal(
1126     const Symbol &component) const {
1127   if (const auto *details{component.detailsIf<ObjectEntityDetails>()}) {
1128     if (const DeclTypeSpec * type{details->type()}) {
1129       if (const auto *derived{type->AsDerived()}) {
1130         bool traverse{false};
1131         if constexpr (componentKind == ComponentKind::Ordered) {
1132           // Order Component (only visit parents)
1133           traverse = component.test(Symbol::Flag::ParentComp);
1134         } else if constexpr (componentKind == ComponentKind::Direct) {
1135           traverse = !IsAllocatableOrPointer(component);
1136         } else if constexpr (componentKind == ComponentKind::Ultimate) {
1137           traverse = !IsAllocatableOrPointer(component);
1138         } else if constexpr (componentKind == ComponentKind::Potential) {
1139           traverse = !IsPointer(component);
1140         } else if constexpr (componentKind == ComponentKind::Scope) {
1141           traverse = !IsAllocatableOrPointer(component);
1142         }
1143         if (traverse) {
1144           const Symbol &newTypeSymbol{derived->typeSymbol()};
1145           // Avoid infinite loop if the type is already part of the types
1146           // being visited. It is possible to have "loops in type" because
1147           // C744 does not forbid to use not yet declared type for
1148           // ALLOCATABLE or POINTER components.
1149           for (const auto &node : componentPath_) {
1150             if (&newTypeSymbol == &node.GetTypeSymbol()) {
1151               return nullptr;
1152             }
1153           }
1154           return derived;
1155         }
1156       }
1157     } // intrinsic & unlimited polymorphic not traversable
1158   }
1159   return nullptr;
1160 }
1161 
1162 template <ComponentKind componentKind>
1163 static bool StopAtComponentPre(const Symbol &component) {
1164   if constexpr (componentKind == ComponentKind::Ordered) {
1165     // Parent components need to be iterated upon after their
1166     // sub-components in structure constructor analysis.
1167     return !component.test(Symbol::Flag::ParentComp);
1168   } else if constexpr (componentKind == ComponentKind::Direct) {
1169     return true;
1170   } else if constexpr (componentKind == ComponentKind::Ultimate) {
1171     return component.has<ProcEntityDetails>() ||
1172         IsAllocatableOrPointer(component) ||
1173         (component.get<ObjectEntityDetails>().type() &&
1174             component.get<ObjectEntityDetails>().type()->AsIntrinsic());
1175   } else if constexpr (componentKind == ComponentKind::Potential) {
1176     return !IsPointer(component);
1177   }
1178 }
1179 
1180 template <ComponentKind componentKind>
1181 static bool StopAtComponentPost(const Symbol &component) {
1182   return componentKind == ComponentKind::Ordered &&
1183       component.test(Symbol::Flag::ParentComp);
1184 }
1185 
1186 template <ComponentKind componentKind>
1187 void ComponentIterator<componentKind>::const_iterator::Increment() {
1188   while (!componentPath_.empty()) {
1189     ComponentPathNode &deepest{componentPath_.back()};
1190     if (deepest.component()) {
1191       if (!deepest.descended()) {
1192         deepest.set_descended(true);
1193         if (const DerivedTypeSpec *
1194             derived{PlanComponentTraversal(*deepest.component())}) {
1195           componentPath_.emplace_back(*derived);
1196           continue;
1197         }
1198       } else if (!deepest.visited()) {
1199         deepest.set_visited(true);
1200         return; // this is the next component to visit, after descending
1201       }
1202     }
1203     auto &nameIterator{deepest.nameIterator()};
1204     if (nameIterator == deepest.nameEnd()) {
1205       componentPath_.pop_back();
1206     } else if constexpr (componentKind == ComponentKind::Scope) {
1207       deepest.set_component(*nameIterator++->second);
1208       deepest.set_descended(false);
1209       deepest.set_visited(true);
1210       return; // this is the next component to visit, before descending
1211     } else {
1212       const Scope &scope{deepest.GetScope()};
1213       auto scopeIter{scope.find(*nameIterator++)};
1214       if (scopeIter != scope.cend()) {
1215         const Symbol &component{*scopeIter->second};
1216         deepest.set_component(component);
1217         deepest.set_descended(false);
1218         if (StopAtComponentPre<componentKind>(component)) {
1219           deepest.set_visited(true);
1220           return; // this is the next component to visit, before descending
1221         } else {
1222           deepest.set_visited(!StopAtComponentPost<componentKind>(component));
1223         }
1224       }
1225     }
1226   }
1227 }
1228 
1229 template <ComponentKind componentKind>
1230 std::string
1231 ComponentIterator<componentKind>::const_iterator::BuildResultDesignatorName()
1232     const {
1233   std::string designator{""};
1234   for (const auto &node : componentPath_) {
1235     designator += "%" + DEREF(node.component()).name().ToString();
1236   }
1237   return designator;
1238 }
1239 
1240 template class ComponentIterator<ComponentKind::Ordered>;
1241 template class ComponentIterator<ComponentKind::Direct>;
1242 template class ComponentIterator<ComponentKind::Ultimate>;
1243 template class ComponentIterator<ComponentKind::Potential>;
1244 template class ComponentIterator<ComponentKind::Scope>;
1245 
1246 UltimateComponentIterator::const_iterator FindCoarrayUltimateComponent(
1247     const DerivedTypeSpec &derived) {
1248   UltimateComponentIterator ultimates{derived};
1249   return std::find_if(ultimates.begin(), ultimates.end(),
1250       [](const Symbol &symbol) { return evaluate::IsCoarray(symbol); });
1251 }
1252 
1253 UltimateComponentIterator::const_iterator FindPointerUltimateComponent(
1254     const DerivedTypeSpec &derived) {
1255   UltimateComponentIterator ultimates{derived};
1256   return std::find_if(ultimates.begin(), ultimates.end(), IsPointer);
1257 }
1258 
1259 PotentialComponentIterator::const_iterator FindEventOrLockPotentialComponent(
1260     const DerivedTypeSpec &derived) {
1261   PotentialComponentIterator potentials{derived};
1262   return std::find_if(
1263       potentials.begin(), potentials.end(), [](const Symbol &component) {
1264         if (const auto *details{component.detailsIf<ObjectEntityDetails>()}) {
1265           const DeclTypeSpec *type{details->type()};
1266           return type && IsEventTypeOrLockType(type->AsDerived());
1267         }
1268         return false;
1269       });
1270 }
1271 
1272 UltimateComponentIterator::const_iterator FindAllocatableUltimateComponent(
1273     const DerivedTypeSpec &derived) {
1274   UltimateComponentIterator ultimates{derived};
1275   return std::find_if(ultimates.begin(), ultimates.end(), IsAllocatable);
1276 }
1277 
1278 UltimateComponentIterator::const_iterator
1279 FindPolymorphicAllocatableUltimateComponent(const DerivedTypeSpec &derived) {
1280   UltimateComponentIterator ultimates{derived};
1281   return std::find_if(
1282       ultimates.begin(), ultimates.end(), IsPolymorphicAllocatable);
1283 }
1284 
1285 UltimateComponentIterator::const_iterator
1286 FindPolymorphicAllocatableNonCoarrayUltimateComponent(
1287     const DerivedTypeSpec &derived) {
1288   UltimateComponentIterator ultimates{derived};
1289   return std::find_if(ultimates.begin(), ultimates.end(), [](const Symbol &x) {
1290     return IsPolymorphicAllocatable(x) && !evaluate::IsCoarray(x);
1291   });
1292 }
1293 
1294 const Symbol *FindUltimateComponent(const DerivedTypeSpec &derived,
1295     const std::function<bool(const Symbol &)> &predicate) {
1296   UltimateComponentIterator ultimates{derived};
1297   if (auto it{std::find_if(ultimates.begin(), ultimates.end(),
1298           [&predicate](const Symbol &component) -> bool {
1299             return predicate(component);
1300           })}) {
1301     return &*it;
1302   }
1303   return nullptr;
1304 }
1305 
1306 const Symbol *FindUltimateComponent(const Symbol &symbol,
1307     const std::function<bool(const Symbol &)> &predicate) {
1308   if (predicate(symbol)) {
1309     return &symbol;
1310   } else if (const auto *object{symbol.detailsIf<ObjectEntityDetails>()}) {
1311     if (const auto *type{object->type()}) {
1312       if (const auto *derived{type->AsDerived()}) {
1313         return FindUltimateComponent(*derived, predicate);
1314       }
1315     }
1316   }
1317   return nullptr;
1318 }
1319 
1320 const Symbol *FindImmediateComponent(const DerivedTypeSpec &type,
1321     const std::function<bool(const Symbol &)> &predicate) {
1322   if (const Scope * scope{type.scope()}) {
1323     const Symbol *parent{nullptr};
1324     for (const auto &pair : *scope) {
1325       const Symbol *symbol{&*pair.second};
1326       if (predicate(*symbol)) {
1327         return symbol;
1328       }
1329       if (symbol->test(Symbol::Flag::ParentComp)) {
1330         parent = symbol;
1331       }
1332     }
1333     if (parent) {
1334       if (const auto *object{parent->detailsIf<ObjectEntityDetails>()}) {
1335         if (const auto *type{object->type()}) {
1336           if (const auto *derived{type->AsDerived()}) {
1337             return FindImmediateComponent(*derived, predicate);
1338           }
1339         }
1340       }
1341     }
1342   }
1343   return nullptr;
1344 }
1345 
1346 const Symbol *IsFunctionResultWithSameNameAsFunction(const Symbol &symbol) {
1347   if (IsFunctionResult(symbol)) {
1348     if (const Symbol * function{symbol.owner().symbol()}) {
1349       if (symbol.name() == function->name()) {
1350         return function;
1351       }
1352     }
1353   }
1354   return nullptr;
1355 }
1356 
1357 void LabelEnforce::Post(const parser::GotoStmt &gotoStmt) {
1358   checkLabelUse(gotoStmt.v);
1359 }
1360 void LabelEnforce::Post(const parser::ComputedGotoStmt &computedGotoStmt) {
1361   for (auto &i : std::get<std::list<parser::Label>>(computedGotoStmt.t)) {
1362     checkLabelUse(i);
1363   }
1364 }
1365 
1366 void LabelEnforce::Post(const parser::ArithmeticIfStmt &arithmeticIfStmt) {
1367   checkLabelUse(std::get<1>(arithmeticIfStmt.t));
1368   checkLabelUse(std::get<2>(arithmeticIfStmt.t));
1369   checkLabelUse(std::get<3>(arithmeticIfStmt.t));
1370 }
1371 
1372 void LabelEnforce::Post(const parser::AssignStmt &assignStmt) {
1373   checkLabelUse(std::get<parser::Label>(assignStmt.t));
1374 }
1375 
1376 void LabelEnforce::Post(const parser::AssignedGotoStmt &assignedGotoStmt) {
1377   for (auto &i : std::get<std::list<parser::Label>>(assignedGotoStmt.t)) {
1378     checkLabelUse(i);
1379   }
1380 }
1381 
1382 void LabelEnforce::Post(const parser::AltReturnSpec &altReturnSpec) {
1383   checkLabelUse(altReturnSpec.v);
1384 }
1385 
1386 void LabelEnforce::Post(const parser::ErrLabel &errLabel) {
1387   checkLabelUse(errLabel.v);
1388 }
1389 void LabelEnforce::Post(const parser::EndLabel &endLabel) {
1390   checkLabelUse(endLabel.v);
1391 }
1392 void LabelEnforce::Post(const parser::EorLabel &eorLabel) {
1393   checkLabelUse(eorLabel.v);
1394 }
1395 
1396 void LabelEnforce::checkLabelUse(const parser::Label &labelUsed) {
1397   if (labels_.find(labelUsed) == labels_.end()) {
1398     SayWithConstruct(context_, currentStatementSourcePosition_,
1399         parser::MessageFormattedText{
1400             "Control flow escapes from %s"_err_en_US, construct_},
1401         constructSourcePosition_);
1402   }
1403 }
1404 
1405 parser::MessageFormattedText LabelEnforce::GetEnclosingConstructMsg() {
1406   return {"Enclosing %s statement"_en_US, construct_};
1407 }
1408 
1409 void LabelEnforce::SayWithConstruct(SemanticsContext &context,
1410     parser::CharBlock stmtLocation, parser::MessageFormattedText &&message,
1411     parser::CharBlock constructLocation) {
1412   context.Say(stmtLocation, message)
1413       .Attach(constructLocation, GetEnclosingConstructMsg());
1414 }
1415 
1416 bool HasAlternateReturns(const Symbol &subprogram) {
1417   for (const auto *dummyArg : subprogram.get<SubprogramDetails>().dummyArgs()) {
1418     if (!dummyArg) {
1419       return true;
1420     }
1421   }
1422   return false;
1423 }
1424 
1425 bool InCommonBlock(const Symbol &symbol) {
1426   const auto *details{symbol.detailsIf<ObjectEntityDetails>()};
1427   return details && details->commonBlock();
1428 }
1429 
1430 const std::optional<parser::Name> &MaybeGetNodeName(
1431     const ConstructNode &construct) {
1432   return std::visit(
1433       common::visitors{
1434           [&](const parser::BlockConstruct *blockConstruct)
1435               -> const std::optional<parser::Name> & {
1436             return std::get<0>(blockConstruct->t).statement.v;
1437           },
1438           [&](const auto *a) -> const std::optional<parser::Name> & {
1439             return std::get<0>(std::get<0>(a->t).statement.t);
1440           },
1441       },
1442       construct);
1443 }
1444 
1445 std::optional<ArraySpec> ToArraySpec(
1446     evaluate::FoldingContext &context, const evaluate::Shape &shape) {
1447   if (auto extents{evaluate::AsConstantExtents(context, shape)}) {
1448     ArraySpec result;
1449     for (const auto &extent : *extents) {
1450       result.emplace_back(ShapeSpec::MakeExplicit(Bound{extent}));
1451     }
1452     return {std::move(result)};
1453   } else {
1454     return std::nullopt;
1455   }
1456 }
1457 
1458 std::optional<ArraySpec> ToArraySpec(evaluate::FoldingContext &context,
1459     const std::optional<evaluate::Shape> &shape) {
1460   return shape ? ToArraySpec(context, *shape) : std::nullopt;
1461 }
1462 
1463 } // namespace Fortran::semantics
1464