xref: /llvm-project/flang/lib/Semantics/check-do-forall.cpp (revision 0f973ac783aa100cfbce1cd2c6e8a3a8f648fae7)
164ab3302SCarolineConcatto //===-- lib/Semantics/check-do-forall.cpp ---------------------------------===//
264ab3302SCarolineConcatto //
364ab3302SCarolineConcatto // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
464ab3302SCarolineConcatto // See https://llvm.org/LICENSE.txt for license information.
564ab3302SCarolineConcatto // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
664ab3302SCarolineConcatto //
764ab3302SCarolineConcatto //===----------------------------------------------------------------------===//
864ab3302SCarolineConcatto 
964ab3302SCarolineConcatto #include "check-do-forall.h"
1027254992SPeter Klausler #include "definable.h"
1164ab3302SCarolineConcatto #include "flang/Common/template.h"
1264ab3302SCarolineConcatto #include "flang/Evaluate/call.h"
1364ab3302SCarolineConcatto #include "flang/Evaluate/expression.h"
1464ab3302SCarolineConcatto #include "flang/Evaluate/tools.h"
15486be17dSPeter Klausler #include "flang/Evaluate/traverse.h"
1664ab3302SCarolineConcatto #include "flang/Parser/message.h"
1764ab3302SCarolineConcatto #include "flang/Parser/parse-tree-visitor.h"
1864ab3302SCarolineConcatto #include "flang/Parser/tools.h"
1964ab3302SCarolineConcatto #include "flang/Semantics/attr.h"
2064ab3302SCarolineConcatto #include "flang/Semantics/scope.h"
2164ab3302SCarolineConcatto #include "flang/Semantics/semantics.h"
2264ab3302SCarolineConcatto #include "flang/Semantics/symbol.h"
2364ab3302SCarolineConcatto #include "flang/Semantics/tools.h"
2464ab3302SCarolineConcatto #include "flang/Semantics/type.h"
2564ab3302SCarolineConcatto 
2664ab3302SCarolineConcatto namespace Fortran::evaluate {
2764ab3302SCarolineConcatto using ActualArgumentRef = common::Reference<const ActualArgument>;
2864ab3302SCarolineConcatto 
2964ab3302SCarolineConcatto inline bool operator<(ActualArgumentRef x, ActualArgumentRef y) {
3064ab3302SCarolineConcatto   return &*x < &*y;
3164ab3302SCarolineConcatto }
321f879005STim Keith } // namespace Fortran::evaluate
3364ab3302SCarolineConcatto 
3464ab3302SCarolineConcatto namespace Fortran::semantics {
3564ab3302SCarolineConcatto 
3664ab3302SCarolineConcatto using namespace parser::literals;
3764ab3302SCarolineConcatto 
3864ab3302SCarolineConcatto using Bounds = parser::LoopControl::Bounds;
3964ab3302SCarolineConcatto using IndexVarKind = SemanticsContext::IndexVarKind;
4064ab3302SCarolineConcatto 
4164ab3302SCarolineConcatto static const parser::ConcurrentHeader &GetConcurrentHeader(
4264ab3302SCarolineConcatto     const parser::LoopControl &loopControl) {
4364ab3302SCarolineConcatto   const auto &concurrent{
4464ab3302SCarolineConcatto       std::get<parser::LoopControl::Concurrent>(loopControl.u)};
4564ab3302SCarolineConcatto   return std::get<parser::ConcurrentHeader>(concurrent.t);
4664ab3302SCarolineConcatto }
4764ab3302SCarolineConcatto static const parser::ConcurrentHeader &GetConcurrentHeader(
4864ab3302SCarolineConcatto     const parser::ForallConstruct &construct) {
4964ab3302SCarolineConcatto   const auto &stmt{
5064ab3302SCarolineConcatto       std::get<parser::Statement<parser::ForallConstructStmt>>(construct.t)};
5164ab3302SCarolineConcatto   return std::get<common::Indirection<parser::ConcurrentHeader>>(
5264ab3302SCarolineConcatto       stmt.statement.t)
5364ab3302SCarolineConcatto       .value();
5464ab3302SCarolineConcatto }
5564ab3302SCarolineConcatto static const parser::ConcurrentHeader &GetConcurrentHeader(
5664ab3302SCarolineConcatto     const parser::ForallStmt &stmt) {
5764ab3302SCarolineConcatto   return std::get<common::Indirection<parser::ConcurrentHeader>>(stmt.t)
5864ab3302SCarolineConcatto       .value();
5964ab3302SCarolineConcatto }
6064ab3302SCarolineConcatto template <typename T>
6164ab3302SCarolineConcatto static const std::list<parser::ConcurrentControl> &GetControls(const T &x) {
6264ab3302SCarolineConcatto   return std::get<std::list<parser::ConcurrentControl>>(
6364ab3302SCarolineConcatto       GetConcurrentHeader(x).t);
6464ab3302SCarolineConcatto }
6564ab3302SCarolineConcatto 
6664ab3302SCarolineConcatto static const Bounds &GetBounds(const parser::DoConstruct &doConstruct) {
6764ab3302SCarolineConcatto   auto &loopControl{doConstruct.GetLoopControl().value()};
6864ab3302SCarolineConcatto   return std::get<Bounds>(loopControl.u);
6964ab3302SCarolineConcatto }
7064ab3302SCarolineConcatto 
7164ab3302SCarolineConcatto static const parser::Name &GetDoVariable(
7264ab3302SCarolineConcatto     const parser::DoConstruct &doConstruct) {
7364ab3302SCarolineConcatto   const Bounds &bounds{GetBounds(doConstruct)};
7464ab3302SCarolineConcatto   return bounds.name.thing;
7564ab3302SCarolineConcatto }
7664ab3302SCarolineConcatto 
7764ab3302SCarolineConcatto static parser::MessageFixedText GetEnclosingDoMsg() {
7864ab3302SCarolineConcatto   return "Enclosing DO CONCURRENT statement"_en_US;
7964ab3302SCarolineConcatto }
8064ab3302SCarolineConcatto 
8164ab3302SCarolineConcatto static void SayWithDo(SemanticsContext &context, parser::CharBlock stmtLocation,
8264ab3302SCarolineConcatto     parser::MessageFixedText &&message, parser::CharBlock doLocation) {
8364ab3302SCarolineConcatto   context.Say(stmtLocation, message).Attach(doLocation, GetEnclosingDoMsg());
8464ab3302SCarolineConcatto }
8564ab3302SCarolineConcatto 
8664ab3302SCarolineConcatto // 11.1.7.5 - enforce semantics constraints on a DO CONCURRENT loop body
8764ab3302SCarolineConcatto class DoConcurrentBodyEnforce {
8864ab3302SCarolineConcatto public:
8964ab3302SCarolineConcatto   DoConcurrentBodyEnforce(
9064ab3302SCarolineConcatto       SemanticsContext &context, parser::CharBlock doConcurrentSourcePosition)
913af717d6Skhaki3       : context_{context},
923af717d6Skhaki3         doConcurrentSourcePosition_{doConcurrentSourcePosition} {}
9364ab3302SCarolineConcatto   std::set<parser::Label> labels() { return labels_; }
94486be17dSPeter Klausler   template <typename T> bool Pre(const T &x) {
95486be17dSPeter Klausler     if (const auto *expr{GetExpr(context_, x)}) {
96486be17dSPeter Klausler       if (auto bad{FindImpureCall(context_.foldingContext(), *expr)}) {
97486be17dSPeter Klausler         context_.Say(currentStatementSourcePosition_,
98486be17dSPeter Klausler             "Impure procedure '%s' may not be referenced in DO CONCURRENT"_err_en_US,
99486be17dSPeter Klausler             *bad);
100486be17dSPeter Klausler       }
101486be17dSPeter Klausler     }
102486be17dSPeter Klausler     return true;
103486be17dSPeter Klausler   }
10464ab3302SCarolineConcatto   template <typename T> bool Pre(const parser::Statement<T> &statement) {
10564ab3302SCarolineConcatto     currentStatementSourcePosition_ = statement.source;
10664ab3302SCarolineConcatto     if (statement.label.has_value()) {
10764ab3302SCarolineConcatto       labels_.insert(*statement.label);
10864ab3302SCarolineConcatto     }
10964ab3302SCarolineConcatto     return true;
11064ab3302SCarolineConcatto   }
11164ab3302SCarolineConcatto   template <typename T> bool Pre(const parser::UnlabeledStatement<T> &stmt) {
11264ab3302SCarolineConcatto     currentStatementSourcePosition_ = stmt.source;
11364ab3302SCarolineConcatto     return true;
11464ab3302SCarolineConcatto   }
115486be17dSPeter Klausler   bool Pre(const parser::CallStmt &x) {
116486be17dSPeter Klausler     if (x.typedCall.get()) {
117486be17dSPeter Klausler       if (auto bad{FindImpureCall(context_.foldingContext(), *x.typedCall)}) {
118486be17dSPeter Klausler         context_.Say(currentStatementSourcePosition_,
119486be17dSPeter Klausler             "Impure procedure '%s' may not be referenced in DO CONCURRENT"_err_en_US,
120486be17dSPeter Klausler             *bad);
121486be17dSPeter Klausler       }
122486be17dSPeter Klausler     }
123486be17dSPeter Klausler     return true;
124486be17dSPeter Klausler   }
12534a4eefcSPeter Klausler   bool Pre(const parser::ConcurrentHeader &) {
12634a4eefcSPeter Klausler     // handled in CheckConcurrentHeader
12734a4eefcSPeter Klausler     return false;
12834a4eefcSPeter Klausler   }
129486be17dSPeter Klausler   template <typename T> void Post(const T &) {}
13064ab3302SCarolineConcatto 
13164ab3302SCarolineConcatto   // C1140 -- Can't deallocate a polymorphic entity in a DO CONCURRENT.
13264ab3302SCarolineConcatto   // Deallocation can be caused by exiting a block that declares an allocatable
13364ab3302SCarolineConcatto   // entity, assignment to an allocatable variable, or an actual DEALLOCATE
13464ab3302SCarolineConcatto   // statement
13564ab3302SCarolineConcatto   //
13664ab3302SCarolineConcatto   // Note also that the deallocation of a derived type entity might cause the
13764ab3302SCarolineConcatto   // invocation of an IMPURE final subroutine. (C1139)
13864ab3302SCarolineConcatto   //
13964ab3302SCarolineConcatto 
14064ab3302SCarolineConcatto   // Predicate for deallocations caused by block exit and direct deallocation
14164ab3302SCarolineConcatto   static bool DeallocateAll(const Symbol &) { return true; }
14264ab3302SCarolineConcatto 
14364ab3302SCarolineConcatto   // Predicate for deallocations caused by intrinsic assignment
14464ab3302SCarolineConcatto   static bool DeallocateNonCoarray(const Symbol &component) {
1451ee6f7adSPeter Klausler     return !evaluate::IsCoarray(component);
14664ab3302SCarolineConcatto   }
14764ab3302SCarolineConcatto 
14864ab3302SCarolineConcatto   static bool WillDeallocatePolymorphic(const Symbol &entity,
14964ab3302SCarolineConcatto       const std::function<bool(const Symbol &)> &WillDeallocate) {
15064ab3302SCarolineConcatto     return WillDeallocate(entity) && IsPolymorphicAllocatable(entity);
15164ab3302SCarolineConcatto   }
15264ab3302SCarolineConcatto 
15364ab3302SCarolineConcatto   // Is it possible that we will we deallocate a polymorphic entity or one
15464ab3302SCarolineConcatto   // of its components?
155a50bb84eSpeter klausler   static bool MightDeallocatePolymorphic(const Symbol &original,
15664ab3302SCarolineConcatto       const std::function<bool(const Symbol &)> &WillDeallocate) {
157a50bb84eSpeter klausler     const Symbol &symbol{ResolveAssociations(original)};
15864ab3302SCarolineConcatto     // Check the entity itself, no coarray exception here
159a50bb84eSpeter klausler     if (IsPolymorphicAllocatable(symbol)) {
16064ab3302SCarolineConcatto       return true;
16164ab3302SCarolineConcatto     }
16264ab3302SCarolineConcatto     // Check the components
163a50bb84eSpeter klausler     if (const auto *details{symbol.detailsIf<ObjectEntityDetails>()}) {
16464ab3302SCarolineConcatto       if (const DeclTypeSpec * entityType{details->type()}) {
16564ab3302SCarolineConcatto         if (const DerivedTypeSpec * derivedType{entityType->AsDerived()}) {
16664ab3302SCarolineConcatto           UltimateComponentIterator ultimates{*derivedType};
16764ab3302SCarolineConcatto           for (const auto &ultimate : ultimates) {
16864ab3302SCarolineConcatto             if (WillDeallocatePolymorphic(ultimate, WillDeallocate)) {
16964ab3302SCarolineConcatto               return true;
17064ab3302SCarolineConcatto             }
17164ab3302SCarolineConcatto           }
17264ab3302SCarolineConcatto         }
17364ab3302SCarolineConcatto       }
17464ab3302SCarolineConcatto     }
17564ab3302SCarolineConcatto     return false;
17664ab3302SCarolineConcatto   }
17764ab3302SCarolineConcatto 
178e9a8ab00SPeter Klausler   void SayDeallocateWithImpureFinal(
179e9a8ab00SPeter Klausler       const Symbol &entity, const char *reason, const Symbol &impure) {
18064ab3302SCarolineConcatto     context_.SayWithDecl(entity, currentStatementSourcePosition_,
181e9a8ab00SPeter Klausler         "Deallocation of an entity with an IMPURE FINAL procedure '%s' caused by %s not allowed in DO CONCURRENT"_err_en_US,
182e9a8ab00SPeter Klausler         impure.name(), reason);
18364ab3302SCarolineConcatto   }
18464ab3302SCarolineConcatto 
18564ab3302SCarolineConcatto   void SayDeallocateOfPolymorph(
18664ab3302SCarolineConcatto       parser::CharBlock location, const Symbol &entity, const char *reason) {
18764ab3302SCarolineConcatto     context_.SayWithDecl(entity, location,
18864ab3302SCarolineConcatto         "Deallocation of a polymorphic entity caused by %s"
18964ab3302SCarolineConcatto         " not allowed in DO CONCURRENT"_err_en_US,
19064ab3302SCarolineConcatto         reason);
19164ab3302SCarolineConcatto   }
19264ab3302SCarolineConcatto 
19364ab3302SCarolineConcatto   // Deallocation caused by block exit
19464ab3302SCarolineConcatto   // Allocatable entities and all of their allocatable subcomponents will be
19564ab3302SCarolineConcatto   // deallocated.  This test is different from the other two because it does
19664ab3302SCarolineConcatto   // not deallocate in cases where the entity itself is not allocatable but
19764ab3302SCarolineConcatto   // has allocatable polymorphic components
19864ab3302SCarolineConcatto   void Post(const parser::BlockConstruct &blockConstruct) {
19964ab3302SCarolineConcatto     const auto &endBlockStmt{
20064ab3302SCarolineConcatto         std::get<parser::Statement<parser::EndBlockStmt>>(blockConstruct.t)};
20164ab3302SCarolineConcatto     const Scope &blockScope{context_.FindScope(endBlockStmt.source)};
20264ab3302SCarolineConcatto     const Scope &doScope{context_.FindScope(doConcurrentSourcePosition_)};
20364ab3302SCarolineConcatto     if (DoesScopeContain(&doScope, blockScope)) {
20464ab3302SCarolineConcatto       const char *reason{"block exit"};
20564ab3302SCarolineConcatto       for (auto &pair : blockScope) {
20664ab3302SCarolineConcatto         const Symbol &entity{*pair.second};
2074171f80dSpeter klausler         if (IsAllocatable(entity) && !IsSaved(entity) &&
20864ab3302SCarolineConcatto             MightDeallocatePolymorphic(entity, DeallocateAll)) {
20964ab3302SCarolineConcatto           SayDeallocateOfPolymorph(endBlockStmt.source, entity, reason);
21064ab3302SCarolineConcatto         }
211e9a8ab00SPeter Klausler         if (const Symbol * impure{HasImpureFinal(entity)}) {
212e9a8ab00SPeter Klausler           SayDeallocateWithImpureFinal(entity, reason, *impure);
21364ab3302SCarolineConcatto         }
21464ab3302SCarolineConcatto       }
21564ab3302SCarolineConcatto     }
21664ab3302SCarolineConcatto   }
21764ab3302SCarolineConcatto 
21864ab3302SCarolineConcatto   // Deallocation caused by assignment
21964ab3302SCarolineConcatto   // Note that this case does not cause deallocation of coarray components
22064ab3302SCarolineConcatto   void Post(const parser::AssignmentStmt &stmt) {
22164ab3302SCarolineConcatto     const auto &variable{std::get<parser::Variable>(stmt.t)};
22264ab3302SCarolineConcatto     if (const Symbol * entity{GetLastName(variable).symbol}) {
22364ab3302SCarolineConcatto       const char *reason{"assignment"};
22464ab3302SCarolineConcatto       if (MightDeallocatePolymorphic(*entity, DeallocateNonCoarray)) {
22564ab3302SCarolineConcatto         SayDeallocateOfPolymorph(variable.GetSource(), *entity, reason);
22664ab3302SCarolineConcatto       }
2270c21377aSKelvin Li       if (const auto *assignment{GetAssignment(stmt)}) {
2280c21377aSKelvin Li         const auto &lhs{assignment->lhs};
2290c21377aSKelvin Li         if (const Symbol * impure{HasImpureFinal(*entity, lhs.Rank())}) {
230e9a8ab00SPeter Klausler           SayDeallocateWithImpureFinal(*entity, reason, *impure);
23164ab3302SCarolineConcatto         }
23264ab3302SCarolineConcatto       }
2330c21377aSKelvin Li     }
234b0f02ceeSPeter Klausler     if (const auto *assignment{GetAssignment(stmt)}) {
235b0f02ceeSPeter Klausler       if (const auto *call{
236b0f02ceeSPeter Klausler               std::get_if<evaluate::ProcedureRef>(&assignment->u)}) {
237b0f02ceeSPeter Klausler         if (auto bad{FindImpureCall(context_.foldingContext(), *call)}) {
238b0f02ceeSPeter Klausler           context_.Say(currentStatementSourcePosition_,
239b0f02ceeSPeter Klausler               "The defined assignment subroutine '%s' is not pure"_err_en_US,
240b0f02ceeSPeter Klausler               *bad);
241b0f02ceeSPeter Klausler         }
242b0f02ceeSPeter Klausler       }
243b0f02ceeSPeter Klausler     }
24464ab3302SCarolineConcatto   }
24564ab3302SCarolineConcatto 
24664ab3302SCarolineConcatto   // Deallocation from a DEALLOCATE statement
24764ab3302SCarolineConcatto   // This case is different because DEALLOCATE statements deallocate both
24864ab3302SCarolineConcatto   // ALLOCATABLE and POINTER entities
24964ab3302SCarolineConcatto   void Post(const parser::DeallocateStmt &stmt) {
25064ab3302SCarolineConcatto     const auto &allocateObjectList{
25164ab3302SCarolineConcatto         std::get<std::list<parser::AllocateObject>>(stmt.t)};
25264ab3302SCarolineConcatto     for (const auto &allocateObject : allocateObjectList) {
25364ab3302SCarolineConcatto       const parser::Name &name{GetLastName(allocateObject)};
25464ab3302SCarolineConcatto       const char *reason{"a DEALLOCATE statement"};
25564ab3302SCarolineConcatto       if (name.symbol) {
25664ab3302SCarolineConcatto         const Symbol &entity{*name.symbol};
25764ab3302SCarolineConcatto         const DeclTypeSpec *entityType{entity.GetType()};
25864ab3302SCarolineConcatto         if ((entityType && entityType->IsPolymorphic()) || // POINTER case
25964ab3302SCarolineConcatto             MightDeallocatePolymorphic(entity, DeallocateAll)) {
26064ab3302SCarolineConcatto           SayDeallocateOfPolymorph(
26164ab3302SCarolineConcatto               currentStatementSourcePosition_, entity, reason);
26264ab3302SCarolineConcatto         }
263e9a8ab00SPeter Klausler         if (const Symbol * impure{HasImpureFinal(entity)}) {
264e9a8ab00SPeter Klausler           SayDeallocateWithImpureFinal(entity, reason, *impure);
26564ab3302SCarolineConcatto         }
26664ab3302SCarolineConcatto       }
26764ab3302SCarolineConcatto     }
26864ab3302SCarolineConcatto   }
26964ab3302SCarolineConcatto 
27064ab3302SCarolineConcatto   // C1137 -- No image control statements in a DO CONCURRENT
27164ab3302SCarolineConcatto   void Post(const parser::ExecutableConstruct &construct) {
27264ab3302SCarolineConcatto     if (IsImageControlStmt(construct)) {
27364ab3302SCarolineConcatto       const parser::CharBlock statementLocation{
27464ab3302SCarolineConcatto           GetImageControlStmtLocation(construct)};
27564ab3302SCarolineConcatto       auto &msg{context_.Say(statementLocation,
2769390eb92SPeter Klausler           "An image control statement is not allowed in DO CONCURRENT"_err_en_US)};
27764ab3302SCarolineConcatto       if (auto coarrayMsg{GetImageControlStmtCoarrayMsg(construct)}) {
27864ab3302SCarolineConcatto         msg.Attach(statementLocation, *coarrayMsg);
27964ab3302SCarolineConcatto       }
28064ab3302SCarolineConcatto       msg.Attach(doConcurrentSourcePosition_, GetEnclosingDoMsg());
28164ab3302SCarolineConcatto     }
28264ab3302SCarolineConcatto   }
28364ab3302SCarolineConcatto 
28464ab3302SCarolineConcatto   // C1136 -- No RETURN statements in a DO CONCURRENT
28564ab3302SCarolineConcatto   void Post(const parser::ReturnStmt &) {
28664ab3302SCarolineConcatto     context_
28764ab3302SCarolineConcatto         .Say(currentStatementSourcePosition_,
28864ab3302SCarolineConcatto             "RETURN is not allowed in DO CONCURRENT"_err_en_US)
28964ab3302SCarolineConcatto         .Attach(doConcurrentSourcePosition_, GetEnclosingDoMsg());
29064ab3302SCarolineConcatto   }
29164ab3302SCarolineConcatto 
2927c512cefSPeter Klausler   // C1145, C1146: cannot call ieee_[gs]et_flag, ieee_[gs]et_halting_mode,
2937c512cefSPeter Klausler   // ieee_[gs]et_status, ieee_set_rounding_mode, or ieee_set_underflow_mode
29464ab3302SCarolineConcatto   void Post(const parser::ProcedureDesignator &procedureDesignator) {
29564ab3302SCarolineConcatto     if (auto *name{std::get_if<parser::Name>(&procedureDesignator.u)}) {
2967c512cefSPeter Klausler       if (name->symbol) {
2977c512cefSPeter Klausler         const Symbol &ultimate{name->symbol->GetUltimate()};
2987c512cefSPeter Klausler         const Scope &scope{ultimate.owner()};
2997c512cefSPeter Klausler         if (const Symbol * module{scope.IsModule() ? scope.symbol() : nullptr};
3007c512cefSPeter Klausler             module &&
3017c512cefSPeter Klausler             (module->name() == "__fortran_ieee_arithmetic" ||
3027c512cefSPeter Klausler                 module->name() == "__fortran_ieee_exceptions")) {
3037c512cefSPeter Klausler           std::string s{ultimate.name().ToString()};
3047c512cefSPeter Klausler           static constexpr const char *badName[]{"ieee_get_flag",
3057c512cefSPeter Klausler               "ieee_set_flag", "ieee_get_halting_mode", "ieee_set_halting_mode",
3067c512cefSPeter Klausler               "ieee_get_status", "ieee_set_status", "ieee_set_rounding_mode",
3077c512cefSPeter Klausler               "ieee_set_underflow_mode", nullptr};
3087c512cefSPeter Klausler           for (std::size_t j{0}; badName[j]; ++j) {
3097c512cefSPeter Klausler             if (s.find(badName[j]) != s.npos) {
3107c512cefSPeter Klausler               context_
3117c512cefSPeter Klausler                   .Say(name->source,
3127c512cefSPeter Klausler                       "'%s' may not be called in DO CONCURRENT"_err_en_US,
3137c512cefSPeter Klausler                       badName[j])
3147c512cefSPeter Klausler                   .Attach(doConcurrentSourcePosition_, GetEnclosingDoMsg());
3157c512cefSPeter Klausler               break;
3167c512cefSPeter Klausler             }
3177c512cefSPeter Klausler           }
31864ab3302SCarolineConcatto         }
31964ab3302SCarolineConcatto       }
32064ab3302SCarolineConcatto     }
32164ab3302SCarolineConcatto   }
32264ab3302SCarolineConcatto 
32364ab3302SCarolineConcatto   // 11.1.7.5, paragraph 5, no ADVANCE specifier in a DO CONCURRENT
32464ab3302SCarolineConcatto   void Post(const parser::IoControlSpec &ioControlSpec) {
32564ab3302SCarolineConcatto     if (auto *charExpr{
32664ab3302SCarolineConcatto             std::get_if<parser::IoControlSpec::CharExpr>(&ioControlSpec.u)}) {
32764ab3302SCarolineConcatto       if (std::get<parser::IoControlSpec::CharExpr::Kind>(charExpr->t) ==
32864ab3302SCarolineConcatto           parser::IoControlSpec::CharExpr::Kind::Advance) {
32964ab3302SCarolineConcatto         SayWithDo(context_, currentStatementSourcePosition_,
33064ab3302SCarolineConcatto             "ADVANCE specifier is not allowed in DO"
33164ab3302SCarolineConcatto             " CONCURRENT"_err_en_US,
33264ab3302SCarolineConcatto             doConcurrentSourcePosition_);
33364ab3302SCarolineConcatto       }
33464ab3302SCarolineConcatto     }
33564ab3302SCarolineConcatto   }
33664ab3302SCarolineConcatto 
33764ab3302SCarolineConcatto private:
33864ab3302SCarolineConcatto   std::set<parser::Label> labels_;
33964ab3302SCarolineConcatto   parser::CharBlock currentStatementSourcePosition_;
34064ab3302SCarolineConcatto   SemanticsContext &context_;
34164ab3302SCarolineConcatto   parser::CharBlock doConcurrentSourcePosition_;
34264ab3302SCarolineConcatto }; // class DoConcurrentBodyEnforce
34364ab3302SCarolineConcatto 
34464ab3302SCarolineConcatto // Class for enforcing C1130 -- in a DO CONCURRENT with DEFAULT(NONE),
34564ab3302SCarolineConcatto // variables from enclosing scopes must have their locality specified
34664ab3302SCarolineConcatto class DoConcurrentVariableEnforce {
34764ab3302SCarolineConcatto public:
34864ab3302SCarolineConcatto   DoConcurrentVariableEnforce(
34964ab3302SCarolineConcatto       SemanticsContext &context, parser::CharBlock doConcurrentSourcePosition)
35064ab3302SCarolineConcatto       : context_{context},
35164ab3302SCarolineConcatto         doConcurrentSourcePosition_{doConcurrentSourcePosition},
35264ab3302SCarolineConcatto         blockScope_{context.FindScope(doConcurrentSourcePosition_)} {}
35364ab3302SCarolineConcatto 
35464ab3302SCarolineConcatto   template <typename T> bool Pre(const T &) { return true; }
35564ab3302SCarolineConcatto   template <typename T> void Post(const T &) {}
35664ab3302SCarolineConcatto 
35764ab3302SCarolineConcatto   // Check to see if the name is a variable from an enclosing scope
35864ab3302SCarolineConcatto   void Post(const parser::Name &name) {
35964ab3302SCarolineConcatto     if (const Symbol * symbol{name.symbol}) {
36064ab3302SCarolineConcatto       if (IsVariableName(*symbol)) {
36164ab3302SCarolineConcatto         const Scope &variableScope{symbol->owner()};
36264ab3302SCarolineConcatto         if (DoesScopeContain(&variableScope, blockScope_)) {
36364ab3302SCarolineConcatto           context_.SayWithDecl(*symbol, name.source,
36464ab3302SCarolineConcatto               "Variable '%s' from an enclosing scope referenced in DO "
36564ab3302SCarolineConcatto               "CONCURRENT with DEFAULT(NONE) must appear in a "
36664ab3302SCarolineConcatto               "locality-spec"_err_en_US,
36764ab3302SCarolineConcatto               symbol->name());
36864ab3302SCarolineConcatto         }
36964ab3302SCarolineConcatto       }
37064ab3302SCarolineConcatto     }
37164ab3302SCarolineConcatto   }
37264ab3302SCarolineConcatto 
37364ab3302SCarolineConcatto private:
37464ab3302SCarolineConcatto   SemanticsContext &context_;
37564ab3302SCarolineConcatto   parser::CharBlock doConcurrentSourcePosition_;
37664ab3302SCarolineConcatto   const Scope &blockScope_;
37764ab3302SCarolineConcatto }; // class DoConcurrentVariableEnforce
37864ab3302SCarolineConcatto 
37964ab3302SCarolineConcatto // Find a DO or FORALL and enforce semantics checks on its body
38064ab3302SCarolineConcatto class DoContext {
38164ab3302SCarolineConcatto public:
38234a4eefcSPeter Klausler   DoContext(SemanticsContext &context, IndexVarKind kind,
38334a4eefcSPeter Klausler       const std::list<IndexVarKind> nesting)
38434a4eefcSPeter Klausler       : context_{context}, kind_{kind} {
38534a4eefcSPeter Klausler     if (!nesting.empty()) {
38634a4eefcSPeter Klausler       concurrentNesting_ = nesting.back();
38734a4eefcSPeter Klausler     }
38834a4eefcSPeter Klausler   }
38964ab3302SCarolineConcatto 
39064ab3302SCarolineConcatto   // Mark this DO construct as a point of definition for the DO variables
39164ab3302SCarolineConcatto   // or index-names it contains.  If they're already defined, emit an error
39264ab3302SCarolineConcatto   // message.  We need to remember both the variable and the source location of
39364ab3302SCarolineConcatto   // the variable in the DO construct so that we can remove it when we leave
39464ab3302SCarolineConcatto   // the DO construct and use its location in error messages.
39564ab3302SCarolineConcatto   void DefineDoVariables(const parser::DoConstruct &doConstruct) {
39664ab3302SCarolineConcatto     if (doConstruct.IsDoNormal()) {
39764ab3302SCarolineConcatto       context_.ActivateIndexVar(GetDoVariable(doConstruct), IndexVarKind::DO);
39864ab3302SCarolineConcatto     } else if (doConstruct.IsDoConcurrent()) {
39964ab3302SCarolineConcatto       if (const auto &loopControl{doConstruct.GetLoopControl()}) {
40064ab3302SCarolineConcatto         ActivateIndexVars(GetControls(*loopControl));
40164ab3302SCarolineConcatto       }
40264ab3302SCarolineConcatto     }
40364ab3302SCarolineConcatto   }
40464ab3302SCarolineConcatto 
40564ab3302SCarolineConcatto   // Called at the end of a DO construct to deactivate the DO construct
40664ab3302SCarolineConcatto   void ResetDoVariables(const parser::DoConstruct &doConstruct) {
40764ab3302SCarolineConcatto     if (doConstruct.IsDoNormal()) {
40864ab3302SCarolineConcatto       context_.DeactivateIndexVar(GetDoVariable(doConstruct));
40964ab3302SCarolineConcatto     } else if (doConstruct.IsDoConcurrent()) {
41064ab3302SCarolineConcatto       if (const auto &loopControl{doConstruct.GetLoopControl()}) {
41164ab3302SCarolineConcatto         DeactivateIndexVars(GetControls(*loopControl));
41264ab3302SCarolineConcatto       }
41364ab3302SCarolineConcatto     }
41464ab3302SCarolineConcatto   }
41564ab3302SCarolineConcatto 
41664ab3302SCarolineConcatto   void ActivateIndexVars(const std::list<parser::ConcurrentControl> &controls) {
41764ab3302SCarolineConcatto     for (const auto &control : controls) {
41864ab3302SCarolineConcatto       context_.ActivateIndexVar(std::get<parser::Name>(control.t), kind_);
41964ab3302SCarolineConcatto     }
42064ab3302SCarolineConcatto   }
42164ab3302SCarolineConcatto   void DeactivateIndexVars(
42264ab3302SCarolineConcatto       const std::list<parser::ConcurrentControl> &controls) {
42364ab3302SCarolineConcatto     for (const auto &control : controls) {
42464ab3302SCarolineConcatto       context_.DeactivateIndexVar(std::get<parser::Name>(control.t));
42564ab3302SCarolineConcatto     }
42664ab3302SCarolineConcatto   }
42764ab3302SCarolineConcatto 
42864ab3302SCarolineConcatto   void Check(const parser::DoConstruct &doConstruct) {
42964ab3302SCarolineConcatto     if (doConstruct.IsDoConcurrent()) {
43064ab3302SCarolineConcatto       CheckDoConcurrent(doConstruct);
431486be17dSPeter Klausler     } else if (doConstruct.IsDoNormal()) {
43264ab3302SCarolineConcatto       CheckDoNormal(doConstruct);
433486be17dSPeter Klausler     } else {
43464ab3302SCarolineConcatto       // TODO: handle the other cases
43564ab3302SCarolineConcatto     }
436486be17dSPeter Klausler   }
43764ab3302SCarolineConcatto 
43864ab3302SCarolineConcatto   void Check(const parser::ForallStmt &stmt) {
43964ab3302SCarolineConcatto     CheckConcurrentHeader(GetConcurrentHeader(stmt));
44064ab3302SCarolineConcatto   }
44164ab3302SCarolineConcatto   void Check(const parser::ForallConstruct &construct) {
44264ab3302SCarolineConcatto     CheckConcurrentHeader(GetConcurrentHeader(construct));
44364ab3302SCarolineConcatto   }
44464ab3302SCarolineConcatto 
44564ab3302SCarolineConcatto   void Check(const parser::ForallAssignmentStmt &stmt) {
446b0f02ceeSPeter Klausler     if (const evaluate::Assignment *
447b0f02ceeSPeter Klausler         assignment{common::visit(
44864ab3302SCarolineConcatto             common::visitors{[&](const auto &x) { return GetAssignment(x); }},
449b0f02ceeSPeter Klausler             stmt.u)}) {
45064ab3302SCarolineConcatto       CheckForallIndexesUsed(*assignment);
45134a4eefcSPeter Klausler       CheckForImpureCall(assignment->lhs, kind_);
45234a4eefcSPeter Klausler       CheckForImpureCall(assignment->rhs, kind_);
453a5f576e5SKelvin Li 
454a5f576e5SKelvin Li       if (IsVariable(assignment->lhs)) {
455a5f576e5SKelvin Li         if (const Symbol * symbol{GetLastSymbol(assignment->lhs)}) {
456a5f576e5SKelvin Li           if (auto impureFinal{
457a5f576e5SKelvin Li                   HasImpureFinal(*symbol, assignment->lhs.Rank())}) {
458a5f576e5SKelvin Li             context_.SayWithDecl(*symbol, parser::FindSourceLocation(stmt),
459a5f576e5SKelvin Li                 "Impure procedure '%s' is referenced by finalization in a %s"_err_en_US,
460a5f576e5SKelvin Li                 impureFinal->name(), LoopKindName());
461a5f576e5SKelvin Li           }
462a5f576e5SKelvin Li         }
463a5f576e5SKelvin Li       }
464a5f576e5SKelvin Li 
46564ab3302SCarolineConcatto       if (const auto *proc{
46664ab3302SCarolineConcatto               std::get_if<evaluate::ProcedureRef>(&assignment->u)}) {
46734a4eefcSPeter Klausler         CheckForImpureCall(*proc, kind_);
46864ab3302SCarolineConcatto       }
469cd03e96fSPeter Klausler       common::visit(
470cd03e96fSPeter Klausler           common::visitors{
47164ab3302SCarolineConcatto               [](const evaluate::Assignment::Intrinsic &) {},
47264ab3302SCarolineConcatto               [&](const evaluate::ProcedureRef &proc) {
47334a4eefcSPeter Klausler                 CheckForImpureCall(proc, kind_);
47464ab3302SCarolineConcatto               },
47564ab3302SCarolineConcatto               [&](const evaluate::Assignment::BoundsSpec &bounds) {
47664ab3302SCarolineConcatto                 for (const auto &bound : bounds) {
47734a4eefcSPeter Klausler                   CheckForImpureCall(SomeExpr{bound}, kind_);
47864ab3302SCarolineConcatto                 }
47964ab3302SCarolineConcatto               },
48064ab3302SCarolineConcatto               [&](const evaluate::Assignment::BoundsRemapping &bounds) {
48164ab3302SCarolineConcatto                 for (const auto &bound : bounds) {
48234a4eefcSPeter Klausler                   CheckForImpureCall(SomeExpr{bound.first}, kind_);
48334a4eefcSPeter Klausler                   CheckForImpureCall(SomeExpr{bound.second}, kind_);
48464ab3302SCarolineConcatto                 }
48564ab3302SCarolineConcatto               },
48664ab3302SCarolineConcatto           },
48764ab3302SCarolineConcatto           assignment->u);
48864ab3302SCarolineConcatto     }
48964ab3302SCarolineConcatto   }
49064ab3302SCarolineConcatto 
49164ab3302SCarolineConcatto private:
49264ab3302SCarolineConcatto   void SayBadDoControl(parser::CharBlock sourceLocation) {
49364ab3302SCarolineConcatto     context_.Say(sourceLocation, "DO controls should be INTEGER"_err_en_US);
49464ab3302SCarolineConcatto   }
49564ab3302SCarolineConcatto 
49664ab3302SCarolineConcatto   void CheckDoControl(const parser::CharBlock &sourceLocation, bool isReal) {
497191d4872SPeter Klausler     if (isReal) {
498*0f973ac7SPeter Klausler       context_.Warn(common::LanguageFeature::RealDoControls, sourceLocation,
499*0f973ac7SPeter Klausler           "DO controls should be INTEGER"_port_en_US);
50064ab3302SCarolineConcatto     } else {
50164ab3302SCarolineConcatto       SayBadDoControl(sourceLocation);
50264ab3302SCarolineConcatto     }
50364ab3302SCarolineConcatto   }
50464ab3302SCarolineConcatto 
50564ab3302SCarolineConcatto   void CheckDoVariable(const parser::ScalarName &scalarName) {
50664ab3302SCarolineConcatto     const parser::CharBlock &sourceLocation{scalarName.thing.source};
50764ab3302SCarolineConcatto     if (const Symbol * symbol{scalarName.thing.symbol}) {
50864ab3302SCarolineConcatto       if (!IsVariableName(*symbol)) {
50964ab3302SCarolineConcatto         context_.Say(
51064ab3302SCarolineConcatto             sourceLocation, "DO control must be an INTEGER variable"_err_en_US);
51127254992SPeter Klausler       } else if (auto why{WhyNotDefinable(sourceLocation,
51227254992SPeter Klausler                      context_.FindScope(sourceLocation), DefinabilityFlags{},
51327254992SPeter Klausler                      *symbol)}) {
51427254992SPeter Klausler         context_
51527254992SPeter Klausler             .Say(sourceLocation,
51627254992SPeter Klausler                 "'%s' may not be used as a DO variable"_err_en_US,
51727254992SPeter Klausler                 symbol->name())
518d5285fefSPeter Klausler             .Attach(std::move(why->set_severity(parser::Severity::Because)));
51964ab3302SCarolineConcatto       } else {
52064ab3302SCarolineConcatto         const DeclTypeSpec *symType{symbol->GetType()};
52164ab3302SCarolineConcatto         if (!symType) {
52264ab3302SCarolineConcatto           SayBadDoControl(sourceLocation);
52364ab3302SCarolineConcatto         } else {
52464ab3302SCarolineConcatto           if (!symType->IsNumeric(TypeCategory::Integer)) {
52564ab3302SCarolineConcatto             CheckDoControl(
52664ab3302SCarolineConcatto                 sourceLocation, symType->IsNumeric(TypeCategory::Real));
52764ab3302SCarolineConcatto           }
52864ab3302SCarolineConcatto         }
52964ab3302SCarolineConcatto       } // No messages for INTEGER
53064ab3302SCarolineConcatto     }
53164ab3302SCarolineConcatto   }
53264ab3302SCarolineConcatto 
53364ab3302SCarolineConcatto   // Semantic checks for the limit and step expressions
53464ab3302SCarolineConcatto   void CheckDoExpression(const parser::ScalarExpr &scalarExpression) {
5357e225423SPeter Klausler     if (const SomeExpr * expr{GetExpr(context_, scalarExpression)}) {
53664ab3302SCarolineConcatto       if (!ExprHasTypeCategory(*expr, TypeCategory::Integer)) {
53764ab3302SCarolineConcatto         // No warnings or errors for type INTEGER
53864ab3302SCarolineConcatto         const parser::CharBlock &loc{scalarExpression.thing.value().source};
53964ab3302SCarolineConcatto         CheckDoControl(loc, ExprHasTypeCategory(*expr, TypeCategory::Real));
54064ab3302SCarolineConcatto       }
54164ab3302SCarolineConcatto     }
54264ab3302SCarolineConcatto   }
54364ab3302SCarolineConcatto 
54464ab3302SCarolineConcatto   void CheckDoNormal(const parser::DoConstruct &doConstruct) {
54564ab3302SCarolineConcatto     // C1120 -- types of DO variables must be INTEGER, extended by allowing
54664ab3302SCarolineConcatto     // REAL and DOUBLE PRECISION
54764ab3302SCarolineConcatto     const Bounds &bounds{GetBounds(doConstruct)};
54864ab3302SCarolineConcatto     CheckDoVariable(bounds.name);
54964ab3302SCarolineConcatto     CheckDoExpression(bounds.lower);
55064ab3302SCarolineConcatto     CheckDoExpression(bounds.upper);
55164ab3302SCarolineConcatto     if (bounds.step) {
55264ab3302SCarolineConcatto       CheckDoExpression(*bounds.step);
553*0f973ac7SPeter Klausler       if (IsZero(*bounds.step)) {
554*0f973ac7SPeter Klausler         context_.Warn(common::UsageWarning::ZeroDoStep,
555*0f973ac7SPeter Klausler             bounds.step->thing.value().source,
556a53967cdSPeter Klausler             "DO step expression should not be zero"_warn_en_US);
55764ab3302SCarolineConcatto       }
55864ab3302SCarolineConcatto     }
55964ab3302SCarolineConcatto   }
56064ab3302SCarolineConcatto 
56164ab3302SCarolineConcatto   void CheckDoConcurrent(const parser::DoConstruct &doConstruct) {
56264ab3302SCarolineConcatto     auto &doStmt{
56364ab3302SCarolineConcatto         std::get<parser::Statement<parser::NonLabelDoStmt>>(doConstruct.t)};
56464ab3302SCarolineConcatto     currentStatementSourcePosition_ = doStmt.source;
56564ab3302SCarolineConcatto 
56664ab3302SCarolineConcatto     const parser::Block &block{std::get<parser::Block>(doConstruct.t)};
56764ab3302SCarolineConcatto     DoConcurrentBodyEnforce doConcurrentBodyEnforce{context_, doStmt.source};
56864ab3302SCarolineConcatto     parser::Walk(block, doConcurrentBodyEnforce);
56964ab3302SCarolineConcatto 
57064ab3302SCarolineConcatto     LabelEnforce doConcurrentLabelEnforce{context_,
57164ab3302SCarolineConcatto         doConcurrentBodyEnforce.labels(), currentStatementSourcePosition_,
57264ab3302SCarolineConcatto         "DO CONCURRENT"};
57364ab3302SCarolineConcatto     parser::Walk(block, doConcurrentLabelEnforce);
57464ab3302SCarolineConcatto 
57564ab3302SCarolineConcatto     const auto &loopControl{doConstruct.GetLoopControl()};
57664ab3302SCarolineConcatto     CheckConcurrentLoopControl(*loopControl);
57764ab3302SCarolineConcatto     CheckLocalitySpecs(*loopControl, block);
57864ab3302SCarolineConcatto   }
57964ab3302SCarolineConcatto 
58064ab3302SCarolineConcatto   // Return a set of symbols whose names are in a Local locality-spec.  Look
58164ab3302SCarolineConcatto   // the names up in the scope that encloses the DO construct to avoid getting
58264ab3302SCarolineConcatto   // the local versions of them.  Then follow the host-, use-, and
58364ab3302SCarolineConcatto   // construct-associations to get the root symbols
5840d8331c0Speter klausler   UnorderedSymbolSet GatherLocals(
58564ab3302SCarolineConcatto       const std::list<parser::LocalitySpec> &localitySpecs) const {
5860d8331c0Speter klausler     UnorderedSymbolSet symbols;
58764ab3302SCarolineConcatto     const Scope &parentScope{
58864ab3302SCarolineConcatto         context_.FindScope(currentStatementSourcePosition_).parent()};
58964ab3302SCarolineConcatto     // Loop through the LocalitySpec::Local locality-specs
59064ab3302SCarolineConcatto     for (const auto &ls : localitySpecs) {
59164ab3302SCarolineConcatto       if (const auto *names{std::get_if<parser::LocalitySpec::Local>(&ls.u)}) {
59264ab3302SCarolineConcatto         // Loop through the names in the Local locality-spec getting their
59364ab3302SCarolineConcatto         // symbols
59464ab3302SCarolineConcatto         for (const parser::Name &name : names->v) {
59564ab3302SCarolineConcatto           if (const Symbol * symbol{parentScope.FindSymbol(name.source)}) {
596a50bb84eSpeter klausler             symbols.insert(ResolveAssociations(*symbol));
59764ab3302SCarolineConcatto           }
59864ab3302SCarolineConcatto         }
59964ab3302SCarolineConcatto       }
60064ab3302SCarolineConcatto     }
60164ab3302SCarolineConcatto     return symbols;
60264ab3302SCarolineConcatto   }
60364ab3302SCarolineConcatto 
6047e225423SPeter Klausler   UnorderedSymbolSet GatherSymbolsFromExpression(
6057e225423SPeter Klausler       const parser::Expr &expression) const {
6060d8331c0Speter klausler     UnorderedSymbolSet result;
6077e225423SPeter Klausler     if (const auto *expr{GetExpr(context_, expression)}) {
60864ab3302SCarolineConcatto       for (const Symbol &symbol : evaluate::CollectSymbols(*expr)) {
609a50bb84eSpeter klausler         result.insert(ResolveAssociations(symbol));
61064ab3302SCarolineConcatto       }
61164ab3302SCarolineConcatto     }
61264ab3302SCarolineConcatto     return result;
61364ab3302SCarolineConcatto   }
61464ab3302SCarolineConcatto 
61564ab3302SCarolineConcatto   // C1121 - procedures in mask must be pure
61664ab3302SCarolineConcatto   void CheckMaskIsPure(const parser::ScalarLogicalExpr &mask) const {
6170d8331c0Speter klausler     UnorderedSymbolSet references{
6180d8331c0Speter klausler         GatherSymbolsFromExpression(mask.thing.thing.value())};
6190d8331c0Speter klausler     for (const Symbol &ref : OrderBySourcePosition(references)) {
62064ab3302SCarolineConcatto       if (IsProcedure(ref) && !IsPureProcedure(ref)) {
62164ab3302SCarolineConcatto         context_.SayWithDecl(ref, parser::Unwrap<parser::Expr>(mask)->source,
62264ab3302SCarolineConcatto             "%s mask expression may not reference impure procedure '%s'"_err_en_US,
62364ab3302SCarolineConcatto             LoopKindName(), ref.name());
62464ab3302SCarolineConcatto         return;
62564ab3302SCarolineConcatto       }
62664ab3302SCarolineConcatto     }
62764ab3302SCarolineConcatto   }
62864ab3302SCarolineConcatto 
6290d8331c0Speter klausler   void CheckNoCollisions(const UnorderedSymbolSet &refs,
6300d8331c0Speter klausler       const UnorderedSymbolSet &uses, parser::MessageFixedText &&errorMessage,
63164ab3302SCarolineConcatto       const parser::CharBlock &refPosition) const {
6320d8331c0Speter klausler     for (const Symbol &ref : OrderBySourcePosition(refs)) {
63364ab3302SCarolineConcatto       if (uses.find(ref) != uses.end()) {
63464ab3302SCarolineConcatto         context_.SayWithDecl(ref, refPosition, std::move(errorMessage),
63564ab3302SCarolineConcatto             LoopKindName(), ref.name());
63664ab3302SCarolineConcatto         return;
63764ab3302SCarolineConcatto       }
63864ab3302SCarolineConcatto     }
63964ab3302SCarolineConcatto   }
64064ab3302SCarolineConcatto 
6410d8331c0Speter klausler   void HasNoReferences(const UnorderedSymbolSet &indexNames,
6420d8331c0Speter klausler       const parser::ScalarIntExpr &expr) const {
64364ab3302SCarolineConcatto     CheckNoCollisions(GatherSymbolsFromExpression(expr.thing.thing.value()),
64464ab3302SCarolineConcatto         indexNames,
64564ab3302SCarolineConcatto         "%s limit expression may not reference index variable '%s'"_err_en_US,
64664ab3302SCarolineConcatto         expr.thing.thing.value().source);
64764ab3302SCarolineConcatto   }
64864ab3302SCarolineConcatto 
64964ab3302SCarolineConcatto   // C1129, names in local locality-specs can't be in mask expressions
6500d8331c0Speter klausler   void CheckMaskDoesNotReferenceLocal(const parser::ScalarLogicalExpr &mask,
6510d8331c0Speter klausler       const UnorderedSymbolSet &localVars) const {
65264ab3302SCarolineConcatto     CheckNoCollisions(GatherSymbolsFromExpression(mask.thing.thing.value()),
65364ab3302SCarolineConcatto         localVars,
65464ab3302SCarolineConcatto         "%s mask expression references variable '%s'"
65564ab3302SCarolineConcatto         " in LOCAL locality-spec"_err_en_US,
65664ab3302SCarolineConcatto         mask.thing.thing.value().source);
65764ab3302SCarolineConcatto   }
65864ab3302SCarolineConcatto 
65964ab3302SCarolineConcatto   // C1129, names in local locality-specs can't be in limit or step
66064ab3302SCarolineConcatto   // expressions
6610d8331c0Speter klausler   void CheckExprDoesNotReferenceLocal(const parser::ScalarIntExpr &expr,
6620d8331c0Speter klausler       const UnorderedSymbolSet &localVars) const {
66364ab3302SCarolineConcatto     CheckNoCollisions(GatherSymbolsFromExpression(expr.thing.thing.value()),
66464ab3302SCarolineConcatto         localVars,
66564ab3302SCarolineConcatto         "%s expression references variable '%s'"
66664ab3302SCarolineConcatto         " in LOCAL locality-spec"_err_en_US,
66764ab3302SCarolineConcatto         expr.thing.thing.value().source);
66864ab3302SCarolineConcatto   }
66964ab3302SCarolineConcatto 
67064ab3302SCarolineConcatto   // C1130, DEFAULT(NONE) locality requires names to be in locality-specs to
67164ab3302SCarolineConcatto   // be used in the body of the DO loop
67264ab3302SCarolineConcatto   void CheckDefaultNoneImpliesExplicitLocality(
67364ab3302SCarolineConcatto       const std::list<parser::LocalitySpec> &localitySpecs,
67464ab3302SCarolineConcatto       const parser::Block &block) const {
67564ab3302SCarolineConcatto     bool hasDefaultNone{false};
67664ab3302SCarolineConcatto     for (auto &ls : localitySpecs) {
67764ab3302SCarolineConcatto       if (std::holds_alternative<parser::LocalitySpec::DefaultNone>(ls.u)) {
67864ab3302SCarolineConcatto         if (hasDefaultNone) {
6791c91d9bdSPeter Klausler           // F'2023 C1129, you can only have one DEFAULT(NONE)
680*0f973ac7SPeter Klausler           context_.Warn(common::LanguageFeature::BenignRedundancy,
681*0f973ac7SPeter Klausler               currentStatementSourcePosition_,
682a53967cdSPeter Klausler               "Only one DEFAULT(NONE) may appear"_port_en_US);
68364ab3302SCarolineConcatto           break;
68464ab3302SCarolineConcatto         }
68564ab3302SCarolineConcatto         hasDefaultNone = true;
68664ab3302SCarolineConcatto       }
68764ab3302SCarolineConcatto     }
68864ab3302SCarolineConcatto     if (hasDefaultNone) {
68964ab3302SCarolineConcatto       DoConcurrentVariableEnforce doConcurrentVariableEnforce{
69064ab3302SCarolineConcatto           context_, currentStatementSourcePosition_};
69164ab3302SCarolineConcatto       parser::Walk(block, doConcurrentVariableEnforce);
69264ab3302SCarolineConcatto     }
69364ab3302SCarolineConcatto   }
69464ab3302SCarolineConcatto 
6953af717d6Skhaki3   void CheckReduce(const parser::LocalitySpec::Reduce &reduce) const {
6963af717d6Skhaki3     const parser::ReductionOperator &reductionOperator{
6973af717d6Skhaki3         std::get<parser::ReductionOperator>(reduce.t)};
6983af717d6Skhaki3     // F'2023 C1132, reduction variables should have suitable intrinsic type
6993af717d6Skhaki3     for (const parser::Name &x : std::get<std::list<parser::Name>>(reduce.t)) {
7003af717d6Skhaki3       bool supportedIdentifier{false};
7013af717d6Skhaki3       if (x.symbol && x.symbol->GetType()) {
7023af717d6Skhaki3         const auto *type{x.symbol->GetType()};
7033af717d6Skhaki3         auto typeMismatch{[&](const char *suitable_types) {
7043af717d6Skhaki3           context_.Say(currentStatementSourcePosition_,
7053af717d6Skhaki3               "Reduction variable '%s' ('%s') does not have a suitable type ('%s')."_err_en_US,
7063af717d6Skhaki3               x.symbol->name(), type->AsFortran(), suitable_types);
7073af717d6Skhaki3         }};
7083af717d6Skhaki3         supportedIdentifier = true;
7093af717d6Skhaki3         switch (reductionOperator.v) {
7103af717d6Skhaki3         case parser::ReductionOperator::Operator::Plus:
7113af717d6Skhaki3         case parser::ReductionOperator::Operator::Multiply:
7123af717d6Skhaki3           if (!(type->IsNumeric(TypeCategory::Complex) ||
7133af717d6Skhaki3                   type->IsNumeric(TypeCategory::Integer) ||
7143af717d6Skhaki3                   type->IsNumeric(TypeCategory::Real))) {
7153af717d6Skhaki3             typeMismatch("COMPLEX', 'INTEGER', or 'REAL");
7163af717d6Skhaki3           }
7173af717d6Skhaki3           break;
7183af717d6Skhaki3         case parser::ReductionOperator::Operator::And:
7193af717d6Skhaki3         case parser::ReductionOperator::Operator::Or:
7203af717d6Skhaki3         case parser::ReductionOperator::Operator::Eqv:
7213af717d6Skhaki3         case parser::ReductionOperator::Operator::Neqv:
7223af717d6Skhaki3           if (type->category() != DeclTypeSpec::Category::Logical) {
7233af717d6Skhaki3             typeMismatch("LOGICAL");
7243af717d6Skhaki3           }
7253af717d6Skhaki3           break;
7263af717d6Skhaki3         case parser::ReductionOperator::Operator::Max:
7273af717d6Skhaki3         case parser::ReductionOperator::Operator::Min:
7283af717d6Skhaki3           if (!(type->IsNumeric(TypeCategory::Integer) ||
7293af717d6Skhaki3                   type->IsNumeric(TypeCategory::Real))) {
7303af717d6Skhaki3             typeMismatch("INTEGER', or 'REAL");
7313af717d6Skhaki3           }
7323af717d6Skhaki3           break;
7333af717d6Skhaki3         case parser::ReductionOperator::Operator::Iand:
7343af717d6Skhaki3         case parser::ReductionOperator::Operator::Ior:
7353af717d6Skhaki3         case parser::ReductionOperator::Operator::Ieor:
7363af717d6Skhaki3           if (!type->IsNumeric(TypeCategory::Integer)) {
7373af717d6Skhaki3             typeMismatch("INTEGER");
7383af717d6Skhaki3           }
7393af717d6Skhaki3           break;
7403af717d6Skhaki3         }
7413af717d6Skhaki3       }
7423af717d6Skhaki3       if (!supportedIdentifier) {
7433af717d6Skhaki3         context_.Say(currentStatementSourcePosition_,
7443af717d6Skhaki3             "Invalid identifier in REDUCE clause."_err_en_US);
7453af717d6Skhaki3       }
7463af717d6Skhaki3     }
7473af717d6Skhaki3   }
7483af717d6Skhaki3 
74964ab3302SCarolineConcatto   // C1123, concurrent limit or step expressions can't reference index-names
75064ab3302SCarolineConcatto   void CheckConcurrentHeader(const parser::ConcurrentHeader &header) const {
75164ab3302SCarolineConcatto     if (const auto &mask{
75264ab3302SCarolineConcatto             std::get<std::optional<parser::ScalarLogicalExpr>>(header.t)}) {
75364ab3302SCarolineConcatto       CheckMaskIsPure(*mask);
75464ab3302SCarolineConcatto     }
7559390eb92SPeter Klausler     const auto &controls{
7569390eb92SPeter Klausler         std::get<std::list<parser::ConcurrentControl>>(header.t)};
7570d8331c0Speter klausler     UnorderedSymbolSet indexNames;
75864ab3302SCarolineConcatto     for (const parser::ConcurrentControl &control : controls) {
75964ab3302SCarolineConcatto       const auto &indexName{std::get<parser::Name>(control.t)};
76064ab3302SCarolineConcatto       if (indexName.symbol) {
76164ab3302SCarolineConcatto         indexNames.insert(*indexName.symbol);
76264ab3302SCarolineConcatto       }
76334a4eefcSPeter Klausler       CheckForImpureCall(std::get<1>(control.t), concurrentNesting_);
76434a4eefcSPeter Klausler       CheckForImpureCall(std::get<2>(control.t), concurrentNesting_);
7659390eb92SPeter Klausler       if (const auto &stride{std::get<3>(control.t)}) {
76634a4eefcSPeter Klausler         CheckForImpureCall(*stride, concurrentNesting_);
7679390eb92SPeter Klausler       }
76864ab3302SCarolineConcatto     }
76964ab3302SCarolineConcatto     if (!indexNames.empty()) {
77064ab3302SCarolineConcatto       for (const parser::ConcurrentControl &control : controls) {
77164ab3302SCarolineConcatto         HasNoReferences(indexNames, std::get<1>(control.t));
77264ab3302SCarolineConcatto         HasNoReferences(indexNames, std::get<2>(control.t));
77364ab3302SCarolineConcatto         if (const auto &intExpr{
77464ab3302SCarolineConcatto                 std::get<std::optional<parser::ScalarIntExpr>>(control.t)}) {
77564ab3302SCarolineConcatto           const parser::Expr &expr{intExpr->thing.thing.value()};
77664ab3302SCarolineConcatto           CheckNoCollisions(GatherSymbolsFromExpression(expr), indexNames,
77764ab3302SCarolineConcatto               "%s step expression may not reference index variable '%s'"_err_en_US,
77864ab3302SCarolineConcatto               expr.source);
77964ab3302SCarolineConcatto           if (IsZero(expr)) {
78064ab3302SCarolineConcatto             context_.Say(expr.source,
78164ab3302SCarolineConcatto                 "%s step expression may not be zero"_err_en_US, LoopKindName());
78264ab3302SCarolineConcatto           }
78364ab3302SCarolineConcatto         }
78464ab3302SCarolineConcatto       }
78564ab3302SCarolineConcatto     }
78664ab3302SCarolineConcatto   }
78764ab3302SCarolineConcatto 
78864ab3302SCarolineConcatto   void CheckLocalitySpecs(
78964ab3302SCarolineConcatto       const parser::LoopControl &control, const parser::Block &block) const {
79064ab3302SCarolineConcatto     const auto &concurrent{
79164ab3302SCarolineConcatto         std::get<parser::LoopControl::Concurrent>(control.u)};
79264ab3302SCarolineConcatto     const auto &header{std::get<parser::ConcurrentHeader>(concurrent.t)};
79364ab3302SCarolineConcatto     const auto &localitySpecs{
79464ab3302SCarolineConcatto         std::get<std::list<parser::LocalitySpec>>(concurrent.t)};
79564ab3302SCarolineConcatto     if (!localitySpecs.empty()) {
7960d8331c0Speter klausler       const UnorderedSymbolSet &localVars{GatherLocals(localitySpecs)};
79764ab3302SCarolineConcatto       for (const auto &c : GetControls(control)) {
79864ab3302SCarolineConcatto         CheckExprDoesNotReferenceLocal(std::get<1>(c.t), localVars);
79964ab3302SCarolineConcatto         CheckExprDoesNotReferenceLocal(std::get<2>(c.t), localVars);
80064ab3302SCarolineConcatto         if (const auto &expr{
80164ab3302SCarolineConcatto                 std::get<std::optional<parser::ScalarIntExpr>>(c.t)}) {
80264ab3302SCarolineConcatto           CheckExprDoesNotReferenceLocal(*expr, localVars);
80364ab3302SCarolineConcatto         }
80464ab3302SCarolineConcatto       }
80564ab3302SCarolineConcatto       if (const auto &mask{
80664ab3302SCarolineConcatto               std::get<std::optional<parser::ScalarLogicalExpr>>(header.t)}) {
80764ab3302SCarolineConcatto         CheckMaskDoesNotReferenceLocal(*mask, localVars);
80864ab3302SCarolineConcatto       }
8093af717d6Skhaki3       for (auto &ls : localitySpecs) {
8103af717d6Skhaki3         if (const auto *reduce{
8113af717d6Skhaki3                 std::get_if<parser::LocalitySpec::Reduce>(&ls.u)}) {
8123af717d6Skhaki3           CheckReduce(*reduce);
8133af717d6Skhaki3         }
8143af717d6Skhaki3       }
81564ab3302SCarolineConcatto       CheckDefaultNoneImpliesExplicitLocality(localitySpecs, block);
81664ab3302SCarolineConcatto     }
81764ab3302SCarolineConcatto   }
81864ab3302SCarolineConcatto 
81964ab3302SCarolineConcatto   // check constraints [C1121 .. C1130]
82064ab3302SCarolineConcatto   void CheckConcurrentLoopControl(const parser::LoopControl &control) const {
82164ab3302SCarolineConcatto     const auto &concurrent{
82264ab3302SCarolineConcatto         std::get<parser::LoopControl::Concurrent>(control.u)};
82364ab3302SCarolineConcatto     CheckConcurrentHeader(std::get<parser::ConcurrentHeader>(concurrent.t));
82464ab3302SCarolineConcatto   }
82564ab3302SCarolineConcatto 
82634a4eefcSPeter Klausler   template <typename T>
82734a4eefcSPeter Klausler   void CheckForImpureCall(
82834a4eefcSPeter Klausler       const T &x, std::optional<IndexVarKind> nesting) const {
829641ede93Speter klausler     if (auto bad{FindImpureCall(context_.foldingContext(), x)}) {
83034a4eefcSPeter Klausler       if (nesting) {
83164ab3302SCarolineConcatto         context_.Say(
83234a4eefcSPeter Klausler             "Impure procedure '%s' may not be referenced in a %s"_err_en_US,
83334a4eefcSPeter Klausler             *bad, LoopKindName(*nesting));
83434a4eefcSPeter Klausler       } else {
83534a4eefcSPeter Klausler         context_.Say(
83634a4eefcSPeter Klausler             "Impure procedure '%s' should not be referenced in a %s header"_warn_en_US,
83734a4eefcSPeter Klausler             *bad, LoopKindName(kind_));
83864ab3302SCarolineConcatto       }
83964ab3302SCarolineConcatto     }
84034a4eefcSPeter Klausler   }
84134a4eefcSPeter Klausler   void CheckForImpureCall(const parser::ScalarIntExpr &x,
84234a4eefcSPeter Klausler       std::optional<IndexVarKind> nesting) const {
8439390eb92SPeter Klausler     const auto &parsedExpr{x.thing.thing.value()};
8449390eb92SPeter Klausler     auto oldLocation{context_.location()};
8459390eb92SPeter Klausler     context_.set_location(parsedExpr.source);
8469390eb92SPeter Klausler     if (const auto &typedExpr{parsedExpr.typedExpr}) {
8479390eb92SPeter Klausler       if (const auto &expr{typedExpr->v}) {
84834a4eefcSPeter Klausler         CheckForImpureCall(*expr, nesting);
8499390eb92SPeter Klausler       }
8509390eb92SPeter Klausler     }
8519390eb92SPeter Klausler     context_.set_location(oldLocation);
8529390eb92SPeter Klausler   }
85364ab3302SCarolineConcatto 
85464ab3302SCarolineConcatto   // Each index should be used on the LHS of each assignment in a FORALL
85564ab3302SCarolineConcatto   void CheckForallIndexesUsed(const evaluate::Assignment &assignment) {
85664ab3302SCarolineConcatto     SymbolVector indexVars{context_.GetIndexVars(IndexVarKind::FORALL)};
85764ab3302SCarolineConcatto     if (!indexVars.empty()) {
8580d8331c0Speter klausler       UnorderedSymbolSet symbols{evaluate::CollectSymbols(assignment.lhs)};
859cd03e96fSPeter Klausler       common::visit(
86064ab3302SCarolineConcatto           common::visitors{
86164ab3302SCarolineConcatto               [&](const evaluate::Assignment::BoundsSpec &spec) {
86264ab3302SCarolineConcatto                 for (const auto &bound : spec) {
863c8756185STim Keith // TODO: this is working around missing std::set::merge in some versions of
864c8756185STim Keith // clang that we are building with
865c8756185STim Keith #ifdef __clang__
866c8756185STim Keith                   auto boundSymbols{evaluate::CollectSymbols(bound)};
867c8756185STim Keith                   symbols.insert(boundSymbols.begin(), boundSymbols.end());
868c8756185STim Keith #else
86964ab3302SCarolineConcatto                   symbols.merge(evaluate::CollectSymbols(bound));
870c8756185STim Keith #endif
87164ab3302SCarolineConcatto                 }
87264ab3302SCarolineConcatto               },
87364ab3302SCarolineConcatto               [&](const evaluate::Assignment::BoundsRemapping &remapping) {
87464ab3302SCarolineConcatto                 for (const auto &bounds : remapping) {
875c8756185STim Keith #ifdef __clang__
876c8756185STim Keith                   auto lbSymbols{evaluate::CollectSymbols(bounds.first)};
877c8756185STim Keith                   symbols.insert(lbSymbols.begin(), lbSymbols.end());
878c8756185STim Keith                   auto ubSymbols{evaluate::CollectSymbols(bounds.second)};
879c8756185STim Keith                   symbols.insert(ubSymbols.begin(), ubSymbols.end());
880c8756185STim Keith #else
88164ab3302SCarolineConcatto                   symbols.merge(evaluate::CollectSymbols(bounds.first));
88264ab3302SCarolineConcatto                   symbols.merge(evaluate::CollectSymbols(bounds.second));
883c8756185STim Keith #endif
88464ab3302SCarolineConcatto                 }
88564ab3302SCarolineConcatto               },
88664ab3302SCarolineConcatto               [](const auto &) {},
88764ab3302SCarolineConcatto           },
88864ab3302SCarolineConcatto           assignment.u);
88964ab3302SCarolineConcatto       for (const Symbol &index : indexVars) {
890*0f973ac7SPeter Klausler         if (symbols.count(index) == 0) {
891*0f973ac7SPeter Klausler           context_.Warn(common::UsageWarning::UnusedForallIndex,
892*0f973ac7SPeter Klausler               "FORALL index variable '%s' not used on left-hand side of assignment"_warn_en_US,
89364ab3302SCarolineConcatto               index.name());
89464ab3302SCarolineConcatto         }
89564ab3302SCarolineConcatto       }
89664ab3302SCarolineConcatto     }
89764ab3302SCarolineConcatto   }
89864ab3302SCarolineConcatto 
89964ab3302SCarolineConcatto   // For messages where the DO loop must be DO CONCURRENT, make that explicit.
90034a4eefcSPeter Klausler   const char *LoopKindName(IndexVarKind kind) const {
90134a4eefcSPeter Klausler     return kind == IndexVarKind::DO ? "DO CONCURRENT" : "FORALL";
90264ab3302SCarolineConcatto   }
90334a4eefcSPeter Klausler   const char *LoopKindName() const { return LoopKindName(kind_); }
90464ab3302SCarolineConcatto 
90564ab3302SCarolineConcatto   SemanticsContext &context_;
90664ab3302SCarolineConcatto   const IndexVarKind kind_;
90764ab3302SCarolineConcatto   parser::CharBlock currentStatementSourcePosition_;
90834a4eefcSPeter Klausler   std::optional<IndexVarKind> concurrentNesting_;
90964ab3302SCarolineConcatto }; // class DoContext
91064ab3302SCarolineConcatto 
91164ab3302SCarolineConcatto void DoForallChecker::Enter(const parser::DoConstruct &doConstruct) {
91234a4eefcSPeter Klausler   DoContext doContext{context_, IndexVarKind::DO, nestedWithinConcurrent_};
91334a4eefcSPeter Klausler   if (doConstruct.IsDoConcurrent()) {
91434a4eefcSPeter Klausler     nestedWithinConcurrent_.push_back(IndexVarKind::DO);
91534a4eefcSPeter Klausler   }
91664ab3302SCarolineConcatto   doContext.DefineDoVariables(doConstruct);
91734a4eefcSPeter Klausler   doContext.Check(doConstruct);
91864ab3302SCarolineConcatto }
91964ab3302SCarolineConcatto 
92064ab3302SCarolineConcatto void DoForallChecker::Leave(const parser::DoConstruct &doConstruct) {
92134a4eefcSPeter Klausler   DoContext doContext{context_, IndexVarKind::DO, nestedWithinConcurrent_};
92264ab3302SCarolineConcatto   doContext.ResetDoVariables(doConstruct);
92334a4eefcSPeter Klausler   if (doConstruct.IsDoConcurrent()) {
92434a4eefcSPeter Klausler     nestedWithinConcurrent_.pop_back();
92534a4eefcSPeter Klausler   }
92664ab3302SCarolineConcatto }
92764ab3302SCarolineConcatto 
92864ab3302SCarolineConcatto void DoForallChecker::Enter(const parser::ForallConstruct &construct) {
92934a4eefcSPeter Klausler   DoContext doContext{context_, IndexVarKind::FORALL, nestedWithinConcurrent_};
93064ab3302SCarolineConcatto   doContext.ActivateIndexVars(GetControls(construct));
93134a4eefcSPeter Klausler   nestedWithinConcurrent_.push_back(IndexVarKind::FORALL);
9329390eb92SPeter Klausler   doContext.Check(construct);
93364ab3302SCarolineConcatto }
93464ab3302SCarolineConcatto void DoForallChecker::Leave(const parser::ForallConstruct &construct) {
93534a4eefcSPeter Klausler   DoContext doContext{context_, IndexVarKind::FORALL, nestedWithinConcurrent_};
93664ab3302SCarolineConcatto   doContext.DeactivateIndexVars(GetControls(construct));
93734a4eefcSPeter Klausler   nestedWithinConcurrent_.pop_back();
93864ab3302SCarolineConcatto }
93964ab3302SCarolineConcatto 
94064ab3302SCarolineConcatto void DoForallChecker::Enter(const parser::ForallStmt &stmt) {
94134a4eefcSPeter Klausler   DoContext doContext{context_, IndexVarKind::FORALL, nestedWithinConcurrent_};
94234a4eefcSPeter Klausler   nestedWithinConcurrent_.push_back(IndexVarKind::FORALL);
9439390eb92SPeter Klausler   doContext.Check(stmt);
94464ab3302SCarolineConcatto   doContext.ActivateIndexVars(GetControls(stmt));
94564ab3302SCarolineConcatto }
94664ab3302SCarolineConcatto void DoForallChecker::Leave(const parser::ForallStmt &stmt) {
94734a4eefcSPeter Klausler   DoContext doContext{context_, IndexVarKind::FORALL, nestedWithinConcurrent_};
94864ab3302SCarolineConcatto   doContext.DeactivateIndexVars(GetControls(stmt));
94934a4eefcSPeter Klausler   nestedWithinConcurrent_.pop_back();
95064ab3302SCarolineConcatto }
95164ab3302SCarolineConcatto void DoForallChecker::Leave(const parser::ForallAssignmentStmt &stmt) {
95234a4eefcSPeter Klausler   DoContext doContext{context_, IndexVarKind::FORALL, nestedWithinConcurrent_};
95364ab3302SCarolineConcatto   doContext.Check(stmt);
95464ab3302SCarolineConcatto }
95564ab3302SCarolineConcatto 
9561f879005STim Keith template <typename A>
9571f879005STim Keith static parser::CharBlock GetConstructPosition(const A &a) {
95864ab3302SCarolineConcatto   return std::get<0>(a.t).source;
95964ab3302SCarolineConcatto }
96064ab3302SCarolineConcatto 
96164ab3302SCarolineConcatto static parser::CharBlock GetNodePosition(const ConstructNode &construct) {
962cd03e96fSPeter Klausler   return common::visit(
96364ab3302SCarolineConcatto       [&](const auto &x) { return GetConstructPosition(*x); }, construct);
96464ab3302SCarolineConcatto }
96564ab3302SCarolineConcatto 
96664ab3302SCarolineConcatto void DoForallChecker::SayBadLeave(StmtType stmtType,
96764ab3302SCarolineConcatto     const char *enclosingStmtName, const ConstructNode &construct) const {
96864ab3302SCarolineConcatto   context_
96964ab3302SCarolineConcatto       .Say("%s must not leave a %s statement"_err_en_US, EnumToString(stmtType),
97064ab3302SCarolineConcatto           enclosingStmtName)
97164ab3302SCarolineConcatto       .Attach(GetNodePosition(construct), "The construct that was left"_en_US);
97264ab3302SCarolineConcatto }
97364ab3302SCarolineConcatto 
97464ab3302SCarolineConcatto static const parser::DoConstruct *MaybeGetDoConstruct(
97564ab3302SCarolineConcatto     const ConstructNode &construct) {
97664ab3302SCarolineConcatto   if (const auto *doNode{
97764ab3302SCarolineConcatto           std::get_if<const parser::DoConstruct *>(&construct)}) {
97864ab3302SCarolineConcatto     return *doNode;
97964ab3302SCarolineConcatto   } else {
98064ab3302SCarolineConcatto     return nullptr;
98164ab3302SCarolineConcatto   }
98264ab3302SCarolineConcatto }
98364ab3302SCarolineConcatto 
98464ab3302SCarolineConcatto static bool ConstructIsDoConcurrent(const ConstructNode &construct) {
98564ab3302SCarolineConcatto   const parser::DoConstruct *doConstruct{MaybeGetDoConstruct(construct)};
98664ab3302SCarolineConcatto   return doConstruct && doConstruct->IsDoConcurrent();
98764ab3302SCarolineConcatto }
98864ab3302SCarolineConcatto 
98964ab3302SCarolineConcatto // Check that CYCLE and EXIT statements do not cause flow of control to
99064ab3302SCarolineConcatto // leave DO CONCURRENT, CRITICAL, or CHANGE TEAM constructs.
99164ab3302SCarolineConcatto void DoForallChecker::CheckForBadLeave(
99264ab3302SCarolineConcatto     StmtType stmtType, const ConstructNode &construct) const {
993cd03e96fSPeter Klausler   common::visit(common::visitors{
99464ab3302SCarolineConcatto                     [&](const parser::DoConstruct *doConstructPtr) {
99564ab3302SCarolineConcatto                       if (doConstructPtr->IsDoConcurrent()) {
996cd03e96fSPeter Klausler                         // C1135 and C1167 -- CYCLE and EXIT statements can't
997cd03e96fSPeter Klausler                         // leave a DO CONCURRENT
99864ab3302SCarolineConcatto                         SayBadLeave(stmtType, "DO CONCURRENT", construct);
99964ab3302SCarolineConcatto                       }
100064ab3302SCarolineConcatto                     },
100164ab3302SCarolineConcatto                     [&](const parser::CriticalConstruct *) {
100264ab3302SCarolineConcatto                       // C1135 and C1168 -- similarly, for CRITICAL
100364ab3302SCarolineConcatto                       SayBadLeave(stmtType, "CRITICAL", construct);
100464ab3302SCarolineConcatto                     },
100564ab3302SCarolineConcatto                     [&](const parser::ChangeTeamConstruct *) {
100664ab3302SCarolineConcatto                       // C1135 and C1168 -- similarly, for CHANGE TEAM
100764ab3302SCarolineConcatto                       SayBadLeave(stmtType, "CHANGE TEAM", construct);
100864ab3302SCarolineConcatto                     },
100964ab3302SCarolineConcatto                     [](const auto *) {},
101064ab3302SCarolineConcatto                 },
101164ab3302SCarolineConcatto       construct);
101264ab3302SCarolineConcatto }
101364ab3302SCarolineConcatto 
101464ab3302SCarolineConcatto static bool StmtMatchesConstruct(const parser::Name *stmtName,
10154a51691aSsameeran joshi     StmtType stmtType, const std::optional<parser::Name> &constructName,
101664ab3302SCarolineConcatto     const ConstructNode &construct) {
1017cc77f818SDavid Truby   bool inDoConstruct{MaybeGetDoConstruct(construct) != nullptr};
101864ab3302SCarolineConcatto   if (!stmtName) {
101964ab3302SCarolineConcatto     return inDoConstruct; // Unlabeled statements match all DO constructs
102064ab3302SCarolineConcatto   } else if (constructName && constructName->source == stmtName->source) {
102164ab3302SCarolineConcatto     return stmtType == StmtType::EXIT || inDoConstruct;
102264ab3302SCarolineConcatto   } else {
102364ab3302SCarolineConcatto     return false;
102464ab3302SCarolineConcatto   }
102564ab3302SCarolineConcatto }
102664ab3302SCarolineConcatto 
102764ab3302SCarolineConcatto // C1167 Can't EXIT from a DO CONCURRENT
102864ab3302SCarolineConcatto void DoForallChecker::CheckDoConcurrentExit(
102964ab3302SCarolineConcatto     StmtType stmtType, const ConstructNode &construct) const {
103064ab3302SCarolineConcatto   if (stmtType == StmtType::EXIT && ConstructIsDoConcurrent(construct)) {
103164ab3302SCarolineConcatto     SayBadLeave(StmtType::EXIT, "DO CONCURRENT", construct);
103264ab3302SCarolineConcatto   }
103364ab3302SCarolineConcatto }
103464ab3302SCarolineConcatto 
103564ab3302SCarolineConcatto // Check nesting violations for a CYCLE or EXIT statement.  Loop up the
103664ab3302SCarolineConcatto // nesting levels looking for a construct that matches the CYCLE or EXIT
103764ab3302SCarolineConcatto // statment.  At every construct, check for a violation.  If we find a match
103864ab3302SCarolineConcatto // without finding a violation, the check is complete.
103964ab3302SCarolineConcatto void DoForallChecker::CheckNesting(
104064ab3302SCarolineConcatto     StmtType stmtType, const parser::Name *stmtName) const {
104164ab3302SCarolineConcatto   const ConstructStack &stack{context_.constructStack()};
104264ab3302SCarolineConcatto   for (auto iter{stack.cend()}; iter-- != stack.cbegin();) {
104364ab3302SCarolineConcatto     const ConstructNode &construct{*iter};
10444a51691aSsameeran joshi     const std::optional<parser::Name> &constructName{
10454a51691aSsameeran joshi         MaybeGetNodeName(construct)};
104664ab3302SCarolineConcatto     if (StmtMatchesConstruct(stmtName, stmtType, constructName, construct)) {
104764ab3302SCarolineConcatto       CheckDoConcurrentExit(stmtType, construct);
104864ab3302SCarolineConcatto       return; // We got a match, so we're finished checking
104964ab3302SCarolineConcatto     }
105064ab3302SCarolineConcatto     CheckForBadLeave(stmtType, construct);
105164ab3302SCarolineConcatto   }
105264ab3302SCarolineConcatto 
105364ab3302SCarolineConcatto   // We haven't found a match in the enclosing constructs
105464ab3302SCarolineConcatto   if (stmtType == StmtType::EXIT) {
105564ab3302SCarolineConcatto     context_.Say("No matching construct for EXIT statement"_err_en_US);
105664ab3302SCarolineConcatto   } else {
105764ab3302SCarolineConcatto     context_.Say("No matching DO construct for CYCLE statement"_err_en_US);
105864ab3302SCarolineConcatto   }
105964ab3302SCarolineConcatto }
106064ab3302SCarolineConcatto 
106164ab3302SCarolineConcatto // C1135 -- Nesting for CYCLE statements
106264ab3302SCarolineConcatto void DoForallChecker::Enter(const parser::CycleStmt &cycleStmt) {
106364ab3302SCarolineConcatto   CheckNesting(StmtType::CYCLE, common::GetPtrFromOptional(cycleStmt.v));
106464ab3302SCarolineConcatto }
106564ab3302SCarolineConcatto 
106664ab3302SCarolineConcatto // C1167 and C1168 -- Nesting for EXIT statements
106764ab3302SCarolineConcatto void DoForallChecker::Enter(const parser::ExitStmt &exitStmt) {
106864ab3302SCarolineConcatto   CheckNesting(StmtType::EXIT, common::GetPtrFromOptional(exitStmt.v));
106964ab3302SCarolineConcatto }
107064ab3302SCarolineConcatto 
107164ab3302SCarolineConcatto void DoForallChecker::Leave(const parser::AssignmentStmt &stmt) {
107264ab3302SCarolineConcatto   const auto &variable{std::get<parser::Variable>(stmt.t)};
107364ab3302SCarolineConcatto   context_.CheckIndexVarRedefine(variable);
107464ab3302SCarolineConcatto }
107564ab3302SCarolineConcatto 
107664ab3302SCarolineConcatto static void CheckIfArgIsDoVar(const evaluate::ActualArgument &arg,
107764ab3302SCarolineConcatto     const parser::CharBlock location, SemanticsContext &context) {
107864ab3302SCarolineConcatto   common::Intent intent{arg.dummyIntent()};
107964ab3302SCarolineConcatto   if (intent == common::Intent::Out || intent == common::Intent::InOut) {
108064ab3302SCarolineConcatto     if (const SomeExpr * argExpr{arg.UnwrapExpr()}) {
108164ab3302SCarolineConcatto       if (const Symbol * var{evaluate::UnwrapWholeSymbolDataRef(*argExpr)}) {
108264ab3302SCarolineConcatto         if (intent == common::Intent::Out) {
108364ab3302SCarolineConcatto           context.CheckIndexVarRedefine(location, *var);
108464ab3302SCarolineConcatto         } else {
108564ab3302SCarolineConcatto           context.WarnIndexVarRedefine(location, *var); // INTENT(INOUT)
108664ab3302SCarolineConcatto         }
108764ab3302SCarolineConcatto       }
108864ab3302SCarolineConcatto     }
108964ab3302SCarolineConcatto   }
109064ab3302SCarolineConcatto }
109164ab3302SCarolineConcatto 
109264ab3302SCarolineConcatto // Check to see if a DO variable is being passed as an actual argument to a
109364ab3302SCarolineConcatto // dummy argument whose intent is OUT or INOUT.  To do this, we need to find
109464ab3302SCarolineConcatto // the expressions for actual arguments which contain DO variables.  We get the
109564ab3302SCarolineConcatto // intents of the dummy arguments from the ProcedureRef in the "typedCall"
109664ab3302SCarolineConcatto // field of the CallStmt which was filled in during expression checking.  At
109764ab3302SCarolineConcatto // the same time, we need to iterate over the parser::Expr versions of the
109864ab3302SCarolineConcatto // actual arguments to get their source locations of the arguments for the
109964ab3302SCarolineConcatto // messages.
110064ab3302SCarolineConcatto void DoForallChecker::Leave(const parser::CallStmt &callStmt) {
110164ab3302SCarolineConcatto   if (const auto &typedCall{callStmt.typedCall}) {
110264ab3302SCarolineConcatto     const auto &parsedArgs{
11034ad72793SPeter Klausler         std::get<std::list<parser::ActualArgSpec>>(callStmt.call.t)};
110464ab3302SCarolineConcatto     auto parsedArgIter{parsedArgs.begin()};
110564ab3302SCarolineConcatto     const evaluate::ActualArguments &checkedArgs{typedCall->arguments()};
110664ab3302SCarolineConcatto     for (const auto &checkedOptionalArg : checkedArgs) {
110764ab3302SCarolineConcatto       if (parsedArgIter == parsedArgs.end()) {
110864ab3302SCarolineConcatto         break; // No more parsed arguments, we're done.
110964ab3302SCarolineConcatto       }
111064ab3302SCarolineConcatto       const auto &parsedArg{std::get<parser::ActualArg>(parsedArgIter->t)};
111164ab3302SCarolineConcatto       ++parsedArgIter;
111264ab3302SCarolineConcatto       if (checkedOptionalArg) {
111364ab3302SCarolineConcatto         const evaluate::ActualArgument &checkedArg{*checkedOptionalArg};
111464ab3302SCarolineConcatto         if (const auto *parsedExpr{
111564ab3302SCarolineConcatto                 std::get_if<common::Indirection<parser::Expr>>(&parsedArg.u)}) {
111664ab3302SCarolineConcatto           CheckIfArgIsDoVar(checkedArg, parsedExpr->value().source, context_);
111764ab3302SCarolineConcatto         }
111864ab3302SCarolineConcatto       }
111964ab3302SCarolineConcatto     }
112064ab3302SCarolineConcatto   }
112164ab3302SCarolineConcatto }
112264ab3302SCarolineConcatto 
112364ab3302SCarolineConcatto void DoForallChecker::Leave(const parser::ConnectSpec &connectSpec) {
112464ab3302SCarolineConcatto   const auto *newunit{
112564ab3302SCarolineConcatto       std::get_if<parser::ConnectSpec::Newunit>(&connectSpec.u)};
112664ab3302SCarolineConcatto   if (newunit) {
112764ab3302SCarolineConcatto     context_.CheckIndexVarRedefine(newunit->v.thing.thing);
112864ab3302SCarolineConcatto   }
112964ab3302SCarolineConcatto }
113064ab3302SCarolineConcatto 
113164ab3302SCarolineConcatto using ActualArgumentSet = std::set<evaluate::ActualArgumentRef>;
113264ab3302SCarolineConcatto 
113364ab3302SCarolineConcatto struct CollectActualArgumentsHelper
113464ab3302SCarolineConcatto     : public evaluate::SetTraverse<CollectActualArgumentsHelper,
113564ab3302SCarolineConcatto           ActualArgumentSet> {
113664ab3302SCarolineConcatto   using Base = SetTraverse<CollectActualArgumentsHelper, ActualArgumentSet>;
113764ab3302SCarolineConcatto   CollectActualArgumentsHelper() : Base{*this} {}
113864ab3302SCarolineConcatto   using Base::operator();
113964ab3302SCarolineConcatto   ActualArgumentSet operator()(const evaluate::ActualArgument &arg) const {
11404d8c00c0SPete Steinfeld     return Combine(ActualArgumentSet{arg},
11414d8c00c0SPete Steinfeld         CollectActualArgumentsHelper{}(arg.UnwrapExpr()));
114264ab3302SCarolineConcatto   }
114364ab3302SCarolineConcatto };
114464ab3302SCarolineConcatto 
114564ab3302SCarolineConcatto template <typename A> ActualArgumentSet CollectActualArguments(const A &x) {
114664ab3302SCarolineConcatto   return CollectActualArgumentsHelper{}(x);
114764ab3302SCarolineConcatto }
114864ab3302SCarolineConcatto 
114964ab3302SCarolineConcatto template ActualArgumentSet CollectActualArguments(const SomeExpr &);
115064ab3302SCarolineConcatto 
11514d8c00c0SPete Steinfeld void DoForallChecker::Enter(const parser::Expr &parsedExpr) { ++exprDepth_; }
11524d8c00c0SPete Steinfeld 
115364ab3302SCarolineConcatto void DoForallChecker::Leave(const parser::Expr &parsedExpr) {
11544d8c00c0SPete Steinfeld   CHECK(exprDepth_ > 0);
11554d8c00c0SPete Steinfeld   if (--exprDepth_ == 0) { // Only check top level expressions
11567e225423SPeter Klausler     if (const SomeExpr * expr{GetExpr(context_, parsedExpr)}) {
115764ab3302SCarolineConcatto       ActualArgumentSet argSet{CollectActualArguments(*expr)};
115864ab3302SCarolineConcatto       for (const evaluate::ActualArgumentRef &argRef : argSet) {
115964ab3302SCarolineConcatto         CheckIfArgIsDoVar(*argRef, parsedExpr.source, context_);
116064ab3302SCarolineConcatto       }
116164ab3302SCarolineConcatto     }
116264ab3302SCarolineConcatto   }
11634d8c00c0SPete Steinfeld }
116464ab3302SCarolineConcatto 
116564ab3302SCarolineConcatto void DoForallChecker::Leave(const parser::InquireSpec &inquireSpec) {
116664ab3302SCarolineConcatto   const auto *intVar{std::get_if<parser::InquireSpec::IntVar>(&inquireSpec.u)};
116764ab3302SCarolineConcatto   if (intVar) {
116864ab3302SCarolineConcatto     const auto &scalar{std::get<parser::ScalarIntVariable>(intVar->t)};
116964ab3302SCarolineConcatto     context_.CheckIndexVarRedefine(scalar.thing.thing);
117064ab3302SCarolineConcatto   }
117164ab3302SCarolineConcatto }
117264ab3302SCarolineConcatto 
117364ab3302SCarolineConcatto void DoForallChecker::Leave(const parser::IoControlSpec &ioControlSpec) {
117464ab3302SCarolineConcatto   const auto *size{std::get_if<parser::IoControlSpec::Size>(&ioControlSpec.u)};
117564ab3302SCarolineConcatto   if (size) {
117664ab3302SCarolineConcatto     context_.CheckIndexVarRedefine(size->v.thing.thing);
117764ab3302SCarolineConcatto   }
117864ab3302SCarolineConcatto }
117964ab3302SCarolineConcatto 
118064ab3302SCarolineConcatto void DoForallChecker::Leave(const parser::OutputImpliedDo &outputImpliedDo) {
118164ab3302SCarolineConcatto   const auto &control{std::get<parser::IoImpliedDoControl>(outputImpliedDo.t)};
118264ab3302SCarolineConcatto   const parser::Name &name{control.name.thing.thing};
118364ab3302SCarolineConcatto   context_.CheckIndexVarRedefine(name.source, *name.symbol);
118464ab3302SCarolineConcatto }
118564ab3302SCarolineConcatto 
118664ab3302SCarolineConcatto void DoForallChecker::Leave(const parser::StatVariable &statVariable) {
118764ab3302SCarolineConcatto   context_.CheckIndexVarRedefine(statVariable.v.thing.thing);
118864ab3302SCarolineConcatto }
118964ab3302SCarolineConcatto 
119064ab3302SCarolineConcatto } // namespace Fortran::semantics
1191