1 //===-- lib/Semantics/check-do-forall.cpp ---------------------------------===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 9 #include "check-do-forall.h" 10 #include "flang/Common/template.h" 11 #include "flang/Evaluate/call.h" 12 #include "flang/Evaluate/expression.h" 13 #include "flang/Evaluate/tools.h" 14 #include "flang/Parser/message.h" 15 #include "flang/Parser/parse-tree-visitor.h" 16 #include "flang/Parser/tools.h" 17 #include "flang/Semantics/attr.h" 18 #include "flang/Semantics/scope.h" 19 #include "flang/Semantics/semantics.h" 20 #include "flang/Semantics/symbol.h" 21 #include "flang/Semantics/tools.h" 22 #include "flang/Semantics/type.h" 23 24 namespace Fortran::evaluate { 25 using ActualArgumentRef = common::Reference<const ActualArgument>; 26 27 inline bool operator<(ActualArgumentRef x, ActualArgumentRef y) { 28 return &*x < &*y; 29 } 30 } // namespace Fortran::evaluate 31 32 namespace Fortran::semantics { 33 34 using namespace parser::literals; 35 36 using Bounds = parser::LoopControl::Bounds; 37 using IndexVarKind = SemanticsContext::IndexVarKind; 38 39 static const parser::ConcurrentHeader &GetConcurrentHeader( 40 const parser::LoopControl &loopControl) { 41 const auto &concurrent{ 42 std::get<parser::LoopControl::Concurrent>(loopControl.u)}; 43 return std::get<parser::ConcurrentHeader>(concurrent.t); 44 } 45 static const parser::ConcurrentHeader &GetConcurrentHeader( 46 const parser::ForallConstruct &construct) { 47 const auto &stmt{ 48 std::get<parser::Statement<parser::ForallConstructStmt>>(construct.t)}; 49 return std::get<common::Indirection<parser::ConcurrentHeader>>( 50 stmt.statement.t) 51 .value(); 52 } 53 static const parser::ConcurrentHeader &GetConcurrentHeader( 54 const parser::ForallStmt &stmt) { 55 return std::get<common::Indirection<parser::ConcurrentHeader>>(stmt.t) 56 .value(); 57 } 58 template <typename T> 59 static const std::list<parser::ConcurrentControl> &GetControls(const T &x) { 60 return std::get<std::list<parser::ConcurrentControl>>( 61 GetConcurrentHeader(x).t); 62 } 63 64 static const Bounds &GetBounds(const parser::DoConstruct &doConstruct) { 65 auto &loopControl{doConstruct.GetLoopControl().value()}; 66 return std::get<Bounds>(loopControl.u); 67 } 68 69 static const parser::Name &GetDoVariable( 70 const parser::DoConstruct &doConstruct) { 71 const Bounds &bounds{GetBounds(doConstruct)}; 72 return bounds.name.thing; 73 } 74 75 static parser::MessageFixedText GetEnclosingDoMsg() { 76 return "Enclosing DO CONCURRENT statement"_en_US; 77 } 78 79 static void SayWithDo(SemanticsContext &context, parser::CharBlock stmtLocation, 80 parser::MessageFixedText &&message, parser::CharBlock doLocation) { 81 context.Say(stmtLocation, message).Attach(doLocation, GetEnclosingDoMsg()); 82 } 83 84 // 11.1.7.5 - enforce semantics constraints on a DO CONCURRENT loop body 85 class DoConcurrentBodyEnforce { 86 public: 87 DoConcurrentBodyEnforce( 88 SemanticsContext &context, parser::CharBlock doConcurrentSourcePosition) 89 : context_{context}, doConcurrentSourcePosition_{ 90 doConcurrentSourcePosition} {} 91 std::set<parser::Label> labels() { return labels_; } 92 template <typename T> bool Pre(const T &) { return true; } 93 template <typename T> void Post(const T &) {} 94 95 template <typename T> bool Pre(const parser::Statement<T> &statement) { 96 currentStatementSourcePosition_ = statement.source; 97 if (statement.label.has_value()) { 98 labels_.insert(*statement.label); 99 } 100 return true; 101 } 102 103 template <typename T> bool Pre(const parser::UnlabeledStatement<T> &stmt) { 104 currentStatementSourcePosition_ = stmt.source; 105 return true; 106 } 107 108 // C1140 -- Can't deallocate a polymorphic entity in a DO CONCURRENT. 109 // Deallocation can be caused by exiting a block that declares an allocatable 110 // entity, assignment to an allocatable variable, or an actual DEALLOCATE 111 // statement 112 // 113 // Note also that the deallocation of a derived type entity might cause the 114 // invocation of an IMPURE final subroutine. (C1139) 115 // 116 117 // Only to be called for symbols with ObjectEntityDetails 118 static bool HasImpureFinal(const Symbol &original) { 119 const Symbol &symbol{ResolveAssociations(original)}; 120 if (symbol.has<ObjectEntityDetails>()) { 121 if (const DeclTypeSpec * symType{symbol.GetType()}) { 122 if (const DerivedTypeSpec * derived{symType->AsDerived()}) { 123 return semantics::HasImpureFinal(*derived); 124 } 125 } 126 } 127 return false; 128 } 129 130 // Predicate for deallocations caused by block exit and direct deallocation 131 static bool DeallocateAll(const Symbol &) { return true; } 132 133 // Predicate for deallocations caused by intrinsic assignment 134 static bool DeallocateNonCoarray(const Symbol &component) { 135 return !evaluate::IsCoarray(component); 136 } 137 138 static bool WillDeallocatePolymorphic(const Symbol &entity, 139 const std::function<bool(const Symbol &)> &WillDeallocate) { 140 return WillDeallocate(entity) && IsPolymorphicAllocatable(entity); 141 } 142 143 // Is it possible that we will we deallocate a polymorphic entity or one 144 // of its components? 145 static bool MightDeallocatePolymorphic(const Symbol &original, 146 const std::function<bool(const Symbol &)> &WillDeallocate) { 147 const Symbol &symbol{ResolveAssociations(original)}; 148 // Check the entity itself, no coarray exception here 149 if (IsPolymorphicAllocatable(symbol)) { 150 return true; 151 } 152 // Check the components 153 if (const auto *details{symbol.detailsIf<ObjectEntityDetails>()}) { 154 if (const DeclTypeSpec * entityType{details->type()}) { 155 if (const DerivedTypeSpec * derivedType{entityType->AsDerived()}) { 156 UltimateComponentIterator ultimates{*derivedType}; 157 for (const auto &ultimate : ultimates) { 158 if (WillDeallocatePolymorphic(ultimate, WillDeallocate)) { 159 return true; 160 } 161 } 162 } 163 } 164 } 165 return false; 166 } 167 168 void SayDeallocateWithImpureFinal(const Symbol &entity, const char *reason) { 169 context_.SayWithDecl(entity, currentStatementSourcePosition_, 170 "Deallocation of an entity with an IMPURE FINAL procedure" 171 " caused by %s not allowed in DO CONCURRENT"_err_en_US, 172 reason); 173 } 174 175 void SayDeallocateOfPolymorph( 176 parser::CharBlock location, const Symbol &entity, const char *reason) { 177 context_.SayWithDecl(entity, location, 178 "Deallocation of a polymorphic entity caused by %s" 179 " not allowed in DO CONCURRENT"_err_en_US, 180 reason); 181 } 182 183 // Deallocation caused by block exit 184 // Allocatable entities and all of their allocatable subcomponents will be 185 // deallocated. This test is different from the other two because it does 186 // not deallocate in cases where the entity itself is not allocatable but 187 // has allocatable polymorphic components 188 void Post(const parser::BlockConstruct &blockConstruct) { 189 const auto &endBlockStmt{ 190 std::get<parser::Statement<parser::EndBlockStmt>>(blockConstruct.t)}; 191 const Scope &blockScope{context_.FindScope(endBlockStmt.source)}; 192 const Scope &doScope{context_.FindScope(doConcurrentSourcePosition_)}; 193 if (DoesScopeContain(&doScope, blockScope)) { 194 const char *reason{"block exit"}; 195 for (auto &pair : blockScope) { 196 const Symbol &entity{*pair.second}; 197 if (IsAllocatable(entity) && !IsSaved(entity) && 198 MightDeallocatePolymorphic(entity, DeallocateAll)) { 199 SayDeallocateOfPolymorph(endBlockStmt.source, entity, reason); 200 } 201 if (HasImpureFinal(entity)) { 202 SayDeallocateWithImpureFinal(entity, reason); 203 } 204 } 205 } 206 } 207 208 // Deallocation caused by assignment 209 // Note that this case does not cause deallocation of coarray components 210 void Post(const parser::AssignmentStmt &stmt) { 211 const auto &variable{std::get<parser::Variable>(stmt.t)}; 212 if (const Symbol * entity{GetLastName(variable).symbol}) { 213 const char *reason{"assignment"}; 214 if (MightDeallocatePolymorphic(*entity, DeallocateNonCoarray)) { 215 SayDeallocateOfPolymorph(variable.GetSource(), *entity, reason); 216 } 217 if (HasImpureFinal(*entity)) { 218 SayDeallocateWithImpureFinal(*entity, reason); 219 } 220 } 221 } 222 223 // Deallocation from a DEALLOCATE statement 224 // This case is different because DEALLOCATE statements deallocate both 225 // ALLOCATABLE and POINTER entities 226 void Post(const parser::DeallocateStmt &stmt) { 227 const auto &allocateObjectList{ 228 std::get<std::list<parser::AllocateObject>>(stmt.t)}; 229 for (const auto &allocateObject : allocateObjectList) { 230 const parser::Name &name{GetLastName(allocateObject)}; 231 const char *reason{"a DEALLOCATE statement"}; 232 if (name.symbol) { 233 const Symbol &entity{*name.symbol}; 234 const DeclTypeSpec *entityType{entity.GetType()}; 235 if ((entityType && entityType->IsPolymorphic()) || // POINTER case 236 MightDeallocatePolymorphic(entity, DeallocateAll)) { 237 SayDeallocateOfPolymorph( 238 currentStatementSourcePosition_, entity, reason); 239 } 240 if (HasImpureFinal(entity)) { 241 SayDeallocateWithImpureFinal(entity, reason); 242 } 243 } 244 } 245 } 246 247 // C1137 -- No image control statements in a DO CONCURRENT 248 void Post(const parser::ExecutableConstruct &construct) { 249 if (IsImageControlStmt(construct)) { 250 const parser::CharBlock statementLocation{ 251 GetImageControlStmtLocation(construct)}; 252 auto &msg{context_.Say(statementLocation, 253 "An image control statement is not allowed in DO" 254 " CONCURRENT"_err_en_US)}; 255 if (auto coarrayMsg{GetImageControlStmtCoarrayMsg(construct)}) { 256 msg.Attach(statementLocation, *coarrayMsg); 257 } 258 msg.Attach(doConcurrentSourcePosition_, GetEnclosingDoMsg()); 259 } 260 } 261 262 // C1136 -- No RETURN statements in a DO CONCURRENT 263 void Post(const parser::ReturnStmt &) { 264 context_ 265 .Say(currentStatementSourcePosition_, 266 "RETURN is not allowed in DO CONCURRENT"_err_en_US) 267 .Attach(doConcurrentSourcePosition_, GetEnclosingDoMsg()); 268 } 269 270 // C1139: call to impure procedure and ... 271 // C1141: cannot call ieee_get_flag, ieee_[gs]et_halting_mode 272 // It's not necessary to check the ieee_get* procedures because they're 273 // not pure, and impure procedures are caught by checks for constraint C1139 274 void Post(const parser::ProcedureDesignator &procedureDesignator) { 275 if (auto *name{std::get_if<parser::Name>(&procedureDesignator.u)}) { 276 if (name->symbol && !IsPureProcedure(*name->symbol)) { 277 SayWithDo(context_, currentStatementSourcePosition_, 278 "Call to an impure procedure is not allowed in DO" 279 " CONCURRENT"_err_en_US, 280 doConcurrentSourcePosition_); 281 } 282 if (name->symbol && 283 fromScope(*name->symbol, "__fortran_ieee_exceptions"s)) { 284 if (name->source == "ieee_set_halting_mode") { 285 SayWithDo(context_, currentStatementSourcePosition_, 286 "IEEE_SET_HALTING_MODE is not allowed in DO " 287 "CONCURRENT"_err_en_US, 288 doConcurrentSourcePosition_); 289 } 290 } 291 } else { 292 // C1139: this a procedure component 293 auto &component{std::get<parser::ProcComponentRef>(procedureDesignator.u) 294 .v.thing.component}; 295 if (component.symbol && !IsPureProcedure(*component.symbol)) { 296 SayWithDo(context_, currentStatementSourcePosition_, 297 "Call to an impure procedure component is not allowed" 298 " in DO CONCURRENT"_err_en_US, 299 doConcurrentSourcePosition_); 300 } 301 } 302 } 303 304 // 11.1.7.5, paragraph 5, no ADVANCE specifier in a DO CONCURRENT 305 void Post(const parser::IoControlSpec &ioControlSpec) { 306 if (auto *charExpr{ 307 std::get_if<parser::IoControlSpec::CharExpr>(&ioControlSpec.u)}) { 308 if (std::get<parser::IoControlSpec::CharExpr::Kind>(charExpr->t) == 309 parser::IoControlSpec::CharExpr::Kind::Advance) { 310 SayWithDo(context_, currentStatementSourcePosition_, 311 "ADVANCE specifier is not allowed in DO" 312 " CONCURRENT"_err_en_US, 313 doConcurrentSourcePosition_); 314 } 315 } 316 } 317 318 private: 319 bool fromScope(const Symbol &symbol, const std::string &moduleName) { 320 if (symbol.GetUltimate().owner().IsModule() && 321 symbol.GetUltimate().owner().GetName().value().ToString() == 322 moduleName) { 323 return true; 324 } 325 return false; 326 } 327 328 std::set<parser::Label> labels_; 329 parser::CharBlock currentStatementSourcePosition_; 330 SemanticsContext &context_; 331 parser::CharBlock doConcurrentSourcePosition_; 332 }; // class DoConcurrentBodyEnforce 333 334 // Class for enforcing C1130 -- in a DO CONCURRENT with DEFAULT(NONE), 335 // variables from enclosing scopes must have their locality specified 336 class DoConcurrentVariableEnforce { 337 public: 338 DoConcurrentVariableEnforce( 339 SemanticsContext &context, parser::CharBlock doConcurrentSourcePosition) 340 : context_{context}, 341 doConcurrentSourcePosition_{doConcurrentSourcePosition}, 342 blockScope_{context.FindScope(doConcurrentSourcePosition_)} {} 343 344 template <typename T> bool Pre(const T &) { return true; } 345 template <typename T> void Post(const T &) {} 346 347 // Check to see if the name is a variable from an enclosing scope 348 void Post(const parser::Name &name) { 349 if (const Symbol * symbol{name.symbol}) { 350 if (IsVariableName(*symbol)) { 351 const Scope &variableScope{symbol->owner()}; 352 if (DoesScopeContain(&variableScope, blockScope_)) { 353 context_.SayWithDecl(*symbol, name.source, 354 "Variable '%s' from an enclosing scope referenced in DO " 355 "CONCURRENT with DEFAULT(NONE) must appear in a " 356 "locality-spec"_err_en_US, 357 symbol->name()); 358 } 359 } 360 } 361 } 362 363 private: 364 SemanticsContext &context_; 365 parser::CharBlock doConcurrentSourcePosition_; 366 const Scope &blockScope_; 367 }; // class DoConcurrentVariableEnforce 368 369 // Find a DO or FORALL and enforce semantics checks on its body 370 class DoContext { 371 public: 372 DoContext(SemanticsContext &context, IndexVarKind kind) 373 : context_{context}, kind_{kind} {} 374 375 // Mark this DO construct as a point of definition for the DO variables 376 // or index-names it contains. If they're already defined, emit an error 377 // message. We need to remember both the variable and the source location of 378 // the variable in the DO construct so that we can remove it when we leave 379 // the DO construct and use its location in error messages. 380 void DefineDoVariables(const parser::DoConstruct &doConstruct) { 381 if (doConstruct.IsDoNormal()) { 382 context_.ActivateIndexVar(GetDoVariable(doConstruct), IndexVarKind::DO); 383 } else if (doConstruct.IsDoConcurrent()) { 384 if (const auto &loopControl{doConstruct.GetLoopControl()}) { 385 ActivateIndexVars(GetControls(*loopControl)); 386 } 387 } 388 } 389 390 // Called at the end of a DO construct to deactivate the DO construct 391 void ResetDoVariables(const parser::DoConstruct &doConstruct) { 392 if (doConstruct.IsDoNormal()) { 393 context_.DeactivateIndexVar(GetDoVariable(doConstruct)); 394 } else if (doConstruct.IsDoConcurrent()) { 395 if (const auto &loopControl{doConstruct.GetLoopControl()}) { 396 DeactivateIndexVars(GetControls(*loopControl)); 397 } 398 } 399 } 400 401 void ActivateIndexVars(const std::list<parser::ConcurrentControl> &controls) { 402 for (const auto &control : controls) { 403 context_.ActivateIndexVar(std::get<parser::Name>(control.t), kind_); 404 } 405 } 406 void DeactivateIndexVars( 407 const std::list<parser::ConcurrentControl> &controls) { 408 for (const auto &control : controls) { 409 context_.DeactivateIndexVar(std::get<parser::Name>(control.t)); 410 } 411 } 412 413 void Check(const parser::DoConstruct &doConstruct) { 414 if (doConstruct.IsDoConcurrent()) { 415 CheckDoConcurrent(doConstruct); 416 return; 417 } 418 if (doConstruct.IsDoNormal()) { 419 CheckDoNormal(doConstruct); 420 return; 421 } 422 // TODO: handle the other cases 423 } 424 425 void Check(const parser::ForallStmt &stmt) { 426 CheckConcurrentHeader(GetConcurrentHeader(stmt)); 427 } 428 void Check(const parser::ForallConstruct &construct) { 429 CheckConcurrentHeader(GetConcurrentHeader(construct)); 430 } 431 432 void Check(const parser::ForallAssignmentStmt &stmt) { 433 const evaluate::Assignment *assignment{common::visit( 434 common::visitors{[&](const auto &x) { return GetAssignment(x); }}, 435 stmt.u)}; 436 if (assignment) { 437 CheckForallIndexesUsed(*assignment); 438 CheckForImpureCall(assignment->lhs); 439 CheckForImpureCall(assignment->rhs); 440 if (const auto *proc{ 441 std::get_if<evaluate::ProcedureRef>(&assignment->u)}) { 442 CheckForImpureCall(*proc); 443 } 444 common::visit( 445 common::visitors{ 446 [](const evaluate::Assignment::Intrinsic &) {}, 447 [&](const evaluate::ProcedureRef &proc) { 448 CheckForImpureCall(proc); 449 }, 450 [&](const evaluate::Assignment::BoundsSpec &bounds) { 451 for (const auto &bound : bounds) { 452 CheckForImpureCall(SomeExpr{bound}); 453 } 454 }, 455 [&](const evaluate::Assignment::BoundsRemapping &bounds) { 456 for (const auto &bound : bounds) { 457 CheckForImpureCall(SomeExpr{bound.first}); 458 CheckForImpureCall(SomeExpr{bound.second}); 459 } 460 }, 461 }, 462 assignment->u); 463 } 464 } 465 466 private: 467 void SayBadDoControl(parser::CharBlock sourceLocation) { 468 context_.Say(sourceLocation, "DO controls should be INTEGER"_err_en_US); 469 } 470 471 void CheckDoControl(const parser::CharBlock &sourceLocation, bool isReal) { 472 const bool warn{context_.warnOnNonstandardUsage() || 473 context_.ShouldWarn(common::LanguageFeature::RealDoControls)}; 474 if (isReal && !warn) { 475 // No messages for the default case 476 } else if (isReal && warn) { 477 context_.Say(sourceLocation, "DO controls should be INTEGER"_port_en_US); 478 } else { 479 SayBadDoControl(sourceLocation); 480 } 481 } 482 483 void CheckDoVariable(const parser::ScalarName &scalarName) { 484 const parser::CharBlock &sourceLocation{scalarName.thing.source}; 485 if (const Symbol * symbol{scalarName.thing.symbol}) { 486 if (!IsVariableName(*symbol)) { 487 context_.Say( 488 sourceLocation, "DO control must be an INTEGER variable"_err_en_US); 489 } else { 490 const DeclTypeSpec *symType{symbol->GetType()}; 491 if (!symType) { 492 SayBadDoControl(sourceLocation); 493 } else { 494 if (!symType->IsNumeric(TypeCategory::Integer)) { 495 CheckDoControl( 496 sourceLocation, symType->IsNumeric(TypeCategory::Real)); 497 } 498 } 499 } // No messages for INTEGER 500 } 501 } 502 503 // Semantic checks for the limit and step expressions 504 void CheckDoExpression(const parser::ScalarExpr &scalarExpression) { 505 if (const SomeExpr * expr{GetExpr(context_, scalarExpression)}) { 506 if (!ExprHasTypeCategory(*expr, TypeCategory::Integer)) { 507 // No warnings or errors for type INTEGER 508 const parser::CharBlock &loc{scalarExpression.thing.value().source}; 509 CheckDoControl(loc, ExprHasTypeCategory(*expr, TypeCategory::Real)); 510 } 511 } 512 } 513 514 void CheckDoNormal(const parser::DoConstruct &doConstruct) { 515 // C1120 -- types of DO variables must be INTEGER, extended by allowing 516 // REAL and DOUBLE PRECISION 517 const Bounds &bounds{GetBounds(doConstruct)}; 518 CheckDoVariable(bounds.name); 519 CheckDoExpression(bounds.lower); 520 CheckDoExpression(bounds.upper); 521 if (bounds.step) { 522 CheckDoExpression(*bounds.step); 523 if (IsZero(*bounds.step)) { 524 context_.Say(bounds.step->thing.value().source, 525 "DO step expression should not be zero"_warn_en_US); 526 } 527 } 528 } 529 530 void CheckDoConcurrent(const parser::DoConstruct &doConstruct) { 531 auto &doStmt{ 532 std::get<parser::Statement<parser::NonLabelDoStmt>>(doConstruct.t)}; 533 currentStatementSourcePosition_ = doStmt.source; 534 535 const parser::Block &block{std::get<parser::Block>(doConstruct.t)}; 536 DoConcurrentBodyEnforce doConcurrentBodyEnforce{context_, doStmt.source}; 537 parser::Walk(block, doConcurrentBodyEnforce); 538 539 LabelEnforce doConcurrentLabelEnforce{context_, 540 doConcurrentBodyEnforce.labels(), currentStatementSourcePosition_, 541 "DO CONCURRENT"}; 542 parser::Walk(block, doConcurrentLabelEnforce); 543 544 const auto &loopControl{doConstruct.GetLoopControl()}; 545 CheckConcurrentLoopControl(*loopControl); 546 CheckLocalitySpecs(*loopControl, block); 547 } 548 549 // Return a set of symbols whose names are in a Local locality-spec. Look 550 // the names up in the scope that encloses the DO construct to avoid getting 551 // the local versions of them. Then follow the host-, use-, and 552 // construct-associations to get the root symbols 553 UnorderedSymbolSet GatherLocals( 554 const std::list<parser::LocalitySpec> &localitySpecs) const { 555 UnorderedSymbolSet symbols; 556 const Scope &parentScope{ 557 context_.FindScope(currentStatementSourcePosition_).parent()}; 558 // Loop through the LocalitySpec::Local locality-specs 559 for (const auto &ls : localitySpecs) { 560 if (const auto *names{std::get_if<parser::LocalitySpec::Local>(&ls.u)}) { 561 // Loop through the names in the Local locality-spec getting their 562 // symbols 563 for (const parser::Name &name : names->v) { 564 if (const Symbol * symbol{parentScope.FindSymbol(name.source)}) { 565 symbols.insert(ResolveAssociations(*symbol)); 566 } 567 } 568 } 569 } 570 return symbols; 571 } 572 573 UnorderedSymbolSet GatherSymbolsFromExpression( 574 const parser::Expr &expression) const { 575 UnorderedSymbolSet result; 576 if (const auto *expr{GetExpr(context_, expression)}) { 577 for (const Symbol &symbol : evaluate::CollectSymbols(*expr)) { 578 result.insert(ResolveAssociations(symbol)); 579 } 580 } 581 return result; 582 } 583 584 // C1121 - procedures in mask must be pure 585 void CheckMaskIsPure(const parser::ScalarLogicalExpr &mask) const { 586 UnorderedSymbolSet references{ 587 GatherSymbolsFromExpression(mask.thing.thing.value())}; 588 for (const Symbol &ref : OrderBySourcePosition(references)) { 589 if (IsProcedure(ref) && !IsPureProcedure(ref)) { 590 context_.SayWithDecl(ref, parser::Unwrap<parser::Expr>(mask)->source, 591 "%s mask expression may not reference impure procedure '%s'"_err_en_US, 592 LoopKindName(), ref.name()); 593 return; 594 } 595 } 596 } 597 598 void CheckNoCollisions(const UnorderedSymbolSet &refs, 599 const UnorderedSymbolSet &uses, parser::MessageFixedText &&errorMessage, 600 const parser::CharBlock &refPosition) const { 601 for (const Symbol &ref : OrderBySourcePosition(refs)) { 602 if (uses.find(ref) != uses.end()) { 603 context_.SayWithDecl(ref, refPosition, std::move(errorMessage), 604 LoopKindName(), ref.name()); 605 return; 606 } 607 } 608 } 609 610 void HasNoReferences(const UnorderedSymbolSet &indexNames, 611 const parser::ScalarIntExpr &expr) const { 612 CheckNoCollisions(GatherSymbolsFromExpression(expr.thing.thing.value()), 613 indexNames, 614 "%s limit expression may not reference index variable '%s'"_err_en_US, 615 expr.thing.thing.value().source); 616 } 617 618 // C1129, names in local locality-specs can't be in mask expressions 619 void CheckMaskDoesNotReferenceLocal(const parser::ScalarLogicalExpr &mask, 620 const UnorderedSymbolSet &localVars) const { 621 CheckNoCollisions(GatherSymbolsFromExpression(mask.thing.thing.value()), 622 localVars, 623 "%s mask expression references variable '%s'" 624 " in LOCAL locality-spec"_err_en_US, 625 mask.thing.thing.value().source); 626 } 627 628 // C1129, names in local locality-specs can't be in limit or step 629 // expressions 630 void CheckExprDoesNotReferenceLocal(const parser::ScalarIntExpr &expr, 631 const UnorderedSymbolSet &localVars) const { 632 CheckNoCollisions(GatherSymbolsFromExpression(expr.thing.thing.value()), 633 localVars, 634 "%s expression references variable '%s'" 635 " in LOCAL locality-spec"_err_en_US, 636 expr.thing.thing.value().source); 637 } 638 639 // C1130, DEFAULT(NONE) locality requires names to be in locality-specs to 640 // be used in the body of the DO loop 641 void CheckDefaultNoneImpliesExplicitLocality( 642 const std::list<parser::LocalitySpec> &localitySpecs, 643 const parser::Block &block) const { 644 bool hasDefaultNone{false}; 645 for (auto &ls : localitySpecs) { 646 if (std::holds_alternative<parser::LocalitySpec::DefaultNone>(ls.u)) { 647 if (hasDefaultNone) { 648 // C1127, you can only have one DEFAULT(NONE) 649 context_.Say(currentStatementSourcePosition_, 650 "Only one DEFAULT(NONE) may appear"_port_en_US); 651 break; 652 } 653 hasDefaultNone = true; 654 } 655 } 656 if (hasDefaultNone) { 657 DoConcurrentVariableEnforce doConcurrentVariableEnforce{ 658 context_, currentStatementSourcePosition_}; 659 parser::Walk(block, doConcurrentVariableEnforce); 660 } 661 } 662 663 // C1123, concurrent limit or step expressions can't reference index-names 664 void CheckConcurrentHeader(const parser::ConcurrentHeader &header) const { 665 if (const auto &mask{ 666 std::get<std::optional<parser::ScalarLogicalExpr>>(header.t)}) { 667 CheckMaskIsPure(*mask); 668 } 669 auto &controls{std::get<std::list<parser::ConcurrentControl>>(header.t)}; 670 UnorderedSymbolSet indexNames; 671 for (const parser::ConcurrentControl &control : controls) { 672 const auto &indexName{std::get<parser::Name>(control.t)}; 673 if (indexName.symbol) { 674 indexNames.insert(*indexName.symbol); 675 } 676 } 677 if (!indexNames.empty()) { 678 for (const parser::ConcurrentControl &control : controls) { 679 HasNoReferences(indexNames, std::get<1>(control.t)); 680 HasNoReferences(indexNames, std::get<2>(control.t)); 681 if (const auto &intExpr{ 682 std::get<std::optional<parser::ScalarIntExpr>>(control.t)}) { 683 const parser::Expr &expr{intExpr->thing.thing.value()}; 684 CheckNoCollisions(GatherSymbolsFromExpression(expr), indexNames, 685 "%s step expression may not reference index variable '%s'"_err_en_US, 686 expr.source); 687 if (IsZero(expr)) { 688 context_.Say(expr.source, 689 "%s step expression may not be zero"_err_en_US, LoopKindName()); 690 } 691 } 692 } 693 } 694 } 695 696 void CheckLocalitySpecs( 697 const parser::LoopControl &control, const parser::Block &block) const { 698 const auto &concurrent{ 699 std::get<parser::LoopControl::Concurrent>(control.u)}; 700 const auto &header{std::get<parser::ConcurrentHeader>(concurrent.t)}; 701 const auto &localitySpecs{ 702 std::get<std::list<parser::LocalitySpec>>(concurrent.t)}; 703 if (!localitySpecs.empty()) { 704 const UnorderedSymbolSet &localVars{GatherLocals(localitySpecs)}; 705 for (const auto &c : GetControls(control)) { 706 CheckExprDoesNotReferenceLocal(std::get<1>(c.t), localVars); 707 CheckExprDoesNotReferenceLocal(std::get<2>(c.t), localVars); 708 if (const auto &expr{ 709 std::get<std::optional<parser::ScalarIntExpr>>(c.t)}) { 710 CheckExprDoesNotReferenceLocal(*expr, localVars); 711 } 712 } 713 if (const auto &mask{ 714 std::get<std::optional<parser::ScalarLogicalExpr>>(header.t)}) { 715 CheckMaskDoesNotReferenceLocal(*mask, localVars); 716 } 717 CheckDefaultNoneImpliesExplicitLocality(localitySpecs, block); 718 } 719 } 720 721 // check constraints [C1121 .. C1130] 722 void CheckConcurrentLoopControl(const parser::LoopControl &control) const { 723 const auto &concurrent{ 724 std::get<parser::LoopControl::Concurrent>(control.u)}; 725 CheckConcurrentHeader(std::get<parser::ConcurrentHeader>(concurrent.t)); 726 } 727 728 template <typename T> void CheckForImpureCall(const T &x) { 729 if (auto bad{FindImpureCall(context_.foldingContext(), x)}) { 730 context_.Say( 731 "Impure procedure '%s' may not be referenced in a %s"_err_en_US, *bad, 732 LoopKindName()); 733 } 734 } 735 736 // Each index should be used on the LHS of each assignment in a FORALL 737 void CheckForallIndexesUsed(const evaluate::Assignment &assignment) { 738 SymbolVector indexVars{context_.GetIndexVars(IndexVarKind::FORALL)}; 739 if (!indexVars.empty()) { 740 UnorderedSymbolSet symbols{evaluate::CollectSymbols(assignment.lhs)}; 741 common::visit( 742 common::visitors{ 743 [&](const evaluate::Assignment::BoundsSpec &spec) { 744 for (const auto &bound : spec) { 745 // TODO: this is working around missing std::set::merge in some versions of 746 // clang that we are building with 747 #ifdef __clang__ 748 auto boundSymbols{evaluate::CollectSymbols(bound)}; 749 symbols.insert(boundSymbols.begin(), boundSymbols.end()); 750 #else 751 symbols.merge(evaluate::CollectSymbols(bound)); 752 #endif 753 } 754 }, 755 [&](const evaluate::Assignment::BoundsRemapping &remapping) { 756 for (const auto &bounds : remapping) { 757 #ifdef __clang__ 758 auto lbSymbols{evaluate::CollectSymbols(bounds.first)}; 759 symbols.insert(lbSymbols.begin(), lbSymbols.end()); 760 auto ubSymbols{evaluate::CollectSymbols(bounds.second)}; 761 symbols.insert(ubSymbols.begin(), ubSymbols.end()); 762 #else 763 symbols.merge(evaluate::CollectSymbols(bounds.first)); 764 symbols.merge(evaluate::CollectSymbols(bounds.second)); 765 #endif 766 } 767 }, 768 [](const auto &) {}, 769 }, 770 assignment.u); 771 for (const Symbol &index : indexVars) { 772 if (symbols.count(index) == 0) { 773 context_.Say("FORALL index variable '%s' not used on left-hand side" 774 " of assignment"_warn_en_US, 775 index.name()); 776 } 777 } 778 } 779 } 780 781 // For messages where the DO loop must be DO CONCURRENT, make that explicit. 782 const char *LoopKindName() const { 783 return kind_ == IndexVarKind::DO ? "DO CONCURRENT" : "FORALL"; 784 } 785 786 SemanticsContext &context_; 787 const IndexVarKind kind_; 788 parser::CharBlock currentStatementSourcePosition_; 789 }; // class DoContext 790 791 void DoForallChecker::Enter(const parser::DoConstruct &doConstruct) { 792 DoContext doContext{context_, IndexVarKind::DO}; 793 doContext.DefineDoVariables(doConstruct); 794 } 795 796 void DoForallChecker::Leave(const parser::DoConstruct &doConstruct) { 797 DoContext doContext{context_, IndexVarKind::DO}; 798 doContext.Check(doConstruct); 799 doContext.ResetDoVariables(doConstruct); 800 } 801 802 void DoForallChecker::Enter(const parser::ForallConstruct &construct) { 803 DoContext doContext{context_, IndexVarKind::FORALL}; 804 doContext.ActivateIndexVars(GetControls(construct)); 805 } 806 void DoForallChecker::Leave(const parser::ForallConstruct &construct) { 807 DoContext doContext{context_, IndexVarKind::FORALL}; 808 doContext.Check(construct); 809 doContext.DeactivateIndexVars(GetControls(construct)); 810 } 811 812 void DoForallChecker::Enter(const parser::ForallStmt &stmt) { 813 DoContext doContext{context_, IndexVarKind::FORALL}; 814 doContext.ActivateIndexVars(GetControls(stmt)); 815 } 816 void DoForallChecker::Leave(const parser::ForallStmt &stmt) { 817 DoContext doContext{context_, IndexVarKind::FORALL}; 818 doContext.Check(stmt); 819 doContext.DeactivateIndexVars(GetControls(stmt)); 820 } 821 void DoForallChecker::Leave(const parser::ForallAssignmentStmt &stmt) { 822 DoContext doContext{context_, IndexVarKind::FORALL}; 823 doContext.Check(stmt); 824 } 825 826 template <typename A> 827 static parser::CharBlock GetConstructPosition(const A &a) { 828 return std::get<0>(a.t).source; 829 } 830 831 static parser::CharBlock GetNodePosition(const ConstructNode &construct) { 832 return common::visit( 833 [&](const auto &x) { return GetConstructPosition(*x); }, construct); 834 } 835 836 void DoForallChecker::SayBadLeave(StmtType stmtType, 837 const char *enclosingStmtName, const ConstructNode &construct) const { 838 context_ 839 .Say("%s must not leave a %s statement"_err_en_US, EnumToString(stmtType), 840 enclosingStmtName) 841 .Attach(GetNodePosition(construct), "The construct that was left"_en_US); 842 } 843 844 static const parser::DoConstruct *MaybeGetDoConstruct( 845 const ConstructNode &construct) { 846 if (const auto *doNode{ 847 std::get_if<const parser::DoConstruct *>(&construct)}) { 848 return *doNode; 849 } else { 850 return nullptr; 851 } 852 } 853 854 static bool ConstructIsDoConcurrent(const ConstructNode &construct) { 855 const parser::DoConstruct *doConstruct{MaybeGetDoConstruct(construct)}; 856 return doConstruct && doConstruct->IsDoConcurrent(); 857 } 858 859 // Check that CYCLE and EXIT statements do not cause flow of control to 860 // leave DO CONCURRENT, CRITICAL, or CHANGE TEAM constructs. 861 void DoForallChecker::CheckForBadLeave( 862 StmtType stmtType, const ConstructNode &construct) const { 863 common::visit(common::visitors{ 864 [&](const parser::DoConstruct *doConstructPtr) { 865 if (doConstructPtr->IsDoConcurrent()) { 866 // C1135 and C1167 -- CYCLE and EXIT statements can't 867 // leave a DO CONCURRENT 868 SayBadLeave(stmtType, "DO CONCURRENT", construct); 869 } 870 }, 871 [&](const parser::CriticalConstruct *) { 872 // C1135 and C1168 -- similarly, for CRITICAL 873 SayBadLeave(stmtType, "CRITICAL", construct); 874 }, 875 [&](const parser::ChangeTeamConstruct *) { 876 // C1135 and C1168 -- similarly, for CHANGE TEAM 877 SayBadLeave(stmtType, "CHANGE TEAM", construct); 878 }, 879 [](const auto *) {}, 880 }, 881 construct); 882 } 883 884 static bool StmtMatchesConstruct(const parser::Name *stmtName, 885 StmtType stmtType, const std::optional<parser::Name> &constructName, 886 const ConstructNode &construct) { 887 bool inDoConstruct{MaybeGetDoConstruct(construct) != nullptr}; 888 if (!stmtName) { 889 return inDoConstruct; // Unlabeled statements match all DO constructs 890 } else if (constructName && constructName->source == stmtName->source) { 891 return stmtType == StmtType::EXIT || inDoConstruct; 892 } else { 893 return false; 894 } 895 } 896 897 // C1167 Can't EXIT from a DO CONCURRENT 898 void DoForallChecker::CheckDoConcurrentExit( 899 StmtType stmtType, const ConstructNode &construct) const { 900 if (stmtType == StmtType::EXIT && ConstructIsDoConcurrent(construct)) { 901 SayBadLeave(StmtType::EXIT, "DO CONCURRENT", construct); 902 } 903 } 904 905 // Check nesting violations for a CYCLE or EXIT statement. Loop up the 906 // nesting levels looking for a construct that matches the CYCLE or EXIT 907 // statment. At every construct, check for a violation. If we find a match 908 // without finding a violation, the check is complete. 909 void DoForallChecker::CheckNesting( 910 StmtType stmtType, const parser::Name *stmtName) const { 911 const ConstructStack &stack{context_.constructStack()}; 912 for (auto iter{stack.cend()}; iter-- != stack.cbegin();) { 913 const ConstructNode &construct{*iter}; 914 const std::optional<parser::Name> &constructName{ 915 MaybeGetNodeName(construct)}; 916 if (StmtMatchesConstruct(stmtName, stmtType, constructName, construct)) { 917 CheckDoConcurrentExit(stmtType, construct); 918 return; // We got a match, so we're finished checking 919 } 920 CheckForBadLeave(stmtType, construct); 921 } 922 923 // We haven't found a match in the enclosing constructs 924 if (stmtType == StmtType::EXIT) { 925 context_.Say("No matching construct for EXIT statement"_err_en_US); 926 } else { 927 context_.Say("No matching DO construct for CYCLE statement"_err_en_US); 928 } 929 } 930 931 // C1135 -- Nesting for CYCLE statements 932 void DoForallChecker::Enter(const parser::CycleStmt &cycleStmt) { 933 CheckNesting(StmtType::CYCLE, common::GetPtrFromOptional(cycleStmt.v)); 934 } 935 936 // C1167 and C1168 -- Nesting for EXIT statements 937 void DoForallChecker::Enter(const parser::ExitStmt &exitStmt) { 938 CheckNesting(StmtType::EXIT, common::GetPtrFromOptional(exitStmt.v)); 939 } 940 941 void DoForallChecker::Leave(const parser::AssignmentStmt &stmt) { 942 const auto &variable{std::get<parser::Variable>(stmt.t)}; 943 context_.CheckIndexVarRedefine(variable); 944 } 945 946 static void CheckIfArgIsDoVar(const evaluate::ActualArgument &arg, 947 const parser::CharBlock location, SemanticsContext &context) { 948 common::Intent intent{arg.dummyIntent()}; 949 if (intent == common::Intent::Out || intent == common::Intent::InOut) { 950 if (const SomeExpr * argExpr{arg.UnwrapExpr()}) { 951 if (const Symbol * var{evaluate::UnwrapWholeSymbolDataRef(*argExpr)}) { 952 if (intent == common::Intent::Out) { 953 context.CheckIndexVarRedefine(location, *var); 954 } else { 955 context.WarnIndexVarRedefine(location, *var); // INTENT(INOUT) 956 } 957 } 958 } 959 } 960 } 961 962 // Check to see if a DO variable is being passed as an actual argument to a 963 // dummy argument whose intent is OUT or INOUT. To do this, we need to find 964 // the expressions for actual arguments which contain DO variables. We get the 965 // intents of the dummy arguments from the ProcedureRef in the "typedCall" 966 // field of the CallStmt which was filled in during expression checking. At 967 // the same time, we need to iterate over the parser::Expr versions of the 968 // actual arguments to get their source locations of the arguments for the 969 // messages. 970 void DoForallChecker::Leave(const parser::CallStmt &callStmt) { 971 if (const auto &typedCall{callStmt.typedCall}) { 972 const auto &parsedArgs{ 973 std::get<std::list<parser::ActualArgSpec>>(callStmt.v.t)}; 974 auto parsedArgIter{parsedArgs.begin()}; 975 const evaluate::ActualArguments &checkedArgs{typedCall->arguments()}; 976 for (const auto &checkedOptionalArg : checkedArgs) { 977 if (parsedArgIter == parsedArgs.end()) { 978 break; // No more parsed arguments, we're done. 979 } 980 const auto &parsedArg{std::get<parser::ActualArg>(parsedArgIter->t)}; 981 ++parsedArgIter; 982 if (checkedOptionalArg) { 983 const evaluate::ActualArgument &checkedArg{*checkedOptionalArg}; 984 if (const auto *parsedExpr{ 985 std::get_if<common::Indirection<parser::Expr>>(&parsedArg.u)}) { 986 CheckIfArgIsDoVar(checkedArg, parsedExpr->value().source, context_); 987 } 988 } 989 } 990 } 991 } 992 993 void DoForallChecker::Leave(const parser::ConnectSpec &connectSpec) { 994 const auto *newunit{ 995 std::get_if<parser::ConnectSpec::Newunit>(&connectSpec.u)}; 996 if (newunit) { 997 context_.CheckIndexVarRedefine(newunit->v.thing.thing); 998 } 999 } 1000 1001 using ActualArgumentSet = std::set<evaluate::ActualArgumentRef>; 1002 1003 struct CollectActualArgumentsHelper 1004 : public evaluate::SetTraverse<CollectActualArgumentsHelper, 1005 ActualArgumentSet> { 1006 using Base = SetTraverse<CollectActualArgumentsHelper, ActualArgumentSet>; 1007 CollectActualArgumentsHelper() : Base{*this} {} 1008 using Base::operator(); 1009 ActualArgumentSet operator()(const evaluate::ActualArgument &arg) const { 1010 return Combine(ActualArgumentSet{arg}, 1011 CollectActualArgumentsHelper{}(arg.UnwrapExpr())); 1012 } 1013 }; 1014 1015 template <typename A> ActualArgumentSet CollectActualArguments(const A &x) { 1016 return CollectActualArgumentsHelper{}(x); 1017 } 1018 1019 template ActualArgumentSet CollectActualArguments(const SomeExpr &); 1020 1021 void DoForallChecker::Enter(const parser::Expr &parsedExpr) { ++exprDepth_; } 1022 1023 void DoForallChecker::Leave(const parser::Expr &parsedExpr) { 1024 CHECK(exprDepth_ > 0); 1025 if (--exprDepth_ == 0) { // Only check top level expressions 1026 if (const SomeExpr * expr{GetExpr(context_, parsedExpr)}) { 1027 ActualArgumentSet argSet{CollectActualArguments(*expr)}; 1028 for (const evaluate::ActualArgumentRef &argRef : argSet) { 1029 CheckIfArgIsDoVar(*argRef, parsedExpr.source, context_); 1030 } 1031 } 1032 } 1033 } 1034 1035 void DoForallChecker::Leave(const parser::InquireSpec &inquireSpec) { 1036 const auto *intVar{std::get_if<parser::InquireSpec::IntVar>(&inquireSpec.u)}; 1037 if (intVar) { 1038 const auto &scalar{std::get<parser::ScalarIntVariable>(intVar->t)}; 1039 context_.CheckIndexVarRedefine(scalar.thing.thing); 1040 } 1041 } 1042 1043 void DoForallChecker::Leave(const parser::IoControlSpec &ioControlSpec) { 1044 const auto *size{std::get_if<parser::IoControlSpec::Size>(&ioControlSpec.u)}; 1045 if (size) { 1046 context_.CheckIndexVarRedefine(size->v.thing.thing); 1047 } 1048 } 1049 1050 void DoForallChecker::Leave(const parser::OutputImpliedDo &outputImpliedDo) { 1051 const auto &control{std::get<parser::IoImpliedDoControl>(outputImpliedDo.t)}; 1052 const parser::Name &name{control.name.thing.thing}; 1053 context_.CheckIndexVarRedefine(name.source, *name.symbol); 1054 } 1055 1056 void DoForallChecker::Leave(const parser::StatVariable &statVariable) { 1057 context_.CheckIndexVarRedefine(statVariable.v.thing.thing); 1058 } 1059 1060 } // namespace Fortran::semantics 1061