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