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