diff --git a/flang/lib/semantics/check-do.cc b/flang/lib/semantics/check-do.cc index 4c3b830847b9..76b232c2ac51 100644 --- a/flang/lib/semantics/check-do.cc +++ b/flang/lib/semantics/check-do.cc @@ -36,6 +36,10 @@ static const parser::Name *MaybeGetConstructName(const A &a) { return common::GetPtrFromOptional(std::get<0>(std::get<0>(a.t).statement.t)); } +static parser::MessageFixedText GetEnclosingDoMsg() { + return "Enclosing DO CONCURRENT statement"_en_US; +} + static const parser::Name *MaybeGetConstructName( const parser::BlockConstruct &blockConstruct) { return common::GetPtrFromOptional( @@ -43,19 +47,38 @@ static const parser::Name *MaybeGetConstructName( .statement.v); } -// Return the (possibly null) name of the statement -template static const parser::Name *MaybeGetStmtName(const A &a) { - return common::GetPtrFromOptional(std::get<0>(a.t)); +static void SayWithDo(SemanticsContext &context, parser::CharBlock stmtLocation, + parser::MessageFixedText &&message, parser::CharBlock doLocation) { + context.Say(stmtLocation, message).Attach(doLocation, GetEnclosingDoMsg()); } // 11.1.7.5 - enforce semantics constraints on a DO CONCURRENT loop body class DoConcurrentBodyEnforce { public: - DoConcurrentBodyEnforce(SemanticsContext &context) : context_{context} {} + DoConcurrentBodyEnforce( + SemanticsContext &context, parser::CharBlock doConcurrentSourcePosition) + : context_{context}, doConcurrentSourcePosition_{ + doConcurrentSourcePosition} {} std::set labels() { return labels_; } std::set names() { return names_; } template bool Pre(const T &) { return true; } template void Post(const T &) {} + + // C1137 -- No image control statements in a DO CONCURRENT + void Post(const parser::ExecutableConstruct &construct) { + if (IsImageControlStmt(construct)) { + const parser::CharBlock statementLocation{ + GetImageControlStmtLocation(construct)}; + auto &msg{context_.Say(statementLocation, + "An image control statement is not allowed in DO" + " CONCURRENT"_err_en_US)}; + if (auto coarrayMsg{GetImageControlStmtCoarrayMsg(construct)}) { + msg.Attach(statementLocation, *coarrayMsg); + } + msg.Attach(doConcurrentSourcePosition_, GetEnclosingDoMsg()); + } + } + template bool Pre(const parser::Statement &statement) { currentStatementSourcePosition_ = statement.source; if (statement.label.has_value()) { @@ -64,7 +87,7 @@ public: return true; } - // C1167 + // C1167 -- EXIT statements can't exit a DO CONCURRENT bool Pre(const parser::WhereConstruct &s) { AddName(MaybeGetConstructName(s)); return true; @@ -115,140 +138,68 @@ public: return true; } - // C1136 + // C1136 -- No RETURN statements in a DO CONCURRENT void Post(const parser::ReturnStmt &) { - context_.Say(currentStatementSourcePosition_, - "RETURN not allowed in DO CONCURRENT"_err_en_US); - } - - // C1137 - void NoImageControl() { - context_.Say(currentStatementSourcePosition_, - "image control statement not allowed in DO CONCURRENT"_err_en_US); - } - - // more C1137 checks - void Post(const parser::SyncAllStmt &) { NoImageControl(); } - void Post(const parser::SyncImagesStmt &) { NoImageControl(); } - void Post(const parser::SyncMemoryStmt &) { NoImageControl(); } - void Post(const parser::SyncTeamStmt &) { NoImageControl(); } - void Post(const parser::ChangeTeamConstruct &) { NoImageControl(); } - void Post(const parser::CriticalConstruct &) { NoImageControl(); } - void Post(const parser::EventPostStmt &) { NoImageControl(); } - void Post(const parser::EventWaitStmt &) { NoImageControl(); } - void Post(const parser::FormTeamStmt &) { NoImageControl(); } - void Post(const parser::LockStmt &) { NoImageControl(); } - void Post(const parser::UnlockStmt &) { NoImageControl(); } - void Post(const parser::StopStmt &) { NoImageControl(); } - - // more C1137 checks - void Post(const parser::AllocateStmt &allocateStmt) { - CheckDoesntContainCoarray(allocateStmt); - } - - void Post(const parser::DeallocateStmt &deallocateStmt) { - CheckDoesntContainCoarray(deallocateStmt); // C1137 - - // C1140: deallocation of polymorphic objects - if (anyObjectIsPolymorphic()) { - context_.Say(currentStatementSourcePosition_, - "DEALLOCATE polymorphic object(s) not allowed" - " in DO CONCURRENT"_err_en_US); - } - } - - template void Post(const parser::Statement &) { - if (EndTDeallocatesCoarray()) { - context_.Say(currentStatementSourcePosition_, - "implicit deallocation of coarray not allowed" - " in DO CONCURRENT"_err_en_US); - } + context_ + .Say(currentStatementSourcePosition_, + "RETURN is not allowed in DO CONCURRENT"_err_en_US) + .Attach(doConcurrentSourcePosition_, GetEnclosingDoMsg()); } + // C1139: call to impure procedure and ... // C1141: cannot call ieee_get_flag, ieee_[gs]et_halting_mode + // It's not necessary to check the ieee_get* procedures because they're + // not pure, and impure procedures are caught by checks for constraint C1139 void Post(const parser::ProcedureDesignator &procedureDesignator) { if (auto *name{std::get_if(&procedureDesignator.u)}) { - // C1137: call move_alloc with coarray arguments - if (name->source == "move_alloc") { - if (anyObjectIsCoarray()) { - context_.Say(currentStatementSourcePosition_, - "call to MOVE_ALLOC intrinsic in DO CONCURRENT with coarray" - " argument(s) not allowed"_err_en_US); - } - } - // C1139: call to impure procedure if (name->symbol && !IsPureProcedure(*name->symbol)) { - context_.Say(currentStatementSourcePosition_, - "call to impure procedure in DO CONCURRENT not allowed"_err_en_US); + SayWithDo(context_, currentStatementSourcePosition_, + "Call to an impure procedure is not allowed in DO" + " CONCURRENT"_err_en_US, + doConcurrentSourcePosition_); } if (name->symbol && fromScope(*name->symbol, "ieee_exceptions"s)) { - if (name->source == "ieee_get_flag") { - context_.Say(currentStatementSourcePosition_, - "IEEE_GET_FLAG not allowed in DO CONCURRENT"_err_en_US); - } else if (name->source == "ieee_set_halting_mode") { - context_.Say(currentStatementSourcePosition_, - "IEEE_SET_HALTING_MODE not allowed in DO CONCURRENT"_err_en_US); - } else if (name->source == "ieee_get_halting_mode") { - context_.Say(currentStatementSourcePosition_, - "IEEE_GET_HALTING_MODE not allowed in DO CONCURRENT"_err_en_US); + if (name->source == "ieee_set_halting_mode") { + SayWithDo(context_, currentStatementSourcePosition_, + "IEEE_SET_HALTING_MODE is not allowed in DO " + "CONCURRENT"_err_en_US, + doConcurrentSourcePosition_); } } } else { - // C1139: this a procedure component + // C1139: check for an impure procedure component auto &component{std::get(procedureDesignator.u) .v.thing.component}; if (component.symbol && !IsPureProcedure(*component.symbol)) { - context_.Say(currentStatementSourcePosition_, - "call to impure procedure in DO CONCURRENT not allowed"_err_en_US); + SayWithDo(context_, currentStatementSourcePosition_, + "Call to an impure procedure component is not allowed" + " in DO CONCURRENT"_err_en_US, + doConcurrentSourcePosition_); } } } - // 11.1.7.5 + // 11.1.7.5, paragraph 5, no ADVANCE specifier in a DO CONCURRENT void Post(const parser::IoControlSpec &ioControlSpec) { if (auto *charExpr{ std::get_if(&ioControlSpec.u)}) { if (std::get(charExpr->t) == parser::IoControlSpec::CharExpr::Kind::Advance) { - context_.Say(currentStatementSourcePosition_, - "ADVANCE specifier not allowed in DO CONCURRENT"_err_en_US); + SayWithDo(context_, currentStatementSourcePosition_, + "ADVANCE specifier is not allowed in DO" + " CONCURRENT"_err_en_US, + doConcurrentSourcePosition_); } } } private: - // C1137 helper functions - void CheckAllocateObjectIsntCoarray( - const parser::AllocateObject &allocateObject, StmtType stmtType) { - const parser::Name &name{GetLastName(allocateObject)}; - if (name.symbol && IsCoarray(*name.symbol)) { - context_.Say(name.source, - "%s coarray not allowed in DO CONCURRENT"_err_en_US, - EnumToString(stmtType)); - } + // Return the (possibly null) name of the statement + template static const parser::Name *MaybeGetStmtName(const A &a) { + return common::GetPtrFromOptional(std::get<0>(a.t)); } - void CheckDoesntContainCoarray(const parser::AllocateStmt &allocateStmt) { - const auto &allocationList{ - std::get>(allocateStmt.t)}; - for (const auto &allocation : allocationList) { - const auto &allocateObject{ - std::get(allocation.t)}; - CheckAllocateObjectIsntCoarray(allocateObject, StmtType::ALLOCATE); - } - } - - void CheckDoesntContainCoarray(const parser::DeallocateStmt &deallocateStmt) { - const auto &allocateObjectList{ - std::get>(deallocateStmt.t)}; - for (const auto &allocateObject : allocateObjectList) { - CheckAllocateObjectIsntCoarray(allocateObject, StmtType::DEALLOCATE); - } - } - - bool anyObjectIsCoarray() { return false; } // FIXME placeholder bool anyObjectIsPolymorphic() { return false; } // FIXME placeholder - bool EndTDeallocatesCoarray() { return false; } // FIXME placeholder bool fromScope(const Symbol &symbol, const std::string &moduleName) { if (symbol.GetUltimate().owner().IsModule() && symbol.GetUltimate().owner().GetName().value().ToString() == @@ -268,6 +219,7 @@ private: std::set labels_; parser::CharBlock currentStatementSourcePosition_; SemanticsContext &context_; + parser::CharBlock doConcurrentSourcePosition_; }; // class DoConcurrentBodyEnforce class DoConcurrentLabelEnforce { @@ -318,8 +270,9 @@ public: void checkLabelUse(const parser::Label &labelUsed) { if (labels_.find(labelUsed) == labels_.end()) { - context_.Say(currentStatementSourcePosition_, - "control flow escapes from DO CONCURRENT"_err_en_US); + SayWithDo(context_, currentStatementSourcePosition_, + "Control flow escapes from DO CONCURRENT"_err_en_US, + doConcurrentSourcePosition_); } } @@ -331,7 +284,8 @@ private: parser::CharBlock doConcurrentSourcePosition_{nullptr}; }; // class DoConcurrentLabelEnforce -// Class for enforcing C1130 +// Class for enforcing C1130 -- in a DO CONCURRENT with DEFAULT(NONE), +// variables from enclosing scopes must have their locality specified class DoConcurrentVariableEnforce { public: DoConcurrentVariableEnforce( @@ -349,11 +303,14 @@ public: if (IsVariableName(*symbol)) { const Scope &variableScope{symbol->owner()}; if (DoesScopeContain(&variableScope, blockScope_)) { - context_.Say(name.source, - "Variable '%s' from an enclosing scope referenced in a DO " - "CONCURRENT with DEFAULT(NONE) must appear in a " - "locality-spec"_err_en_US, - name.source); + context_ + .Say(name.source, + "Variable '%s' from an enclosing scope referenced in a DO " + "CONCURRENT with DEFAULT(NONE) must appear in a " + "locality-spec"_err_en_US, + name.source) + .Attach(symbol->name(), "Declaration of variable '%s'"_en_US, + symbol->name()); } } } @@ -439,8 +396,8 @@ private: } void CheckDoNormal(const parser::DoConstruct &doConstruct) { - // C1120 extended by allowing REAL and DOUBLE PRECISION - // Get the bounds, then check the variable, init, final, and step + // C1120 -- types of DO variables must be INTEGER, extended by allowing + // REAL and DOUBLE PRECISION const Bounds &bounds{GetBounds(doConstruct)}; CheckDoVariable(bounds.name); CheckDoExpression(bounds.lower); @@ -456,7 +413,7 @@ private: currentStatementSourcePosition_ = doStmt.source; const parser::Block &block{std::get(doConstruct.t)}; - DoConcurrentBodyEnforce doConcurrentBodyEnforce{context_}; + DoConcurrentBodyEnforce doConcurrentBodyEnforce{context_, doStmt.source}; parser::Walk(block, doConcurrentBodyEnforce); DoConcurrentLabelEnforce doConcurrentLabelEnforce{context_, @@ -519,7 +476,7 @@ private: const parser::CharBlock &name{ref->name()}; context_ .Say(currentStatementSourcePosition_, - "concurrent-header mask expression cannot reference an impure" + "Concurrent-header mask expression cannot reference an impure" " procedure"_err_en_US) .Attach(name, "Declaration of impure procedure '%s'"_en_US, name); return; @@ -558,7 +515,8 @@ private: mask.thing.thing.value().source); } - // C1129, names in local locality-specs can't be in limit or step expressions + // C1129, names in local locality-specs can't be in limit or step + // expressions void CheckExprDoesNotReferenceLocal( const parser::ScalarIntExpr &expr, const SymbolSet &localVars) const { CheckNoCollisions(GatherSymbolsFromExpression(expr.thing.thing.value()), @@ -568,8 +526,8 @@ private: expr.thing.thing.value().source); } - // C1130, default(none) locality requires names to be in locality-specs to be - // used in the body of the DO loop + // C1130, DEFAULT(NONE) locality requires names to be in locality-specs to + // be used in the body of the DO loop void CheckDefaultNoneImpliesExplicitLocality( const std::list &localitySpecs, const parser::Block &block) const { @@ -579,7 +537,7 @@ private: if (hasDefaultNone) { // C1127, you can only have one DEFAULT(NONE) context_.Say(currentStatementSourcePosition_, - "only one DEFAULT(NONE) may appear"_en_US); + "Only one DEFAULT(NONE) may appear"_en_US); break; } hasDefaultNone = true; @@ -710,16 +668,17 @@ void DoChecker::CheckForBadLeave( common::visitors{ [&](const parser::DoConstruct *doConstructPtr) { if (doConstructPtr->IsDoConcurrent()) { - // C1135 and C1167 + // C1135 and C1167 -- CYCLE and EXIT statements can't leave a + // DO CONCURRENT SayBadLeave(stmtType, "DO CONCURRENT", construct); } }, [&](const parser::CriticalConstruct *) { - // C1135 and C1168 + // C1135 and C1168 -- similarly, for CRITICAL SayBadLeave(stmtType, "CRITICAL", construct); }, [&](const parser::ChangeTeamConstruct *) { - // C1135 and C1168 + // C1135 and C1168 -- similarly, for CHANGE TEAM SayBadLeave(stmtType, "CHANGE TEAM", construct); }, [](const auto *) {}, @@ -748,10 +707,10 @@ void DoChecker::CheckDoConcurrentExit( } } -// Check nesting violations for a CYCLE or EXIT statement. Loop up the nesting -// levels looking for a construct that matches the CYCLE or EXIT statment. At -// every construct, check for a violation. If we find a match without finding -// a violation, the check is complete. +// Check nesting violations for a CYCLE or EXIT statement. Loop up the +// nesting levels looking for a construct that matches the CYCLE or EXIT +// statment. At every construct, check for a violation. If we find a match +// without finding a violation, the check is complete. void DoChecker::CheckNesting( StmtType stmtType, const parser::Name *stmtName) const { const ConstructStack &stack{context_.constructStack()}; @@ -773,12 +732,12 @@ void DoChecker::CheckNesting( } } -// C1135 +// C1135 -- Nesting for CYCLE statements void DoChecker::Enter(const parser::CycleStmt &cycleStmt) { CheckNesting(StmtType::CYCLE, common::GetPtrFromOptional(cycleStmt.v)); } -// C1167 and C1168 +// C1167 and C1168 -- Nesting for EXIT statements void DoChecker::Enter(const parser::ExitStmt &exitStmt) { CheckNesting(StmtType::EXIT, common::GetPtrFromOptional(exitStmt.v)); } diff --git a/flang/lib/semantics/check-do.h b/flang/lib/semantics/check-do.h index 1e8a15357e25..2bd4c942016a 100644 --- a/flang/lib/semantics/check-do.h +++ b/flang/lib/semantics/check-do.h @@ -27,7 +27,7 @@ struct ExitStmt; namespace Fortran::semantics { // To specify different statement types used in semantic checking. -ENUM_CLASS(StmtType, CYCLE, EXIT, ALLOCATE, DEALLOCATE) +ENUM_CLASS(StmtType, CYCLE, EXIT) class DoChecker : public virtual BaseChecker { public: diff --git a/flang/lib/semantics/tools.cc b/flang/lib/semantics/tools.cc index c6dab3c1aec2..4ab9fd74e0b3 100644 --- a/flang/lib/semantics/tools.cc +++ b/flang/lib/semantics/tools.cc @@ -21,6 +21,7 @@ #include "../common/indirection.h" #include "../parser/message.h" #include "../parser/parse-tree.h" +#include "../parser/tools.h" #include #include #include @@ -541,6 +542,138 @@ std::unique_ptr WhyNotModifiable(parser::CharBlock at, return {}; } +struct ImageControlStmtHelper { + using ImageControlStmts = std::variant; + template bool operator()(const T &) { + return common::HasMember; + } + template bool operator()(const common::Indirection &x) { + return (*this)(x.value()); + } + bool IsCoarrayObject(const parser::AllocateObject &allocateObject) { + const parser::Name &name{GetLastName(allocateObject)}; + return name.symbol && IsCoarray(*name.symbol); + } + bool operator()(const parser::AllocateStmt &stmt) { + const auto &allocationList{std::get>(stmt.t)}; + for (const auto &allocation : allocationList) { + const auto &allocateObject{ + std::get(allocation.t)}; + if (IsCoarrayObject(allocateObject)) { + return true; + } + } + return false; + } + bool operator()(const parser::DeallocateStmt &stmt) { + const auto &allocateObjectList{ + std::get>(stmt.t)}; + for (const auto &allocateObject : allocateObjectList) { + if (IsCoarrayObject(allocateObject)) { + return true; + } + } + return false; + } + bool operator()(const parser::CallStmt &stmt) { + const auto &procedureDesignator{ + std::get(stmt.v.t)}; + if (auto *name{std::get_if(&procedureDesignator.u)}) { + // TODO: also ensure that the procedure is, in fact, an intrinsic + if (name->source == "move_alloc") { + const auto &args{std::get>(stmt.v.t)}; + if (!args.empty()) { + const parser::ActualArg &actualArg{ + std::get(args.front().t)}; + if (const auto *argExpr{ + std::get_if>( + &actualArg.u)}) { + return HasCoarray(argExpr->value()); + } + } + } + } + return false; + } + bool operator()(const parser::Statement &stmt) { + return std::visit(*this, stmt.statement.u); + } +}; + +bool IsImageControlStmt(const parser::ExecutableConstruct &construct) { + return std::visit(ImageControlStmtHelper{}, construct.u); +} + +std::optional GetImageControlStmtCoarrayMsg( + const parser::ExecutableConstruct &construct) { + if (const auto *actionStmt{ + std::get_if>(&construct.u)}) { + return std::visit( + common::visitors{ + [](const common::Indirection &) + -> std::optional { + return "ALLOCATE of a coarray is an image control" + " statement"_en_US; + }, + [](const common::Indirection &) + -> std::optional { + return "DEALLOCATE of a coarray is an image control" + " statement"_en_US; + }, + [](const common::Indirection &) + -> std::optional { + return "MOVE_ALLOC of a coarray is an image control" + " statement "_en_US; + }, + [](const auto &) -> std::optional { + return std::nullopt; + }, + }, + actionStmt->statement.u); + } + return std::nullopt; +} + +const parser::CharBlock GetImageControlStmtLocation( + const parser::ExecutableConstruct &executableConstruct) { + return std::visit( + common::visitors{ + [](const common::Indirection + &construct) { + return std::get>( + construct.value().t) + .source; + }, + [](const common::Indirection &construct) { + return std::get>( + construct.value().t) + .source; + }, + [](const parser::Statement &actionStmt) { + return actionStmt.source; + }, + [](const auto &) { return parser::CharBlock{}; }, + }, + executableConstruct.u); +} + +bool HasCoarray(const parser::Expr &expression) { + if (const auto *expr{GetExpr(expression)}) { + for (const Symbol *symbol : evaluate::CollectSymbols(*expr)) { + if (const Symbol * root{GetAssociationRoot(DEREF(symbol))}) { + if (IsCoarray(*root)) { + return true; + } + } + } + } + return false; +} + static const DeclTypeSpec &InstantiateIntrinsicType(Scope &scope, const DeclTypeSpec &spec, SemanticsContext &semanticsContext) { const IntrinsicTypeSpec *intrinsic{spec.AsIntrinsic()}; @@ -906,8 +1039,8 @@ enum class ComponentVisitState { Resume, Pre, Post }; template void ComponentIterator::const_iterator::Increment() { std::int64_t level{static_cast(componentPath_.size()) - 1}; - // Need to know if this is the first incrementation or if the visit is resumed - // after a user increment. + // Need to know if this is the first incrementation or if the visit is + // resumed after a user increment. ComponentVisitState state{ level >= 0 && GetComponentSymbol(componentPath_[level]) ? ComponentVisitState::Resume diff --git a/flang/lib/semantics/tools.h b/flang/lib/semantics/tools.h index b8ed73dd6712..cdb44a60d1c0 100644 --- a/flang/lib/semantics/tools.h +++ b/flang/lib/semantics/tools.h @@ -120,12 +120,24 @@ inline bool IsAssumedSizeArray(const Symbol &symbol) { } bool IsAssumedLengthCharacter(const Symbol &); bool IsAssumedLengthCharacterFunction(const Symbol &); +// Is the symbol modifiable in this scope std::optional WhyNotModifiable( const Symbol &, const Scope &); std::unique_ptr WhyNotModifiable(SourceName, const SomeExpr &, const Scope &, bool vectorSubscriptIsOk = false); -// Is the symbol modifiable in this scope bool IsExternalInPureContext(const Symbol &symbol, const Scope &scope); +bool HasCoarray(const parser::Expr &expression); + +// Analysis of image control statements +bool IsImageControlStmt(const parser::ExecutableConstruct &); +// Get the location of the image control statement in this ExecutableConstruct +const parser::CharBlock GetImageControlStmtLocation( + const parser::ExecutableConstruct &); +// Image control statements that reference coarrays need an extra message +// to clarify why they're image control statements. This function returns +// std::nullopt for ExecutableConstructs that do not require an extra message +std::optional GetImageControlStmtCoarrayMsg( + const parser::ExecutableConstruct &); // Returns the complete list of derived type parameter symbols in // the order in which their declarations appear in the derived type diff --git a/flang/test/semantics/CMakeLists.txt b/flang/test/semantics/CMakeLists.txt index 72a30d52d4e8..f04c506b0fdb 100644 --- a/flang/test/semantics/CMakeLists.txt +++ b/flang/test/semantics/CMakeLists.txt @@ -242,7 +242,6 @@ set(DOCONCURRENT_TESTS doconcurrent03.f90 doconcurrent04.f90 doconcurrent07.f90 - doconcurrent08.f90 ) set(CANONDO_TESTS diff --git a/flang/test/semantics/doconcurrent01.f90 b/flang/test/semantics/doconcurrent01.f90 index 3d92affbc5ff..afbdd18fb015 100644 --- a/flang/test/semantics/doconcurrent01.f90 +++ b/flang/test/semantics/doconcurrent01.f90 @@ -13,9 +13,8 @@ ! limitations under the License. ! ! C1141 -! A reference to the procedure IEEE_GET_FLAG, IEEE_SET_HALTING_MODE, or -! IEEE_GET_HALTING_MODE from the intrinsic module IEEE_EXCEPTIONS, shall not -! appear within a DO CONCURRENT construct. +! A reference to the procedure IEEE_SET_HALTING_MODE ! from the intrinsic +! module IEEE_EXCEPTIONS, shall not ! appear within a DO CONCURRENT construct. ! ! C1137 ! An image control statement shall not appear within a DO CONCURRENT construct. @@ -30,13 +29,13 @@ subroutine do_concurrent_test1(i,n) implicit none integer :: i, n do 10 concurrent (i = 1:n) -!ERROR: image control statement not allowed in DO CONCURRENT +!ERROR: An image control statement is not allowed in DO CONCURRENT SYNC ALL -!ERROR: image control statement not allowed in DO CONCURRENT +!ERROR: An image control statement is not allowed in DO CONCURRENT SYNC IMAGES (*) -!ERROR: image control statement not allowed in DO CONCURRENT +!ERROR: An image control statement is not allowed in DO CONCURRENT SYNC MEMORY -!ERROR: RETURN not allowed in DO CONCURRENT +!ERROR: RETURN is not allowed in DO CONCURRENT return 10 continue end subroutine do_concurrent_test1 @@ -50,32 +49,35 @@ subroutine do_concurrent_test2(i,j,n,flag) logical :: flagValue, halting type(team_type) :: j do concurrent (i = 1:n) -!ERROR: image control statement not allowed in DO CONCURRENT +!ERROR: An image control statement is not allowed in DO CONCURRENT sync team (j) +!ERROR: An image control statement is not allowed in DO CONCURRENT change team (j) +!ERROR: An image control statement is not allowed in DO CONCURRENT critical -!ERROR: call to impure procedure in DO CONCURRENT not allowed -!ERROR: IEEE_GET_FLAG not allowed in DO CONCURRENT +!ERROR: Call to an impure procedure is not allowed in DO CONCURRENT call ieee_get_flag(flag, flagValue) -!ERROR: call to impure procedure in DO CONCURRENT not allowed -!ERROR: IEEE_GET_HALTING_MODE not allowed in DO CONCURRENT +!ERROR: Call to an impure procedure is not allowed in DO CONCURRENT call ieee_get_halting_mode(flag, halting) -!ERROR: IEEE_SET_HALTING_MODE not allowed in DO CONCURRENT +!ERROR: IEEE_SET_HALTING_MODE is not allowed in DO CONCURRENT call ieee_set_halting_mode(flag, halting) -!ERROR: image control statement not allowed in DO CONCURRENT end critical -!ERROR: image control statement not allowed in DO CONCURRENT end team -!ERROR: ADVANCE specifier not allowed in DO CONCURRENT +!ERROR: ADVANCE specifier is not allowed in DO CONCURRENT write(*,'(a35)',advance='no') end do + +! The following is OK + do concurrent (i = 1:n) + call ieee_set_flag(flag, flagValue) + end do end subroutine do_concurrent_test2 subroutine s1() use iso_fortran_env type(event_type) :: x do concurrent (i = 1:n) -!ERROR: image control statement not allowed in DO CONCURRENT +!ERROR: An image control statement is not allowed in DO CONCURRENT event post (x) end do end subroutine s1 @@ -84,7 +86,7 @@ subroutine s2() use iso_fortran_env type(event_type) :: x do concurrent (i = 1:n) -!ERROR: image control statement not allowed in DO CONCURRENT +!ERROR: An image control statement is not allowed in DO CONCURRENT event wait (x) end do end subroutine s2 @@ -94,7 +96,7 @@ subroutine s3() type(team_type) :: t do concurrent (i = 1:n) -!ERROR: image control statement not allowed in DO CONCURRENT +!ERROR: An image control statement is not allowed in DO CONCURRENT form team(1, t) end do end subroutine s3 @@ -104,22 +106,17 @@ subroutine s4() type(lock_type) :: l do concurrent (i = 1:n) -!ERROR: image control statement not allowed in DO CONCURRENT +!ERROR: An image control statement is not allowed in DO CONCURRENT lock(l) -!ERROR: image control statement not allowed in DO CONCURRENT +!ERROR: An image control statement is not allowed in DO CONCURRENT unlock(l) end do end subroutine s4 subroutine s5() - use iso_fortran_env - type(lock_type) :: l - do concurrent (i = 1:n) -!ERROR: image control statement not allowed in DO CONCURRENT - lock(l) -!ERROR: image control statement not allowed in DO CONCURRENT - unlock(l) +!ERROR: An image control statement is not allowed in DO CONCURRENT + stop end do end subroutine s5 @@ -133,52 +130,77 @@ subroutine s6() type(type0) :: type1_field end type - type(type1), allocatable :: pvar; - type(type1), allocatable :: qvar; + type(type1) :: pvar; + type(type1) :: qvar; integer, allocatable, dimension(:) :: array1 integer, allocatable, dimension(:) :: array2 - integer, allocatable, codimension[*] :: ca + integer, allocatable, codimension[*] :: ca, cb + integer, allocatable :: aa, ab ! All of the following are allowable outside a DO CONCURRENT - allocate(pvar) allocate(array1(3), pvar%type1_field%type0_field(3), array2(9)) allocate(pvar%type1_field%coarray_type0_field(3)[*]) allocate(ca[*]) - allocate(pvar, ca[*], qvar, pvar%type1_field%coarray_type0_field(3)[*]) + allocate(ca[*], pvar%type1_field%coarray_type0_field(3)[*]) do concurrent (i = 1:10) - allocate(pvar%type1_field%type0_field(3)) + allocate(pvar%type1_field%type0_field(3)) end do do concurrent (i = 1:10) -!ERROR: ALLOCATE coarray not allowed in DO CONCURRENT +!ERROR: An image control statement is not allowed in DO CONCURRENT allocate(ca[*]) end do do concurrent (i = 1:10) -!ERROR: DEALLOCATE coarray not allowed in DO CONCURRENT +!ERROR: An image control statement is not allowed in DO CONCURRENT deallocate(ca) end do do concurrent (i = 1:10) -!ERROR: ALLOCATE coarray not allowed in DO CONCURRENT - allocate(pvar%type1_field%coarray_type0_field(3)[*]) +!ERROR: An image control statement is not allowed in DO CONCURRENT + allocate(pvar%type1_field%coarray_type0_field(3)[*]) end do do concurrent (i = 1:10) -!ERROR: DEALLOCATE coarray not allowed in DO CONCURRENT - deallocate(pvar%type1_field%coarray_type0_field) +!ERROR: An image control statement is not allowed in DO CONCURRENT + deallocate(pvar%type1_field%coarray_type0_field) end do do concurrent (i = 1:10) -!ERROR: ALLOCATE coarray not allowed in DO CONCURRENT -!ERROR: ALLOCATE coarray not allowed in DO CONCURRENT - allocate(pvar, ca[*], qvar, pvar%type1_field%coarray_type0_field(3)[*]) +!ERROR: An image control statement is not allowed in DO CONCURRENT + allocate(ca[*], pvar%type1_field%coarray_type0_field(3)[*]) end do do concurrent (i = 1:10) -!ERROR: DEALLOCATE coarray not allowed in DO CONCURRENT -!ERROR: DEALLOCATE coarray not allowed in DO CONCURRENT - deallocate(pvar, ca, qvar, pvar%type1_field%coarray_type0_field) +!ERROR: An image control statement is not allowed in DO CONCURRENT + deallocate(ca, pvar%type1_field%coarray_type0_field) + end do + +! Call to MOVE_ALLOC of a coarray outside a DO CONCURRENT. This is OK. +call move_alloc(ca, cb) + +! Note that the errors below relating to MOVE_ALLOC() bing impure are bogus. +! They're the result of the fact that access to the move_alloc() instrinsic +! is not yet possible. + + allocate(aa) + do concurrent (i = 1:10) +!ERROR: Call to an impure procedure is not allowed in DO CONCURRENT + call move_alloc(aa, ab) + end do + +! Call to MOVE_ALLOC with non-coarray arguments in a DO CONCURRENT. This is OK. + + do concurrent (i = 1:10) +!ERROR: Call to an impure procedure is not allowed in DO CONCURRENT +!ERROR: An image control statement is not allowed in DO CONCURRENT + call move_alloc(ca, cb) + end do + + do concurrent (i = 1:10) +!ERROR: Call to an impure procedure is not allowed in DO CONCURRENT +!ERROR: An image control statement is not allowed in DO CONCURRENT + call move_alloc(pvar%type1_field%coarray_type0_field, qvar%type1_field%coarray_type0_field) end do end subroutine s6 diff --git a/flang/test/semantics/doconcurrent03.f90 b/flang/test/semantics/doconcurrent03.f90 index 35e7d4a02ed1..b2ab092e7bd3 100644 --- a/flang/test/semantics/doconcurrent03.f90 +++ b/flang/test/semantics/doconcurrent03.f90 @@ -13,7 +13,7 @@ ! limitations under the License. ! RUN: ${F18} -funparse-with-symbols %s 2>&1 | ${FileCheck} %s -! CHECK: control flow escapes from DO CONCURRENT +! CHECK: Control flow escapes from DO CONCURRENT ! CHECK: branch into loop body from outside ! CHECK: the loop branched into diff --git a/flang/test/semantics/doconcurrent08.f90 b/flang/test/semantics/doconcurrent08.f90 deleted file mode 100644 index 765ed38384ff..000000000000 --- a/flang/test/semantics/doconcurrent08.f90 +++ /dev/null @@ -1,25 +0,0 @@ -! Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. -! -! Licensed under the Apache License, Version 2.0 (the "License"); -! you may not use this file except in compliance with the License. -! You may obtain a copy of the License at -! -! http://www.apache.org/licenses/LICENSE-2.0 -! -! Unless required by applicable law or agreed to in writing, software -! distributed under the License is distributed on an "AS IS" BASIS, -! WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -! See the License for the specific language governing permissions and -! limitations under the License. - -! RUN: ${F18} -funparse-with-symbols %s 2>&1 | ${FileCheck} %s -! CHECK: image control statement not allowed in DO CONCURRENT -! CHECK: SYNC ALL - -subroutine do_concurrent_test1(i,n) - implicit none - integer :: i, n - do 10 concurrent (i = 1:n) - SYNC ALL -10 continue -end subroutine do_concurrent_test1 diff --git a/flang/test/semantics/dosemantics02.f90 b/flang/test/semantics/dosemantics02.f90 index 858d53982c93..0f66b96b9bcc 100644 --- a/flang/test/semantics/dosemantics02.f90 +++ b/flang/test/semantics/dosemantics02.f90 @@ -17,7 +17,7 @@ SUBROUTINE do_concurrent_c1121(i,n) IMPLICIT NONE INTEGER :: i, n, flag -!ERROR: concurrent-header mask expression cannot reference an impure procedure +!ERROR: Concurrent-header mask expression cannot reference an impure procedure DO CONCURRENT (i = 1:n, random() < 3) flag = 3 END DO diff --git a/flang/test/semantics/dosemantics08.f90 b/flang/test/semantics/dosemantics08.f90 index f9b04e47f305..274bb28098aa 100644 --- a/flang/test/semantics/dosemantics08.f90 +++ b/flang/test/semantics/dosemantics08.f90 @@ -18,7 +18,7 @@ subroutine s1() do concurrent (i=1:10) -!ERROR: control flow escapes from DO CONCURRENT +!ERROR: Control flow escapes from DO CONCURRENT goto 99 end do