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 &symbol) { 119 if (const Symbol * root{GetAssociationRoot(symbol)}) { 120 CHECK(root->has<ObjectEntityDetails>()); 121 if (const DeclTypeSpec * symType{root->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 !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 &entity, 146 const std::function<bool(const Symbol &)> &WillDeallocate) { 147 if (const Symbol * root{GetAssociationRoot(entity)}) { 148 // Check the entity itself, no coarray exception here 149 if (IsPolymorphicAllocatable(*root)) { 150 return true; 151 } 152 // Check the components 153 if (const auto *details{root->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 } 166 return false; 167 } 168 169 void SayDeallocateWithImpureFinal(const Symbol &entity, const char *reason) { 170 context_.SayWithDecl(entity, currentStatementSourcePosition_, 171 "Deallocation of an entity with an IMPURE FINAL procedure" 172 " caused by %s not allowed in DO CONCURRENT"_err_en_US, 173 reason); 174 } 175 176 void SayDeallocateOfPolymorph( 177 parser::CharBlock location, const Symbol &entity, const char *reason) { 178 context_.SayWithDecl(entity, location, 179 "Deallocation of a polymorphic entity caused by %s" 180 " not allowed in DO CONCURRENT"_err_en_US, 181 reason); 182 } 183 184 // Deallocation caused by block exit 185 // Allocatable entities and all of their allocatable subcomponents will be 186 // deallocated. This test is different from the other two because it does 187 // not deallocate in cases where the entity itself is not allocatable but 188 // has allocatable polymorphic components 189 void Post(const parser::BlockConstruct &blockConstruct) { 190 const auto &endBlockStmt{ 191 std::get<parser::Statement<parser::EndBlockStmt>>(blockConstruct.t)}; 192 const Scope &blockScope{context_.FindScope(endBlockStmt.source)}; 193 const Scope &doScope{context_.FindScope(doConcurrentSourcePosition_)}; 194 if (DoesScopeContain(&doScope, blockScope)) { 195 const char *reason{"block exit"}; 196 for (auto &pair : blockScope) { 197 const Symbol &entity{*pair.second}; 198 if (IsAllocatable(entity) && !IsSaved(entity) && 199 MightDeallocatePolymorphic(entity, DeallocateAll)) { 200 SayDeallocateOfPolymorph(endBlockStmt.source, entity, reason); 201 } 202 if (HasImpureFinal(entity)) { 203 SayDeallocateWithImpureFinal(entity, reason); 204 } 205 } 206 } 207 } 208 209 // Deallocation caused by assignment 210 // Note that this case does not cause deallocation of coarray components 211 void Post(const parser::AssignmentStmt &stmt) { 212 const auto &variable{std::get<parser::Variable>(stmt.t)}; 213 if (const Symbol * entity{GetLastName(variable).symbol}) { 214 const char *reason{"assignment"}; 215 if (MightDeallocatePolymorphic(*entity, DeallocateNonCoarray)) { 216 SayDeallocateOfPolymorph(variable.GetSource(), *entity, reason); 217 } 218 if (HasImpureFinal(*entity)) { 219 SayDeallocateWithImpureFinal(*entity, reason); 220 } 221 } 222 } 223 224 // Deallocation from a DEALLOCATE statement 225 // This case is different because DEALLOCATE statements deallocate both 226 // ALLOCATABLE and POINTER entities 227 void Post(const parser::DeallocateStmt &stmt) { 228 const auto &allocateObjectList{ 229 std::get<std::list<parser::AllocateObject>>(stmt.t)}; 230 for (const auto &allocateObject : allocateObjectList) { 231 const parser::Name &name{GetLastName(allocateObject)}; 232 const char *reason{"a DEALLOCATE statement"}; 233 if (name.symbol) { 234 const Symbol &entity{*name.symbol}; 235 const DeclTypeSpec *entityType{entity.GetType()}; 236 if ((entityType && entityType->IsPolymorphic()) || // POINTER case 237 MightDeallocatePolymorphic(entity, DeallocateAll)) { 238 SayDeallocateOfPolymorph( 239 currentStatementSourcePosition_, entity, reason); 240 } 241 if (HasImpureFinal(entity)) { 242 SayDeallocateWithImpureFinal(entity, reason); 243 } 244 } 245 } 246 } 247 248 // C1137 -- No image control statements in a DO CONCURRENT 249 void Post(const parser::ExecutableConstruct &construct) { 250 if (IsImageControlStmt(construct)) { 251 const parser::CharBlock statementLocation{ 252 GetImageControlStmtLocation(construct)}; 253 auto &msg{context_.Say(statementLocation, 254 "An image control statement is not allowed in DO" 255 " CONCURRENT"_err_en_US)}; 256 if (auto coarrayMsg{GetImageControlStmtCoarrayMsg(construct)}) { 257 msg.Attach(statementLocation, *coarrayMsg); 258 } 259 msg.Attach(doConcurrentSourcePosition_, GetEnclosingDoMsg()); 260 } 261 } 262 263 // C1136 -- No RETURN statements in a DO CONCURRENT 264 void Post(const parser::ReturnStmt &) { 265 context_ 266 .Say(currentStatementSourcePosition_, 267 "RETURN is not allowed in DO CONCURRENT"_err_en_US) 268 .Attach(doConcurrentSourcePosition_, GetEnclosingDoMsg()); 269 } 270 271 // C1139: call to impure procedure and ... 272 // C1141: cannot call ieee_get_flag, ieee_[gs]et_halting_mode 273 // It's not necessary to check the ieee_get* procedures because they're 274 // not pure, and impure procedures are caught by checks for constraint C1139 275 void Post(const parser::ProcedureDesignator &procedureDesignator) { 276 if (auto *name{std::get_if<parser::Name>(&procedureDesignator.u)}) { 277 if (name->symbol && !IsPureProcedure(*name->symbol)) { 278 SayWithDo(context_, currentStatementSourcePosition_, 279 "Call to an impure procedure is not allowed in DO" 280 " CONCURRENT"_err_en_US, 281 doConcurrentSourcePosition_); 282 } 283 if (name->symbol && fromScope(*name->symbol, "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{std::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 std::visit(common::visitors{ 445 [](const evaluate::Assignment::Intrinsic &) {}, 446 [&](const evaluate::ProcedureRef &proc) { 447 CheckForImpureCall(proc); 448 }, 449 [&](const evaluate::Assignment::BoundsSpec &bounds) { 450 for (const auto &bound : bounds) { 451 CheckForImpureCall(SomeExpr{bound}); 452 } 453 }, 454 [&](const evaluate::Assignment::BoundsRemapping &bounds) { 455 for (const auto &bound : bounds) { 456 CheckForImpureCall(SomeExpr{bound.first}); 457 CheckForImpureCall(SomeExpr{bound.second}); 458 } 459 }, 460 }, 461 assignment->u); 462 } 463 } 464 465 private: 466 void SayBadDoControl(parser::CharBlock sourceLocation) { 467 context_.Say(sourceLocation, "DO controls should be INTEGER"_err_en_US); 468 } 469 470 void CheckDoControl(const parser::CharBlock &sourceLocation, bool isReal) { 471 const bool warn{context_.warnOnNonstandardUsage() || 472 context_.ShouldWarn(common::LanguageFeature::RealDoControls)}; 473 if (isReal && !warn) { 474 // No messages for the default case 475 } else if (isReal && warn) { 476 context_.Say(sourceLocation, "DO controls should be INTEGER"_en_US); 477 } else { 478 SayBadDoControl(sourceLocation); 479 } 480 } 481 482 void CheckDoVariable(const parser::ScalarName &scalarName) { 483 const parser::CharBlock &sourceLocation{scalarName.thing.source}; 484 if (const Symbol * symbol{scalarName.thing.symbol}) { 485 if (!IsVariableName(*symbol)) { 486 context_.Say( 487 sourceLocation, "DO control must be an INTEGER variable"_err_en_US); 488 } else { 489 const DeclTypeSpec *symType{symbol->GetType()}; 490 if (!symType) { 491 SayBadDoControl(sourceLocation); 492 } else { 493 if (!symType->IsNumeric(TypeCategory::Integer)) { 494 CheckDoControl( 495 sourceLocation, symType->IsNumeric(TypeCategory::Real)); 496 } 497 } 498 } // No messages for INTEGER 499 } 500 } 501 502 // Semantic checks for the limit and step expressions 503 void CheckDoExpression(const parser::ScalarExpr &scalarExpression) { 504 if (const SomeExpr * expr{GetExpr(scalarExpression)}) { 505 if (!ExprHasTypeCategory(*expr, TypeCategory::Integer)) { 506 // No warnings or errors for type INTEGER 507 const parser::CharBlock &loc{scalarExpression.thing.value().source}; 508 CheckDoControl(loc, ExprHasTypeCategory(*expr, TypeCategory::Real)); 509 } 510 } 511 } 512 513 void CheckDoNormal(const parser::DoConstruct &doConstruct) { 514 // C1120 -- types of DO variables must be INTEGER, extended by allowing 515 // REAL and DOUBLE PRECISION 516 const Bounds &bounds{GetBounds(doConstruct)}; 517 CheckDoVariable(bounds.name); 518 CheckDoExpression(bounds.lower); 519 CheckDoExpression(bounds.upper); 520 if (bounds.step) { 521 CheckDoExpression(*bounds.step); 522 if (IsZero(*bounds.step)) { 523 context_.Say(bounds.step->thing.value().source, 524 "DO step expression should not be zero"_en_US); 525 } 526 } 527 } 528 529 void CheckDoConcurrent(const parser::DoConstruct &doConstruct) { 530 auto &doStmt{ 531 std::get<parser::Statement<parser::NonLabelDoStmt>>(doConstruct.t)}; 532 currentStatementSourcePosition_ = doStmt.source; 533 534 const parser::Block &block{std::get<parser::Block>(doConstruct.t)}; 535 DoConcurrentBodyEnforce doConcurrentBodyEnforce{context_, doStmt.source}; 536 parser::Walk(block, doConcurrentBodyEnforce); 537 538 LabelEnforce doConcurrentLabelEnforce{context_, 539 doConcurrentBodyEnforce.labels(), currentStatementSourcePosition_, 540 "DO CONCURRENT"}; 541 parser::Walk(block, doConcurrentLabelEnforce); 542 543 const auto &loopControl{doConstruct.GetLoopControl()}; 544 CheckConcurrentLoopControl(*loopControl); 545 CheckLocalitySpecs(*loopControl, block); 546 } 547 548 // Return a set of symbols whose names are in a Local locality-spec. Look 549 // the names up in the scope that encloses the DO construct to avoid getting 550 // the local versions of them. Then follow the host-, use-, and 551 // construct-associations to get the root symbols 552 SymbolSet GatherLocals( 553 const std::list<parser::LocalitySpec> &localitySpecs) const { 554 SymbolSet symbols; 555 const Scope &parentScope{ 556 context_.FindScope(currentStatementSourcePosition_).parent()}; 557 // Loop through the LocalitySpec::Local locality-specs 558 for (const auto &ls : localitySpecs) { 559 if (const auto *names{std::get_if<parser::LocalitySpec::Local>(&ls.u)}) { 560 // Loop through the names in the Local locality-spec getting their 561 // symbols 562 for (const parser::Name &name : names->v) { 563 if (const Symbol * symbol{parentScope.FindSymbol(name.source)}) { 564 if (const Symbol * root{GetAssociationRoot(*symbol)}) { 565 symbols.insert(*root); 566 } 567 } 568 } 569 } 570 } 571 return symbols; 572 } 573 574 static SymbolSet GatherSymbolsFromExpression(const parser::Expr &expression) { 575 SymbolSet result; 576 if (const auto *expr{GetExpr(expression)}) { 577 for (const Symbol &symbol : evaluate::CollectSymbols(*expr)) { 578 if (const Symbol * root{GetAssociationRoot(symbol)}) { 579 result.insert(*root); 580 } 581 } 582 } 583 return result; 584 } 585 586 // C1121 - procedures in mask must be pure 587 void CheckMaskIsPure(const parser::ScalarLogicalExpr &mask) const { 588 SymbolSet references{GatherSymbolsFromExpression(mask.thing.thing.value())}; 589 for (const Symbol &ref : references) { 590 if (IsProcedure(ref) && !IsPureProcedure(ref)) { 591 context_.SayWithDecl(ref, parser::Unwrap<parser::Expr>(mask)->source, 592 "%s mask expression may not reference impure procedure '%s'"_err_en_US, 593 LoopKindName(), ref.name()); 594 return; 595 } 596 } 597 } 598 599 void CheckNoCollisions(const SymbolSet &refs, const SymbolSet &uses, 600 parser::MessageFixedText &&errorMessage, 601 const parser::CharBlock &refPosition) const { 602 for (const Symbol &ref : refs) { 603 if (uses.find(ref) != uses.end()) { 604 context_.SayWithDecl(ref, refPosition, std::move(errorMessage), 605 LoopKindName(), ref.name()); 606 return; 607 } 608 } 609 } 610 611 void HasNoReferences( 612 const SymbolSet &indexNames, const parser::ScalarIntExpr &expr) const { 613 CheckNoCollisions(GatherSymbolsFromExpression(expr.thing.thing.value()), 614 indexNames, 615 "%s limit expression may not reference index variable '%s'"_err_en_US, 616 expr.thing.thing.value().source); 617 } 618 619 // C1129, names in local locality-specs can't be in mask expressions 620 void CheckMaskDoesNotReferenceLocal( 621 const parser::ScalarLogicalExpr &mask, const SymbolSet &localVars) const { 622 CheckNoCollisions(GatherSymbolsFromExpression(mask.thing.thing.value()), 623 localVars, 624 "%s mask expression references variable '%s'" 625 " in LOCAL locality-spec"_err_en_US, 626 mask.thing.thing.value().source); 627 } 628 629 // C1129, names in local locality-specs can't be in limit or step 630 // expressions 631 void CheckExprDoesNotReferenceLocal( 632 const parser::ScalarIntExpr &expr, const SymbolSet &localVars) const { 633 CheckNoCollisions(GatherSymbolsFromExpression(expr.thing.thing.value()), 634 localVars, 635 "%s expression references variable '%s'" 636 " in LOCAL locality-spec"_err_en_US, 637 expr.thing.thing.value().source); 638 } 639 640 // C1130, DEFAULT(NONE) locality requires names to be in locality-specs to 641 // be used in the body of the DO loop 642 void CheckDefaultNoneImpliesExplicitLocality( 643 const std::list<parser::LocalitySpec> &localitySpecs, 644 const parser::Block &block) const { 645 bool hasDefaultNone{false}; 646 for (auto &ls : localitySpecs) { 647 if (std::holds_alternative<parser::LocalitySpec::DefaultNone>(ls.u)) { 648 if (hasDefaultNone) { 649 // C1127, you can only have one DEFAULT(NONE) 650 context_.Say(currentStatementSourcePosition_, 651 "Only one DEFAULT(NONE) may appear"_en_US); 652 break; 653 } 654 hasDefaultNone = true; 655 } 656 } 657 if (hasDefaultNone) { 658 DoConcurrentVariableEnforce doConcurrentVariableEnforce{ 659 context_, currentStatementSourcePosition_}; 660 parser::Walk(block, doConcurrentVariableEnforce); 661 } 662 } 663 664 // C1123, concurrent limit or step expressions can't reference index-names 665 void CheckConcurrentHeader(const parser::ConcurrentHeader &header) const { 666 if (const auto &mask{ 667 std::get<std::optional<parser::ScalarLogicalExpr>>(header.t)}) { 668 CheckMaskIsPure(*mask); 669 } 670 auto &controls{std::get<std::list<parser::ConcurrentControl>>(header.t)}; 671 SymbolSet indexNames; 672 for (const parser::ConcurrentControl &control : controls) { 673 const auto &indexName{std::get<parser::Name>(control.t)}; 674 if (indexName.symbol) { 675 indexNames.insert(*indexName.symbol); 676 } 677 } 678 if (!indexNames.empty()) { 679 for (const parser::ConcurrentControl &control : controls) { 680 HasNoReferences(indexNames, std::get<1>(control.t)); 681 HasNoReferences(indexNames, std::get<2>(control.t)); 682 if (const auto &intExpr{ 683 std::get<std::optional<parser::ScalarIntExpr>>(control.t)}) { 684 const parser::Expr &expr{intExpr->thing.thing.value()}; 685 CheckNoCollisions(GatherSymbolsFromExpression(expr), indexNames, 686 "%s step expression may not reference index variable '%s'"_err_en_US, 687 expr.source); 688 if (IsZero(expr)) { 689 context_.Say(expr.source, 690 "%s step expression may not be zero"_err_en_US, LoopKindName()); 691 } 692 } 693 } 694 } 695 } 696 697 void CheckLocalitySpecs( 698 const parser::LoopControl &control, const parser::Block &block) const { 699 const auto &concurrent{ 700 std::get<parser::LoopControl::Concurrent>(control.u)}; 701 const auto &header{std::get<parser::ConcurrentHeader>(concurrent.t)}; 702 const auto &localitySpecs{ 703 std::get<std::list<parser::LocalitySpec>>(concurrent.t)}; 704 if (!localitySpecs.empty()) { 705 const SymbolSet &localVars{GatherLocals(localitySpecs)}; 706 for (const auto &c : GetControls(control)) { 707 CheckExprDoesNotReferenceLocal(std::get<1>(c.t), localVars); 708 CheckExprDoesNotReferenceLocal(std::get<2>(c.t), localVars); 709 if (const auto &expr{ 710 std::get<std::optional<parser::ScalarIntExpr>>(c.t)}) { 711 CheckExprDoesNotReferenceLocal(*expr, localVars); 712 } 713 } 714 if (const auto &mask{ 715 std::get<std::optional<parser::ScalarLogicalExpr>>(header.t)}) { 716 CheckMaskDoesNotReferenceLocal(*mask, localVars); 717 } 718 CheckDefaultNoneImpliesExplicitLocality(localitySpecs, block); 719 } 720 } 721 722 // check constraints [C1121 .. C1130] 723 void CheckConcurrentLoopControl(const parser::LoopControl &control) const { 724 const auto &concurrent{ 725 std::get<parser::LoopControl::Concurrent>(control.u)}; 726 CheckConcurrentHeader(std::get<parser::ConcurrentHeader>(concurrent.t)); 727 } 728 729 template <typename T> void CheckForImpureCall(const T &x) { 730 const auto &intrinsics{context_.foldingContext().intrinsics()}; 731 if (auto bad{FindImpureCall(intrinsics, x)}) { 732 context_.Say( 733 "Impure procedure '%s' may not be referenced in a %s"_err_en_US, *bad, 734 LoopKindName()); 735 } 736 } 737 738 // Each index should be used on the LHS of each assignment in a FORALL 739 void CheckForallIndexesUsed(const evaluate::Assignment &assignment) { 740 SymbolVector indexVars{context_.GetIndexVars(IndexVarKind::FORALL)}; 741 if (!indexVars.empty()) { 742 SymbolSet symbols{evaluate::CollectSymbols(assignment.lhs)}; 743 std::visit( 744 common::visitors{ 745 [&](const evaluate::Assignment::BoundsSpec &spec) { 746 for (const auto &bound : spec) { 747 // TODO: this is working around missing std::set::merge in some versions of 748 // clang that we are building with 749 #ifdef __clang__ 750 auto boundSymbols{evaluate::CollectSymbols(bound)}; 751 symbols.insert(boundSymbols.begin(), boundSymbols.end()); 752 #else 753 symbols.merge(evaluate::CollectSymbols(bound)); 754 #endif 755 } 756 }, 757 [&](const evaluate::Assignment::BoundsRemapping &remapping) { 758 for (const auto &bounds : remapping) { 759 #ifdef __clang__ 760 auto lbSymbols{evaluate::CollectSymbols(bounds.first)}; 761 symbols.insert(lbSymbols.begin(), lbSymbols.end()); 762 auto ubSymbols{evaluate::CollectSymbols(bounds.second)}; 763 symbols.insert(ubSymbols.begin(), ubSymbols.end()); 764 #else 765 symbols.merge(evaluate::CollectSymbols(bounds.first)); 766 symbols.merge(evaluate::CollectSymbols(bounds.second)); 767 #endif 768 } 769 }, 770 [](const auto &) {}, 771 }, 772 assignment.u); 773 for (const Symbol &index : indexVars) { 774 if (symbols.count(index) == 0) { 775 context_.Say( 776 "Warning: FORALL index variable '%s' not used on left-hand side" 777 " of assignment"_en_US, 778 index.name()); 779 } 780 } 781 } 782 } 783 784 // For messages where the DO loop must be DO CONCURRENT, make that explicit. 785 const char *LoopKindName() const { 786 return kind_ == IndexVarKind::DO ? "DO CONCURRENT" : "FORALL"; 787 } 788 789 SemanticsContext &context_; 790 const IndexVarKind kind_; 791 parser::CharBlock currentStatementSourcePosition_; 792 }; // class DoContext 793 794 void DoForallChecker::Enter(const parser::DoConstruct &doConstruct) { 795 DoContext doContext{context_, IndexVarKind::DO}; 796 doContext.DefineDoVariables(doConstruct); 797 } 798 799 void DoForallChecker::Leave(const parser::DoConstruct &doConstruct) { 800 DoContext doContext{context_, IndexVarKind::DO}; 801 doContext.Check(doConstruct); 802 doContext.ResetDoVariables(doConstruct); 803 } 804 805 void DoForallChecker::Enter(const parser::ForallConstruct &construct) { 806 DoContext doContext{context_, IndexVarKind::FORALL}; 807 doContext.ActivateIndexVars(GetControls(construct)); 808 } 809 void DoForallChecker::Leave(const parser::ForallConstruct &construct) { 810 DoContext doContext{context_, IndexVarKind::FORALL}; 811 doContext.Check(construct); 812 doContext.DeactivateIndexVars(GetControls(construct)); 813 } 814 815 void DoForallChecker::Enter(const parser::ForallStmt &stmt) { 816 DoContext doContext{context_, IndexVarKind::FORALL}; 817 doContext.ActivateIndexVars(GetControls(stmt)); 818 } 819 void DoForallChecker::Leave(const parser::ForallStmt &stmt) { 820 DoContext doContext{context_, IndexVarKind::FORALL}; 821 doContext.Check(stmt); 822 doContext.DeactivateIndexVars(GetControls(stmt)); 823 } 824 void DoForallChecker::Leave(const parser::ForallAssignmentStmt &stmt) { 825 DoContext doContext{context_, IndexVarKind::FORALL}; 826 doContext.Check(stmt); 827 } 828 829 template <typename A> 830 static parser::CharBlock GetConstructPosition(const A &a) { 831 return std::get<0>(a.t).source; 832 } 833 834 static parser::CharBlock GetNodePosition(const ConstructNode &construct) { 835 return std::visit( 836 [&](const auto &x) { return GetConstructPosition(*x); }, construct); 837 } 838 839 void DoForallChecker::SayBadLeave(StmtType stmtType, 840 const char *enclosingStmtName, const ConstructNode &construct) const { 841 context_ 842 .Say("%s must not leave a %s statement"_err_en_US, EnumToString(stmtType), 843 enclosingStmtName) 844 .Attach(GetNodePosition(construct), "The construct that was left"_en_US); 845 } 846 847 static const parser::DoConstruct *MaybeGetDoConstruct( 848 const ConstructNode &construct) { 849 if (const auto *doNode{ 850 std::get_if<const parser::DoConstruct *>(&construct)}) { 851 return *doNode; 852 } else { 853 return nullptr; 854 } 855 } 856 857 static bool ConstructIsDoConcurrent(const ConstructNode &construct) { 858 const parser::DoConstruct *doConstruct{MaybeGetDoConstruct(construct)}; 859 return doConstruct && doConstruct->IsDoConcurrent(); 860 } 861 862 // Check that CYCLE and EXIT statements do not cause flow of control to 863 // leave DO CONCURRENT, CRITICAL, or CHANGE TEAM constructs. 864 void DoForallChecker::CheckForBadLeave( 865 StmtType stmtType, const ConstructNode &construct) const { 866 std::visit(common::visitors{ 867 [&](const parser::DoConstruct *doConstructPtr) { 868 if (doConstructPtr->IsDoConcurrent()) { 869 // C1135 and C1167 -- CYCLE and EXIT statements can't leave 870 // a DO CONCURRENT 871 SayBadLeave(stmtType, "DO CONCURRENT", construct); 872 } 873 }, 874 [&](const parser::CriticalConstruct *) { 875 // C1135 and C1168 -- similarly, for CRITICAL 876 SayBadLeave(stmtType, "CRITICAL", construct); 877 }, 878 [&](const parser::ChangeTeamConstruct *) { 879 // C1135 and C1168 -- similarly, for CHANGE TEAM 880 SayBadLeave(stmtType, "CHANGE TEAM", construct); 881 }, 882 [](const auto *) {}, 883 }, 884 construct); 885 } 886 887 static bool StmtMatchesConstruct(const parser::Name *stmtName, 888 StmtType stmtType, const std::optional<parser::Name> &constructName, 889 const ConstructNode &construct) { 890 bool inDoConstruct{MaybeGetDoConstruct(construct) != nullptr}; 891 if (!stmtName) { 892 return inDoConstruct; // Unlabeled statements match all DO constructs 893 } else if (constructName && constructName->source == stmtName->source) { 894 return stmtType == StmtType::EXIT || inDoConstruct; 895 } else { 896 return false; 897 } 898 } 899 900 // C1167 Can't EXIT from a DO CONCURRENT 901 void DoForallChecker::CheckDoConcurrentExit( 902 StmtType stmtType, const ConstructNode &construct) const { 903 if (stmtType == StmtType::EXIT && ConstructIsDoConcurrent(construct)) { 904 SayBadLeave(StmtType::EXIT, "DO CONCURRENT", construct); 905 } 906 } 907 908 // Check nesting violations for a CYCLE or EXIT statement. Loop up the 909 // nesting levels looking for a construct that matches the CYCLE or EXIT 910 // statment. At every construct, check for a violation. If we find a match 911 // without finding a violation, the check is complete. 912 void DoForallChecker::CheckNesting( 913 StmtType stmtType, const parser::Name *stmtName) const { 914 const ConstructStack &stack{context_.constructStack()}; 915 for (auto iter{stack.cend()}; iter-- != stack.cbegin();) { 916 const ConstructNode &construct{*iter}; 917 const std::optional<parser::Name> &constructName{ 918 MaybeGetNodeName(construct)}; 919 if (StmtMatchesConstruct(stmtName, stmtType, constructName, construct)) { 920 CheckDoConcurrentExit(stmtType, construct); 921 return; // We got a match, so we're finished checking 922 } 923 CheckForBadLeave(stmtType, construct); 924 } 925 926 // We haven't found a match in the enclosing constructs 927 if (stmtType == StmtType::EXIT) { 928 context_.Say("No matching construct for EXIT statement"_err_en_US); 929 } else { 930 context_.Say("No matching DO construct for CYCLE statement"_err_en_US); 931 } 932 } 933 934 // C1135 -- Nesting for CYCLE statements 935 void DoForallChecker::Enter(const parser::CycleStmt &cycleStmt) { 936 CheckNesting(StmtType::CYCLE, common::GetPtrFromOptional(cycleStmt.v)); 937 } 938 939 // C1167 and C1168 -- Nesting for EXIT statements 940 void DoForallChecker::Enter(const parser::ExitStmt &exitStmt) { 941 CheckNesting(StmtType::EXIT, common::GetPtrFromOptional(exitStmt.v)); 942 } 943 944 void DoForallChecker::Leave(const parser::AssignmentStmt &stmt) { 945 const auto &variable{std::get<parser::Variable>(stmt.t)}; 946 context_.CheckIndexVarRedefine(variable); 947 } 948 949 static void CheckIfArgIsDoVar(const evaluate::ActualArgument &arg, 950 const parser::CharBlock location, SemanticsContext &context) { 951 common::Intent intent{arg.dummyIntent()}; 952 if (intent == common::Intent::Out || intent == common::Intent::InOut) { 953 if (const SomeExpr * argExpr{arg.UnwrapExpr()}) { 954 if (const Symbol * var{evaluate::UnwrapWholeSymbolDataRef(*argExpr)}) { 955 if (intent == common::Intent::Out) { 956 context.CheckIndexVarRedefine(location, *var); 957 } else { 958 context.WarnIndexVarRedefine(location, *var); // INTENT(INOUT) 959 } 960 } 961 } 962 } 963 } 964 965 // Check to see if a DO variable is being passed as an actual argument to a 966 // dummy argument whose intent is OUT or INOUT. To do this, we need to find 967 // the expressions for actual arguments which contain DO variables. We get the 968 // intents of the dummy arguments from the ProcedureRef in the "typedCall" 969 // field of the CallStmt which was filled in during expression checking. At 970 // the same time, we need to iterate over the parser::Expr versions of the 971 // actual arguments to get their source locations of the arguments for the 972 // messages. 973 void DoForallChecker::Leave(const parser::CallStmt &callStmt) { 974 if (const auto &typedCall{callStmt.typedCall}) { 975 const auto &parsedArgs{ 976 std::get<std::list<parser::ActualArgSpec>>(callStmt.v.t)}; 977 auto parsedArgIter{parsedArgs.begin()}; 978 const evaluate::ActualArguments &checkedArgs{typedCall->arguments()}; 979 for (const auto &checkedOptionalArg : checkedArgs) { 980 if (parsedArgIter == parsedArgs.end()) { 981 break; // No more parsed arguments, we're done. 982 } 983 const auto &parsedArg{std::get<parser::ActualArg>(parsedArgIter->t)}; 984 ++parsedArgIter; 985 if (checkedOptionalArg) { 986 const evaluate::ActualArgument &checkedArg{*checkedOptionalArg}; 987 if (const auto *parsedExpr{ 988 std::get_if<common::Indirection<parser::Expr>>(&parsedArg.u)}) { 989 CheckIfArgIsDoVar(checkedArg, parsedExpr->value().source, context_); 990 } 991 } 992 } 993 } 994 } 995 996 void DoForallChecker::Leave(const parser::ConnectSpec &connectSpec) { 997 const auto *newunit{ 998 std::get_if<parser::ConnectSpec::Newunit>(&connectSpec.u)}; 999 if (newunit) { 1000 context_.CheckIndexVarRedefine(newunit->v.thing.thing); 1001 } 1002 } 1003 1004 using ActualArgumentSet = std::set<evaluate::ActualArgumentRef>; 1005 1006 struct CollectActualArgumentsHelper 1007 : public evaluate::SetTraverse<CollectActualArgumentsHelper, 1008 ActualArgumentSet> { 1009 using Base = SetTraverse<CollectActualArgumentsHelper, ActualArgumentSet>; 1010 CollectActualArgumentsHelper() : Base{*this} {} 1011 using Base::operator(); 1012 ActualArgumentSet operator()(const evaluate::ActualArgument &arg) const { 1013 return Combine(ActualArgumentSet{arg}, 1014 CollectActualArgumentsHelper{}(arg.UnwrapExpr())); 1015 } 1016 }; 1017 1018 template <typename A> ActualArgumentSet CollectActualArguments(const A &x) { 1019 return CollectActualArgumentsHelper{}(x); 1020 } 1021 1022 template ActualArgumentSet CollectActualArguments(const SomeExpr &); 1023 1024 void DoForallChecker::Enter(const parser::Expr &parsedExpr) { ++exprDepth_; } 1025 1026 void DoForallChecker::Leave(const parser::Expr &parsedExpr) { 1027 CHECK(exprDepth_ > 0); 1028 if (--exprDepth_ == 0) { // Only check top level expressions 1029 if (const SomeExpr * expr{GetExpr(parsedExpr)}) { 1030 ActualArgumentSet argSet{CollectActualArguments(*expr)}; 1031 for (const evaluate::ActualArgumentRef &argRef : argSet) { 1032 CheckIfArgIsDoVar(*argRef, parsedExpr.source, context_); 1033 } 1034 } 1035 } 1036 } 1037 1038 void DoForallChecker::Leave(const parser::InquireSpec &inquireSpec) { 1039 const auto *intVar{std::get_if<parser::InquireSpec::IntVar>(&inquireSpec.u)}; 1040 if (intVar) { 1041 const auto &scalar{std::get<parser::ScalarIntVariable>(intVar->t)}; 1042 context_.CheckIndexVarRedefine(scalar.thing.thing); 1043 } 1044 } 1045 1046 void DoForallChecker::Leave(const parser::IoControlSpec &ioControlSpec) { 1047 const auto *size{std::get_if<parser::IoControlSpec::Size>(&ioControlSpec.u)}; 1048 if (size) { 1049 context_.CheckIndexVarRedefine(size->v.thing.thing); 1050 } 1051 } 1052 1053 void DoForallChecker::Leave(const parser::OutputImpliedDo &outputImpliedDo) { 1054 const auto &control{std::get<parser::IoImpliedDoControl>(outputImpliedDo.t)}; 1055 const parser::Name &name{control.name.thing.thing}; 1056 context_.CheckIndexVarRedefine(name.source, *name.symbol); 1057 } 1058 1059 void DoForallChecker::Leave(const parser::StatVariable &statVariable) { 1060 context_.CheckIndexVarRedefine(statVariable.v.thing.thing); 1061 } 1062 1063 } // namespace Fortran::semantics 1064