xref: /llvm-project/flang/lib/Semantics/tools.cpp (revision 657aaf8b8d6c0df6156025bb1db73280cf3d7870)
164ab3302SCarolineConcatto //===-- lib/Semantics/tools.cpp -------------------------------------------===//
264ab3302SCarolineConcatto //
364ab3302SCarolineConcatto // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
464ab3302SCarolineConcatto // See https://llvm.org/LICENSE.txt for license information.
564ab3302SCarolineConcatto // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
664ab3302SCarolineConcatto //
764ab3302SCarolineConcatto //===----------------------------------------------------------------------===//
864ab3302SCarolineConcatto 
964ab3302SCarolineConcatto #include "flang/Parser/tools.h"
1064ab3302SCarolineConcatto #include "flang/Common/Fortran.h"
1164ab3302SCarolineConcatto #include "flang/Common/indirection.h"
1264ab3302SCarolineConcatto #include "flang/Parser/dump-parse-tree.h"
1364ab3302SCarolineConcatto #include "flang/Parser/message.h"
1464ab3302SCarolineConcatto #include "flang/Parser/parse-tree.h"
1564ab3302SCarolineConcatto #include "flang/Semantics/scope.h"
1664ab3302SCarolineConcatto #include "flang/Semantics/semantics.h"
1764ab3302SCarolineConcatto #include "flang/Semantics/symbol.h"
1864ab3302SCarolineConcatto #include "flang/Semantics/tools.h"
1964ab3302SCarolineConcatto #include "flang/Semantics/type.h"
2064ab3302SCarolineConcatto #include <algorithm>
2164ab3302SCarolineConcatto #include <set>
2264ab3302SCarolineConcatto #include <sstream>
2364ab3302SCarolineConcatto #include <variant>
2464ab3302SCarolineConcatto 
2564ab3302SCarolineConcatto namespace Fortran::semantics {
2664ab3302SCarolineConcatto 
2764ab3302SCarolineConcatto const Symbol *FindCommonBlockContaining(const Symbol &object) {
2864ab3302SCarolineConcatto   if (const auto *details{object.detailsIf<ObjectEntityDetails>()}) {
2964ab3302SCarolineConcatto     return details->commonBlock();
3064ab3302SCarolineConcatto   } else {
3164ab3302SCarolineConcatto     return nullptr;
3264ab3302SCarolineConcatto   }
3364ab3302SCarolineConcatto }
3464ab3302SCarolineConcatto 
3564ab3302SCarolineConcatto const Scope *FindProgramUnitContaining(const Scope &start) {
3664ab3302SCarolineConcatto   const Scope *scope{&start};
3764ab3302SCarolineConcatto   while (scope) {
3864ab3302SCarolineConcatto     switch (scope->kind()) {
3964ab3302SCarolineConcatto     case Scope::Kind::Module:
4064ab3302SCarolineConcatto     case Scope::Kind::MainProgram:
4164ab3302SCarolineConcatto     case Scope::Kind::Subprogram:
4264ab3302SCarolineConcatto     case Scope::Kind::BlockData: return scope;
4364ab3302SCarolineConcatto     case Scope::Kind::Global: return nullptr;
4464ab3302SCarolineConcatto     case Scope::Kind::DerivedType:
4564ab3302SCarolineConcatto     case Scope::Kind::Block:
4664ab3302SCarolineConcatto     case Scope::Kind::Forall:
4764ab3302SCarolineConcatto     case Scope::Kind::ImpliedDos: scope = &scope->parent();
4864ab3302SCarolineConcatto     }
4964ab3302SCarolineConcatto   }
5064ab3302SCarolineConcatto   return nullptr;
5164ab3302SCarolineConcatto }
5264ab3302SCarolineConcatto 
5364ab3302SCarolineConcatto const Scope *FindProgramUnitContaining(const Symbol &symbol) {
5464ab3302SCarolineConcatto   return FindProgramUnitContaining(symbol.owner());
5564ab3302SCarolineConcatto }
5664ab3302SCarolineConcatto 
5764ab3302SCarolineConcatto const Scope *FindPureProcedureContaining(const Scope &start) {
5864ab3302SCarolineConcatto   // N.B. We only need to examine the innermost containing program unit
5964ab3302SCarolineConcatto   // because an internal subprogram of a pure subprogram must also
6064ab3302SCarolineConcatto   // be pure (C1592).
6164ab3302SCarolineConcatto   if (const Scope * scope{FindProgramUnitContaining(start)}) {
6264ab3302SCarolineConcatto     if (IsPureProcedure(*scope)) {
6364ab3302SCarolineConcatto       return scope;
6464ab3302SCarolineConcatto     }
6564ab3302SCarolineConcatto   }
6664ab3302SCarolineConcatto   return nullptr;
6764ab3302SCarolineConcatto }
6864ab3302SCarolineConcatto 
6964ab3302SCarolineConcatto Tristate IsDefinedAssignment(
7064ab3302SCarolineConcatto     const std::optional<evaluate::DynamicType> &lhsType, int lhsRank,
7164ab3302SCarolineConcatto     const std::optional<evaluate::DynamicType> &rhsType, int rhsRank) {
7264ab3302SCarolineConcatto   if (!lhsType || !rhsType) {
7364ab3302SCarolineConcatto     return Tristate::No;  // error or rhs is untyped
7464ab3302SCarolineConcatto   }
7564ab3302SCarolineConcatto   TypeCategory lhsCat{lhsType->category()};
7664ab3302SCarolineConcatto   TypeCategory rhsCat{rhsType->category()};
7764ab3302SCarolineConcatto   if (rhsRank > 0 && lhsRank != rhsRank) {
7864ab3302SCarolineConcatto     return Tristate::Yes;
7964ab3302SCarolineConcatto   } else if (lhsCat != TypeCategory::Derived) {
8064ab3302SCarolineConcatto     return ToTristate(lhsCat != rhsCat &&
8164ab3302SCarolineConcatto         (!IsNumericTypeCategory(lhsCat) || !IsNumericTypeCategory(rhsCat)));
8264ab3302SCarolineConcatto   } else {
8364ab3302SCarolineConcatto     const auto *lhsDerived{evaluate::GetDerivedTypeSpec(lhsType)};
8464ab3302SCarolineConcatto     const auto *rhsDerived{evaluate::GetDerivedTypeSpec(rhsType)};
8564ab3302SCarolineConcatto     if (lhsDerived && rhsDerived && *lhsDerived == *rhsDerived) {
8664ab3302SCarolineConcatto       return Tristate::Maybe;  // TYPE(t) = TYPE(t) can be defined or
8764ab3302SCarolineConcatto                                // intrinsic
8864ab3302SCarolineConcatto     } else {
8964ab3302SCarolineConcatto       return Tristate::Yes;
9064ab3302SCarolineConcatto     }
9164ab3302SCarolineConcatto   }
9264ab3302SCarolineConcatto }
9364ab3302SCarolineConcatto 
9464ab3302SCarolineConcatto bool IsIntrinsicRelational(common::RelationalOperator opr,
9564ab3302SCarolineConcatto     const evaluate::DynamicType &type0, int rank0,
9664ab3302SCarolineConcatto     const evaluate::DynamicType &type1, int rank1) {
9764ab3302SCarolineConcatto   if (!evaluate::AreConformable(rank0, rank1)) {
9864ab3302SCarolineConcatto     return false;
9964ab3302SCarolineConcatto   } else {
10064ab3302SCarolineConcatto     auto cat0{type0.category()};
10164ab3302SCarolineConcatto     auto cat1{type1.category()};
10264ab3302SCarolineConcatto     if (IsNumericTypeCategory(cat0) && IsNumericTypeCategory(cat1)) {
10364ab3302SCarolineConcatto       // numeric types: EQ/NE always ok, others ok for non-complex
10464ab3302SCarolineConcatto       return opr == common::RelationalOperator::EQ ||
10564ab3302SCarolineConcatto           opr == common::RelationalOperator::NE ||
10664ab3302SCarolineConcatto           (cat0 != TypeCategory::Complex && cat1 != TypeCategory::Complex);
10764ab3302SCarolineConcatto     } else {
10864ab3302SCarolineConcatto       // not both numeric: only Character is ok
10964ab3302SCarolineConcatto       return cat0 == TypeCategory::Character && cat1 == TypeCategory::Character;
11064ab3302SCarolineConcatto     }
11164ab3302SCarolineConcatto   }
11264ab3302SCarolineConcatto }
11364ab3302SCarolineConcatto 
11464ab3302SCarolineConcatto bool IsIntrinsicNumeric(const evaluate::DynamicType &type0) {
11564ab3302SCarolineConcatto   return IsNumericTypeCategory(type0.category());
11664ab3302SCarolineConcatto }
11764ab3302SCarolineConcatto bool IsIntrinsicNumeric(const evaluate::DynamicType &type0, int rank0,
11864ab3302SCarolineConcatto     const evaluate::DynamicType &type1, int rank1) {
11964ab3302SCarolineConcatto   return evaluate::AreConformable(rank0, rank1) &&
12064ab3302SCarolineConcatto       IsNumericTypeCategory(type0.category()) &&
12164ab3302SCarolineConcatto       IsNumericTypeCategory(type1.category());
12264ab3302SCarolineConcatto }
12364ab3302SCarolineConcatto 
12464ab3302SCarolineConcatto bool IsIntrinsicLogical(const evaluate::DynamicType &type0) {
12564ab3302SCarolineConcatto   return type0.category() == TypeCategory::Logical;
12664ab3302SCarolineConcatto }
12764ab3302SCarolineConcatto bool IsIntrinsicLogical(const evaluate::DynamicType &type0, int rank0,
12864ab3302SCarolineConcatto     const evaluate::DynamicType &type1, int rank1) {
12964ab3302SCarolineConcatto   return evaluate::AreConformable(rank0, rank1) &&
13064ab3302SCarolineConcatto       type0.category() == TypeCategory::Logical &&
13164ab3302SCarolineConcatto       type1.category() == TypeCategory::Logical;
13264ab3302SCarolineConcatto }
13364ab3302SCarolineConcatto 
13464ab3302SCarolineConcatto bool IsIntrinsicConcat(const evaluate::DynamicType &type0, int rank0,
13564ab3302SCarolineConcatto     const evaluate::DynamicType &type1, int rank1) {
13664ab3302SCarolineConcatto   return evaluate::AreConformable(rank0, rank1) &&
13764ab3302SCarolineConcatto       type0.category() == TypeCategory::Character &&
13864ab3302SCarolineConcatto       type1.category() == TypeCategory::Character &&
13964ab3302SCarolineConcatto       type0.kind() == type1.kind();
14064ab3302SCarolineConcatto }
14164ab3302SCarolineConcatto 
14264ab3302SCarolineConcatto bool IsGenericDefinedOp(const Symbol &symbol) {
14364ab3302SCarolineConcatto   const Symbol &ultimate{symbol.GetUltimate()};
14464ab3302SCarolineConcatto   if (const auto *generic{ultimate.detailsIf<GenericDetails>()}) {
14564ab3302SCarolineConcatto     return generic->kind().IsDefinedOperator();
14664ab3302SCarolineConcatto   } else if (const auto *misc{ultimate.detailsIf<MiscDetails>()}) {
14764ab3302SCarolineConcatto     return misc->kind() == MiscDetails::Kind::TypeBoundDefinedOp;
14864ab3302SCarolineConcatto   } else {
14964ab3302SCarolineConcatto     return false;
15064ab3302SCarolineConcatto   }
15164ab3302SCarolineConcatto }
15264ab3302SCarolineConcatto 
15364ab3302SCarolineConcatto bool IsCommonBlockContaining(const Symbol &block, const Symbol &object) {
15464ab3302SCarolineConcatto   const auto &objects{block.get<CommonBlockDetails>().objects()};
15564ab3302SCarolineConcatto   auto found{std::find(objects.begin(), objects.end(), object)};
15664ab3302SCarolineConcatto   return found != objects.end();
15764ab3302SCarolineConcatto }
15864ab3302SCarolineConcatto 
15964ab3302SCarolineConcatto bool IsUseAssociated(const Symbol &symbol, const Scope &scope) {
16064ab3302SCarolineConcatto   const Scope *owner{FindProgramUnitContaining(symbol.GetUltimate().owner())};
16164ab3302SCarolineConcatto   return owner && owner->kind() == Scope::Kind::Module &&
16264ab3302SCarolineConcatto       owner != FindProgramUnitContaining(scope);
16364ab3302SCarolineConcatto }
16464ab3302SCarolineConcatto 
16564ab3302SCarolineConcatto bool DoesScopeContain(
16664ab3302SCarolineConcatto     const Scope *maybeAncestor, const Scope &maybeDescendent) {
16764ab3302SCarolineConcatto   if (maybeAncestor) {
16864ab3302SCarolineConcatto     const Scope *scope{&maybeDescendent};
16964ab3302SCarolineConcatto     while (!scope->IsGlobal()) {
17064ab3302SCarolineConcatto       scope = &scope->parent();
17164ab3302SCarolineConcatto       if (scope == maybeAncestor) {
17264ab3302SCarolineConcatto         return true;
17364ab3302SCarolineConcatto       }
17464ab3302SCarolineConcatto     }
17564ab3302SCarolineConcatto   }
17664ab3302SCarolineConcatto   return false;
17764ab3302SCarolineConcatto }
17864ab3302SCarolineConcatto 
17964ab3302SCarolineConcatto bool DoesScopeContain(const Scope *maybeAncestor, const Symbol &symbol) {
18064ab3302SCarolineConcatto   return DoesScopeContain(maybeAncestor, symbol.owner());
18164ab3302SCarolineConcatto }
18264ab3302SCarolineConcatto 
18364ab3302SCarolineConcatto bool IsHostAssociated(const Symbol &symbol, const Scope &scope) {
18464ab3302SCarolineConcatto   const Scope *subprogram{FindProgramUnitContaining(scope)};
18564ab3302SCarolineConcatto   return subprogram &&
18664ab3302SCarolineConcatto       DoesScopeContain(FindProgramUnitContaining(symbol), *subprogram);
18764ab3302SCarolineConcatto }
18864ab3302SCarolineConcatto 
18964ab3302SCarolineConcatto bool IsDummy(const Symbol &symbol) {
19064ab3302SCarolineConcatto   if (const auto *details{symbol.detailsIf<ObjectEntityDetails>()}) {
19164ab3302SCarolineConcatto     return details->isDummy();
19264ab3302SCarolineConcatto   } else if (const auto *details{symbol.detailsIf<ProcEntityDetails>()}) {
19364ab3302SCarolineConcatto     return details->isDummy();
19464ab3302SCarolineConcatto   } else {
19564ab3302SCarolineConcatto     return false;
19664ab3302SCarolineConcatto   }
19764ab3302SCarolineConcatto }
19864ab3302SCarolineConcatto 
199*657aaf8bSPete Steinfeld bool IsStmtFunction(const Symbol &symbol) {
200*657aaf8bSPete Steinfeld   const auto *subprogram{symbol.detailsIf<SubprogramDetails>()};
201*657aaf8bSPete Steinfeld   if (subprogram && subprogram->stmtFunction()) {
202*657aaf8bSPete Steinfeld     return true;
203*657aaf8bSPete Steinfeld   }
204*657aaf8bSPete Steinfeld   return false;
205*657aaf8bSPete Steinfeld }
206*657aaf8bSPete Steinfeld 
207*657aaf8bSPete Steinfeld bool IsInStmtFunction(const Symbol &symbol) {
208*657aaf8bSPete Steinfeld   if (const Symbol * function{symbol.owner().symbol()}) {
209*657aaf8bSPete Steinfeld     return IsStmtFunction(*function);
210*657aaf8bSPete Steinfeld   }
211*657aaf8bSPete Steinfeld   return false;
212*657aaf8bSPete Steinfeld }
213*657aaf8bSPete Steinfeld 
214*657aaf8bSPete Steinfeld bool IsStmtFunctionDummy(const Symbol &symbol) {
215*657aaf8bSPete Steinfeld   return IsDummy(symbol) && IsInStmtFunction(symbol);
216*657aaf8bSPete Steinfeld }
217*657aaf8bSPete Steinfeld 
218*657aaf8bSPete Steinfeld bool IsStmtFunctionResult(const Symbol &symbol) {
219*657aaf8bSPete Steinfeld   return IsFunctionResult(symbol) && IsInStmtFunction(symbol);
220*657aaf8bSPete Steinfeld }
221*657aaf8bSPete Steinfeld 
22264ab3302SCarolineConcatto bool IsPointerDummy(const Symbol &symbol) {
22364ab3302SCarolineConcatto   return IsPointer(symbol) && IsDummy(symbol);
22464ab3302SCarolineConcatto }
22564ab3302SCarolineConcatto 
22664ab3302SCarolineConcatto // variable-name
22764ab3302SCarolineConcatto bool IsVariableName(const Symbol &symbol) {
22864ab3302SCarolineConcatto   if (const Symbol * root{GetAssociationRoot(symbol)}) {
22964ab3302SCarolineConcatto     return root->has<ObjectEntityDetails>() && !IsNamedConstant(*root);
23064ab3302SCarolineConcatto   } else {
23164ab3302SCarolineConcatto     return false;
23264ab3302SCarolineConcatto   }
23364ab3302SCarolineConcatto }
23464ab3302SCarolineConcatto 
23564ab3302SCarolineConcatto // proc-name
23664ab3302SCarolineConcatto bool IsProcName(const Symbol &symbol) {
23764ab3302SCarolineConcatto   return symbol.GetUltimate().has<ProcEntityDetails>();
23864ab3302SCarolineConcatto }
23964ab3302SCarolineConcatto 
24064ab3302SCarolineConcatto bool IsFunction(const Symbol &symbol) {
24164ab3302SCarolineConcatto   return std::visit(
24264ab3302SCarolineConcatto       common::visitors{
24364ab3302SCarolineConcatto           [](const SubprogramDetails &x) { return x.isFunction(); },
24464ab3302SCarolineConcatto           [&](const SubprogramNameDetails &) {
24564ab3302SCarolineConcatto             return symbol.test(Symbol::Flag::Function);
24664ab3302SCarolineConcatto           },
24764ab3302SCarolineConcatto           [](const ProcEntityDetails &x) {
24864ab3302SCarolineConcatto             const auto &ifc{x.interface()};
24964ab3302SCarolineConcatto             return ifc.type() || (ifc.symbol() && IsFunction(*ifc.symbol()));
25064ab3302SCarolineConcatto           },
25164ab3302SCarolineConcatto           [](const ProcBindingDetails &x) { return IsFunction(x.symbol()); },
25264ab3302SCarolineConcatto           [](const UseDetails &x) { return IsFunction(x.symbol()); },
25364ab3302SCarolineConcatto           [](const auto &) { return false; },
25464ab3302SCarolineConcatto       },
25564ab3302SCarolineConcatto       symbol.details());
25664ab3302SCarolineConcatto }
25764ab3302SCarolineConcatto 
25864ab3302SCarolineConcatto bool IsPureProcedure(const Symbol &symbol) {
25964ab3302SCarolineConcatto   if (const auto *procDetails{symbol.detailsIf<ProcEntityDetails>()}) {
26064ab3302SCarolineConcatto     if (const Symbol * procInterface{procDetails->interface().symbol()}) {
26164ab3302SCarolineConcatto       // procedure component with a pure interface
26264ab3302SCarolineConcatto       return IsPureProcedure(*procInterface);
26364ab3302SCarolineConcatto     }
26464ab3302SCarolineConcatto   } else if (const auto *details{symbol.detailsIf<ProcBindingDetails>()}) {
26564ab3302SCarolineConcatto     return IsPureProcedure(details->symbol());
26664ab3302SCarolineConcatto   } else if (!IsProcedure(symbol)) {
26764ab3302SCarolineConcatto     return false;
26864ab3302SCarolineConcatto   }
26964ab3302SCarolineConcatto   return symbol.attrs().test(Attr::PURE) ||
27064ab3302SCarolineConcatto       (symbol.attrs().test(Attr::ELEMENTAL) &&
27164ab3302SCarolineConcatto           !symbol.attrs().test(Attr::IMPURE));
27264ab3302SCarolineConcatto }
27364ab3302SCarolineConcatto 
27464ab3302SCarolineConcatto bool IsPureProcedure(const Scope &scope) {
27564ab3302SCarolineConcatto   if (const Symbol * symbol{scope.GetSymbol()}) {
27664ab3302SCarolineConcatto     return IsPureProcedure(*symbol);
27764ab3302SCarolineConcatto   } else {
27864ab3302SCarolineConcatto     return false;
27964ab3302SCarolineConcatto   }
28064ab3302SCarolineConcatto }
28164ab3302SCarolineConcatto 
28264ab3302SCarolineConcatto bool IsBindCProcedure(const Symbol &symbol) {
28364ab3302SCarolineConcatto   if (const auto *procDetails{symbol.detailsIf<ProcEntityDetails>()}) {
28464ab3302SCarolineConcatto     if (const Symbol * procInterface{procDetails->interface().symbol()}) {
28564ab3302SCarolineConcatto       // procedure component with a BIND(C) interface
28664ab3302SCarolineConcatto       return IsBindCProcedure(*procInterface);
28764ab3302SCarolineConcatto     }
28864ab3302SCarolineConcatto   }
28964ab3302SCarolineConcatto   return symbol.attrs().test(Attr::BIND_C) && IsProcedure(symbol);
29064ab3302SCarolineConcatto }
29164ab3302SCarolineConcatto 
29264ab3302SCarolineConcatto bool IsBindCProcedure(const Scope &scope) {
29364ab3302SCarolineConcatto   if (const Symbol * symbol{scope.GetSymbol()}) {
29464ab3302SCarolineConcatto     return IsBindCProcedure(*symbol);
29564ab3302SCarolineConcatto   } else {
29664ab3302SCarolineConcatto     return false;
29764ab3302SCarolineConcatto   }
29864ab3302SCarolineConcatto }
29964ab3302SCarolineConcatto 
30064ab3302SCarolineConcatto bool IsProcedure(const Symbol &symbol) {
30164ab3302SCarolineConcatto   return std::visit(
30264ab3302SCarolineConcatto       common::visitors{
30364ab3302SCarolineConcatto           [](const SubprogramDetails &) { return true; },
30464ab3302SCarolineConcatto           [](const SubprogramNameDetails &) { return true; },
30564ab3302SCarolineConcatto           [](const ProcEntityDetails &) { return true; },
30664ab3302SCarolineConcatto           [](const GenericDetails &) { return true; },
30764ab3302SCarolineConcatto           [](const ProcBindingDetails &) { return true; },
30864ab3302SCarolineConcatto           [](const UseDetails &x) { return IsProcedure(x.symbol()); },
30964ab3302SCarolineConcatto           // TODO: FinalProcDetails?
31064ab3302SCarolineConcatto           [](const auto &) { return false; },
31164ab3302SCarolineConcatto       },
31264ab3302SCarolineConcatto       symbol.details());
31364ab3302SCarolineConcatto }
31464ab3302SCarolineConcatto 
31564ab3302SCarolineConcatto bool IsProcedurePointer(const Symbol &symbol) {
31664ab3302SCarolineConcatto   return symbol.has<ProcEntityDetails>() && IsPointer(symbol);
31764ab3302SCarolineConcatto }
31864ab3302SCarolineConcatto 
31964ab3302SCarolineConcatto static const Symbol *FindPointerComponent(
32064ab3302SCarolineConcatto     const Scope &scope, std::set<const Scope *> &visited) {
32164ab3302SCarolineConcatto   if (!scope.IsDerivedType()) {
32264ab3302SCarolineConcatto     return nullptr;
32364ab3302SCarolineConcatto   }
32464ab3302SCarolineConcatto   if (!visited.insert(&scope).second) {
32564ab3302SCarolineConcatto     return nullptr;
32664ab3302SCarolineConcatto   }
32764ab3302SCarolineConcatto   // If there's a top-level pointer component, return it for clearer error
32864ab3302SCarolineConcatto   // messaging.
32964ab3302SCarolineConcatto   for (const auto &pair : scope) {
33064ab3302SCarolineConcatto     const Symbol &symbol{*pair.second};
33164ab3302SCarolineConcatto     if (IsPointer(symbol)) {
33264ab3302SCarolineConcatto       return &symbol;
33364ab3302SCarolineConcatto     }
33464ab3302SCarolineConcatto   }
33564ab3302SCarolineConcatto   for (const auto &pair : scope) {
33664ab3302SCarolineConcatto     const Symbol &symbol{*pair.second};
33764ab3302SCarolineConcatto     if (const auto *details{symbol.detailsIf<ObjectEntityDetails>()}) {
33864ab3302SCarolineConcatto       if (const DeclTypeSpec * type{details->type()}) {
33964ab3302SCarolineConcatto         if (const DerivedTypeSpec * derived{type->AsDerived()}) {
34064ab3302SCarolineConcatto           if (const Scope * nested{derived->scope()}) {
34164ab3302SCarolineConcatto             if (const Symbol *
34264ab3302SCarolineConcatto                 pointer{FindPointerComponent(*nested, visited)}) {
34364ab3302SCarolineConcatto               return pointer;
34464ab3302SCarolineConcatto             }
34564ab3302SCarolineConcatto           }
34664ab3302SCarolineConcatto         }
34764ab3302SCarolineConcatto       }
34864ab3302SCarolineConcatto     }
34964ab3302SCarolineConcatto   }
35064ab3302SCarolineConcatto   return nullptr;
35164ab3302SCarolineConcatto }
35264ab3302SCarolineConcatto 
35364ab3302SCarolineConcatto const Symbol *FindPointerComponent(const Scope &scope) {
35464ab3302SCarolineConcatto   std::set<const Scope *> visited;
35564ab3302SCarolineConcatto   return FindPointerComponent(scope, visited);
35664ab3302SCarolineConcatto }
35764ab3302SCarolineConcatto 
35864ab3302SCarolineConcatto const Symbol *FindPointerComponent(const DerivedTypeSpec &derived) {
35964ab3302SCarolineConcatto   if (const Scope * scope{derived.scope()}) {
36064ab3302SCarolineConcatto     return FindPointerComponent(*scope);
36164ab3302SCarolineConcatto   } else {
36264ab3302SCarolineConcatto     return nullptr;
36364ab3302SCarolineConcatto   }
36464ab3302SCarolineConcatto }
36564ab3302SCarolineConcatto 
36664ab3302SCarolineConcatto const Symbol *FindPointerComponent(const DeclTypeSpec &type) {
36764ab3302SCarolineConcatto   if (const DerivedTypeSpec * derived{type.AsDerived()}) {
36864ab3302SCarolineConcatto     return FindPointerComponent(*derived);
36964ab3302SCarolineConcatto   } else {
37064ab3302SCarolineConcatto     return nullptr;
37164ab3302SCarolineConcatto   }
37264ab3302SCarolineConcatto }
37364ab3302SCarolineConcatto 
37464ab3302SCarolineConcatto const Symbol *FindPointerComponent(const DeclTypeSpec *type) {
37564ab3302SCarolineConcatto   return type ? FindPointerComponent(*type) : nullptr;
37664ab3302SCarolineConcatto }
37764ab3302SCarolineConcatto 
37864ab3302SCarolineConcatto const Symbol *FindPointerComponent(const Symbol &symbol) {
37964ab3302SCarolineConcatto   return IsPointer(symbol) ? &symbol : FindPointerComponent(symbol.GetType());
38064ab3302SCarolineConcatto }
38164ab3302SCarolineConcatto 
38264ab3302SCarolineConcatto // C1594 specifies several ways by which an object might be globally visible.
38364ab3302SCarolineConcatto const Symbol *FindExternallyVisibleObject(
38464ab3302SCarolineConcatto     const Symbol &object, const Scope &scope) {
38564ab3302SCarolineConcatto   // TODO: Storage association with any object for which this predicate holds,
38664ab3302SCarolineConcatto   // once EQUIVALENCE is supported.
38764ab3302SCarolineConcatto   if (IsUseAssociated(object, scope) || IsHostAssociated(object, scope) ||
38864ab3302SCarolineConcatto       (IsPureProcedure(scope) && IsPointerDummy(object)) ||
38964ab3302SCarolineConcatto       (IsIntentIn(object) && IsDummy(object))) {
39064ab3302SCarolineConcatto     return &object;
39164ab3302SCarolineConcatto   } else if (const Symbol * block{FindCommonBlockContaining(object)}) {
39264ab3302SCarolineConcatto     return block;
39364ab3302SCarolineConcatto   } else {
39464ab3302SCarolineConcatto     return nullptr;
39564ab3302SCarolineConcatto   }
39664ab3302SCarolineConcatto }
39764ab3302SCarolineConcatto 
39864ab3302SCarolineConcatto bool ExprHasTypeCategory(
39964ab3302SCarolineConcatto     const SomeExpr &expr, const common::TypeCategory &type) {
40064ab3302SCarolineConcatto   auto dynamicType{expr.GetType()};
40164ab3302SCarolineConcatto   return dynamicType && dynamicType->category() == type;
40264ab3302SCarolineConcatto }
40364ab3302SCarolineConcatto 
40464ab3302SCarolineConcatto bool ExprTypeKindIsDefault(
40564ab3302SCarolineConcatto     const SomeExpr &expr, const SemanticsContext &context) {
40664ab3302SCarolineConcatto   auto dynamicType{expr.GetType()};
40764ab3302SCarolineConcatto   return dynamicType &&
40864ab3302SCarolineConcatto       dynamicType->category() != common::TypeCategory::Derived &&
40964ab3302SCarolineConcatto       dynamicType->kind() == context.GetDefaultKind(dynamicType->category());
41064ab3302SCarolineConcatto }
41164ab3302SCarolineConcatto 
41264ab3302SCarolineConcatto // If an analyzed expr or assignment is missing, dump the node and die.
41364ab3302SCarolineConcatto template<typename T> static void CheckMissingAnalysis(bool absent, const T &x) {
41464ab3302SCarolineConcatto   if (absent) {
41564ab3302SCarolineConcatto     std::ostringstream ss;
41664ab3302SCarolineConcatto     ss << "node has not been analyzed:\n";
41764ab3302SCarolineConcatto     parser::DumpTree(ss, x);
41864ab3302SCarolineConcatto     common::die(ss.str().c_str());
41964ab3302SCarolineConcatto   }
42064ab3302SCarolineConcatto }
42164ab3302SCarolineConcatto 
42264ab3302SCarolineConcatto const SomeExpr *GetExprHelper::Get(const parser::Expr &x) {
42364ab3302SCarolineConcatto   CheckMissingAnalysis(!x.typedExpr, x);
42464ab3302SCarolineConcatto   return common::GetPtrFromOptional(x.typedExpr->v);
42564ab3302SCarolineConcatto }
42664ab3302SCarolineConcatto const SomeExpr *GetExprHelper::Get(const parser::Variable &x) {
42764ab3302SCarolineConcatto   CheckMissingAnalysis(!x.typedExpr, x);
42864ab3302SCarolineConcatto   return common::GetPtrFromOptional(x.typedExpr->v);
42964ab3302SCarolineConcatto }
43064ab3302SCarolineConcatto 
43164ab3302SCarolineConcatto const evaluate::Assignment *GetAssignment(const parser::AssignmentStmt &x) {
43264ab3302SCarolineConcatto   CheckMissingAnalysis(!x.typedAssignment, x);
43364ab3302SCarolineConcatto   return common::GetPtrFromOptional(x.typedAssignment->v);
43464ab3302SCarolineConcatto }
43564ab3302SCarolineConcatto const evaluate::Assignment *GetAssignment(
43664ab3302SCarolineConcatto     const parser::PointerAssignmentStmt &x) {
43764ab3302SCarolineConcatto   CheckMissingAnalysis(!x.typedAssignment, x);
43864ab3302SCarolineConcatto   return common::GetPtrFromOptional(x.typedAssignment->v);
43964ab3302SCarolineConcatto }
44064ab3302SCarolineConcatto 
44164ab3302SCarolineConcatto const Symbol *FindInterface(const Symbol &symbol) {
44264ab3302SCarolineConcatto   return std::visit(
44364ab3302SCarolineConcatto       common::visitors{
44464ab3302SCarolineConcatto           [](const ProcEntityDetails &details) {
44564ab3302SCarolineConcatto             return details.interface().symbol();
44664ab3302SCarolineConcatto           },
44764ab3302SCarolineConcatto           [](const ProcBindingDetails &details) { return &details.symbol(); },
44864ab3302SCarolineConcatto           [](const auto &) -> const Symbol * { return nullptr; },
44964ab3302SCarolineConcatto       },
45064ab3302SCarolineConcatto       symbol.details());
45164ab3302SCarolineConcatto }
45264ab3302SCarolineConcatto 
45364ab3302SCarolineConcatto const Symbol *FindSubprogram(const Symbol &symbol) {
45464ab3302SCarolineConcatto   return std::visit(
45564ab3302SCarolineConcatto       common::visitors{
45664ab3302SCarolineConcatto           [&](const ProcEntityDetails &details) -> const Symbol * {
45764ab3302SCarolineConcatto             if (const Symbol * interface{details.interface().symbol()}) {
45864ab3302SCarolineConcatto               return FindSubprogram(*interface);
45964ab3302SCarolineConcatto             } else {
46064ab3302SCarolineConcatto               return &symbol;
46164ab3302SCarolineConcatto             }
46264ab3302SCarolineConcatto           },
46364ab3302SCarolineConcatto           [](const ProcBindingDetails &details) {
46464ab3302SCarolineConcatto             return FindSubprogram(details.symbol());
46564ab3302SCarolineConcatto           },
46664ab3302SCarolineConcatto           [&](const SubprogramDetails &) { return &symbol; },
46764ab3302SCarolineConcatto           [](const UseDetails &details) {
46864ab3302SCarolineConcatto             return FindSubprogram(details.symbol());
46964ab3302SCarolineConcatto           },
47064ab3302SCarolineConcatto           [](const HostAssocDetails &details) {
47164ab3302SCarolineConcatto             return FindSubprogram(details.symbol());
47264ab3302SCarolineConcatto           },
47364ab3302SCarolineConcatto           [](const auto &) -> const Symbol * { return nullptr; },
47464ab3302SCarolineConcatto       },
47564ab3302SCarolineConcatto       symbol.details());
47664ab3302SCarolineConcatto }
47764ab3302SCarolineConcatto 
47864ab3302SCarolineConcatto const Symbol *FindFunctionResult(const Symbol &symbol) {
47964ab3302SCarolineConcatto   if (const Symbol * subp{FindSubprogram(symbol)}) {
48064ab3302SCarolineConcatto     if (const auto &subpDetails{subp->detailsIf<SubprogramDetails>()}) {
48164ab3302SCarolineConcatto       if (subpDetails->isFunction()) {
48264ab3302SCarolineConcatto         return &subpDetails->result();
48364ab3302SCarolineConcatto       }
48464ab3302SCarolineConcatto     }
48564ab3302SCarolineConcatto   }
48664ab3302SCarolineConcatto   return nullptr;
48764ab3302SCarolineConcatto }
48864ab3302SCarolineConcatto 
48964ab3302SCarolineConcatto const Symbol *FindOverriddenBinding(const Symbol &symbol) {
49064ab3302SCarolineConcatto   if (symbol.has<ProcBindingDetails>()) {
49164ab3302SCarolineConcatto     if (const DeclTypeSpec * parentType{FindParentTypeSpec(symbol.owner())}) {
49264ab3302SCarolineConcatto       if (const DerivedTypeSpec * parentDerived{parentType->AsDerived()}) {
49364ab3302SCarolineConcatto         if (const Scope * parentScope{parentDerived->typeSymbol().scope()}) {
49464ab3302SCarolineConcatto           return parentScope->FindComponent(symbol.name());
49564ab3302SCarolineConcatto         }
49664ab3302SCarolineConcatto       }
49764ab3302SCarolineConcatto     }
49864ab3302SCarolineConcatto   }
49964ab3302SCarolineConcatto   return nullptr;
50064ab3302SCarolineConcatto }
50164ab3302SCarolineConcatto 
50264ab3302SCarolineConcatto const DeclTypeSpec *FindParentTypeSpec(const DerivedTypeSpec &derived) {
50364ab3302SCarolineConcatto   return FindParentTypeSpec(derived.typeSymbol());
50464ab3302SCarolineConcatto }
50564ab3302SCarolineConcatto 
50664ab3302SCarolineConcatto const DeclTypeSpec *FindParentTypeSpec(const DeclTypeSpec &decl) {
50764ab3302SCarolineConcatto   if (const DerivedTypeSpec * derived{decl.AsDerived()}) {
50864ab3302SCarolineConcatto     return FindParentTypeSpec(*derived);
50964ab3302SCarolineConcatto   } else {
51064ab3302SCarolineConcatto     return nullptr;
51164ab3302SCarolineConcatto   }
51264ab3302SCarolineConcatto }
51364ab3302SCarolineConcatto 
51464ab3302SCarolineConcatto const DeclTypeSpec *FindParentTypeSpec(const Scope &scope) {
51564ab3302SCarolineConcatto   if (scope.kind() == Scope::Kind::DerivedType) {
51664ab3302SCarolineConcatto     if (const auto *symbol{scope.symbol()}) {
51764ab3302SCarolineConcatto       return FindParentTypeSpec(*symbol);
51864ab3302SCarolineConcatto     }
51964ab3302SCarolineConcatto   }
52064ab3302SCarolineConcatto   return nullptr;
52164ab3302SCarolineConcatto }
52264ab3302SCarolineConcatto 
52364ab3302SCarolineConcatto const DeclTypeSpec *FindParentTypeSpec(const Symbol &symbol) {
52464ab3302SCarolineConcatto   if (const Scope * scope{symbol.scope()}) {
52564ab3302SCarolineConcatto     if (const auto *details{symbol.detailsIf<DerivedTypeDetails>()}) {
52664ab3302SCarolineConcatto       if (const Symbol * parent{details->GetParentComponent(*scope)}) {
52764ab3302SCarolineConcatto         return parent->GetType();
52864ab3302SCarolineConcatto       }
52964ab3302SCarolineConcatto     }
53064ab3302SCarolineConcatto   }
53164ab3302SCarolineConcatto   return nullptr;
53264ab3302SCarolineConcatto }
53364ab3302SCarolineConcatto 
53464ab3302SCarolineConcatto // When a construct association maps to a variable, and that variable
53564ab3302SCarolineConcatto // is not an array with a vector-valued subscript, return the base
53664ab3302SCarolineConcatto // Symbol of that variable, else nullptr.  Descends into other construct
53764ab3302SCarolineConcatto // associations when one associations maps to another.
53864ab3302SCarolineConcatto static const Symbol *GetAssociatedVariable(const AssocEntityDetails &details) {
53964ab3302SCarolineConcatto   if (const MaybeExpr & expr{details.expr()}) {
54064ab3302SCarolineConcatto     if (evaluate::IsVariable(*expr) && !evaluate::HasVectorSubscript(*expr)) {
54164ab3302SCarolineConcatto       if (const Symbol * varSymbol{evaluate::GetFirstSymbol(*expr)}) {
54264ab3302SCarolineConcatto         return GetAssociationRoot(*varSymbol);
54364ab3302SCarolineConcatto       }
54464ab3302SCarolineConcatto     }
54564ab3302SCarolineConcatto   }
54664ab3302SCarolineConcatto   return nullptr;
54764ab3302SCarolineConcatto }
54864ab3302SCarolineConcatto 
54964ab3302SCarolineConcatto // Return the Symbol of the variable of a construct association, if it exists
55064ab3302SCarolineConcatto // Return nullptr if the name is associated with an expression
55164ab3302SCarolineConcatto const Symbol *GetAssociationRoot(const Symbol &symbol) {
55264ab3302SCarolineConcatto   const Symbol &ultimate{symbol.GetUltimate()};
55364ab3302SCarolineConcatto   if (const auto *details{ultimate.detailsIf<AssocEntityDetails>()}) {
55464ab3302SCarolineConcatto     // We have a construct association
55564ab3302SCarolineConcatto     return GetAssociatedVariable(*details);
55664ab3302SCarolineConcatto   } else {
55764ab3302SCarolineConcatto     return &ultimate;
55864ab3302SCarolineConcatto   }
55964ab3302SCarolineConcatto }
56064ab3302SCarolineConcatto 
56164ab3302SCarolineConcatto bool IsExtensibleType(const DerivedTypeSpec *derived) {
56264ab3302SCarolineConcatto   return derived && !IsIsoCType(derived) &&
56364ab3302SCarolineConcatto       !derived->typeSymbol().attrs().test(Attr::BIND_C) &&
56464ab3302SCarolineConcatto       !derived->typeSymbol().get<DerivedTypeDetails>().sequence();
56564ab3302SCarolineConcatto }
56664ab3302SCarolineConcatto 
56764ab3302SCarolineConcatto bool IsBuiltinDerivedType(const DerivedTypeSpec *derived, const char *name) {
56864ab3302SCarolineConcatto   if (!derived) {
56964ab3302SCarolineConcatto     return false;
57064ab3302SCarolineConcatto   } else {
57164ab3302SCarolineConcatto     const auto &symbol{derived->typeSymbol()};
57264ab3302SCarolineConcatto     return symbol.owner().IsModule() &&
57364ab3302SCarolineConcatto         symbol.owner().GetName().value() == "__fortran_builtins" &&
57464ab3302SCarolineConcatto         symbol.name() == "__builtin_"s + name;
57564ab3302SCarolineConcatto   }
57664ab3302SCarolineConcatto }
57764ab3302SCarolineConcatto 
57864ab3302SCarolineConcatto bool IsIsoCType(const DerivedTypeSpec *derived) {
57964ab3302SCarolineConcatto   return IsBuiltinDerivedType(derived, "c_ptr") ||
58064ab3302SCarolineConcatto       IsBuiltinDerivedType(derived, "c_funptr");
58164ab3302SCarolineConcatto }
58264ab3302SCarolineConcatto 
58364ab3302SCarolineConcatto bool IsTeamType(const DerivedTypeSpec *derived) {
58464ab3302SCarolineConcatto   return IsBuiltinDerivedType(derived, "team_type");
58564ab3302SCarolineConcatto }
58664ab3302SCarolineConcatto 
58764ab3302SCarolineConcatto bool IsEventTypeOrLockType(const DerivedTypeSpec *derivedTypeSpec) {
58864ab3302SCarolineConcatto   return IsBuiltinDerivedType(derivedTypeSpec, "event_type") ||
58964ab3302SCarolineConcatto       IsBuiltinDerivedType(derivedTypeSpec, "lock_type");
59064ab3302SCarolineConcatto }
59164ab3302SCarolineConcatto 
59264ab3302SCarolineConcatto bool IsOrContainsEventOrLockComponent(const Symbol &symbol) {
59364ab3302SCarolineConcatto   if (const Symbol * root{GetAssociationRoot(symbol)}) {
59464ab3302SCarolineConcatto     if (const auto *details{root->detailsIf<ObjectEntityDetails>()}) {
59564ab3302SCarolineConcatto       if (const DeclTypeSpec * type{details->type()}) {
59664ab3302SCarolineConcatto         if (const DerivedTypeSpec * derived{type->AsDerived()}) {
59764ab3302SCarolineConcatto           return IsEventTypeOrLockType(derived) ||
59864ab3302SCarolineConcatto               FindEventOrLockPotentialComponent(*derived);
59964ab3302SCarolineConcatto         }
60064ab3302SCarolineConcatto       }
60164ab3302SCarolineConcatto     }
60264ab3302SCarolineConcatto   }
60364ab3302SCarolineConcatto   return false;
60464ab3302SCarolineConcatto }
60564ab3302SCarolineConcatto 
60664ab3302SCarolineConcatto bool IsSaved(const Symbol &symbol) {
60764ab3302SCarolineConcatto   auto scopeKind{symbol.owner().kind()};
60864ab3302SCarolineConcatto   if (scopeKind == Scope::Kind::MainProgram ||
60964ab3302SCarolineConcatto       scopeKind == Scope::Kind::Module) {
61064ab3302SCarolineConcatto     return true;
61164ab3302SCarolineConcatto   } else if (scopeKind == Scope::Kind::DerivedType) {
61264ab3302SCarolineConcatto     return false;  // this is a component
61364ab3302SCarolineConcatto   } else if (IsNamedConstant(symbol)) {
61464ab3302SCarolineConcatto     return false;
61564ab3302SCarolineConcatto   } else if (symbol.attrs().test(Attr::SAVE)) {
61664ab3302SCarolineConcatto     return true;
61764ab3302SCarolineConcatto   } else {
61864ab3302SCarolineConcatto     if (const auto *object{symbol.detailsIf<ObjectEntityDetails>()}) {
61964ab3302SCarolineConcatto       if (object->init()) {
62064ab3302SCarolineConcatto         return true;
62164ab3302SCarolineConcatto       }
62264ab3302SCarolineConcatto     } else if (IsProcedurePointer(symbol)) {
62364ab3302SCarolineConcatto       if (symbol.get<ProcEntityDetails>().init()) {
62464ab3302SCarolineConcatto         return true;
62564ab3302SCarolineConcatto       }
62664ab3302SCarolineConcatto     }
62764ab3302SCarolineConcatto     if (const Symbol * block{FindCommonBlockContaining(symbol)}) {
62864ab3302SCarolineConcatto       if (block->attrs().test(Attr::SAVE)) {
62964ab3302SCarolineConcatto         return true;
63064ab3302SCarolineConcatto       }
63164ab3302SCarolineConcatto     }
63264ab3302SCarolineConcatto     return false;
63364ab3302SCarolineConcatto   }
63464ab3302SCarolineConcatto }
63564ab3302SCarolineConcatto 
63664ab3302SCarolineConcatto // Check this symbol suitable as a type-bound procedure - C769
63764ab3302SCarolineConcatto bool CanBeTypeBoundProc(const Symbol *symbol) {
63864ab3302SCarolineConcatto   if (!symbol || IsDummy(*symbol) || IsProcedurePointer(*symbol)) {
63964ab3302SCarolineConcatto     return false;
64064ab3302SCarolineConcatto   } else if (symbol->has<SubprogramNameDetails>()) {
64164ab3302SCarolineConcatto     return symbol->owner().kind() == Scope::Kind::Module;
64264ab3302SCarolineConcatto   } else if (auto *details{symbol->detailsIf<SubprogramDetails>()}) {
64364ab3302SCarolineConcatto     return symbol->owner().kind() == Scope::Kind::Module ||
64464ab3302SCarolineConcatto         details->isInterface();
64564ab3302SCarolineConcatto   } else if (const auto *proc{symbol->detailsIf<ProcEntityDetails>()}) {
64664ab3302SCarolineConcatto     return !symbol->attrs().test(Attr::INTRINSIC) &&
64764ab3302SCarolineConcatto         proc->HasExplicitInterface();
64864ab3302SCarolineConcatto   } else {
64964ab3302SCarolineConcatto     return false;
65064ab3302SCarolineConcatto   }
65164ab3302SCarolineConcatto }
65264ab3302SCarolineConcatto 
65364ab3302SCarolineConcatto bool IsInitialized(const Symbol &symbol) {
65464ab3302SCarolineConcatto   if (symbol.test(Symbol::Flag::InDataStmt)) {
65564ab3302SCarolineConcatto     return true;
65664ab3302SCarolineConcatto   } else if (IsNamedConstant(symbol)) {
65764ab3302SCarolineConcatto     return false;
65864ab3302SCarolineConcatto   } else if (const auto *object{symbol.detailsIf<ObjectEntityDetails>()}) {
65964ab3302SCarolineConcatto     if (IsAllocatable(symbol) || object->init()) {
66064ab3302SCarolineConcatto       return true;
66164ab3302SCarolineConcatto     }
66264ab3302SCarolineConcatto     if (!IsPointer(symbol) && object->type()) {
66364ab3302SCarolineConcatto       if (const auto *derived{object->type()->AsDerived()}) {
66464ab3302SCarolineConcatto         if (derived->HasDefaultInitialization()) {
66564ab3302SCarolineConcatto           return true;
66664ab3302SCarolineConcatto         }
66764ab3302SCarolineConcatto       }
66864ab3302SCarolineConcatto     }
66964ab3302SCarolineConcatto   } else if (const auto *proc{symbol.detailsIf<ProcEntityDetails>()}) {
67064ab3302SCarolineConcatto     return proc->init().has_value();
67164ab3302SCarolineConcatto   }
67264ab3302SCarolineConcatto   return false;
67364ab3302SCarolineConcatto }
67464ab3302SCarolineConcatto 
67564ab3302SCarolineConcatto bool IsFinalizable(const Symbol &symbol) {
67664ab3302SCarolineConcatto   if (const DeclTypeSpec * type{symbol.GetType()}) {
67764ab3302SCarolineConcatto     if (const DerivedTypeSpec * derived{type->AsDerived()}) {
67864ab3302SCarolineConcatto       return IsFinalizable(*derived);
67964ab3302SCarolineConcatto     }
68064ab3302SCarolineConcatto   }
68164ab3302SCarolineConcatto   return false;
68264ab3302SCarolineConcatto }
68364ab3302SCarolineConcatto 
68464ab3302SCarolineConcatto bool IsFinalizable(const DerivedTypeSpec &derived) {
68564ab3302SCarolineConcatto   ScopeComponentIterator components{derived};
68664ab3302SCarolineConcatto   return std::find_if(components.begin(), components.end(),
68764ab3302SCarolineConcatto              [](const Symbol &x) { return x.has<FinalProcDetails>(); }) !=
68864ab3302SCarolineConcatto       components.end();
68964ab3302SCarolineConcatto }
69064ab3302SCarolineConcatto 
69164ab3302SCarolineConcatto // TODO The following function returns true for all types with FINAL procedures
69264ab3302SCarolineConcatto // This is because we don't yet fill in the data for FinalProcDetails
69364ab3302SCarolineConcatto bool HasImpureFinal(const DerivedTypeSpec &derived) {
69464ab3302SCarolineConcatto   ScopeComponentIterator components{derived};
69564ab3302SCarolineConcatto   return std::find_if(
69664ab3302SCarolineConcatto              components.begin(), components.end(), [](const Symbol &x) {
69764ab3302SCarolineConcatto                return x.has<FinalProcDetails>() && !x.attrs().test(Attr::PURE);
69864ab3302SCarolineConcatto              }) != components.end();
69964ab3302SCarolineConcatto }
70064ab3302SCarolineConcatto 
70164ab3302SCarolineConcatto bool IsCoarray(const Symbol &symbol) { return symbol.Corank() > 0; }
70264ab3302SCarolineConcatto 
70364ab3302SCarolineConcatto bool IsAssumedLengthCharacter(const Symbol &symbol) {
70464ab3302SCarolineConcatto   if (const DeclTypeSpec * type{symbol.GetType()}) {
70564ab3302SCarolineConcatto     return type->category() == DeclTypeSpec::Character &&
70664ab3302SCarolineConcatto         type->characterTypeSpec().length().isAssumed();
70764ab3302SCarolineConcatto   } else {
70864ab3302SCarolineConcatto     return false;
70964ab3302SCarolineConcatto   }
71064ab3302SCarolineConcatto }
71164ab3302SCarolineConcatto 
712*657aaf8bSPete Steinfeld // C722 and C723:  For a function to be assumed length, it must be external and
713*657aaf8bSPete Steinfeld // of CHARACTER type
714*657aaf8bSPete Steinfeld bool IsAssumedLengthExternalCharacterFunction(const Symbol &symbol) {
715*657aaf8bSPete Steinfeld   return IsAssumedLengthCharacter(symbol) &&
716*657aaf8bSPete Steinfeld       ((symbol.has<SubprogramDetails>() && symbol.owner().IsGlobal()) ||
717*657aaf8bSPete Steinfeld           (symbol.test(Symbol::Flag::Function) &&
718*657aaf8bSPete Steinfeld               symbol.attrs().test(Attr::EXTERNAL)));
71964ab3302SCarolineConcatto }
72064ab3302SCarolineConcatto 
72164ab3302SCarolineConcatto const Symbol *IsExternalInPureContext(
72264ab3302SCarolineConcatto     const Symbol &symbol, const Scope &scope) {
72364ab3302SCarolineConcatto   if (const auto *pureProc{semantics::FindPureProcedureContaining(scope)}) {
72464ab3302SCarolineConcatto     if (const Symbol * root{GetAssociationRoot(symbol)}) {
72564ab3302SCarolineConcatto       if (const Symbol *
72664ab3302SCarolineConcatto           visible{FindExternallyVisibleObject(*root, *pureProc)}) {
72764ab3302SCarolineConcatto         return visible;
72864ab3302SCarolineConcatto       }
72964ab3302SCarolineConcatto     }
73064ab3302SCarolineConcatto   }
73164ab3302SCarolineConcatto   return nullptr;
73264ab3302SCarolineConcatto }
73364ab3302SCarolineConcatto 
73464ab3302SCarolineConcatto PotentialComponentIterator::const_iterator FindPolymorphicPotentialComponent(
73564ab3302SCarolineConcatto     const DerivedTypeSpec &derived) {
73664ab3302SCarolineConcatto   PotentialComponentIterator potentials{derived};
73764ab3302SCarolineConcatto   return std::find_if(
73864ab3302SCarolineConcatto       potentials.begin(), potentials.end(), [](const Symbol &component) {
73964ab3302SCarolineConcatto         if (const auto *details{component.detailsIf<ObjectEntityDetails>()}) {
74064ab3302SCarolineConcatto           const DeclTypeSpec *type{details->type()};
74164ab3302SCarolineConcatto           return type && type->IsPolymorphic();
74264ab3302SCarolineConcatto         }
74364ab3302SCarolineConcatto         return false;
74464ab3302SCarolineConcatto       });
74564ab3302SCarolineConcatto }
74664ab3302SCarolineConcatto 
74764ab3302SCarolineConcatto bool IsOrContainsPolymorphicComponent(const Symbol &symbol) {
74864ab3302SCarolineConcatto   if (const Symbol * root{GetAssociationRoot(symbol)}) {
74964ab3302SCarolineConcatto     if (const auto *details{root->detailsIf<ObjectEntityDetails>()}) {
75064ab3302SCarolineConcatto       if (const DeclTypeSpec * type{details->type()}) {
75164ab3302SCarolineConcatto         if (type->IsPolymorphic()) {
75264ab3302SCarolineConcatto           return true;
75364ab3302SCarolineConcatto         }
75464ab3302SCarolineConcatto         if (const DerivedTypeSpec * derived{type->AsDerived()}) {
75564ab3302SCarolineConcatto           return (bool)FindPolymorphicPotentialComponent(*derived);
75664ab3302SCarolineConcatto         }
75764ab3302SCarolineConcatto       }
75864ab3302SCarolineConcatto     }
75964ab3302SCarolineConcatto   }
76064ab3302SCarolineConcatto   return false;
76164ab3302SCarolineConcatto }
76264ab3302SCarolineConcatto 
76364ab3302SCarolineConcatto bool InProtectedContext(const Symbol &symbol, const Scope &currentScope) {
76464ab3302SCarolineConcatto   return IsProtected(symbol) && !IsHostAssociated(symbol, currentScope);
76564ab3302SCarolineConcatto }
76664ab3302SCarolineConcatto 
76764ab3302SCarolineConcatto // C1101 and C1158
76864ab3302SCarolineConcatto // TODO Need to check for a coindexed object (why? C1103?)
76964ab3302SCarolineConcatto std::optional<parser::MessageFixedText> WhyNotModifiable(
77064ab3302SCarolineConcatto     const Symbol &symbol, const Scope &scope) {
77164ab3302SCarolineConcatto   const Symbol *root{GetAssociationRoot(symbol)};
77264ab3302SCarolineConcatto   if (!root) {
77364ab3302SCarolineConcatto     return "'%s' is construct associated with an expression"_en_US;
77464ab3302SCarolineConcatto   } else if (InProtectedContext(*root, scope)) {
77564ab3302SCarolineConcatto     return "'%s' is protected in this scope"_en_US;
77664ab3302SCarolineConcatto   } else if (IsExternalInPureContext(*root, scope)) {
77764ab3302SCarolineConcatto     return "'%s' is externally visible and referenced in a pure"
77864ab3302SCarolineConcatto            " procedure"_en_US;
77964ab3302SCarolineConcatto   } else if (IsOrContainsEventOrLockComponent(*root)) {
78064ab3302SCarolineConcatto     return "'%s' is an entity with either an EVENT_TYPE or LOCK_TYPE"_en_US;
78164ab3302SCarolineConcatto   } else if (IsIntentIn(*root)) {
78264ab3302SCarolineConcatto     return "'%s' is an INTENT(IN) dummy argument"_en_US;
78364ab3302SCarolineConcatto   } else if (!IsVariableName(*root)) {
78464ab3302SCarolineConcatto     return "'%s' is not a variable"_en_US;
78564ab3302SCarolineConcatto   } else {
78664ab3302SCarolineConcatto     return std::nullopt;
78764ab3302SCarolineConcatto   }
78864ab3302SCarolineConcatto }
78964ab3302SCarolineConcatto 
79064ab3302SCarolineConcatto std::unique_ptr<parser::Message> WhyNotModifiable(parser::CharBlock at,
79164ab3302SCarolineConcatto     const SomeExpr &expr, const Scope &scope, bool vectorSubscriptIsOk) {
79264ab3302SCarolineConcatto   if (evaluate::IsVariable(expr)) {
79364ab3302SCarolineConcatto     if (auto dataRef{evaluate::ExtractDataRef(expr)}) {
79464ab3302SCarolineConcatto       if (!vectorSubscriptIsOk && evaluate::HasVectorSubscript(expr)) {
79564ab3302SCarolineConcatto         return std::make_unique<parser::Message>(
79664ab3302SCarolineConcatto             at, "variable has a vector subscript"_en_US);
79764ab3302SCarolineConcatto       } else {
79864ab3302SCarolineConcatto         const Symbol &symbol{dataRef->GetFirstSymbol()};
79964ab3302SCarolineConcatto         if (auto maybeWhy{WhyNotModifiable(symbol, scope)}) {
80064ab3302SCarolineConcatto           return std::make_unique<parser::Message>(symbol.name(),
80164ab3302SCarolineConcatto               parser::MessageFormattedText{
80264ab3302SCarolineConcatto                   std::move(*maybeWhy), symbol.name()});
80364ab3302SCarolineConcatto         }
80464ab3302SCarolineConcatto       }
80564ab3302SCarolineConcatto     } else {
80664ab3302SCarolineConcatto       // reference to function returning POINTER
80764ab3302SCarolineConcatto     }
80864ab3302SCarolineConcatto   } else {
80964ab3302SCarolineConcatto     return std::make_unique<parser::Message>(
81064ab3302SCarolineConcatto         at, "expression is not a variable"_en_US);
81164ab3302SCarolineConcatto   }
81264ab3302SCarolineConcatto   return {};
81364ab3302SCarolineConcatto }
81464ab3302SCarolineConcatto 
81564ab3302SCarolineConcatto class ImageControlStmtHelper {
81664ab3302SCarolineConcatto   using ImageControlStmts = std::variant<parser::ChangeTeamConstruct,
81764ab3302SCarolineConcatto       parser::CriticalConstruct, parser::EventPostStmt, parser::EventWaitStmt,
81864ab3302SCarolineConcatto       parser::FormTeamStmt, parser::LockStmt, parser::StopStmt,
81964ab3302SCarolineConcatto       parser::SyncAllStmt, parser::SyncImagesStmt, parser::SyncMemoryStmt,
82064ab3302SCarolineConcatto       parser::SyncTeamStmt, parser::UnlockStmt>;
82164ab3302SCarolineConcatto 
82264ab3302SCarolineConcatto public:
82364ab3302SCarolineConcatto   template<typename T> bool operator()(const T &) {
82464ab3302SCarolineConcatto     return common::HasMember<T, ImageControlStmts>;
82564ab3302SCarolineConcatto   }
82664ab3302SCarolineConcatto   template<typename T> bool operator()(const common::Indirection<T> &x) {
82764ab3302SCarolineConcatto     return (*this)(x.value());
82864ab3302SCarolineConcatto   }
82964ab3302SCarolineConcatto   bool operator()(const parser::AllocateStmt &stmt) {
83064ab3302SCarolineConcatto     const auto &allocationList{std::get<std::list<parser::Allocation>>(stmt.t)};
83164ab3302SCarolineConcatto     for (const auto &allocation : allocationList) {
83264ab3302SCarolineConcatto       const auto &allocateObject{
83364ab3302SCarolineConcatto           std::get<parser::AllocateObject>(allocation.t)};
83464ab3302SCarolineConcatto       if (IsCoarrayObject(allocateObject)) {
83564ab3302SCarolineConcatto         return true;
83664ab3302SCarolineConcatto       }
83764ab3302SCarolineConcatto     }
83864ab3302SCarolineConcatto     return false;
83964ab3302SCarolineConcatto   }
84064ab3302SCarolineConcatto   bool operator()(const parser::DeallocateStmt &stmt) {
84164ab3302SCarolineConcatto     const auto &allocateObjectList{
84264ab3302SCarolineConcatto         std::get<std::list<parser::AllocateObject>>(stmt.t)};
84364ab3302SCarolineConcatto     for (const auto &allocateObject : allocateObjectList) {
84464ab3302SCarolineConcatto       if (IsCoarrayObject(allocateObject)) {
84564ab3302SCarolineConcatto         return true;
84664ab3302SCarolineConcatto       }
84764ab3302SCarolineConcatto     }
84864ab3302SCarolineConcatto     return false;
84964ab3302SCarolineConcatto   }
85064ab3302SCarolineConcatto   bool operator()(const parser::CallStmt &stmt) {
85164ab3302SCarolineConcatto     const auto &procedureDesignator{
85264ab3302SCarolineConcatto         std::get<parser::ProcedureDesignator>(stmt.v.t)};
85364ab3302SCarolineConcatto     if (auto *name{std::get_if<parser::Name>(&procedureDesignator.u)}) {
85464ab3302SCarolineConcatto       // TODO: also ensure that the procedure is, in fact, an intrinsic
85564ab3302SCarolineConcatto       if (name->source == "move_alloc") {
85664ab3302SCarolineConcatto         const auto &args{std::get<std::list<parser::ActualArgSpec>>(stmt.v.t)};
85764ab3302SCarolineConcatto         if (!args.empty()) {
85864ab3302SCarolineConcatto           const parser::ActualArg &actualArg{
85964ab3302SCarolineConcatto               std::get<parser::ActualArg>(args.front().t)};
86064ab3302SCarolineConcatto           if (const auto *argExpr{
86164ab3302SCarolineConcatto                   std::get_if<common::Indirection<parser::Expr>>(
86264ab3302SCarolineConcatto                       &actualArg.u)}) {
86364ab3302SCarolineConcatto             return HasCoarray(argExpr->value());
86464ab3302SCarolineConcatto           }
86564ab3302SCarolineConcatto         }
86664ab3302SCarolineConcatto       }
86764ab3302SCarolineConcatto     }
86864ab3302SCarolineConcatto     return false;
86964ab3302SCarolineConcatto   }
87064ab3302SCarolineConcatto   bool operator()(const parser::Statement<parser::ActionStmt> &stmt) {
87164ab3302SCarolineConcatto     return std::visit(*this, stmt.statement.u);
87264ab3302SCarolineConcatto   }
87364ab3302SCarolineConcatto 
87464ab3302SCarolineConcatto private:
87564ab3302SCarolineConcatto   bool IsCoarrayObject(const parser::AllocateObject &allocateObject) {
87664ab3302SCarolineConcatto     const parser::Name &name{GetLastName(allocateObject)};
87764ab3302SCarolineConcatto     return name.symbol && IsCoarray(*name.symbol);
87864ab3302SCarolineConcatto   }
87964ab3302SCarolineConcatto };
88064ab3302SCarolineConcatto 
88164ab3302SCarolineConcatto bool IsImageControlStmt(const parser::ExecutableConstruct &construct) {
88264ab3302SCarolineConcatto   return std::visit(ImageControlStmtHelper{}, construct.u);
88364ab3302SCarolineConcatto }
88464ab3302SCarolineConcatto 
88564ab3302SCarolineConcatto std::optional<parser::MessageFixedText> GetImageControlStmtCoarrayMsg(
88664ab3302SCarolineConcatto     const parser::ExecutableConstruct &construct) {
88764ab3302SCarolineConcatto   if (const auto *actionStmt{
88864ab3302SCarolineConcatto           std::get_if<parser::Statement<parser::ActionStmt>>(&construct.u)}) {
88964ab3302SCarolineConcatto     return std::visit(
89064ab3302SCarolineConcatto         common::visitors{
89164ab3302SCarolineConcatto             [](const common::Indirection<parser::AllocateStmt> &)
89264ab3302SCarolineConcatto                 -> std::optional<parser::MessageFixedText> {
89364ab3302SCarolineConcatto               return "ALLOCATE of a coarray is an image control"
89464ab3302SCarolineConcatto                      " statement"_en_US;
89564ab3302SCarolineConcatto             },
89664ab3302SCarolineConcatto             [](const common::Indirection<parser::DeallocateStmt> &)
89764ab3302SCarolineConcatto                 -> std::optional<parser::MessageFixedText> {
89864ab3302SCarolineConcatto               return "DEALLOCATE of a coarray is an image control"
89964ab3302SCarolineConcatto                      " statement"_en_US;
90064ab3302SCarolineConcatto             },
90164ab3302SCarolineConcatto             [](const common::Indirection<parser::CallStmt> &)
90264ab3302SCarolineConcatto                 -> std::optional<parser::MessageFixedText> {
90364ab3302SCarolineConcatto               return "MOVE_ALLOC of a coarray is an image control"
90464ab3302SCarolineConcatto                      " statement "_en_US;
90564ab3302SCarolineConcatto             },
90664ab3302SCarolineConcatto             [](const auto &) -> std::optional<parser::MessageFixedText> {
90764ab3302SCarolineConcatto               return std::nullopt;
90864ab3302SCarolineConcatto             },
90964ab3302SCarolineConcatto         },
91064ab3302SCarolineConcatto         actionStmt->statement.u);
91164ab3302SCarolineConcatto   }
91264ab3302SCarolineConcatto   return std::nullopt;
91364ab3302SCarolineConcatto }
91464ab3302SCarolineConcatto 
91564ab3302SCarolineConcatto parser::CharBlock GetImageControlStmtLocation(
91664ab3302SCarolineConcatto     const parser::ExecutableConstruct &executableConstruct) {
91764ab3302SCarolineConcatto   return std::visit(
91864ab3302SCarolineConcatto       common::visitors{
91964ab3302SCarolineConcatto           [](const common::Indirection<parser::ChangeTeamConstruct>
92064ab3302SCarolineConcatto                   &construct) {
92164ab3302SCarolineConcatto             return std::get<parser::Statement<parser::ChangeTeamStmt>>(
92264ab3302SCarolineConcatto                 construct.value().t)
92364ab3302SCarolineConcatto                 .source;
92464ab3302SCarolineConcatto           },
92564ab3302SCarolineConcatto           [](const common::Indirection<parser::CriticalConstruct> &construct) {
92664ab3302SCarolineConcatto             return std::get<parser::Statement<parser::CriticalStmt>>(
92764ab3302SCarolineConcatto                 construct.value().t)
92864ab3302SCarolineConcatto                 .source;
92964ab3302SCarolineConcatto           },
93064ab3302SCarolineConcatto           [](const parser::Statement<parser::ActionStmt> &actionStmt) {
93164ab3302SCarolineConcatto             return actionStmt.source;
93264ab3302SCarolineConcatto           },
93364ab3302SCarolineConcatto           [](const auto &) { return parser::CharBlock{}; },
93464ab3302SCarolineConcatto       },
93564ab3302SCarolineConcatto       executableConstruct.u);
93664ab3302SCarolineConcatto }
93764ab3302SCarolineConcatto 
93864ab3302SCarolineConcatto bool HasCoarray(const parser::Expr &expression) {
93964ab3302SCarolineConcatto   if (const auto *expr{GetExpr(expression)}) {
94064ab3302SCarolineConcatto     for (const Symbol &symbol : evaluate::CollectSymbols(*expr)) {
94164ab3302SCarolineConcatto       if (const Symbol * root{GetAssociationRoot(symbol)}) {
94264ab3302SCarolineConcatto         if (IsCoarray(*root)) {
94364ab3302SCarolineConcatto           return true;
94464ab3302SCarolineConcatto         }
94564ab3302SCarolineConcatto       }
94664ab3302SCarolineConcatto     }
94764ab3302SCarolineConcatto   }
94864ab3302SCarolineConcatto   return false;
94964ab3302SCarolineConcatto }
95064ab3302SCarolineConcatto 
95164ab3302SCarolineConcatto bool IsPolymorphic(const Symbol &symbol) {
95264ab3302SCarolineConcatto   if (const DeclTypeSpec * type{symbol.GetType()}) {
95364ab3302SCarolineConcatto     return type->IsPolymorphic();
95464ab3302SCarolineConcatto   }
95564ab3302SCarolineConcatto   return false;
95664ab3302SCarolineConcatto }
95764ab3302SCarolineConcatto 
95864ab3302SCarolineConcatto bool IsPolymorphicAllocatable(const Symbol &symbol) {
95964ab3302SCarolineConcatto   return IsAllocatable(symbol) && IsPolymorphic(symbol);
96064ab3302SCarolineConcatto }
96164ab3302SCarolineConcatto 
96264ab3302SCarolineConcatto std::list<SourceName> OrderParameterNames(const Symbol &typeSymbol) {
96364ab3302SCarolineConcatto   std::list<SourceName> result;
96464ab3302SCarolineConcatto   if (const DerivedTypeSpec * spec{typeSymbol.GetParentTypeSpec()}) {
96564ab3302SCarolineConcatto     result = OrderParameterNames(spec->typeSymbol());
96664ab3302SCarolineConcatto   }
96764ab3302SCarolineConcatto   const auto &paramNames{typeSymbol.get<DerivedTypeDetails>().paramNames()};
96864ab3302SCarolineConcatto   result.insert(result.end(), paramNames.begin(), paramNames.end());
96964ab3302SCarolineConcatto   return result;
97064ab3302SCarolineConcatto }
97164ab3302SCarolineConcatto 
97264ab3302SCarolineConcatto SymbolVector OrderParameterDeclarations(const Symbol &typeSymbol) {
97364ab3302SCarolineConcatto   SymbolVector result;
97464ab3302SCarolineConcatto   if (const DerivedTypeSpec * spec{typeSymbol.GetParentTypeSpec()}) {
97564ab3302SCarolineConcatto     result = OrderParameterDeclarations(spec->typeSymbol());
97664ab3302SCarolineConcatto   }
97764ab3302SCarolineConcatto   const auto &paramDecls{typeSymbol.get<DerivedTypeDetails>().paramDecls()};
97864ab3302SCarolineConcatto   result.insert(result.end(), paramDecls.begin(), paramDecls.end());
97964ab3302SCarolineConcatto   return result;
98064ab3302SCarolineConcatto }
98164ab3302SCarolineConcatto 
98264ab3302SCarolineConcatto const DeclTypeSpec &FindOrInstantiateDerivedType(Scope &scope,
98364ab3302SCarolineConcatto     DerivedTypeSpec &&spec, SemanticsContext &semanticsContext,
98464ab3302SCarolineConcatto     DeclTypeSpec::Category category) {
98564ab3302SCarolineConcatto   spec.CookParameters(semanticsContext.foldingContext());
98664ab3302SCarolineConcatto   spec.EvaluateParameters(semanticsContext.foldingContext());
98764ab3302SCarolineConcatto   if (const DeclTypeSpec *
98864ab3302SCarolineConcatto       type{scope.FindInstantiatedDerivedType(spec, category)}) {
98964ab3302SCarolineConcatto     return *type;
99064ab3302SCarolineConcatto   }
99164ab3302SCarolineConcatto   // Create a new instantiation of this parameterized derived type
99264ab3302SCarolineConcatto   // for this particular distinct set of actual parameter values.
99364ab3302SCarolineConcatto   DeclTypeSpec &type{scope.MakeDerivedType(category, std::move(spec))};
99464ab3302SCarolineConcatto   type.derivedTypeSpec().Instantiate(scope, semanticsContext);
99564ab3302SCarolineConcatto   return type;
99664ab3302SCarolineConcatto }
99764ab3302SCarolineConcatto 
99864ab3302SCarolineConcatto // ComponentIterator implementation
99964ab3302SCarolineConcatto 
100064ab3302SCarolineConcatto template<ComponentKind componentKind>
100164ab3302SCarolineConcatto typename ComponentIterator<componentKind>::const_iterator
100264ab3302SCarolineConcatto ComponentIterator<componentKind>::const_iterator::Create(
100364ab3302SCarolineConcatto     const DerivedTypeSpec &derived) {
100464ab3302SCarolineConcatto   const_iterator it{};
100564ab3302SCarolineConcatto   it.componentPath_.emplace_back(derived);
100664ab3302SCarolineConcatto   it.Increment();  // cue up first relevant component, if any
100764ab3302SCarolineConcatto   return it;
100864ab3302SCarolineConcatto }
100964ab3302SCarolineConcatto 
101064ab3302SCarolineConcatto template<ComponentKind componentKind>
101164ab3302SCarolineConcatto const DerivedTypeSpec *
101264ab3302SCarolineConcatto ComponentIterator<componentKind>::const_iterator::PlanComponentTraversal(
101364ab3302SCarolineConcatto     const Symbol &component) const {
101464ab3302SCarolineConcatto   if (const auto *details{component.detailsIf<ObjectEntityDetails>()}) {
101564ab3302SCarolineConcatto     if (const DeclTypeSpec * type{details->type()}) {
101664ab3302SCarolineConcatto       if (const auto *derived{type->AsDerived()}) {
101764ab3302SCarolineConcatto         bool traverse{false};
101864ab3302SCarolineConcatto         if constexpr (componentKind == ComponentKind::Ordered) {
101964ab3302SCarolineConcatto           // Order Component (only visit parents)
102064ab3302SCarolineConcatto           traverse = component.test(Symbol::Flag::ParentComp);
102164ab3302SCarolineConcatto         } else if constexpr (componentKind == ComponentKind::Direct) {
102264ab3302SCarolineConcatto           traverse = !IsAllocatableOrPointer(component);
102364ab3302SCarolineConcatto         } else if constexpr (componentKind == ComponentKind::Ultimate) {
102464ab3302SCarolineConcatto           traverse = !IsAllocatableOrPointer(component);
102564ab3302SCarolineConcatto         } else if constexpr (componentKind == ComponentKind::Potential) {
102664ab3302SCarolineConcatto           traverse = !IsPointer(component);
102764ab3302SCarolineConcatto         } else if constexpr (componentKind == ComponentKind::Scope) {
102864ab3302SCarolineConcatto           traverse = !IsAllocatableOrPointer(component);
102964ab3302SCarolineConcatto         }
103064ab3302SCarolineConcatto         if (traverse) {
103164ab3302SCarolineConcatto           const Symbol &newTypeSymbol{derived->typeSymbol()};
103264ab3302SCarolineConcatto           // Avoid infinite loop if the type is already part of the types
103364ab3302SCarolineConcatto           // being visited. It is possible to have "loops in type" because
103464ab3302SCarolineConcatto           // C744 does not forbid to use not yet declared type for
103564ab3302SCarolineConcatto           // ALLOCATABLE or POINTER components.
103664ab3302SCarolineConcatto           for (const auto &node : componentPath_) {
103764ab3302SCarolineConcatto             if (&newTypeSymbol == &node.GetTypeSymbol()) {
103864ab3302SCarolineConcatto               return nullptr;
103964ab3302SCarolineConcatto             }
104064ab3302SCarolineConcatto           }
104164ab3302SCarolineConcatto           return derived;
104264ab3302SCarolineConcatto         }
104364ab3302SCarolineConcatto       }
104464ab3302SCarolineConcatto     }  // intrinsic & unlimited polymorphic not traversable
104564ab3302SCarolineConcatto   }
104664ab3302SCarolineConcatto   return nullptr;
104764ab3302SCarolineConcatto }
104864ab3302SCarolineConcatto 
104964ab3302SCarolineConcatto template<ComponentKind componentKind>
105064ab3302SCarolineConcatto static bool StopAtComponentPre(const Symbol &component) {
105164ab3302SCarolineConcatto   if constexpr (componentKind == ComponentKind::Ordered) {
105264ab3302SCarolineConcatto     // Parent components need to be iterated upon after their
105364ab3302SCarolineConcatto     // sub-components in structure constructor analysis.
105464ab3302SCarolineConcatto     return !component.test(Symbol::Flag::ParentComp);
105564ab3302SCarolineConcatto   } else if constexpr (componentKind == ComponentKind::Direct) {
105664ab3302SCarolineConcatto     return true;
105764ab3302SCarolineConcatto   } else if constexpr (componentKind == ComponentKind::Ultimate) {
105864ab3302SCarolineConcatto     return component.has<ProcEntityDetails>() ||
105964ab3302SCarolineConcatto         IsAllocatableOrPointer(component) ||
106064ab3302SCarolineConcatto         (component.get<ObjectEntityDetails>().type() &&
106164ab3302SCarolineConcatto             component.get<ObjectEntityDetails>().type()->AsIntrinsic());
106264ab3302SCarolineConcatto   } else if constexpr (componentKind == ComponentKind::Potential) {
106364ab3302SCarolineConcatto     return !IsPointer(component);
106464ab3302SCarolineConcatto   }
106564ab3302SCarolineConcatto }
106664ab3302SCarolineConcatto 
106764ab3302SCarolineConcatto template<ComponentKind componentKind>
106864ab3302SCarolineConcatto static bool StopAtComponentPost(const Symbol &component) {
106964ab3302SCarolineConcatto   return componentKind == ComponentKind::Ordered &&
107064ab3302SCarolineConcatto       component.test(Symbol::Flag::ParentComp);
107164ab3302SCarolineConcatto }
107264ab3302SCarolineConcatto 
107364ab3302SCarolineConcatto template<ComponentKind componentKind>
107464ab3302SCarolineConcatto void ComponentIterator<componentKind>::const_iterator::Increment() {
107564ab3302SCarolineConcatto   while (!componentPath_.empty()) {
107664ab3302SCarolineConcatto     ComponentPathNode &deepest{componentPath_.back()};
107764ab3302SCarolineConcatto     if (deepest.component()) {
107864ab3302SCarolineConcatto       if (!deepest.descended()) {
107964ab3302SCarolineConcatto         deepest.set_descended(true);
108064ab3302SCarolineConcatto         if (const DerivedTypeSpec *
108164ab3302SCarolineConcatto             derived{PlanComponentTraversal(*deepest.component())}) {
108264ab3302SCarolineConcatto           componentPath_.emplace_back(*derived);
108364ab3302SCarolineConcatto           continue;
108464ab3302SCarolineConcatto         }
108564ab3302SCarolineConcatto       } else if (!deepest.visited()) {
108664ab3302SCarolineConcatto         deepest.set_visited(true);
108764ab3302SCarolineConcatto         return;  // this is the next component to visit, after descending
108864ab3302SCarolineConcatto       }
108964ab3302SCarolineConcatto     }
109064ab3302SCarolineConcatto     auto &nameIterator{deepest.nameIterator()};
109164ab3302SCarolineConcatto     if (nameIterator == deepest.nameEnd()) {
109264ab3302SCarolineConcatto       componentPath_.pop_back();
109364ab3302SCarolineConcatto     } else if constexpr (componentKind == ComponentKind::Scope) {
109464ab3302SCarolineConcatto       deepest.set_component(*nameIterator++->second);
109564ab3302SCarolineConcatto       deepest.set_descended(false);
109664ab3302SCarolineConcatto       deepest.set_visited(true);
109764ab3302SCarolineConcatto       return;  // this is the next component to visit, before descending
109864ab3302SCarolineConcatto     } else {
109964ab3302SCarolineConcatto       const Scope &scope{deepest.GetScope()};
110064ab3302SCarolineConcatto       auto scopeIter{scope.find(*nameIterator++)};
110164ab3302SCarolineConcatto       if (scopeIter != scope.cend()) {
110264ab3302SCarolineConcatto         const Symbol &component{*scopeIter->second};
110364ab3302SCarolineConcatto         deepest.set_component(component);
110464ab3302SCarolineConcatto         deepest.set_descended(false);
110564ab3302SCarolineConcatto         if (StopAtComponentPre<componentKind>(component)) {
110664ab3302SCarolineConcatto           deepest.set_visited(true);
110764ab3302SCarolineConcatto           return;  // this is the next component to visit, before descending
110864ab3302SCarolineConcatto         } else {
110964ab3302SCarolineConcatto           deepest.set_visited(!StopAtComponentPost<componentKind>(component));
111064ab3302SCarolineConcatto         }
111164ab3302SCarolineConcatto       }
111264ab3302SCarolineConcatto     }
111364ab3302SCarolineConcatto   }
111464ab3302SCarolineConcatto }
111564ab3302SCarolineConcatto 
111664ab3302SCarolineConcatto template<ComponentKind componentKind>
111764ab3302SCarolineConcatto std::string
111864ab3302SCarolineConcatto ComponentIterator<componentKind>::const_iterator::BuildResultDesignatorName()
111964ab3302SCarolineConcatto     const {
112064ab3302SCarolineConcatto   std::string designator{""};
112164ab3302SCarolineConcatto   for (const auto &node : componentPath_) {
112264ab3302SCarolineConcatto     designator += "%" + DEREF(node.component()).name().ToString();
112364ab3302SCarolineConcatto   }
112464ab3302SCarolineConcatto   return designator;
112564ab3302SCarolineConcatto }
112664ab3302SCarolineConcatto 
112764ab3302SCarolineConcatto template class ComponentIterator<ComponentKind::Ordered>;
112864ab3302SCarolineConcatto template class ComponentIterator<ComponentKind::Direct>;
112964ab3302SCarolineConcatto template class ComponentIterator<ComponentKind::Ultimate>;
113064ab3302SCarolineConcatto template class ComponentIterator<ComponentKind::Potential>;
113164ab3302SCarolineConcatto template class ComponentIterator<ComponentKind::Scope>;
113264ab3302SCarolineConcatto 
113364ab3302SCarolineConcatto UltimateComponentIterator::const_iterator FindCoarrayUltimateComponent(
113464ab3302SCarolineConcatto     const DerivedTypeSpec &derived) {
113564ab3302SCarolineConcatto   UltimateComponentIterator ultimates{derived};
113664ab3302SCarolineConcatto   return std::find_if(ultimates.begin(), ultimates.end(), IsCoarray);
113764ab3302SCarolineConcatto }
113864ab3302SCarolineConcatto 
113964ab3302SCarolineConcatto UltimateComponentIterator::const_iterator FindPointerUltimateComponent(
114064ab3302SCarolineConcatto     const DerivedTypeSpec &derived) {
114164ab3302SCarolineConcatto   UltimateComponentIterator ultimates{derived};
114264ab3302SCarolineConcatto   return std::find_if(ultimates.begin(), ultimates.end(), IsPointer);
114364ab3302SCarolineConcatto }
114464ab3302SCarolineConcatto 
114564ab3302SCarolineConcatto PotentialComponentIterator::const_iterator FindEventOrLockPotentialComponent(
114664ab3302SCarolineConcatto     const DerivedTypeSpec &derived) {
114764ab3302SCarolineConcatto   PotentialComponentIterator potentials{derived};
114864ab3302SCarolineConcatto   return std::find_if(
114964ab3302SCarolineConcatto       potentials.begin(), potentials.end(), [](const Symbol &component) {
115064ab3302SCarolineConcatto         if (const auto *details{component.detailsIf<ObjectEntityDetails>()}) {
115164ab3302SCarolineConcatto           const DeclTypeSpec *type{details->type()};
115264ab3302SCarolineConcatto           return type && IsEventTypeOrLockType(type->AsDerived());
115364ab3302SCarolineConcatto         }
115464ab3302SCarolineConcatto         return false;
115564ab3302SCarolineConcatto       });
115664ab3302SCarolineConcatto }
115764ab3302SCarolineConcatto 
115864ab3302SCarolineConcatto UltimateComponentIterator::const_iterator FindAllocatableUltimateComponent(
115964ab3302SCarolineConcatto     const DerivedTypeSpec &derived) {
116064ab3302SCarolineConcatto   UltimateComponentIterator ultimates{derived};
116164ab3302SCarolineConcatto   return std::find_if(ultimates.begin(), ultimates.end(), IsAllocatable);
116264ab3302SCarolineConcatto }
116364ab3302SCarolineConcatto 
116464ab3302SCarolineConcatto UltimateComponentIterator::const_iterator
116564ab3302SCarolineConcatto FindPolymorphicAllocatableUltimateComponent(const DerivedTypeSpec &derived) {
116664ab3302SCarolineConcatto   UltimateComponentIterator ultimates{derived};
116764ab3302SCarolineConcatto   return std::find_if(
116864ab3302SCarolineConcatto       ultimates.begin(), ultimates.end(), IsPolymorphicAllocatable);
116964ab3302SCarolineConcatto }
117064ab3302SCarolineConcatto 
117164ab3302SCarolineConcatto UltimateComponentIterator::const_iterator
117264ab3302SCarolineConcatto FindPolymorphicAllocatableNonCoarrayUltimateComponent(
117364ab3302SCarolineConcatto     const DerivedTypeSpec &derived) {
117464ab3302SCarolineConcatto   UltimateComponentIterator ultimates{derived};
117564ab3302SCarolineConcatto   return std::find_if(ultimates.begin(), ultimates.end(), [](const Symbol &x) {
117664ab3302SCarolineConcatto     return IsPolymorphicAllocatable(x) && !IsCoarray(x);
117764ab3302SCarolineConcatto   });
117864ab3302SCarolineConcatto }
117964ab3302SCarolineConcatto 
118064ab3302SCarolineConcatto const Symbol *FindUltimateComponent(const DerivedTypeSpec &derived,
118164ab3302SCarolineConcatto     const std::function<bool(const Symbol &)> &predicate) {
118264ab3302SCarolineConcatto   UltimateComponentIterator ultimates{derived};
118364ab3302SCarolineConcatto   if (auto it{std::find_if(ultimates.begin(), ultimates.end(),
118464ab3302SCarolineConcatto           [&predicate](const Symbol &component) -> bool {
118564ab3302SCarolineConcatto             return predicate(component);
118664ab3302SCarolineConcatto           })}) {
118764ab3302SCarolineConcatto     return &*it;
118864ab3302SCarolineConcatto   }
118964ab3302SCarolineConcatto   return nullptr;
119064ab3302SCarolineConcatto }
119164ab3302SCarolineConcatto 
119264ab3302SCarolineConcatto const Symbol *FindUltimateComponent(const Symbol &symbol,
119364ab3302SCarolineConcatto     const std::function<bool(const Symbol &)> &predicate) {
119464ab3302SCarolineConcatto   if (predicate(symbol)) {
119564ab3302SCarolineConcatto     return &symbol;
119664ab3302SCarolineConcatto   } else if (const auto *object{symbol.detailsIf<ObjectEntityDetails>()}) {
119764ab3302SCarolineConcatto     if (const auto *type{object->type()}) {
119864ab3302SCarolineConcatto       if (const auto *derived{type->AsDerived()}) {
119964ab3302SCarolineConcatto         return FindUltimateComponent(*derived, predicate);
120064ab3302SCarolineConcatto       }
120164ab3302SCarolineConcatto     }
120264ab3302SCarolineConcatto   }
120364ab3302SCarolineConcatto   return nullptr;
120464ab3302SCarolineConcatto }
120564ab3302SCarolineConcatto 
120664ab3302SCarolineConcatto const Symbol *FindImmediateComponent(const DerivedTypeSpec &type,
120764ab3302SCarolineConcatto     const std::function<bool(const Symbol &)> &predicate) {
120864ab3302SCarolineConcatto   if (const Scope * scope{type.scope()}) {
120964ab3302SCarolineConcatto     const Symbol *parent{nullptr};
121064ab3302SCarolineConcatto     for (const auto &pair : *scope) {
121164ab3302SCarolineConcatto       const Symbol *symbol{&*pair.second};
121264ab3302SCarolineConcatto       if (predicate(*symbol)) {
121364ab3302SCarolineConcatto         return symbol;
121464ab3302SCarolineConcatto       }
121564ab3302SCarolineConcatto       if (symbol->test(Symbol::Flag::ParentComp)) {
121664ab3302SCarolineConcatto         parent = symbol;
121764ab3302SCarolineConcatto       }
121864ab3302SCarolineConcatto     }
121964ab3302SCarolineConcatto     if (parent) {
122064ab3302SCarolineConcatto       if (const auto *object{parent->detailsIf<ObjectEntityDetails>()}) {
122164ab3302SCarolineConcatto         if (const auto *type{object->type()}) {
122264ab3302SCarolineConcatto           if (const auto *derived{type->AsDerived()}) {
122364ab3302SCarolineConcatto             return FindImmediateComponent(*derived, predicate);
122464ab3302SCarolineConcatto           }
122564ab3302SCarolineConcatto         }
122664ab3302SCarolineConcatto       }
122764ab3302SCarolineConcatto     }
122864ab3302SCarolineConcatto   }
122964ab3302SCarolineConcatto   return nullptr;
123064ab3302SCarolineConcatto }
123164ab3302SCarolineConcatto 
123264ab3302SCarolineConcatto bool IsFunctionResult(const Symbol &symbol) {
123364ab3302SCarolineConcatto   return (symbol.has<semantics::ObjectEntityDetails>() &&
123464ab3302SCarolineConcatto              symbol.get<semantics::ObjectEntityDetails>().isFuncResult()) ||
123564ab3302SCarolineConcatto       (symbol.has<semantics::ProcEntityDetails>() &&
123664ab3302SCarolineConcatto           symbol.get<semantics::ProcEntityDetails>().isFuncResult());
123764ab3302SCarolineConcatto }
123864ab3302SCarolineConcatto 
123964ab3302SCarolineConcatto bool IsFunctionResultWithSameNameAsFunction(const Symbol &symbol) {
124064ab3302SCarolineConcatto   if (IsFunctionResult(symbol)) {
124164ab3302SCarolineConcatto     if (const Symbol * function{symbol.owner().symbol()}) {
124264ab3302SCarolineConcatto       return symbol.name() == function->name();
124364ab3302SCarolineConcatto     }
124464ab3302SCarolineConcatto   }
124564ab3302SCarolineConcatto   return false;
124664ab3302SCarolineConcatto }
124764ab3302SCarolineConcatto 
124864ab3302SCarolineConcatto void LabelEnforce::Post(const parser::GotoStmt &gotoStmt) {
124964ab3302SCarolineConcatto   checkLabelUse(gotoStmt.v);
125064ab3302SCarolineConcatto }
125164ab3302SCarolineConcatto void LabelEnforce::Post(const parser::ComputedGotoStmt &computedGotoStmt) {
125264ab3302SCarolineConcatto   for (auto &i : std::get<std::list<parser::Label>>(computedGotoStmt.t)) {
125364ab3302SCarolineConcatto     checkLabelUse(i);
125464ab3302SCarolineConcatto   }
125564ab3302SCarolineConcatto }
125664ab3302SCarolineConcatto 
125764ab3302SCarolineConcatto void LabelEnforce::Post(const parser::ArithmeticIfStmt &arithmeticIfStmt) {
125864ab3302SCarolineConcatto   checkLabelUse(std::get<1>(arithmeticIfStmt.t));
125964ab3302SCarolineConcatto   checkLabelUse(std::get<2>(arithmeticIfStmt.t));
126064ab3302SCarolineConcatto   checkLabelUse(std::get<3>(arithmeticIfStmt.t));
126164ab3302SCarolineConcatto }
126264ab3302SCarolineConcatto 
126364ab3302SCarolineConcatto void LabelEnforce::Post(const parser::AssignStmt &assignStmt) {
126464ab3302SCarolineConcatto   checkLabelUse(std::get<parser::Label>(assignStmt.t));
126564ab3302SCarolineConcatto }
126664ab3302SCarolineConcatto 
126764ab3302SCarolineConcatto void LabelEnforce::Post(const parser::AssignedGotoStmt &assignedGotoStmt) {
126864ab3302SCarolineConcatto   for (auto &i : std::get<std::list<parser::Label>>(assignedGotoStmt.t)) {
126964ab3302SCarolineConcatto     checkLabelUse(i);
127064ab3302SCarolineConcatto   }
127164ab3302SCarolineConcatto }
127264ab3302SCarolineConcatto 
127364ab3302SCarolineConcatto void LabelEnforce::Post(const parser::AltReturnSpec &altReturnSpec) {
127464ab3302SCarolineConcatto   checkLabelUse(altReturnSpec.v);
127564ab3302SCarolineConcatto }
127664ab3302SCarolineConcatto 
127764ab3302SCarolineConcatto void LabelEnforce::Post(const parser::ErrLabel &errLabel) {
127864ab3302SCarolineConcatto   checkLabelUse(errLabel.v);
127964ab3302SCarolineConcatto }
128064ab3302SCarolineConcatto void LabelEnforce::Post(const parser::EndLabel &endLabel) {
128164ab3302SCarolineConcatto   checkLabelUse(endLabel.v);
128264ab3302SCarolineConcatto }
128364ab3302SCarolineConcatto void LabelEnforce::Post(const parser::EorLabel &eorLabel) {
128464ab3302SCarolineConcatto   checkLabelUse(eorLabel.v);
128564ab3302SCarolineConcatto }
128664ab3302SCarolineConcatto 
128764ab3302SCarolineConcatto void LabelEnforce::checkLabelUse(const parser::Label &labelUsed) {
128864ab3302SCarolineConcatto   if (labels_.find(labelUsed) == labels_.end()) {
128964ab3302SCarolineConcatto     SayWithConstruct(context_, currentStatementSourcePosition_,
129064ab3302SCarolineConcatto         parser::MessageFormattedText{
129164ab3302SCarolineConcatto             "Control flow escapes from %s"_err_en_US, construct_},
129264ab3302SCarolineConcatto         constructSourcePosition_);
129364ab3302SCarolineConcatto   }
129464ab3302SCarolineConcatto }
129564ab3302SCarolineConcatto 
129664ab3302SCarolineConcatto parser::MessageFormattedText LabelEnforce::GetEnclosingConstructMsg() {
129764ab3302SCarolineConcatto   return {"Enclosing %s statement"_en_US, construct_};
129864ab3302SCarolineConcatto }
129964ab3302SCarolineConcatto 
130064ab3302SCarolineConcatto void LabelEnforce::SayWithConstruct(SemanticsContext &context,
130164ab3302SCarolineConcatto     parser::CharBlock stmtLocation, parser::MessageFormattedText &&message,
130264ab3302SCarolineConcatto     parser::CharBlock constructLocation) {
130364ab3302SCarolineConcatto   context.Say(stmtLocation, message)
130464ab3302SCarolineConcatto       .Attach(constructLocation, GetEnclosingConstructMsg());
130564ab3302SCarolineConcatto }
130664ab3302SCarolineConcatto }
1307