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