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