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