diff --git a/flang/docs/OpenACC.md b/flang/docs/OpenACC.md index c63fcd9a9daa0..79b7e3000b02a 100644 --- a/flang/docs/OpenACC.md +++ b/flang/docs/OpenACC.md @@ -17,3 +17,4 @@ * The end directive for combined construct can omit the `loop` keyword. * An `!$acc routine` with no parallelism clause is treated as if the `seq` clause was present. +* `!$acc end loop` does not trigger a parsing error and is just ignored. diff --git a/flang/include/flang/Parser/dump-parse-tree.h b/flang/include/flang/Parser/dump-parse-tree.h index 97e93dae84df3..7a009e8cc7082 100644 --- a/flang/include/flang/Parser/dump-parse-tree.h +++ b/flang/include/flang/Parser/dump-parse-tree.h @@ -105,6 +105,7 @@ class ParseTreeDumper { NODE(parser, AccTileExpr) NODE(parser, AccTileExprList) NODE(parser, AccLoopDirective) + NODE(parser, AccEndLoop) NODE(parser, AccWaitArgument) static std::string GetNodeName(const llvm::acc::Directive &x) { return llvm::Twine( diff --git a/flang/include/flang/Parser/parse-tree.h b/flang/include/flang/Parser/parse-tree.h index e70f5aeae117e..d8449c8b812ae 100644 --- a/flang/include/flang/Parser/parse-tree.h +++ b/flang/include/flang/Parser/parse-tree.h @@ -4244,11 +4244,10 @@ struct OpenACCDeclarativeConstruct { }; // OpenACC directives enclosing do loop +EMPTY_CLASS(AccEndLoop); struct OpenACCLoopConstruct { TUPLE_CLASS_BOILERPLATE(OpenACCLoopConstruct); - OpenACCLoopConstruct(AccBeginLoopDirective &&a) - : t({std::move(a), std::nullopt}) {} - std::tuple> t; + std::tuple> t; }; struct OpenACCStandaloneConstruct { diff --git a/flang/lib/Parser/openacc-parsers.cpp b/flang/lib/Parser/openacc-parsers.cpp index dff95decad3f4..afa12b88019bd 100644 --- a/flang/lib/Parser/openacc-parsers.cpp +++ b/flang/lib/Parser/openacc-parsers.cpp @@ -150,8 +150,13 @@ TYPE_PARSER(sourced(construct( TYPE_PARSER(construct( sourced(Parser{}), Parser{})) -TYPE_PARSER( - construct(sourced(Parser{}))) +TYPE_PARSER(construct(startAccLine >> "END LOOP"_tok)) + +TYPE_PARSER(construct( + sourced(Parser{} / endAccLine), + withMessage("A DO loop must follow the loop construct"_err_en_US, + Parser{}), + maybe(Parser{} / endAccLine))) // 2.15.1 Routine directive TYPE_PARSER(sourced(construct(verbatim("ROUTINE"_tok), diff --git a/flang/lib/Parser/unparse.cpp b/flang/lib/Parser/unparse.cpp index d86049886fc32..d7626c0ea7629 100644 --- a/flang/lib/Parser/unparse.cpp +++ b/flang/lib/Parser/unparse.cpp @@ -1947,7 +1947,7 @@ class UnparseVisitor { Walk(std::get(x.t)); Put("\n"); EndOpenACC(); - Walk(std::get>(x.t)); + Walk(std::get(x.t)); } void Unparse(const AccBeginLoopDirective &x) { Walk(std::get(x.t)); diff --git a/flang/lib/Semantics/canonicalize-acc.cpp b/flang/lib/Semantics/canonicalize-acc.cpp index 5ad8257bec694..e79ab997637b0 100644 --- a/flang/lib/Semantics/canonicalize-acc.cpp +++ b/flang/lib/Semantics/canonicalize-acc.cpp @@ -52,7 +52,8 @@ class CanonicalizationOfAcc { // If there are n tile sizes in the list, the loop construct must be // immediately followed by n tightly-nested loops. template - void CheckTileClauseRestriction(const C &x) { + void CheckTileClauseRestriction( + const C &x, const parser::DoConstruct &outer) { const auto &beginLoopDirective = std::get(x.t); const auto &accClauseList = std::get(beginLoopDirective.t); @@ -63,11 +64,10 @@ class CanonicalizationOfAcc { const std::list &listTileExpr = tileExprList.v; std::size_t tileArgNb = listTileExpr.size(); - const auto &outer{std::get>(x.t)}; - if (outer->IsDoConcurrent()) { + if (outer.IsDoConcurrent()) { return; // Tile is not allowed on DO CONCURRENT } - for (const parser::DoConstruct *loop{&*outer}; loop && tileArgNb > 0; + for (const parser::DoConstruct *loop{&outer}; loop && tileArgNb > 0; --tileArgNb) { const auto &block{std::get(loop->t)}; const auto it{block.begin()}; @@ -89,9 +89,9 @@ class CanonicalizationOfAcc { // A tile and collapse clause may not appear on loop that is associated with // do concurrent. template - void CheckDoConcurrentClauseRestriction(const C &x) { - const auto &doCons{std::get>(x.t)}; - if (!doCons->IsDoConcurrent()) { + void CheckDoConcurrentClauseRestriction( + const C &x, const parser::DoConstruct &doCons) { + if (!doCons.IsDoConcurrent()) { return; } const auto &beginLoopDirective = std::get(x.t); @@ -109,73 +109,36 @@ class CanonicalizationOfAcc { void RewriteOpenACCLoopConstruct(parser::OpenACCLoopConstruct &x, parser::Block &block, parser::Block::iterator it) { - // Check the sequence of DoConstruct in the same iteration - // - // Original: - // ExecutableConstruct -> OpenACCConstruct -> OpenACCLoopConstruct - // ACCBeginLoopDirective - // ExecutableConstruct -> DoConstruct - // - // After rewriting: - // ExecutableConstruct -> OpenACCConstruct -> OpenACCLoopConstruct - // AccBeginLoopDirective - // DoConstruct - parser::Block::iterator nextIt; auto &beginDir{std::get(x.t)}; auto &dir{std::get(beginDir.t)}; + const auto &doCons{std::get(x.t)}; - nextIt = it; - if (++nextIt != block.end()) { - if (auto *doCons{parser::Unwrap(*nextIt)}) { - if (!doCons->GetLoopControl()) { - messages_.Say(dir.source, - "DO loop after the %s directive must have loop control"_err_en_US, - parser::ToUpperCaseLetters(dir.source.ToString())); - return; - } - - // move DoConstruct - std::get>(x.t) = std::move(*doCons); - nextIt = block.erase(nextIt); - - CheckDoConcurrentClauseRestriction(x); - CheckTileClauseRestriction(x); - - return; // found do-loop - } + if (!doCons.GetLoopControl()) { + messages_.Say(dir.source, + "DO loop after the %s directive must have loop control"_err_en_US, + parser::ToUpperCaseLetters(dir.source.ToString())); + return; } - messages_.Say(dir.source, - "A DO loop must follow the %s directive"_err_en_US, - parser::ToUpperCaseLetters(dir.source.ToString())); + + CheckDoConcurrentClauseRestriction(x, doCons); + CheckTileClauseRestriction(x, doCons); } void RewriteOpenACCCombinedConstruct(parser::OpenACCCombinedConstruct &x, parser::Block &block, parser::Block::iterator it) { - // Check the sequence of DoConstruct in the same iteration - // - // Original: - // ExecutableConstruct -> OpenACCConstruct -> OpenACCCombinedConstruct - // ACCBeginCombinedDirective - // ExecutableConstruct -> DoConstruct - // ExecutableConstruct -> AccEndCombinedDirective (if available) - // - // After rewriting: - // ExecutableConstruct -> OpenACCConstruct -> OpenACCCombinedConstruct - // ACCBeginCombinedDirective - // DoConstruct - // AccEndCombinedDirective (if available) + // Check the sequence of DoConstruct in the same iteration. parser::Block::iterator nextIt; auto &beginDir{std::get(x.t)}; auto &dir{std::get(beginDir.t)}; - auto &doConstruct{std::get>(x.t)}; + const auto &doConstruct{std::get>(x.t)}; if (doConstruct) { CheckDoConcurrentClauseRestriction(x); + parser::AccBeginCombinedDirective>(x, *doConstruct); CheckTileClauseRestriction(x); + parser::AccBeginCombinedDirective>(x, *doConstruct); if (!doConstruct->GetLoopControl()) { messages_.Say(dir.source, "DO loop after the %s directive must have loop control"_err_en_US, diff --git a/flang/lib/Semantics/resolve-directives.cpp b/flang/lib/Semantics/resolve-directives.cpp index 83c44a556d19f..c60c693072f49 100644 --- a/flang/lib/Semantics/resolve-directives.cpp +++ b/flang/lib/Semantics/resolve-directives.cpp @@ -1088,8 +1088,8 @@ void AccAttributeVisitor::PrivatizeAssociatedLoopIndex( return nullptr; }; - const auto &outer{std::get>(x.t)}; - for (const parser::DoConstruct *loop{&*outer}; loop && level > 0; --level) { + const auto &outer{std::get(x.t)}; + for (const parser::DoConstruct *loop{&outer}; loop && level > 0; --level) { // go through all the nested do-loops and resolve index variables const parser::Name *iv{GetLoopIndex(*loop)}; if (iv) { diff --git a/flang/test/Semantics/OpenACC/acc-canonicalization-validity.f90 b/flang/test/Semantics/OpenACC/acc-canonicalization-validity.f90 index b431e4fb06850..ced7dd46803ee 100644 --- a/flang/test/Semantics/OpenACC/acc-canonicalization-validity.f90 +++ b/flang/test/Semantics/OpenACC/acc-canonicalization-validity.f90 @@ -15,9 +15,7 @@ program openacc_clause_validity real(8) :: a(256) real(8) :: aa(256, 256) - !ERROR: A DO loop must follow the LOOP directive - !$acc loop - i = 1 + i = 0 !ERROR: DO loop after the LOOP directive must have loop control !$acc loop diff --git a/flang/test/Semantics/OpenACC/acc-loop-validity.f90 b/flang/test/Semantics/OpenACC/acc-loop-validity.f90 new file mode 100644 index 0000000000000..906602640efac --- /dev/null +++ b/flang/test/Semantics/OpenACC/acc-loop-validity.f90 @@ -0,0 +1,15 @@ +! RUN: %python %S/../test_errors.py %s %flang -fopenacc + +program openacc_clause_validity + + implicit none + + integer :: i + + i = 0 + + !$acc loop + !ERROR: A DO loop must follow the loop construct + i = 1 + +end diff --git a/flang/test/Semantics/OpenACC/acc-loop.f90 b/flang/test/Semantics/OpenACC/acc-loop.f90 index 349ac5bd2be1c..1c0a515441f43 100644 --- a/flang/test/Semantics/OpenACC/acc-loop.f90 +++ b/flang/test/Semantics/OpenACC/acc-loop.f90 @@ -263,4 +263,9 @@ program openacc_loop_validity do i = 1, N end do + !$acc loop + do i = 1, N + end do + !$acc end loop + end program openacc_loop_validity