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