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