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