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   return symbol.attrs().test(Attr::PURE) ||
274       (symbol.attrs().test(Attr::ELEMENTAL) &&
275           !symbol.attrs().test(Attr::IMPURE));
276 }
277 
278 bool IsPureProcedure(const Scope &scope) {
279   if (const Symbol * symbol{scope.GetSymbol()}) {
280     return IsPureProcedure(*symbol);
281   } else {
282     return false;
283   }
284 }
285 
286 bool IsBindCProcedure(const Symbol &symbol) {
287   if (const auto *procDetails{symbol.detailsIf<ProcEntityDetails>()}) {
288     if (const Symbol * procInterface{procDetails->interface().symbol()}) {
289       // procedure component with a BIND(C) interface
290       return IsBindCProcedure(*procInterface);
291     }
292   }
293   return symbol.attrs().test(Attr::BIND_C) && IsProcedure(symbol);
294 }
295 
296 bool IsBindCProcedure(const Scope &scope) {
297   if (const Symbol * symbol{scope.GetSymbol()}) {
298     return IsBindCProcedure(*symbol);
299   } else {
300     return false;
301   }
302 }
303 
304 bool IsProcedure(const Symbol &symbol) {
305   return std::visit(
306       common::visitors{
307           [](const SubprogramDetails &) { return true; },
308           [](const SubprogramNameDetails &) { return true; },
309           [](const ProcEntityDetails &) { return true; },
310           [](const GenericDetails &) { return true; },
311           [](const ProcBindingDetails &) { return true; },
312           [](const UseDetails &x) { return IsProcedure(x.symbol()); },
313           // TODO: FinalProcDetails?
314           [](const auto &) { return false; },
315       },
316       symbol.details());
317 }
318 
319 bool IsProcedurePointer(const Symbol &symbol) {
320   return symbol.has<ProcEntityDetails>() && IsPointer(symbol);
321 }
322 
323 static const Symbol *FindPointerComponent(
324     const Scope &scope, std::set<const Scope *> &visited) {
325   if (!scope.IsDerivedType()) {
326     return nullptr;
327   }
328   if (!visited.insert(&scope).second) {
329     return nullptr;
330   }
331   // If there's a top-level pointer component, return it for clearer error
332   // messaging.
333   for (const auto &pair : scope) {
334     const Symbol &symbol{*pair.second};
335     if (IsPointer(symbol)) {
336       return &symbol;
337     }
338   }
339   for (const auto &pair : scope) {
340     const Symbol &symbol{*pair.second};
341     if (const auto *details{symbol.detailsIf<ObjectEntityDetails>()}) {
342       if (const DeclTypeSpec * type{details->type()}) {
343         if (const DerivedTypeSpec * derived{type->AsDerived()}) {
344           if (const Scope * nested{derived->scope()}) {
345             if (const Symbol *
346                 pointer{FindPointerComponent(*nested, visited)}) {
347               return pointer;
348             }
349           }
350         }
351       }
352     }
353   }
354   return nullptr;
355 }
356 
357 const Symbol *FindPointerComponent(const Scope &scope) {
358   std::set<const Scope *> visited;
359   return FindPointerComponent(scope, visited);
360 }
361 
362 const Symbol *FindPointerComponent(const DerivedTypeSpec &derived) {
363   if (const Scope * scope{derived.scope()}) {
364     return FindPointerComponent(*scope);
365   } else {
366     return nullptr;
367   }
368 }
369 
370 const Symbol *FindPointerComponent(const DeclTypeSpec &type) {
371   if (const DerivedTypeSpec * derived{type.AsDerived()}) {
372     return FindPointerComponent(*derived);
373   } else {
374     return nullptr;
375   }
376 }
377 
378 const Symbol *FindPointerComponent(const DeclTypeSpec *type) {
379   return type ? FindPointerComponent(*type) : nullptr;
380 }
381 
382 const Symbol *FindPointerComponent(const Symbol &symbol) {
383   return IsPointer(symbol) ? &symbol : FindPointerComponent(symbol.GetType());
384 }
385 
386 // C1594 specifies several ways by which an object might be globally visible.
387 const Symbol *FindExternallyVisibleObject(
388     const Symbol &object, const Scope &scope) {
389   // TODO: Storage association with any object for which this predicate holds,
390   // once EQUIVALENCE is supported.
391   if (IsUseAssociated(object, scope) || IsHostAssociated(object, scope) ||
392       (IsPureProcedure(scope) && IsPointerDummy(object)) ||
393       (IsIntentIn(object) && IsDummy(object))) {
394     return &object;
395   } else if (const Symbol * block{FindCommonBlockContaining(object)}) {
396     return block;
397   } else {
398     return nullptr;
399   }
400 }
401 
402 bool ExprHasTypeCategory(
403     const SomeExpr &expr, const common::TypeCategory &type) {
404   auto dynamicType{expr.GetType()};
405   return dynamicType && dynamicType->category() == type;
406 }
407 
408 bool ExprTypeKindIsDefault(
409     const SomeExpr &expr, const SemanticsContext &context) {
410   auto dynamicType{expr.GetType()};
411   return dynamicType &&
412       dynamicType->category() != common::TypeCategory::Derived &&
413       dynamicType->kind() == context.GetDefaultKind(dynamicType->category());
414 }
415 
416 // If an analyzed expr or assignment is missing, dump the node and die.
417 template <typename T>
418 static void CheckMissingAnalysis(bool absent, const T &x) {
419   if (absent) {
420     std::string buf;
421     llvm::raw_string_ostream ss{buf};
422     ss << "node has not been analyzed:\n";
423     parser::DumpTree(ss, x);
424     common::die(ss.str().c_str());
425   }
426 }
427 
428 const SomeExpr *GetExprHelper::Get(const parser::Expr &x) {
429   CheckMissingAnalysis(!x.typedExpr, x);
430   return common::GetPtrFromOptional(x.typedExpr->v);
431 }
432 const SomeExpr *GetExprHelper::Get(const parser::Variable &x) {
433   CheckMissingAnalysis(!x.typedExpr, x);
434   return common::GetPtrFromOptional(x.typedExpr->v);
435 }
436 
437 const evaluate::Assignment *GetAssignment(const parser::AssignmentStmt &x) {
438   CheckMissingAnalysis(!x.typedAssignment, x);
439   return common::GetPtrFromOptional(x.typedAssignment->v);
440 }
441 const evaluate::Assignment *GetAssignment(
442     const parser::PointerAssignmentStmt &x) {
443   CheckMissingAnalysis(!x.typedAssignment, x);
444   return common::GetPtrFromOptional(x.typedAssignment->v);
445 }
446 
447 const Symbol *FindInterface(const Symbol &symbol) {
448   return std::visit(
449       common::visitors{
450           [](const ProcEntityDetails &details) {
451             return details.interface().symbol();
452           },
453           [](const ProcBindingDetails &details) { return &details.symbol(); },
454           [](const auto &) -> const Symbol * { return nullptr; },
455       },
456       symbol.details());
457 }
458 
459 const Symbol *FindSubprogram(const Symbol &symbol) {
460   return std::visit(
461       common::visitors{
462           [&](const ProcEntityDetails &details) -> const Symbol * {
463             if (const Symbol * interface{details.interface().symbol()}) {
464               return FindSubprogram(*interface);
465             } else {
466               return &symbol;
467             }
468           },
469           [](const ProcBindingDetails &details) {
470             return FindSubprogram(details.symbol());
471           },
472           [&](const SubprogramDetails &) { return &symbol; },
473           [](const UseDetails &details) {
474             return FindSubprogram(details.symbol());
475           },
476           [](const HostAssocDetails &details) {
477             return FindSubprogram(details.symbol());
478           },
479           [](const auto &) -> const Symbol * { return nullptr; },
480       },
481       symbol.details());
482 }
483 
484 const Symbol *FindFunctionResult(const Symbol &symbol) {
485   if (const Symbol * subp{FindSubprogram(symbol)}) {
486     if (const auto &subpDetails{subp->detailsIf<SubprogramDetails>()}) {
487       if (subpDetails->isFunction()) {
488         return &subpDetails->result();
489       }
490     }
491   }
492   return nullptr;
493 }
494 
495 const Symbol *FindOverriddenBinding(const Symbol &symbol) {
496   if (symbol.has<ProcBindingDetails>()) {
497     if (const DeclTypeSpec * parentType{FindParentTypeSpec(symbol.owner())}) {
498       if (const DerivedTypeSpec * parentDerived{parentType->AsDerived()}) {
499         if (const Scope * parentScope{parentDerived->typeSymbol().scope()}) {
500           return parentScope->FindComponent(symbol.name());
501         }
502       }
503     }
504   }
505   return nullptr;
506 }
507 
508 const DeclTypeSpec *FindParentTypeSpec(const DerivedTypeSpec &derived) {
509   return FindParentTypeSpec(derived.typeSymbol());
510 }
511 
512 const DeclTypeSpec *FindParentTypeSpec(const DeclTypeSpec &decl) {
513   if (const DerivedTypeSpec * derived{decl.AsDerived()}) {
514     return FindParentTypeSpec(*derived);
515   } else {
516     return nullptr;
517   }
518 }
519 
520 const DeclTypeSpec *FindParentTypeSpec(const Scope &scope) {
521   if (scope.kind() == Scope::Kind::DerivedType) {
522     if (const auto *symbol{scope.symbol()}) {
523       return FindParentTypeSpec(*symbol);
524     }
525   }
526   return nullptr;
527 }
528 
529 const DeclTypeSpec *FindParentTypeSpec(const Symbol &symbol) {
530   if (const Scope * scope{symbol.scope()}) {
531     if (const auto *details{symbol.detailsIf<DerivedTypeDetails>()}) {
532       if (const Symbol * parent{details->GetParentComponent(*scope)}) {
533         return parent->GetType();
534       }
535     }
536   }
537   return nullptr;
538 }
539 
540 // When a construct association maps to a variable, and that variable
541 // is not an array with a vector-valued subscript, return the base
542 // Symbol of that variable, else nullptr.  Descends into other construct
543 // associations when one associations maps to another.
544 static const Symbol *GetAssociatedVariable(const AssocEntityDetails &details) {
545   if (const MaybeExpr & expr{details.expr()}) {
546     if (evaluate::IsVariable(*expr) && !evaluate::HasVectorSubscript(*expr)) {
547       if (const Symbol * varSymbol{evaluate::GetFirstSymbol(*expr)}) {
548         return GetAssociationRoot(*varSymbol);
549       }
550     }
551   }
552   return nullptr;
553 }
554 
555 // Return the Symbol of the variable of a construct association, if it exists
556 // Return nullptr if the name is associated with an expression
557 const Symbol *GetAssociationRoot(const Symbol &symbol) {
558   const Symbol &ultimate{symbol.GetUltimate()};
559   if (const auto *details{ultimate.detailsIf<AssocEntityDetails>()}) {
560     // We have a construct association
561     return GetAssociatedVariable(*details);
562   } else {
563     return &ultimate;
564   }
565 }
566 
567 bool IsExtensibleType(const DerivedTypeSpec *derived) {
568   return derived && !IsIsoCType(derived) &&
569       !derived->typeSymbol().attrs().test(Attr::BIND_C) &&
570       !derived->typeSymbol().get<DerivedTypeDetails>().sequence();
571 }
572 
573 bool IsBuiltinDerivedType(const DerivedTypeSpec *derived, const char *name) {
574   if (!derived) {
575     return false;
576   } else {
577     const auto &symbol{derived->typeSymbol()};
578     return symbol.owner().IsModule() &&
579         symbol.owner().GetName().value() == "__fortran_builtins" &&
580         symbol.name() == "__builtin_"s + name;
581   }
582 }
583 
584 bool IsIsoCType(const DerivedTypeSpec *derived) {
585   return IsBuiltinDerivedType(derived, "c_ptr") ||
586       IsBuiltinDerivedType(derived, "c_funptr");
587 }
588 
589 bool IsTeamType(const DerivedTypeSpec *derived) {
590   return IsBuiltinDerivedType(derived, "team_type");
591 }
592 
593 bool IsEventTypeOrLockType(const DerivedTypeSpec *derivedTypeSpec) {
594   return IsBuiltinDerivedType(derivedTypeSpec, "event_type") ||
595       IsBuiltinDerivedType(derivedTypeSpec, "lock_type");
596 }
597 
598 bool IsOrContainsEventOrLockComponent(const Symbol &symbol) {
599   if (const Symbol * root{GetAssociationRoot(symbol)}) {
600     if (const auto *details{root->detailsIf<ObjectEntityDetails>()}) {
601       if (const DeclTypeSpec * type{details->type()}) {
602         if (const DerivedTypeSpec * derived{type->AsDerived()}) {
603           return IsEventTypeOrLockType(derived) ||
604               FindEventOrLockPotentialComponent(*derived);
605         }
606       }
607     }
608   }
609   return false;
610 }
611 
612 bool IsSaved(const Symbol &symbol) {
613   auto scopeKind{symbol.owner().kind()};
614   if (scopeKind == Scope::Kind::Module || scopeKind == Scope::Kind::BlockData) {
615     return true;
616   } else if (scopeKind == Scope::Kind::DerivedType) {
617     return false; // this is a component
618   } else if (IsNamedConstant(symbol)) {
619     return false;
620   } else if (symbol.attrs().test(Attr::SAVE)) {
621     return true;
622   } else {
623     if (const auto *object{symbol.detailsIf<ObjectEntityDetails>()}) {
624       if (object->init()) {
625         return true;
626       }
627     } else if (IsProcedurePointer(symbol)) {
628       if (symbol.get<ProcEntityDetails>().init()) {
629         return true;
630       }
631     }
632     if (const Symbol * block{FindCommonBlockContaining(symbol)}) {
633       if (block->attrs().test(Attr::SAVE)) {
634         return true;
635       }
636     }
637     return false;
638   }
639 }
640 
641 // Check this symbol suitable as a type-bound procedure - C769
642 bool CanBeTypeBoundProc(const Symbol *symbol) {
643   if (!symbol || IsDummy(*symbol) || IsProcedurePointer(*symbol)) {
644     return false;
645   } else if (symbol->has<SubprogramNameDetails>()) {
646     return symbol->owner().kind() == Scope::Kind::Module;
647   } else if (auto *details{symbol->detailsIf<SubprogramDetails>()}) {
648     return symbol->owner().kind() == Scope::Kind::Module ||
649         details->isInterface();
650   } else if (const auto *proc{symbol->detailsIf<ProcEntityDetails>()}) {
651     return !symbol->attrs().test(Attr::INTRINSIC) &&
652         proc->HasExplicitInterface();
653   } else {
654     return false;
655   }
656 }
657 
658 bool IsInitialized(const Symbol &symbol) {
659   if (symbol.test(Symbol::Flag::InDataStmt)) {
660     return true;
661   } else if (IsNamedConstant(symbol)) {
662     return false;
663   } else if (const auto *object{symbol.detailsIf<ObjectEntityDetails>()}) {
664     if (IsAllocatable(symbol) || object->init()) {
665       return true;
666     }
667     if (!IsPointer(symbol) && object->type()) {
668       if (const auto *derived{object->type()->AsDerived()}) {
669         if (derived->HasDefaultInitialization()) {
670           return true;
671         }
672       }
673     }
674   } else if (const auto *proc{symbol.detailsIf<ProcEntityDetails>()}) {
675     return proc->init().has_value();
676   }
677   return false;
678 }
679 
680 bool HasIntrinsicTypeName(const Symbol &symbol) {
681   std::string name{symbol.name().ToString()};
682   if (name == "doubleprecision") {
683     return true;
684   } else if (name == "derived") {
685     return false;
686   } else {
687     for (int i{0}; i != common::TypeCategory_enumSize; ++i) {
688       if (name == parser::ToLowerCaseLetters(EnumToString(TypeCategory{i}))) {
689         return true;
690       }
691     }
692     return false;
693   }
694 }
695 
696 bool IsSeparateModuleProcedureInterface(const Symbol *symbol) {
697   if (symbol && symbol->attrs().test(Attr::MODULE)) {
698     if (auto *details{symbol->detailsIf<SubprogramDetails>()}) {
699       return details->isInterface();
700     }
701   }
702   return false;
703 }
704 
705 bool IsFinalizable(const Symbol &symbol) {
706   if (const DeclTypeSpec * type{symbol.GetType()}) {
707     if (const DerivedTypeSpec * derived{type->AsDerived()}) {
708       return IsFinalizable(*derived);
709     }
710   }
711   return false;
712 }
713 
714 bool IsFinalizable(const DerivedTypeSpec &derived) {
715   ScopeComponentIterator components{derived};
716   return std::find_if(components.begin(), components.end(),
717              [](const Symbol &x) { return x.has<FinalProcDetails>(); }) !=
718       components.end();
719 }
720 
721 // TODO The following function returns true for all types with FINAL procedures
722 // This is because we don't yet fill in the data for FinalProcDetails
723 bool HasImpureFinal(const DerivedTypeSpec &derived) {
724   ScopeComponentIterator components{derived};
725   return std::find_if(
726              components.begin(), components.end(), [](const Symbol &x) {
727                return x.has<FinalProcDetails>() && !x.attrs().test(Attr::PURE);
728              }) != components.end();
729 }
730 
731 bool IsCoarray(const Symbol &symbol) { return symbol.Corank() > 0; }
732 
733 bool IsAssumedLengthCharacter(const Symbol &symbol) {
734   if (const DeclTypeSpec * type{symbol.GetType()}) {
735     return type->category() == DeclTypeSpec::Character &&
736         type->characterTypeSpec().length().isAssumed();
737   } else {
738     return false;
739   }
740 }
741 
742 // C722 and C723:  For a function to be assumed length, it must be external and
743 // of CHARACTER type
744 bool IsExternal(const Symbol &symbol) {
745   return (symbol.has<SubprogramDetails>() && symbol.owner().IsGlobal()) ||
746       symbol.attrs().test(Attr::EXTERNAL);
747 }
748 
749 const Symbol *IsExternalInPureContext(
750     const Symbol &symbol, const Scope &scope) {
751   if (const auto *pureProc{FindPureProcedureContaining(scope)}) {
752     if (const Symbol * root{GetAssociationRoot(symbol)}) {
753       if (const Symbol *
754           visible{FindExternallyVisibleObject(*root, *pureProc)}) {
755         return visible;
756       }
757     }
758   }
759   return nullptr;
760 }
761 
762 PotentialComponentIterator::const_iterator FindPolymorphicPotentialComponent(
763     const DerivedTypeSpec &derived) {
764   PotentialComponentIterator potentials{derived};
765   return std::find_if(
766       potentials.begin(), potentials.end(), [](const Symbol &component) {
767         if (const auto *details{component.detailsIf<ObjectEntityDetails>()}) {
768           const DeclTypeSpec *type{details->type()};
769           return type && type->IsPolymorphic();
770         }
771         return false;
772       });
773 }
774 
775 bool IsOrContainsPolymorphicComponent(const Symbol &symbol) {
776   if (const Symbol * root{GetAssociationRoot(symbol)}) {
777     if (const auto *details{root->detailsIf<ObjectEntityDetails>()}) {
778       if (const DeclTypeSpec * type{details->type()}) {
779         if (type->IsPolymorphic()) {
780           return true;
781         }
782         if (const DerivedTypeSpec * derived{type->AsDerived()}) {
783           return (bool)FindPolymorphicPotentialComponent(*derived);
784         }
785       }
786     }
787   }
788   return false;
789 }
790 
791 bool InProtectedContext(const Symbol &symbol, const Scope &currentScope) {
792   return IsProtected(symbol) && !IsHostAssociated(symbol, currentScope);
793 }
794 
795 // C1101 and C1158
796 // TODO Need to check for a coindexed object (why? C1103?)
797 std::optional<parser::MessageFixedText> WhyNotModifiable(
798     const Symbol &symbol, const Scope &scope) {
799   const Symbol *root{GetAssociationRoot(symbol)};
800   if (!root) {
801     return "'%s' is construct associated with an expression"_en_US;
802   } else if (InProtectedContext(*root, scope)) {
803     return "'%s' is protected in this scope"_en_US;
804   } else if (IsExternalInPureContext(*root, scope)) {
805     return "'%s' is externally visible and referenced in a pure"
806            " procedure"_en_US;
807   } else if (IsOrContainsEventOrLockComponent(*root)) {
808     return "'%s' is an entity with either an EVENT_TYPE or LOCK_TYPE"_en_US;
809   } else if (IsIntentIn(*root)) {
810     return "'%s' is an INTENT(IN) dummy argument"_en_US;
811   } else if (!IsVariableName(*root)) {
812     return "'%s' is not a variable"_en_US;
813   } else {
814     return std::nullopt;
815   }
816 }
817 
818 std::optional<parser::Message> WhyNotModifiable(parser::CharBlock at,
819     const SomeExpr &expr, const Scope &scope, bool vectorSubscriptIsOk) {
820   if (!evaluate::IsVariable(expr)) {
821     return parser::Message{at, "Expression is not a variable"_en_US};
822   } else if (auto dataRef{evaluate::ExtractDataRef(expr, true)}) {
823     if (!vectorSubscriptIsOk && evaluate::HasVectorSubscript(expr)) {
824       return parser::Message{at, "Variable has a vector subscript"_en_US};
825     }
826     const Symbol &symbol{dataRef->GetFirstSymbol()};
827     if (auto maybeWhy{WhyNotModifiable(symbol, scope)}) {
828       return parser::Message{symbol.name(),
829           parser::MessageFormattedText{std::move(*maybeWhy), symbol.name()}};
830     }
831   } else {
832     // reference to function returning POINTER
833   }
834   return std::nullopt;
835 }
836 
837 class ImageControlStmtHelper {
838   using ImageControlStmts = std::variant<parser::ChangeTeamConstruct,
839       parser::CriticalConstruct, parser::EventPostStmt, parser::EventWaitStmt,
840       parser::FormTeamStmt, parser::LockStmt, parser::StopStmt,
841       parser::SyncAllStmt, parser::SyncImagesStmt, parser::SyncMemoryStmt,
842       parser::SyncTeamStmt, parser::UnlockStmt>;
843 
844 public:
845   template <typename T> bool operator()(const T &) {
846     return common::HasMember<T, ImageControlStmts>;
847   }
848   template <typename T> bool operator()(const common::Indirection<T> &x) {
849     return (*this)(x.value());
850   }
851   bool operator()(const parser::AllocateStmt &stmt) {
852     const auto &allocationList{std::get<std::list<parser::Allocation>>(stmt.t)};
853     for (const auto &allocation : allocationList) {
854       const auto &allocateObject{
855           std::get<parser::AllocateObject>(allocation.t)};
856       if (IsCoarrayObject(allocateObject)) {
857         return true;
858       }
859     }
860     return false;
861   }
862   bool operator()(const parser::DeallocateStmt &stmt) {
863     const auto &allocateObjectList{
864         std::get<std::list<parser::AllocateObject>>(stmt.t)};
865     for (const auto &allocateObject : allocateObjectList) {
866       if (IsCoarrayObject(allocateObject)) {
867         return true;
868       }
869     }
870     return false;
871   }
872   bool operator()(const parser::CallStmt &stmt) {
873     const auto &procedureDesignator{
874         std::get<parser::ProcedureDesignator>(stmt.v.t)};
875     if (auto *name{std::get_if<parser::Name>(&procedureDesignator.u)}) {
876       // TODO: also ensure that the procedure is, in fact, an intrinsic
877       if (name->source == "move_alloc") {
878         const auto &args{std::get<std::list<parser::ActualArgSpec>>(stmt.v.t)};
879         if (!args.empty()) {
880           const parser::ActualArg &actualArg{
881               std::get<parser::ActualArg>(args.front().t)};
882           if (const auto *argExpr{
883                   std::get_if<common::Indirection<parser::Expr>>(
884                       &actualArg.u)}) {
885             return HasCoarray(argExpr->value());
886           }
887         }
888       }
889     }
890     return false;
891   }
892   bool operator()(const parser::Statement<parser::ActionStmt> &stmt) {
893     return std::visit(*this, stmt.statement.u);
894   }
895 
896 private:
897   bool IsCoarrayObject(const parser::AllocateObject &allocateObject) {
898     const parser::Name &name{GetLastName(allocateObject)};
899     return name.symbol && IsCoarray(*name.symbol);
900   }
901 };
902 
903 bool IsImageControlStmt(const parser::ExecutableConstruct &construct) {
904   return std::visit(ImageControlStmtHelper{}, construct.u);
905 }
906 
907 std::optional<parser::MessageFixedText> GetImageControlStmtCoarrayMsg(
908     const parser::ExecutableConstruct &construct) {
909   if (const auto *actionStmt{
910           std::get_if<parser::Statement<parser::ActionStmt>>(&construct.u)}) {
911     return std::visit(
912         common::visitors{
913             [](const common::Indirection<parser::AllocateStmt> &)
914                 -> std::optional<parser::MessageFixedText> {
915               return "ALLOCATE of a coarray is an image control"
916                      " statement"_en_US;
917             },
918             [](const common::Indirection<parser::DeallocateStmt> &)
919                 -> std::optional<parser::MessageFixedText> {
920               return "DEALLOCATE of a coarray is an image control"
921                      " statement"_en_US;
922             },
923             [](const common::Indirection<parser::CallStmt> &)
924                 -> std::optional<parser::MessageFixedText> {
925               return "MOVE_ALLOC of a coarray is an image control"
926                      " statement "_en_US;
927             },
928             [](const auto &) -> std::optional<parser::MessageFixedText> {
929               return std::nullopt;
930             },
931         },
932         actionStmt->statement.u);
933   }
934   return std::nullopt;
935 }
936 
937 parser::CharBlock GetImageControlStmtLocation(
938     const parser::ExecutableConstruct &executableConstruct) {
939   return std::visit(
940       common::visitors{
941           [](const common::Indirection<parser::ChangeTeamConstruct>
942                   &construct) {
943             return std::get<parser::Statement<parser::ChangeTeamStmt>>(
944                 construct.value().t)
945                 .source;
946           },
947           [](const common::Indirection<parser::CriticalConstruct> &construct) {
948             return std::get<parser::Statement<parser::CriticalStmt>>(
949                 construct.value().t)
950                 .source;
951           },
952           [](const parser::Statement<parser::ActionStmt> &actionStmt) {
953             return actionStmt.source;
954           },
955           [](const auto &) { return parser::CharBlock{}; },
956       },
957       executableConstruct.u);
958 }
959 
960 bool HasCoarray(const parser::Expr &expression) {
961   if (const auto *expr{GetExpr(expression)}) {
962     for (const Symbol &symbol : evaluate::CollectSymbols(*expr)) {
963       if (const Symbol * root{GetAssociationRoot(symbol)}) {
964         if (IsCoarray(*root)) {
965           return true;
966         }
967       }
968     }
969   }
970   return false;
971 }
972 
973 bool IsPolymorphic(const Symbol &symbol) {
974   if (const DeclTypeSpec * type{symbol.GetType()}) {
975     return type->IsPolymorphic();
976   }
977   return false;
978 }
979 
980 bool IsPolymorphicAllocatable(const Symbol &symbol) {
981   return IsAllocatable(symbol) && IsPolymorphic(symbol);
982 }
983 
984 std::optional<parser::MessageFormattedText> CheckAccessibleComponent(
985     const Scope &scope, const Symbol &symbol) {
986   CHECK(symbol.owner().IsDerivedType()); // symbol must be a component
987   if (symbol.attrs().test(Attr::PRIVATE)) {
988     if (const Scope * moduleScope{FindModuleContaining(symbol.owner())}) {
989       if (!moduleScope->Contains(scope)) {
990         return parser::MessageFormattedText{
991             "PRIVATE component '%s' is only accessible within module '%s'"_err_en_US,
992             symbol.name(), moduleScope->GetName().value()};
993       }
994     }
995   }
996   return std::nullopt;
997 }
998 
999 std::list<SourceName> OrderParameterNames(const Symbol &typeSymbol) {
1000   std::list<SourceName> result;
1001   if (const DerivedTypeSpec * spec{typeSymbol.GetParentTypeSpec()}) {
1002     result = OrderParameterNames(spec->typeSymbol());
1003   }
1004   const auto &paramNames{typeSymbol.get<DerivedTypeDetails>().paramNames()};
1005   result.insert(result.end(), paramNames.begin(), paramNames.end());
1006   return result;
1007 }
1008 
1009 SymbolVector OrderParameterDeclarations(const Symbol &typeSymbol) {
1010   SymbolVector result;
1011   if (const DerivedTypeSpec * spec{typeSymbol.GetParentTypeSpec()}) {
1012     result = OrderParameterDeclarations(spec->typeSymbol());
1013   }
1014   const auto &paramDecls{typeSymbol.get<DerivedTypeDetails>().paramDecls()};
1015   result.insert(result.end(), paramDecls.begin(), paramDecls.end());
1016   return result;
1017 }
1018 
1019 const DeclTypeSpec &FindOrInstantiateDerivedType(Scope &scope,
1020     DerivedTypeSpec &&spec, SemanticsContext &semanticsContext,
1021     DeclTypeSpec::Category category) {
1022   spec.CookParameters(semanticsContext.foldingContext());
1023   spec.EvaluateParameters(semanticsContext.foldingContext());
1024   if (const DeclTypeSpec *
1025       type{scope.FindInstantiatedDerivedType(spec, category)}) {
1026     return *type;
1027   }
1028   // Create a new instantiation of this parameterized derived type
1029   // for this particular distinct set of actual parameter values.
1030   DeclTypeSpec &type{scope.MakeDerivedType(category, std::move(spec))};
1031   type.derivedTypeSpec().Instantiate(scope, semanticsContext);
1032   return type;
1033 }
1034 
1035 const Symbol *FindSeparateModuleSubprogramInterface(const Symbol *proc) {
1036   if (proc) {
1037     if (const Symbol * submodule{proc->owner().symbol()}) {
1038       if (const auto *details{submodule->detailsIf<ModuleDetails>()}) {
1039         if (const Scope * ancestor{details->ancestor()}) {
1040           const Symbol *iface{ancestor->FindSymbol(proc->name())};
1041           if (IsSeparateModuleProcedureInterface(iface)) {
1042             return iface;
1043           }
1044         }
1045       }
1046     }
1047   }
1048   return nullptr;
1049 }
1050 
1051 // ComponentIterator implementation
1052 
1053 template <ComponentKind componentKind>
1054 typename ComponentIterator<componentKind>::const_iterator
1055 ComponentIterator<componentKind>::const_iterator::Create(
1056     const DerivedTypeSpec &derived) {
1057   const_iterator it{};
1058   it.componentPath_.emplace_back(derived);
1059   it.Increment(); // cue up first relevant component, if any
1060   return it;
1061 }
1062 
1063 template <ComponentKind componentKind>
1064 const DerivedTypeSpec *
1065 ComponentIterator<componentKind>::const_iterator::PlanComponentTraversal(
1066     const Symbol &component) const {
1067   if (const auto *details{component.detailsIf<ObjectEntityDetails>()}) {
1068     if (const DeclTypeSpec * type{details->type()}) {
1069       if (const auto *derived{type->AsDerived()}) {
1070         bool traverse{false};
1071         if constexpr (componentKind == ComponentKind::Ordered) {
1072           // Order Component (only visit parents)
1073           traverse = component.test(Symbol::Flag::ParentComp);
1074         } else if constexpr (componentKind == ComponentKind::Direct) {
1075           traverse = !IsAllocatableOrPointer(component);
1076         } else if constexpr (componentKind == ComponentKind::Ultimate) {
1077           traverse = !IsAllocatableOrPointer(component);
1078         } else if constexpr (componentKind == ComponentKind::Potential) {
1079           traverse = !IsPointer(component);
1080         } else if constexpr (componentKind == ComponentKind::Scope) {
1081           traverse = !IsAllocatableOrPointer(component);
1082         }
1083         if (traverse) {
1084           const Symbol &newTypeSymbol{derived->typeSymbol()};
1085           // Avoid infinite loop if the type is already part of the types
1086           // being visited. It is possible to have "loops in type" because
1087           // C744 does not forbid to use not yet declared type for
1088           // ALLOCATABLE or POINTER components.
1089           for (const auto &node : componentPath_) {
1090             if (&newTypeSymbol == &node.GetTypeSymbol()) {
1091               return nullptr;
1092             }
1093           }
1094           return derived;
1095         }
1096       }
1097     } // intrinsic & unlimited polymorphic not traversable
1098   }
1099   return nullptr;
1100 }
1101 
1102 template <ComponentKind componentKind>
1103 static bool StopAtComponentPre(const Symbol &component) {
1104   if constexpr (componentKind == ComponentKind::Ordered) {
1105     // Parent components need to be iterated upon after their
1106     // sub-components in structure constructor analysis.
1107     return !component.test(Symbol::Flag::ParentComp);
1108   } else if constexpr (componentKind == ComponentKind::Direct) {
1109     return true;
1110   } else if constexpr (componentKind == ComponentKind::Ultimate) {
1111     return component.has<ProcEntityDetails>() ||
1112         IsAllocatableOrPointer(component) ||
1113         (component.get<ObjectEntityDetails>().type() &&
1114             component.get<ObjectEntityDetails>().type()->AsIntrinsic());
1115   } else if constexpr (componentKind == ComponentKind::Potential) {
1116     return !IsPointer(component);
1117   }
1118 }
1119 
1120 template <ComponentKind componentKind>
1121 static bool StopAtComponentPost(const Symbol &component) {
1122   return componentKind == ComponentKind::Ordered &&
1123       component.test(Symbol::Flag::ParentComp);
1124 }
1125 
1126 template <ComponentKind componentKind>
1127 void ComponentIterator<componentKind>::const_iterator::Increment() {
1128   while (!componentPath_.empty()) {
1129     ComponentPathNode &deepest{componentPath_.back()};
1130     if (deepest.component()) {
1131       if (!deepest.descended()) {
1132         deepest.set_descended(true);
1133         if (const DerivedTypeSpec *
1134             derived{PlanComponentTraversal(*deepest.component())}) {
1135           componentPath_.emplace_back(*derived);
1136           continue;
1137         }
1138       } else if (!deepest.visited()) {
1139         deepest.set_visited(true);
1140         return; // this is the next component to visit, after descending
1141       }
1142     }
1143     auto &nameIterator{deepest.nameIterator()};
1144     if (nameIterator == deepest.nameEnd()) {
1145       componentPath_.pop_back();
1146     } else if constexpr (componentKind == ComponentKind::Scope) {
1147       deepest.set_component(*nameIterator++->second);
1148       deepest.set_descended(false);
1149       deepest.set_visited(true);
1150       return; // this is the next component to visit, before descending
1151     } else {
1152       const Scope &scope{deepest.GetScope()};
1153       auto scopeIter{scope.find(*nameIterator++)};
1154       if (scopeIter != scope.cend()) {
1155         const Symbol &component{*scopeIter->second};
1156         deepest.set_component(component);
1157         deepest.set_descended(false);
1158         if (StopAtComponentPre<componentKind>(component)) {
1159           deepest.set_visited(true);
1160           return; // this is the next component to visit, before descending
1161         } else {
1162           deepest.set_visited(!StopAtComponentPost<componentKind>(component));
1163         }
1164       }
1165     }
1166   }
1167 }
1168 
1169 template <ComponentKind componentKind>
1170 std::string
1171 ComponentIterator<componentKind>::const_iterator::BuildResultDesignatorName()
1172     const {
1173   std::string designator{""};
1174   for (const auto &node : componentPath_) {
1175     designator += "%" + DEREF(node.component()).name().ToString();
1176   }
1177   return designator;
1178 }
1179 
1180 template class ComponentIterator<ComponentKind::Ordered>;
1181 template class ComponentIterator<ComponentKind::Direct>;
1182 template class ComponentIterator<ComponentKind::Ultimate>;
1183 template class ComponentIterator<ComponentKind::Potential>;
1184 template class ComponentIterator<ComponentKind::Scope>;
1185 
1186 UltimateComponentIterator::const_iterator FindCoarrayUltimateComponent(
1187     const DerivedTypeSpec &derived) {
1188   UltimateComponentIterator ultimates{derived};
1189   return std::find_if(ultimates.begin(), ultimates.end(), IsCoarray);
1190 }
1191 
1192 UltimateComponentIterator::const_iterator FindPointerUltimateComponent(
1193     const DerivedTypeSpec &derived) {
1194   UltimateComponentIterator ultimates{derived};
1195   return std::find_if(ultimates.begin(), ultimates.end(), IsPointer);
1196 }
1197 
1198 PotentialComponentIterator::const_iterator FindEventOrLockPotentialComponent(
1199     const DerivedTypeSpec &derived) {
1200   PotentialComponentIterator potentials{derived};
1201   return std::find_if(
1202       potentials.begin(), potentials.end(), [](const Symbol &component) {
1203         if (const auto *details{component.detailsIf<ObjectEntityDetails>()}) {
1204           const DeclTypeSpec *type{details->type()};
1205           return type && IsEventTypeOrLockType(type->AsDerived());
1206         }
1207         return false;
1208       });
1209 }
1210 
1211 UltimateComponentIterator::const_iterator FindAllocatableUltimateComponent(
1212     const DerivedTypeSpec &derived) {
1213   UltimateComponentIterator ultimates{derived};
1214   return std::find_if(ultimates.begin(), ultimates.end(), IsAllocatable);
1215 }
1216 
1217 UltimateComponentIterator::const_iterator
1218 FindPolymorphicAllocatableUltimateComponent(const DerivedTypeSpec &derived) {
1219   UltimateComponentIterator ultimates{derived};
1220   return std::find_if(
1221       ultimates.begin(), ultimates.end(), IsPolymorphicAllocatable);
1222 }
1223 
1224 UltimateComponentIterator::const_iterator
1225 FindPolymorphicAllocatableNonCoarrayUltimateComponent(
1226     const DerivedTypeSpec &derived) {
1227   UltimateComponentIterator ultimates{derived};
1228   return std::find_if(ultimates.begin(), ultimates.end(), [](const Symbol &x) {
1229     return IsPolymorphicAllocatable(x) && !IsCoarray(x);
1230   });
1231 }
1232 
1233 const Symbol *FindUltimateComponent(const DerivedTypeSpec &derived,
1234     const std::function<bool(const Symbol &)> &predicate) {
1235   UltimateComponentIterator ultimates{derived};
1236   if (auto it{std::find_if(ultimates.begin(), ultimates.end(),
1237           [&predicate](const Symbol &component) -> bool {
1238             return predicate(component);
1239           })}) {
1240     return &*it;
1241   }
1242   return nullptr;
1243 }
1244 
1245 const Symbol *FindUltimateComponent(const Symbol &symbol,
1246     const std::function<bool(const Symbol &)> &predicate) {
1247   if (predicate(symbol)) {
1248     return &symbol;
1249   } else if (const auto *object{symbol.detailsIf<ObjectEntityDetails>()}) {
1250     if (const auto *type{object->type()}) {
1251       if (const auto *derived{type->AsDerived()}) {
1252         return FindUltimateComponent(*derived, predicate);
1253       }
1254     }
1255   }
1256   return nullptr;
1257 }
1258 
1259 const Symbol *FindImmediateComponent(const DerivedTypeSpec &type,
1260     const std::function<bool(const Symbol &)> &predicate) {
1261   if (const Scope * scope{type.scope()}) {
1262     const Symbol *parent{nullptr};
1263     for (const auto &pair : *scope) {
1264       const Symbol *symbol{&*pair.second};
1265       if (predicate(*symbol)) {
1266         return symbol;
1267       }
1268       if (symbol->test(Symbol::Flag::ParentComp)) {
1269         parent = symbol;
1270       }
1271     }
1272     if (parent) {
1273       if (const auto *object{parent->detailsIf<ObjectEntityDetails>()}) {
1274         if (const auto *type{object->type()}) {
1275           if (const auto *derived{type->AsDerived()}) {
1276             return FindImmediateComponent(*derived, predicate);
1277           }
1278         }
1279       }
1280     }
1281   }
1282   return nullptr;
1283 }
1284 
1285 bool IsFunctionResult(const Symbol &symbol) {
1286   return (symbol.has<ObjectEntityDetails>() &&
1287              symbol.get<ObjectEntityDetails>().isFuncResult()) ||
1288       (symbol.has<ProcEntityDetails>() &&
1289           symbol.get<ProcEntityDetails>().isFuncResult());
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 } // namespace Fortran::semantics
1360