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
FindScopeContaining(const Scope & start,std::function<bool (const Scope &)> 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 
GetTopLevelUnitContaining(const Scope & start)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 
GetTopLevelUnitContaining(const Symbol & symbol)46 const Scope &GetTopLevelUnitContaining(const Symbol &symbol) {
47   return GetTopLevelUnitContaining(symbol.owner());
48 }
49 
FindModuleContaining(const Scope & start)50 const Scope *FindModuleContaining(const Scope &start) {
51   return FindScopeContaining(
52       start, [](const Scope &scope) { return scope.IsModule(); });
53 }
54 
FindModuleFileContaining(const Scope & start)55 const Scope *FindModuleFileContaining(const Scope &start) {
56   return FindScopeContaining(
57       start, [](const Scope &scope) { return scope.IsModuleFile(); });
58 }
59 
GetProgramUnitContaining(const Scope & start)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 
GetProgramUnitContaining(const Symbol & symbol)75 const Scope &GetProgramUnitContaining(const Symbol &symbol) {
76   return GetProgramUnitContaining(symbol.owner());
77 }
78 
FindPureProcedureContaining(const Scope & start)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 
MightHaveCompatibleDerivedtypes(const std::optional<evaluate::DynamicType> & lhsType,const std::optional<evaluate::DynamicType> & rhsType)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 
IsDefinedAssignment(const std::optional<evaluate::DynamicType> & lhsType,int lhsRank,const std::optional<evaluate::DynamicType> & rhsType,int rhsRank)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 
IsIntrinsicRelational(common::RelationalOperator opr,const evaluate::DynamicType & type0,int rank0,const evaluate::DynamicType & type1,int rank1)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 
IsIntrinsicNumeric(const evaluate::DynamicType & type0)143 bool IsIntrinsicNumeric(const evaluate::DynamicType &type0) {
144   return IsNumericTypeCategory(type0.category());
145 }
IsIntrinsicNumeric(const evaluate::DynamicType & type0,int rank0,const evaluate::DynamicType & type1,int rank1)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 
IsIntrinsicLogical(const evaluate::DynamicType & type0)153 bool IsIntrinsicLogical(const evaluate::DynamicType &type0) {
154   return type0.category() == TypeCategory::Logical;
155 }
IsIntrinsicLogical(const evaluate::DynamicType & type0,int rank0,const evaluate::DynamicType & type1,int rank1)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 
IsIntrinsicConcat(const evaluate::DynamicType & type0,int rank0,const evaluate::DynamicType & type1,int rank1)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 
IsGenericDefinedOp(const Symbol & symbol)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 
IsDefinedOperator(SourceName name)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 
MakeOpName(SourceName name)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 
IsCommonBlockContaining(const Symbol & block,const Symbol & object)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 
IsUseAssociated(const Symbol & symbol,const Scope & scope)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 
DoesScopeContain(const Scope * maybeAncestor,const Scope & maybeDescendent)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 
DoesScopeContain(const Scope * maybeAncestor,const Symbol & symbol)214 bool DoesScopeContain(const Scope *maybeAncestor, const Symbol &symbol) {
215   return DoesScopeContain(maybeAncestor, symbol.owner());
216 }
217 
FollowHostAssoc(const Symbol & symbol)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 
IsHostAssociated(const Symbol & symbol,const Scope & scope)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 
IsInStmtFunction(const Symbol & symbol)234 bool IsInStmtFunction(const Symbol &symbol) {
235   if (const Symbol * function{symbol.owner().symbol()}) {
236     return IsStmtFunction(*function);
237   }
238   return false;
239 }
240 
IsStmtFunctionDummy(const Symbol & symbol)241 bool IsStmtFunctionDummy(const Symbol &symbol) {
242   return IsDummy(symbol) && IsInStmtFunction(symbol);
243 }
244 
IsStmtFunctionResult(const Symbol & symbol)245 bool IsStmtFunctionResult(const Symbol &symbol) {
246   return IsFunctionResult(symbol) && IsInStmtFunction(symbol);
247 }
248 
IsPointerDummy(const Symbol & symbol)249 bool IsPointerDummy(const Symbol &symbol) {
250   return IsPointer(symbol) && IsDummy(symbol);
251 }
252 
253 // proc-name
IsProcName(const Symbol & symbol)254 bool IsProcName(const Symbol &symbol) {
255   return symbol.GetUltimate().has<ProcEntityDetails>();
256 }
257 
IsBindCProcedure(const Symbol & symbol)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 
IsBindCProcedure(const Scope & scope)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 
FindPointerComponent(const Scope & scope,std::set<const Scope * > & visited)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 
FindPointerComponent(const Scope & scope)310 const Symbol *FindPointerComponent(const Scope &scope) {
311   std::set<const Scope *> visited;
312   return FindPointerComponent(scope, visited);
313 }
314 
FindPointerComponent(const DerivedTypeSpec & derived)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 
FindPointerComponent(const DeclTypeSpec & type)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 
FindPointerComponent(const DeclTypeSpec * type)331 const Symbol *FindPointerComponent(const DeclTypeSpec *type) {
332   return type ? FindPointerComponent(*type) : nullptr;
333 }
334 
FindPointerComponent(const Symbol & symbol)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.
FindExternallyVisibleObject(const Symbol & object,const Scope & scope)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 
BypassGeneric(const Symbol & symbol)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 
ExprHasTypeCategory(const SomeExpr & expr,const common::TypeCategory & type)372 bool ExprHasTypeCategory(
373     const SomeExpr &expr, const common::TypeCategory &type) {
374   auto dynamicType{expr.GetType()};
375   return dynamicType && dynamicType->category() == type;
376 }
377 
ExprTypeKindIsDefault(const SomeExpr & expr,const SemanticsContext & context)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>
CheckMissingAnalysis(bool absent,const T & x)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 
GetTypedExpr(const T & x)398 template <typename T> static const SomeExpr *GetTypedExpr(const T &x) {
399   CheckMissingAnalysis(!x.typedExpr, x);
400   return common::GetPtrFromOptional(x.typedExpr->v);
401 }
Get(const parser::Expr & x)402 const SomeExpr *GetExprHelper::Get(const parser::Expr &x) {
403   return GetTypedExpr(x);
404 }
Get(const parser::Variable & x)405 const SomeExpr *GetExprHelper::Get(const parser::Variable &x) {
406   return GetTypedExpr(x);
407 }
Get(const parser::DataStmtConstant & x)408 const SomeExpr *GetExprHelper::Get(const parser::DataStmtConstant &x) {
409   return GetTypedExpr(x);
410 }
Get(const parser::AllocateObject & x)411 const SomeExpr *GetExprHelper::Get(const parser::AllocateObject &x) {
412   return GetTypedExpr(x);
413 }
Get(const parser::PointerObject & x)414 const SomeExpr *GetExprHelper::Get(const parser::PointerObject &x) {
415   return GetTypedExpr(x);
416 }
417 
GetAssignment(const parser::AssignmentStmt & x)418 const evaluate::Assignment *GetAssignment(const parser::AssignmentStmt &x) {
419   CheckMissingAnalysis(!x.typedAssignment, x);
420   return common::GetPtrFromOptional(x.typedAssignment->v);
421 }
GetAssignment(const parser::PointerAssignmentStmt & x)422 const evaluate::Assignment *GetAssignment(
423     const parser::PointerAssignmentStmt &x) {
424   CheckMissingAnalysis(!x.typedAssignment, x);
425   return common::GetPtrFromOptional(x.typedAssignment->v);
426 }
427 
FindInterface(const Symbol & symbol)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 
FindSubprogram(const Symbol & symbol)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 
FindOverriddenBinding(const Symbol & symbol)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 
FindParentTypeSpec(const DerivedTypeSpec & derived)478 const DeclTypeSpec *FindParentTypeSpec(const DerivedTypeSpec &derived) {
479   return FindParentTypeSpec(derived.typeSymbol());
480 }
481 
FindParentTypeSpec(const DeclTypeSpec & decl)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 
FindParentTypeSpec(const Scope & scope)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 
FindParentTypeSpec(const Symbol & symbol)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 
IsExtensibleType(const DerivedTypeSpec * derived)510 bool IsExtensibleType(const DerivedTypeSpec *derived) {
511   return derived && !IsIsoCType(derived) &&
512       !derived->typeSymbol().attrs().test(Attr::BIND_C) &&
513       !derived->typeSymbol().get<DerivedTypeDetails>().sequence();
514 }
515 
IsBuiltinDerivedType(const DerivedTypeSpec * derived,const char * name)516 bool IsBuiltinDerivedType(const DerivedTypeSpec *derived, const char *name) {
517   if (!derived) {
518     return false;
519   } else {
520     const auto &symbol{derived->typeSymbol()};
521     return symbol.owner().IsModule() &&
522         (symbol.owner().GetName().value() == "__fortran_builtins" ||
523             symbol.owner().GetName().value() == "__fortran_type_info") &&
524         symbol.name() == "__builtin_"s + name;
525   }
526 }
527 
IsIsoCType(const DerivedTypeSpec * derived)528 bool IsIsoCType(const DerivedTypeSpec *derived) {
529   return IsBuiltinDerivedType(derived, "c_ptr") ||
530       IsBuiltinDerivedType(derived, "c_funptr");
531 }
532 
IsTeamType(const DerivedTypeSpec * derived)533 bool IsTeamType(const DerivedTypeSpec *derived) {
534   return IsBuiltinDerivedType(derived, "team_type");
535 }
536 
IsEventTypeOrLockType(const DerivedTypeSpec * derivedTypeSpec)537 bool IsEventTypeOrLockType(const DerivedTypeSpec *derivedTypeSpec) {
538   return IsBuiltinDerivedType(derivedTypeSpec, "event_type") ||
539       IsBuiltinDerivedType(derivedTypeSpec, "lock_type");
540 }
541 
IsOrContainsEventOrLockComponent(const Symbol & original)542 bool IsOrContainsEventOrLockComponent(const Symbol &original) {
543   const Symbol &symbol{ResolveAssociations(original)};
544   if (const auto *details{symbol.detailsIf<ObjectEntityDetails>()}) {
545     if (const DeclTypeSpec * type{details->type()}) {
546       if (const DerivedTypeSpec * derived{type->AsDerived()}) {
547         return IsEventTypeOrLockType(derived) ||
548             FindEventOrLockPotentialComponent(*derived);
549       }
550     }
551   }
552   return false;
553 }
554 
555 // Check this symbol suitable as a type-bound procedure - C769
CanBeTypeBoundProc(const Symbol * symbol)556 bool CanBeTypeBoundProc(const Symbol *symbol) {
557   if (!symbol || IsDummy(*symbol) || IsProcedurePointer(*symbol)) {
558     return false;
559   } else if (symbol->has<SubprogramNameDetails>()) {
560     return symbol->owner().kind() == Scope::Kind::Module;
561   } else if (auto *details{symbol->detailsIf<SubprogramDetails>()}) {
562     return symbol->owner().kind() == Scope::Kind::Module ||
563         details->isInterface();
564   } else if (const auto *proc{symbol->detailsIf<ProcEntityDetails>()}) {
565     return !symbol->attrs().test(Attr::INTRINSIC) &&
566         proc->HasExplicitInterface();
567   } else {
568     return false;
569   }
570 }
571 
IsStaticallyInitialized(const Symbol & symbol,bool ignoreDATAstatements)572 bool IsStaticallyInitialized(const Symbol &symbol, bool ignoreDATAstatements) {
573   if (!ignoreDATAstatements && symbol.test(Symbol::Flag::InDataStmt)) {
574     return true;
575   } else if (IsNamedConstant(symbol)) {
576     return false;
577   } else if (const auto *object{symbol.detailsIf<ObjectEntityDetails>()}) {
578     return object->init().has_value();
579   } else if (const auto *proc{symbol.detailsIf<ProcEntityDetails>()}) {
580     return proc->init().has_value();
581   }
582   return false;
583 }
584 
IsInitialized(const Symbol & symbol,bool ignoreDATAstatements,const Symbol * derivedTypeSymbol)585 bool IsInitialized(const Symbol &symbol, bool ignoreDATAstatements,
586     const Symbol *derivedTypeSymbol) {
587   if (IsStaticallyInitialized(symbol, ignoreDATAstatements) ||
588       IsAllocatable(symbol)) {
589     return true;
590   } else if (IsNamedConstant(symbol) || IsFunctionResult(symbol) ||
591       IsPointer(symbol)) {
592     return false;
593   } else if (const auto *object{symbol.detailsIf<ObjectEntityDetails>()}) {
594     if (!object->isDummy() && object->type()) {
595       const auto *derived{object->type()->AsDerived()};
596       // error recovery: avoid infinite recursion on invalid
597       // recursive usage of a derived type
598       return derived && &derived->typeSymbol() != derivedTypeSymbol &&
599           derived->HasDefaultInitialization();
600     }
601   }
602   return false;
603 }
604 
IsDestructible(const Symbol & symbol,const Symbol * derivedTypeSymbol)605 bool IsDestructible(const Symbol &symbol, const Symbol *derivedTypeSymbol) {
606   if (IsAllocatable(symbol) || IsAutomatic(symbol)) {
607     return true;
608   } else if (IsNamedConstant(symbol) || IsFunctionResult(symbol) ||
609       IsPointer(symbol)) {
610     return false;
611   } else if (const auto *object{symbol.detailsIf<ObjectEntityDetails>()}) {
612     if (!object->isDummy() && object->type()) {
613       if (const auto *derived{object->type()->AsDerived()}) {
614         return &derived->typeSymbol() != derivedTypeSymbol &&
615             derived->HasDestruction();
616       }
617     }
618   }
619   return false;
620 }
621 
HasIntrinsicTypeName(const Symbol & symbol)622 bool HasIntrinsicTypeName(const Symbol &symbol) {
623   std::string name{symbol.name().ToString()};
624   if (name == "doubleprecision") {
625     return true;
626   } else if (name == "derived") {
627     return false;
628   } else {
629     for (int i{0}; i != common::TypeCategory_enumSize; ++i) {
630       if (name == parser::ToLowerCaseLetters(EnumToString(TypeCategory{i}))) {
631         return true;
632       }
633     }
634     return false;
635   }
636 }
637 
IsSeparateModuleProcedureInterface(const Symbol * symbol)638 bool IsSeparateModuleProcedureInterface(const Symbol *symbol) {
639   if (symbol && symbol->attrs().test(Attr::MODULE)) {
640     if (auto *details{symbol->detailsIf<SubprogramDetails>()}) {
641       return details->isInterface();
642     }
643   }
644   return false;
645 }
646 
647 // 3.11 automatic data object
IsAutomatic(const Symbol & symbol)648 bool IsAutomatic(const Symbol &symbol) {
649   if (const auto *object{symbol.detailsIf<ObjectEntityDetails>()}) {
650     if (!object->isDummy() && !IsAllocatable(symbol) && !IsPointer(symbol)) {
651       if (const DeclTypeSpec * type{symbol.GetType()}) {
652         // If a type parameter value is not a constant expression, the
653         // object is automatic.
654         if (type->category() == DeclTypeSpec::Character) {
655           if (const auto &length{
656                   type->characterTypeSpec().length().GetExplicit()}) {
657             if (!evaluate::IsConstantExpr(*length)) {
658               return true;
659             }
660           }
661         } else if (const DerivedTypeSpec * derived{type->AsDerived()}) {
662           for (const auto &pair : derived->parameters()) {
663             if (const auto &value{pair.second.GetExplicit()}) {
664               if (!evaluate::IsConstantExpr(*value)) {
665                 return true;
666               }
667             }
668           }
669         }
670       }
671       // If an array bound is not a constant expression, the object is
672       // automatic.
673       for (const ShapeSpec &dim : object->shape()) {
674         if (const auto &lb{dim.lbound().GetExplicit()}) {
675           if (!evaluate::IsConstantExpr(*lb)) {
676             return true;
677           }
678         }
679         if (const auto &ub{dim.ubound().GetExplicit()}) {
680           if (!evaluate::IsConstantExpr(*ub)) {
681             return true;
682           }
683         }
684       }
685     }
686   }
687   return false;
688 }
689 
IsFinalizable(const Symbol & symbol)690 bool IsFinalizable(const Symbol &symbol) {
691   if (IsPointer(symbol)) {
692     return false;
693   }
694   if (const auto *object{symbol.detailsIf<ObjectEntityDetails>()}) {
695     if (object->isDummy() && !IsIntentOut(symbol)) {
696       return false;
697     }
698     const DeclTypeSpec *type{object->type()};
699     const DerivedTypeSpec *derived{type ? type->AsDerived() : nullptr};
700     return derived && IsFinalizable(*derived);
701   }
702   return false;
703 }
704 
IsFinalizable(const DerivedTypeSpec & derived)705 bool IsFinalizable(const DerivedTypeSpec &derived) {
706   if (!derived.typeSymbol().get<DerivedTypeDetails>().finals().empty()) {
707     return true;
708   }
709   DirectComponentIterator components{derived};
710   return bool{std::find_if(components.begin(), components.end(),
711       [](const Symbol &component) { return IsFinalizable(component); })};
712 }
713 
HasImpureFinal(const DerivedTypeSpec & derived)714 bool HasImpureFinal(const DerivedTypeSpec &derived) {
715   if (const auto *details{
716           derived.typeSymbol().detailsIf<DerivedTypeDetails>()}) {
717     const auto &finals{details->finals()};
718     return std::any_of(finals.begin(), finals.end(),
719         [](const auto &x) { return !x.second->attrs().test(Attr::PURE); });
720   } else {
721     return false;
722   }
723 }
724 
IsCoarray(const Symbol & symbol)725 bool IsCoarray(const Symbol &symbol) { return symbol.Corank() > 0; }
726 
IsAutomaticObject(const Symbol & symbol)727 bool IsAutomaticObject(const Symbol &symbol) {
728   if (IsDummy(symbol) || IsPointer(symbol) || IsAllocatable(symbol)) {
729     return false;
730   }
731   if (const DeclTypeSpec * type{symbol.GetType()}) {
732     if (type->category() == DeclTypeSpec::Character) {
733       ParamValue length{type->characterTypeSpec().length()};
734       if (length.isExplicit()) {
735         if (MaybeIntExpr lengthExpr{length.GetExplicit()}) {
736           if (!ToInt64(lengthExpr)) {
737             return true;
738           }
739         }
740       }
741     }
742   }
743   if (symbol.IsObjectArray()) {
744     for (const ShapeSpec &spec : symbol.get<ObjectEntityDetails>().shape()) {
745       auto &lbound{spec.lbound().GetExplicit()};
746       auto &ubound{spec.ubound().GetExplicit()};
747       if ((lbound && !evaluate::ToInt64(*lbound)) ||
748           (ubound && !evaluate::ToInt64(*ubound))) {
749         return true;
750       }
751     }
752   }
753   return false;
754 }
755 
IsAssumedLengthCharacter(const Symbol & symbol)756 bool IsAssumedLengthCharacter(const Symbol &symbol) {
757   if (const DeclTypeSpec * type{symbol.GetType()}) {
758     return type->category() == DeclTypeSpec::Character &&
759         type->characterTypeSpec().length().isAssumed();
760   } else {
761     return false;
762   }
763 }
764 
IsInBlankCommon(const Symbol & symbol)765 bool IsInBlankCommon(const Symbol &symbol) {
766   const Symbol *block{FindCommonBlockContaining(symbol)};
767   return block && block->name().empty();
768 }
769 
770 // C722 and C723:  For a function to be assumed length, it must be external and
771 // of CHARACTER type
IsExternal(const Symbol & symbol)772 bool IsExternal(const Symbol &symbol) {
773   return ClassifyProcedure(symbol) == ProcedureDefinitionClass::External;
774 }
775 
IsModuleProcedure(const Symbol & symbol)776 bool IsModuleProcedure(const Symbol &symbol) {
777   return ClassifyProcedure(symbol) == ProcedureDefinitionClass::Module;
778 }
IsExternalInPureContext(const Symbol & symbol,const Scope & scope)779 const Symbol *IsExternalInPureContext(
780     const Symbol &symbol, const Scope &scope) {
781   if (const auto *pureProc{FindPureProcedureContaining(scope)}) {
782     return FindExternallyVisibleObject(symbol.GetUltimate(), *pureProc);
783   }
784   return nullptr;
785 }
786 
FindPolymorphicPotentialComponent(const DerivedTypeSpec & derived)787 PotentialComponentIterator::const_iterator FindPolymorphicPotentialComponent(
788     const DerivedTypeSpec &derived) {
789   PotentialComponentIterator potentials{derived};
790   return std::find_if(
791       potentials.begin(), potentials.end(), [](const Symbol &component) {
792         if (const auto *details{component.detailsIf<ObjectEntityDetails>()}) {
793           const DeclTypeSpec *type{details->type()};
794           return type && type->IsPolymorphic();
795         }
796         return false;
797       });
798 }
799 
IsOrContainsPolymorphicComponent(const Symbol & original)800 bool IsOrContainsPolymorphicComponent(const Symbol &original) {
801   const Symbol &symbol{ResolveAssociations(original)};
802   if (const auto *details{symbol.detailsIf<ObjectEntityDetails>()}) {
803     if (const DeclTypeSpec * type{details->type()}) {
804       if (type->IsPolymorphic()) {
805         return true;
806       }
807       if (const DerivedTypeSpec * derived{type->AsDerived()}) {
808         return (bool)FindPolymorphicPotentialComponent(*derived);
809       }
810     }
811   }
812   return false;
813 }
814 
InProtectedContext(const Symbol & symbol,const Scope & currentScope)815 bool InProtectedContext(const Symbol &symbol, const Scope &currentScope) {
816   return IsProtected(symbol) && !IsHostAssociated(symbol, currentScope);
817 }
818 
819 // C1101 and C1158
820 // Modifiability checks on the leftmost symbol ("base object")
821 // of a data-ref
WhyNotModifiableFirst(const Symbol & symbol,const Scope & scope)822 std::optional<parser::MessageFixedText> WhyNotModifiableFirst(
823     const Symbol &symbol, const Scope &scope) {
824   if (symbol.has<AssocEntityDetails>()) {
825     return "'%s' is construct associated with an expression"_en_US;
826   } else if (IsExternalInPureContext(symbol, scope)) {
827     return "'%s' is externally visible and referenced in a pure"
828            " procedure"_en_US;
829   } else if (!IsVariableName(symbol)) {
830     return "'%s' is not a variable"_en_US;
831   } else {
832     return std::nullopt;
833   }
834 }
835 
836 // Modifiability checks on the rightmost symbol of a data-ref
WhyNotModifiableLast(const Symbol & symbol,const Scope & scope)837 std::optional<parser::MessageFixedText> WhyNotModifiableLast(
838     const Symbol &symbol, const Scope &scope) {
839   if (IsOrContainsEventOrLockComponent(symbol)) {
840     return "'%s' is an entity with either an EVENT_TYPE or LOCK_TYPE"_en_US;
841   } else {
842     return std::nullopt;
843   }
844 }
845 
846 // Modifiability checks on the leftmost (base) symbol of a data-ref
847 // that apply only when there are no pointer components or a base
848 // that is a pointer.
WhyNotModifiableIfNoPtr(const Symbol & symbol,const Scope & scope)849 std::optional<parser::MessageFixedText> WhyNotModifiableIfNoPtr(
850     const Symbol &symbol, const Scope &scope) {
851   if (InProtectedContext(symbol, scope)) {
852     return "'%s' is protected in this scope"_en_US;
853   } else if (IsIntentIn(symbol)) {
854     return "'%s' is an INTENT(IN) dummy argument"_en_US;
855   } else {
856     return std::nullopt;
857   }
858 }
859 
860 // Apply all modifiability checks to a single symbol
WhyNotModifiable(const Symbol & original,const Scope & scope)861 std::optional<parser::MessageFixedText> WhyNotModifiable(
862     const Symbol &original, const Scope &scope) {
863   const Symbol &symbol{GetAssociationRoot(original)};
864   if (auto first{WhyNotModifiableFirst(symbol, scope)}) {
865     return first;
866   } else if (auto last{WhyNotModifiableLast(symbol, scope)}) {
867     return last;
868   } else if (!IsPointer(symbol)) {
869     return WhyNotModifiableIfNoPtr(symbol, scope);
870   } else {
871     return std::nullopt;
872   }
873 }
874 
875 // Modifiability checks for a data-ref
WhyNotModifiable(parser::CharBlock at,const SomeExpr & expr,const Scope & scope,bool vectorSubscriptIsOk)876 std::optional<parser::Message> WhyNotModifiable(parser::CharBlock at,
877     const SomeExpr &expr, const Scope &scope, bool vectorSubscriptIsOk) {
878   if (auto dataRef{evaluate::ExtractDataRef(expr, true)}) {
879     if (!vectorSubscriptIsOk && evaluate::HasVectorSubscript(expr)) {
880       return parser::Message{at, "Variable has a vector subscript"_en_US};
881     }
882     const Symbol &first{GetAssociationRoot(dataRef->GetFirstSymbol())};
883     if (auto maybeWhyFirst{WhyNotModifiableFirst(first, scope)}) {
884       return parser::Message{first.name(),
885           parser::MessageFormattedText{
886               std::move(*maybeWhyFirst), first.name()}};
887     }
888     const Symbol &last{dataRef->GetLastSymbol()};
889     if (auto maybeWhyLast{WhyNotModifiableLast(last, scope)}) {
890       return parser::Message{last.name(),
891           parser::MessageFormattedText{std::move(*maybeWhyLast), last.name()}};
892     }
893     if (!GetLastPointerSymbol(*dataRef)) {
894       if (auto maybeWhyFirst{WhyNotModifiableIfNoPtr(first, scope)}) {
895         return parser::Message{first.name(),
896             parser::MessageFormattedText{
897                 std::move(*maybeWhyFirst), first.name()}};
898       }
899     }
900   } else if (!evaluate::IsVariable(expr)) {
901     return parser::Message{
902         at, "'%s' is not a variable"_en_US, expr.AsFortran()};
903   } else {
904     // reference to function returning POINTER
905   }
906   return std::nullopt;
907 }
908 
909 class ImageControlStmtHelper {
910   using ImageControlStmts = std::variant<parser::ChangeTeamConstruct,
911       parser::CriticalConstruct, parser::EventPostStmt, parser::EventWaitStmt,
912       parser::FormTeamStmt, parser::LockStmt, parser::StopStmt,
913       parser::SyncAllStmt, parser::SyncImagesStmt, parser::SyncMemoryStmt,
914       parser::SyncTeamStmt, parser::UnlockStmt>;
915 
916 public:
operator ()(const T &)917   template <typename T> bool operator()(const T &) {
918     return common::HasMember<T, ImageControlStmts>;
919   }
operator ()(const common::Indirection<T> & x)920   template <typename T> bool operator()(const common::Indirection<T> &x) {
921     return (*this)(x.value());
922   }
operator ()(const parser::AllocateStmt & stmt)923   bool operator()(const parser::AllocateStmt &stmt) {
924     const auto &allocationList{std::get<std::list<parser::Allocation>>(stmt.t)};
925     for (const auto &allocation : allocationList) {
926       const auto &allocateObject{
927           std::get<parser::AllocateObject>(allocation.t)};
928       if (IsCoarrayObject(allocateObject)) {
929         return true;
930       }
931     }
932     return false;
933   }
operator ()(const parser::DeallocateStmt & stmt)934   bool operator()(const parser::DeallocateStmt &stmt) {
935     const auto &allocateObjectList{
936         std::get<std::list<parser::AllocateObject>>(stmt.t)};
937     for (const auto &allocateObject : allocateObjectList) {
938       if (IsCoarrayObject(allocateObject)) {
939         return true;
940       }
941     }
942     return false;
943   }
operator ()(const parser::CallStmt & stmt)944   bool operator()(const parser::CallStmt &stmt) {
945     const auto &procedureDesignator{
946         std::get<parser::ProcedureDesignator>(stmt.v.t)};
947     if (auto *name{std::get_if<parser::Name>(&procedureDesignator.u)}) {
948       // TODO: also ensure that the procedure is, in fact, an intrinsic
949       if (name->source == "move_alloc") {
950         const auto &args{std::get<std::list<parser::ActualArgSpec>>(stmt.v.t)};
951         if (!args.empty()) {
952           const parser::ActualArg &actualArg{
953               std::get<parser::ActualArg>(args.front().t)};
954           if (const auto *argExpr{
955                   std::get_if<common::Indirection<parser::Expr>>(
956                       &actualArg.u)}) {
957             return HasCoarray(argExpr->value());
958           }
959         }
960       }
961     }
962     return false;
963   }
operator ()(const parser::Statement<parser::ActionStmt> & stmt)964   bool operator()(const parser::Statement<parser::ActionStmt> &stmt) {
965     return std::visit(*this, stmt.statement.u);
966   }
967 
968 private:
IsCoarrayObject(const parser::AllocateObject & allocateObject)969   bool IsCoarrayObject(const parser::AllocateObject &allocateObject) {
970     const parser::Name &name{GetLastName(allocateObject)};
971     return name.symbol && IsCoarray(*name.symbol);
972   }
973 };
974 
IsImageControlStmt(const parser::ExecutableConstruct & construct)975 bool IsImageControlStmt(const parser::ExecutableConstruct &construct) {
976   return std::visit(ImageControlStmtHelper{}, construct.u);
977 }
978 
GetImageControlStmtCoarrayMsg(const parser::ExecutableConstruct & construct)979 std::optional<parser::MessageFixedText> GetImageControlStmtCoarrayMsg(
980     const parser::ExecutableConstruct &construct) {
981   if (const auto *actionStmt{
982           std::get_if<parser::Statement<parser::ActionStmt>>(&construct.u)}) {
983     return std::visit(
984         common::visitors{
985             [](const common::Indirection<parser::AllocateStmt> &)
986                 -> std::optional<parser::MessageFixedText> {
987               return "ALLOCATE of a coarray is an image control"
988                      " statement"_en_US;
989             },
990             [](const common::Indirection<parser::DeallocateStmt> &)
991                 -> std::optional<parser::MessageFixedText> {
992               return "DEALLOCATE of a coarray is an image control"
993                      " statement"_en_US;
994             },
995             [](const common::Indirection<parser::CallStmt> &)
996                 -> std::optional<parser::MessageFixedText> {
997               return "MOVE_ALLOC of a coarray is an image control"
998                      " statement "_en_US;
999             },
1000             [](const auto &) -> std::optional<parser::MessageFixedText> {
1001               return std::nullopt;
1002             },
1003         },
1004         actionStmt->statement.u);
1005   }
1006   return std::nullopt;
1007 }
1008 
GetImageControlStmtLocation(const parser::ExecutableConstruct & executableConstruct)1009 parser::CharBlock GetImageControlStmtLocation(
1010     const parser::ExecutableConstruct &executableConstruct) {
1011   return std::visit(
1012       common::visitors{
1013           [](const common::Indirection<parser::ChangeTeamConstruct>
1014                   &construct) {
1015             return std::get<parser::Statement<parser::ChangeTeamStmt>>(
1016                 construct.value().t)
1017                 .source;
1018           },
1019           [](const common::Indirection<parser::CriticalConstruct> &construct) {
1020             return std::get<parser::Statement<parser::CriticalStmt>>(
1021                 construct.value().t)
1022                 .source;
1023           },
1024           [](const parser::Statement<parser::ActionStmt> &actionStmt) {
1025             return actionStmt.source;
1026           },
1027           [](const auto &) { return parser::CharBlock{}; },
1028       },
1029       executableConstruct.u);
1030 }
1031 
HasCoarray(const parser::Expr & expression)1032 bool HasCoarray(const parser::Expr &expression) {
1033   if (const auto *expr{GetExpr(expression)}) {
1034     for (const Symbol &symbol : evaluate::CollectSymbols(*expr)) {
1035       if (IsCoarray(GetAssociationRoot(symbol))) {
1036         return true;
1037       }
1038     }
1039   }
1040   return false;
1041 }
1042 
IsPolymorphic(const Symbol & symbol)1043 bool IsPolymorphic(const Symbol &symbol) {
1044   if (const DeclTypeSpec * type{symbol.GetType()}) {
1045     return type->IsPolymorphic();
1046   }
1047   return false;
1048 }
1049 
IsPolymorphicAllocatable(const Symbol & symbol)1050 bool IsPolymorphicAllocatable(const Symbol &symbol) {
1051   return IsAllocatable(symbol) && IsPolymorphic(symbol);
1052 }
1053 
CheckAccessibleComponent(const Scope & scope,const Symbol & symbol)1054 std::optional<parser::MessageFormattedText> CheckAccessibleComponent(
1055     const Scope &scope, const Symbol &symbol) {
1056   CHECK(symbol.owner().IsDerivedType()); // symbol must be a component
1057   if (symbol.attrs().test(Attr::PRIVATE)) {
1058     if (FindModuleFileContaining(scope)) {
1059       // Don't enforce component accessibility checks in module files;
1060       // there may be forward-substituted named constants of derived type
1061       // whose structure constructors reference private components.
1062     } else if (const Scope *
1063         moduleScope{FindModuleContaining(symbol.owner())}) {
1064       if (!moduleScope->Contains(scope)) {
1065         return parser::MessageFormattedText{
1066             "PRIVATE component '%s' is only accessible within module '%s'"_err_en_US,
1067             symbol.name(), moduleScope->GetName().value()};
1068       }
1069     }
1070   }
1071   return std::nullopt;
1072 }
1073 
OrderParameterNames(const Symbol & typeSymbol)1074 std::list<SourceName> OrderParameterNames(const Symbol &typeSymbol) {
1075   std::list<SourceName> result;
1076   if (const DerivedTypeSpec * spec{typeSymbol.GetParentTypeSpec()}) {
1077     result = OrderParameterNames(spec->typeSymbol());
1078   }
1079   const auto &paramNames{typeSymbol.get<DerivedTypeDetails>().paramNames()};
1080   result.insert(result.end(), paramNames.begin(), paramNames.end());
1081   return result;
1082 }
1083 
OrderParameterDeclarations(const Symbol & typeSymbol)1084 SymbolVector OrderParameterDeclarations(const Symbol &typeSymbol) {
1085   SymbolVector result;
1086   if (const DerivedTypeSpec * spec{typeSymbol.GetParentTypeSpec()}) {
1087     result = OrderParameterDeclarations(spec->typeSymbol());
1088   }
1089   const auto &paramDecls{typeSymbol.get<DerivedTypeDetails>().paramDecls()};
1090   result.insert(result.end(), paramDecls.begin(), paramDecls.end());
1091   return result;
1092 }
1093 
FindOrInstantiateDerivedType(Scope & scope,DerivedTypeSpec && spec,DeclTypeSpec::Category category)1094 const DeclTypeSpec &FindOrInstantiateDerivedType(
1095     Scope &scope, DerivedTypeSpec &&spec, DeclTypeSpec::Category category) {
1096   spec.EvaluateParameters(scope.context());
1097   if (const DeclTypeSpec *
1098       type{scope.FindInstantiatedDerivedType(spec, category)}) {
1099     return *type;
1100   }
1101   // Create a new instantiation of this parameterized derived type
1102   // for this particular distinct set of actual parameter values.
1103   DeclTypeSpec &type{scope.MakeDerivedType(category, std::move(spec))};
1104   type.derivedTypeSpec().Instantiate(scope);
1105   return type;
1106 }
1107 
FindSeparateModuleSubprogramInterface(const Symbol * proc)1108 const Symbol *FindSeparateModuleSubprogramInterface(const Symbol *proc) {
1109   if (proc) {
1110     if (const Symbol * submodule{proc->owner().symbol()}) {
1111       if (const auto *details{submodule->detailsIf<ModuleDetails>()}) {
1112         if (const Scope * ancestor{details->ancestor()}) {
1113           const Symbol *iface{ancestor->FindSymbol(proc->name())};
1114           if (IsSeparateModuleProcedureInterface(iface)) {
1115             return iface;
1116           }
1117         }
1118       }
1119     }
1120   }
1121   return nullptr;
1122 }
1123 
ClassifyProcedure(const Symbol & symbol)1124 ProcedureDefinitionClass ClassifyProcedure(const Symbol &symbol) { // 15.2.2
1125   const Symbol &ultimate{symbol.GetUltimate()};
1126   if (ultimate.attrs().test(Attr::INTRINSIC)) {
1127     return ProcedureDefinitionClass::Intrinsic;
1128   } else if (ultimate.attrs().test(Attr::EXTERNAL)) {
1129     return ProcedureDefinitionClass::External;
1130   } else if (const auto *procDetails{ultimate.detailsIf<ProcEntityDetails>()}) {
1131     if (procDetails->isDummy()) {
1132       return ProcedureDefinitionClass::Dummy;
1133     } else if (IsPointer(ultimate)) {
1134       return ProcedureDefinitionClass::Pointer;
1135     }
1136   } else if (const Symbol * subp{FindSubprogram(symbol)}) {
1137     if (const auto *subpDetails{subp->detailsIf<SubprogramDetails>()}) {
1138       if (subpDetails->stmtFunction()) {
1139         return ProcedureDefinitionClass::StatementFunction;
1140       }
1141     }
1142     switch (ultimate.owner().kind()) {
1143     case Scope::Kind::Global:
1144       return ProcedureDefinitionClass::External;
1145     case Scope::Kind::Module:
1146       return ProcedureDefinitionClass::Module;
1147     case Scope::Kind::MainProgram:
1148     case Scope::Kind::Subprogram:
1149       return ProcedureDefinitionClass::Internal;
1150     default:
1151       break;
1152     }
1153   }
1154   return ProcedureDefinitionClass::None;
1155 }
1156 
1157 // ComponentIterator implementation
1158 
1159 template <ComponentKind componentKind>
1160 typename ComponentIterator<componentKind>::const_iterator
Create(const DerivedTypeSpec & derived)1161 ComponentIterator<componentKind>::const_iterator::Create(
1162     const DerivedTypeSpec &derived) {
1163   const_iterator it{};
1164   it.componentPath_.emplace_back(derived);
1165   it.Increment(); // cue up first relevant component, if any
1166   return it;
1167 }
1168 
1169 template <ComponentKind componentKind>
1170 const DerivedTypeSpec *
PlanComponentTraversal(const Symbol & component) const1171 ComponentIterator<componentKind>::const_iterator::PlanComponentTraversal(
1172     const Symbol &component) const {
1173   if (const auto *details{component.detailsIf<ObjectEntityDetails>()}) {
1174     if (const DeclTypeSpec * type{details->type()}) {
1175       if (const auto *derived{type->AsDerived()}) {
1176         bool traverse{false};
1177         if constexpr (componentKind == ComponentKind::Ordered) {
1178           // Order Component (only visit parents)
1179           traverse = component.test(Symbol::Flag::ParentComp);
1180         } else if constexpr (componentKind == ComponentKind::Direct) {
1181           traverse = !IsAllocatableOrPointer(component);
1182         } else if constexpr (componentKind == ComponentKind::Ultimate) {
1183           traverse = !IsAllocatableOrPointer(component);
1184         } else if constexpr (componentKind == ComponentKind::Potential) {
1185           traverse = !IsPointer(component);
1186         } else if constexpr (componentKind == ComponentKind::Scope) {
1187           traverse = !IsAllocatableOrPointer(component);
1188         }
1189         if (traverse) {
1190           const Symbol &newTypeSymbol{derived->typeSymbol()};
1191           // Avoid infinite loop if the type is already part of the types
1192           // being visited. It is possible to have "loops in type" because
1193           // C744 does not forbid to use not yet declared type for
1194           // ALLOCATABLE or POINTER components.
1195           for (const auto &node : componentPath_) {
1196             if (&newTypeSymbol == &node.GetTypeSymbol()) {
1197               return nullptr;
1198             }
1199           }
1200           return derived;
1201         }
1202       }
1203     } // intrinsic & unlimited polymorphic not traversable
1204   }
1205   return nullptr;
1206 }
1207 
1208 template <ComponentKind componentKind>
StopAtComponentPre(const Symbol & component)1209 static bool StopAtComponentPre(const Symbol &component) {
1210   if constexpr (componentKind == ComponentKind::Ordered) {
1211     // Parent components need to be iterated upon after their
1212     // sub-components in structure constructor analysis.
1213     return !component.test(Symbol::Flag::ParentComp);
1214   } else if constexpr (componentKind == ComponentKind::Direct) {
1215     return true;
1216   } else if constexpr (componentKind == ComponentKind::Ultimate) {
1217     return component.has<ProcEntityDetails>() ||
1218         IsAllocatableOrPointer(component) ||
1219         (component.get<ObjectEntityDetails>().type() &&
1220             component.get<ObjectEntityDetails>().type()->AsIntrinsic());
1221   } else if constexpr (componentKind == ComponentKind::Potential) {
1222     return !IsPointer(component);
1223   }
1224 }
1225 
1226 template <ComponentKind componentKind>
StopAtComponentPost(const Symbol & component)1227 static bool StopAtComponentPost(const Symbol &component) {
1228   return componentKind == ComponentKind::Ordered &&
1229       component.test(Symbol::Flag::ParentComp);
1230 }
1231 
1232 template <ComponentKind componentKind>
Increment()1233 void ComponentIterator<componentKind>::const_iterator::Increment() {
1234   while (!componentPath_.empty()) {
1235     ComponentPathNode &deepest{componentPath_.back()};
1236     if (deepest.component()) {
1237       if (!deepest.descended()) {
1238         deepest.set_descended(true);
1239         if (const DerivedTypeSpec *
1240             derived{PlanComponentTraversal(*deepest.component())}) {
1241           componentPath_.emplace_back(*derived);
1242           continue;
1243         }
1244       } else if (!deepest.visited()) {
1245         deepest.set_visited(true);
1246         return; // this is the next component to visit, after descending
1247       }
1248     }
1249     auto &nameIterator{deepest.nameIterator()};
1250     if (nameIterator == deepest.nameEnd()) {
1251       componentPath_.pop_back();
1252     } else if constexpr (componentKind == ComponentKind::Scope) {
1253       deepest.set_component(*nameIterator++->second);
1254       deepest.set_descended(false);
1255       deepest.set_visited(true);
1256       return; // this is the next component to visit, before descending
1257     } else {
1258       const Scope &scope{deepest.GetScope()};
1259       auto scopeIter{scope.find(*nameIterator++)};
1260       if (scopeIter != scope.cend()) {
1261         const Symbol &component{*scopeIter->second};
1262         deepest.set_component(component);
1263         deepest.set_descended(false);
1264         if (StopAtComponentPre<componentKind>(component)) {
1265           deepest.set_visited(true);
1266           return; // this is the next component to visit, before descending
1267         } else {
1268           deepest.set_visited(!StopAtComponentPost<componentKind>(component));
1269         }
1270       }
1271     }
1272   }
1273 }
1274 
1275 template <ComponentKind componentKind>
1276 std::string
BuildResultDesignatorName() const1277 ComponentIterator<componentKind>::const_iterator::BuildResultDesignatorName()
1278     const {
1279   std::string designator{""};
1280   for (const auto &node : componentPath_) {
1281     designator += "%" + DEREF(node.component()).name().ToString();
1282   }
1283   return designator;
1284 }
1285 
1286 template class ComponentIterator<ComponentKind::Ordered>;
1287 template class ComponentIterator<ComponentKind::Direct>;
1288 template class ComponentIterator<ComponentKind::Ultimate>;
1289 template class ComponentIterator<ComponentKind::Potential>;
1290 template class ComponentIterator<ComponentKind::Scope>;
1291 
FindCoarrayUltimateComponent(const DerivedTypeSpec & derived)1292 UltimateComponentIterator::const_iterator FindCoarrayUltimateComponent(
1293     const DerivedTypeSpec &derived) {
1294   UltimateComponentIterator ultimates{derived};
1295   return std::find_if(ultimates.begin(), ultimates.end(), IsCoarray);
1296 }
1297 
FindPointerUltimateComponent(const DerivedTypeSpec & derived)1298 UltimateComponentIterator::const_iterator FindPointerUltimateComponent(
1299     const DerivedTypeSpec &derived) {
1300   UltimateComponentIterator ultimates{derived};
1301   return std::find_if(ultimates.begin(), ultimates.end(), IsPointer);
1302 }
1303 
FindEventOrLockPotentialComponent(const DerivedTypeSpec & derived)1304 PotentialComponentIterator::const_iterator FindEventOrLockPotentialComponent(
1305     const DerivedTypeSpec &derived) {
1306   PotentialComponentIterator potentials{derived};
1307   return std::find_if(
1308       potentials.begin(), potentials.end(), [](const Symbol &component) {
1309         if (const auto *details{component.detailsIf<ObjectEntityDetails>()}) {
1310           const DeclTypeSpec *type{details->type()};
1311           return type && IsEventTypeOrLockType(type->AsDerived());
1312         }
1313         return false;
1314       });
1315 }
1316 
FindAllocatableUltimateComponent(const DerivedTypeSpec & derived)1317 UltimateComponentIterator::const_iterator FindAllocatableUltimateComponent(
1318     const DerivedTypeSpec &derived) {
1319   UltimateComponentIterator ultimates{derived};
1320   return std::find_if(ultimates.begin(), ultimates.end(), IsAllocatable);
1321 }
1322 
1323 UltimateComponentIterator::const_iterator
FindPolymorphicAllocatableUltimateComponent(const DerivedTypeSpec & derived)1324 FindPolymorphicAllocatableUltimateComponent(const DerivedTypeSpec &derived) {
1325   UltimateComponentIterator ultimates{derived};
1326   return std::find_if(
1327       ultimates.begin(), ultimates.end(), IsPolymorphicAllocatable);
1328 }
1329 
1330 UltimateComponentIterator::const_iterator
FindPolymorphicAllocatableNonCoarrayUltimateComponent(const DerivedTypeSpec & derived)1331 FindPolymorphicAllocatableNonCoarrayUltimateComponent(
1332     const DerivedTypeSpec &derived) {
1333   UltimateComponentIterator ultimates{derived};
1334   return std::find_if(ultimates.begin(), ultimates.end(), [](const Symbol &x) {
1335     return IsPolymorphicAllocatable(x) && !IsCoarray(x);
1336   });
1337 }
1338 
FindUltimateComponent(const DerivedTypeSpec & derived,const std::function<bool (const Symbol &)> & predicate)1339 const Symbol *FindUltimateComponent(const DerivedTypeSpec &derived,
1340     const std::function<bool(const Symbol &)> &predicate) {
1341   UltimateComponentIterator ultimates{derived};
1342   if (auto it{std::find_if(ultimates.begin(), ultimates.end(),
1343           [&predicate](const Symbol &component) -> bool {
1344             return predicate(component);
1345           })}) {
1346     return &*it;
1347   }
1348   return nullptr;
1349 }
1350 
FindUltimateComponent(const Symbol & symbol,const std::function<bool (const Symbol &)> & predicate)1351 const Symbol *FindUltimateComponent(const Symbol &symbol,
1352     const std::function<bool(const Symbol &)> &predicate) {
1353   if (predicate(symbol)) {
1354     return &symbol;
1355   } else if (const auto *object{symbol.detailsIf<ObjectEntityDetails>()}) {
1356     if (const auto *type{object->type()}) {
1357       if (const auto *derived{type->AsDerived()}) {
1358         return FindUltimateComponent(*derived, predicate);
1359       }
1360     }
1361   }
1362   return nullptr;
1363 }
1364 
FindImmediateComponent(const DerivedTypeSpec & type,const std::function<bool (const Symbol &)> & predicate)1365 const Symbol *FindImmediateComponent(const DerivedTypeSpec &type,
1366     const std::function<bool(const Symbol &)> &predicate) {
1367   if (const Scope * scope{type.scope()}) {
1368     const Symbol *parent{nullptr};
1369     for (const auto &pair : *scope) {
1370       const Symbol *symbol{&*pair.second};
1371       if (predicate(*symbol)) {
1372         return symbol;
1373       }
1374       if (symbol->test(Symbol::Flag::ParentComp)) {
1375         parent = symbol;
1376       }
1377     }
1378     if (parent) {
1379       if (const auto *object{parent->detailsIf<ObjectEntityDetails>()}) {
1380         if (const auto *type{object->type()}) {
1381           if (const auto *derived{type->AsDerived()}) {
1382             return FindImmediateComponent(*derived, predicate);
1383           }
1384         }
1385       }
1386     }
1387   }
1388   return nullptr;
1389 }
1390 
IsFunctionResultWithSameNameAsFunction(const Symbol & symbol)1391 bool IsFunctionResultWithSameNameAsFunction(const Symbol &symbol) {
1392   if (IsFunctionResult(symbol)) {
1393     if (const Symbol * function{symbol.owner().symbol()}) {
1394       return symbol.name() == function->name();
1395     }
1396   }
1397   return false;
1398 }
1399 
Post(const parser::GotoStmt & gotoStmt)1400 void LabelEnforce::Post(const parser::GotoStmt &gotoStmt) {
1401   checkLabelUse(gotoStmt.v);
1402 }
Post(const parser::ComputedGotoStmt & computedGotoStmt)1403 void LabelEnforce::Post(const parser::ComputedGotoStmt &computedGotoStmt) {
1404   for (auto &i : std::get<std::list<parser::Label>>(computedGotoStmt.t)) {
1405     checkLabelUse(i);
1406   }
1407 }
1408 
Post(const parser::ArithmeticIfStmt & arithmeticIfStmt)1409 void LabelEnforce::Post(const parser::ArithmeticIfStmt &arithmeticIfStmt) {
1410   checkLabelUse(std::get<1>(arithmeticIfStmt.t));
1411   checkLabelUse(std::get<2>(arithmeticIfStmt.t));
1412   checkLabelUse(std::get<3>(arithmeticIfStmt.t));
1413 }
1414 
Post(const parser::AssignStmt & assignStmt)1415 void LabelEnforce::Post(const parser::AssignStmt &assignStmt) {
1416   checkLabelUse(std::get<parser::Label>(assignStmt.t));
1417 }
1418 
Post(const parser::AssignedGotoStmt & assignedGotoStmt)1419 void LabelEnforce::Post(const parser::AssignedGotoStmt &assignedGotoStmt) {
1420   for (auto &i : std::get<std::list<parser::Label>>(assignedGotoStmt.t)) {
1421     checkLabelUse(i);
1422   }
1423 }
1424 
Post(const parser::AltReturnSpec & altReturnSpec)1425 void LabelEnforce::Post(const parser::AltReturnSpec &altReturnSpec) {
1426   checkLabelUse(altReturnSpec.v);
1427 }
1428 
Post(const parser::ErrLabel & errLabel)1429 void LabelEnforce::Post(const parser::ErrLabel &errLabel) {
1430   checkLabelUse(errLabel.v);
1431 }
Post(const parser::EndLabel & endLabel)1432 void LabelEnforce::Post(const parser::EndLabel &endLabel) {
1433   checkLabelUse(endLabel.v);
1434 }
Post(const parser::EorLabel & eorLabel)1435 void LabelEnforce::Post(const parser::EorLabel &eorLabel) {
1436   checkLabelUse(eorLabel.v);
1437 }
1438 
checkLabelUse(const parser::Label & labelUsed)1439 void LabelEnforce::checkLabelUse(const parser::Label &labelUsed) {
1440   if (labels_.find(labelUsed) == labels_.end()) {
1441     SayWithConstruct(context_, currentStatementSourcePosition_,
1442         parser::MessageFormattedText{
1443             "Control flow escapes from %s"_err_en_US, construct_},
1444         constructSourcePosition_);
1445   }
1446 }
1447 
GetEnclosingConstructMsg()1448 parser::MessageFormattedText LabelEnforce::GetEnclosingConstructMsg() {
1449   return {"Enclosing %s statement"_en_US, construct_};
1450 }
1451 
SayWithConstruct(SemanticsContext & context,parser::CharBlock stmtLocation,parser::MessageFormattedText && message,parser::CharBlock constructLocation)1452 void LabelEnforce::SayWithConstruct(SemanticsContext &context,
1453     parser::CharBlock stmtLocation, parser::MessageFormattedText &&message,
1454     parser::CharBlock constructLocation) {
1455   context.Say(stmtLocation, message)
1456       .Attach(constructLocation, GetEnclosingConstructMsg());
1457 }
1458 
HasAlternateReturns(const Symbol & subprogram)1459 bool HasAlternateReturns(const Symbol &subprogram) {
1460   for (const auto *dummyArg : subprogram.get<SubprogramDetails>().dummyArgs()) {
1461     if (!dummyArg) {
1462       return true;
1463     }
1464   }
1465   return false;
1466 }
1467 
InCommonBlock(const Symbol & symbol)1468 bool InCommonBlock(const Symbol &symbol) {
1469   const auto *details{symbol.detailsIf<ObjectEntityDetails>()};
1470   return details && details->commonBlock();
1471 }
1472 
MaybeGetNodeName(const ConstructNode & construct)1473 const std::optional<parser::Name> &MaybeGetNodeName(
1474     const ConstructNode &construct) {
1475   return std::visit(
1476       common::visitors{
1477           [&](const parser::BlockConstruct *blockConstruct)
1478               -> const std::optional<parser::Name> & {
1479             return std::get<0>(blockConstruct->t).statement.v;
1480           },
1481           [&](const auto *a) -> const std::optional<parser::Name> & {
1482             return std::get<0>(std::get<0>(a->t).statement.t);
1483           },
1484       },
1485       construct);
1486 }
1487 
ToArraySpec(evaluate::FoldingContext & context,const evaluate::Shape & shape)1488 std::optional<ArraySpec> ToArraySpec(
1489     evaluate::FoldingContext &context, const evaluate::Shape &shape) {
1490   if (auto extents{evaluate::AsConstantExtents(context, shape)}) {
1491     ArraySpec result;
1492     for (const auto &extent : *extents) {
1493       result.emplace_back(ShapeSpec::MakeExplicit(Bound{extent}));
1494     }
1495     return {std::move(result)};
1496   } else {
1497     return std::nullopt;
1498   }
1499 }
1500 
ToArraySpec(evaluate::FoldingContext & context,const std::optional<evaluate::Shape> & shape)1501 std::optional<ArraySpec> ToArraySpec(evaluate::FoldingContext &context,
1502     const std::optional<evaluate::Shape> &shape) {
1503   return shape ? ToArraySpec(context, *shape) : std::nullopt;
1504 }
1505 
1506 } // namespace Fortran::semantics
1507