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