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