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