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