[flang-commits] [flang] [flang][OpenMP] Fix subtle bug in GetAffectedNestDepthWithReason (PR #190645)
Krzysztof Parzyszek via flang-commits
flang-commits at lists.llvm.org
Tue Apr 7 05:50:24 PDT 2026
https://github.com/kparzysz updated https://github.com/llvm/llvm-project/pull/190645
>From 87a4c8d9384b4454a88c6d75d0cab148b5fde8d9 Mon Sep 17 00:00:00 2001
From: Krzysztof Parzyszek <Krzysztof.Parzyszek at amd.com>
Date: Mon, 6 Apr 2026 10:18:06 -0500
Subject: [PATCH 1/3] [flang][OpenMP] Use OmpDirectiveSpecifications in helper
functions
This will make them more reusable, for example when processing APPLY
clause in the future.
Issue: https://github.com/llvm/llvm-project/issues/185287
---
flang/include/flang/Semantics/openmp-utils.h | 6 +-
flang/lib/Semantics/openmp-utils.cpp | 99 +++++++++-----------
2 files changed, 48 insertions(+), 57 deletions(-)
diff --git a/flang/include/flang/Semantics/openmp-utils.h b/flang/include/flang/Semantics/openmp-utils.h
index c29cc4e3a33e2..6a41ada9067c5 100644
--- a/flang/include/flang/Semantics/openmp-utils.h
+++ b/flang/include/flang/Semantics/openmp-utils.h
@@ -113,6 +113,9 @@ bool IsPointerAssignment(const evaluate::Assignment &x);
MaybeExpr MakeEvaluateExpr(const parser::OmpStylizedInstance &inp);
+bool IsLoopTransforming(llvm::omp::Directive dir);
+bool IsFullUnroll(const parser::OmpDirectiveSpecification &spec);
+
/// A representation of a "because" message.
struct Reason {
Reason() = default;
@@ -157,9 +160,6 @@ WithReason<int64_t> GetNumArgumentsWithReason(
const parser::OmpDirectiveSpecification &spec, llvm::omp::Clause clauseId,
unsigned version);
-bool IsLoopTransforming(llvm::omp::Directive dir);
-bool IsFullUnroll(const parser::OpenMPLoopConstruct &x);
-
// Return the depth of the affected nests:
// {affected-depth, reason, must-be-perfect-nest}.
std::pair<WithReason<int64_t>, bool> GetAffectedNestDepthWithReason(
diff --git a/flang/lib/Semantics/openmp-utils.cpp b/flang/lib/Semantics/openmp-utils.cpp
index e9548816fc665..33707f9d0d95b 100644
--- a/flang/lib/Semantics/openmp-utils.cpp
+++ b/flang/lib/Semantics/openmp-utils.cpp
@@ -554,6 +554,44 @@ MaybeExpr MakeEvaluateExpr(const parser::OmpStylizedInstance &inp) {
instance.u);
}
+bool IsLoopTransforming(llvm::omp::Directive dir) {
+ switch (dir) {
+ // TODO case llvm::omp::Directive::OMPD_flatten:
+ case llvm::omp::Directive::OMPD_fuse:
+ case llvm::omp::Directive::OMPD_interchange:
+ case llvm::omp::Directive::OMPD_nothing:
+ case llvm::omp::Directive::OMPD_reverse:
+ // TODO case llvm::omp::Directive::OMPD_split:
+ case llvm::omp::Directive::OMPD_stripe:
+ case llvm::omp::Directive::OMPD_tile:
+ case llvm::omp::Directive::OMPD_unroll:
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool IsFullUnroll(const parser::OmpDirectiveSpecification &spec) {
+ if (spec.DirId() == llvm::omp::Directive::OMPD_unroll) {
+ return !parser::omp::FindClause(spec, llvm::omp::Clause::OMPC_partial);
+ }
+ return false;
+}
+
+static bool IsTransformableLoop(const parser::OmpDirectiveSpecification &spec) {
+ return !IsFullUnroll(spec) && IsLoopTransforming(spec.DirId());
+}
+
+static bool IsTransformableLoop(const parser::ExecutionPartConstruct &epc) {
+ if (auto *loop{parser::Unwrap<parser::DoConstruct>(epc)}) {
+ return loop->IsDoNormal();
+ }
+ if (auto *omp{parser::Unwrap<parser::OpenMPLoopConstruct>(epc)}) {
+ return IsTransformableLoop(omp->BeginDir());
+ }
+ return false;
+}
+
static const auto MsgNotValidAffectedLoop{
"%s is not a valid affected loop"_because_en_US};
static const auto MsgClauseAbsentAssume{
@@ -638,33 +676,6 @@ WithReason<int64_t> GetNumArgumentsWithReason(
return {};
}
-bool IsLoopTransforming(llvm::omp::Directive dir) {
- switch (dir) {
- // TODO case llvm::omp::Directive::OMPD_flatten:
- case llvm::omp::Directive::OMPD_fuse:
- case llvm::omp::Directive::OMPD_interchange:
- case llvm::omp::Directive::OMPD_nothing:
- case llvm::omp::Directive::OMPD_reverse:
- // TODO case llvm::omp::Directive::OMPD_split:
- case llvm::omp::Directive::OMPD_stripe:
- case llvm::omp::Directive::OMPD_tile:
- case llvm::omp::Directive::OMPD_unroll:
- return true;
- default:
- return false;
- }
-}
-
-bool IsFullUnroll(const parser::OpenMPLoopConstruct &x) {
- const parser::OmpDirectiveSpecification &beginSpec{x.BeginDir()};
-
- if (beginSpec.DirName().v == llvm::omp::Directive::OMPD_unroll) {
- return parser::omp::FindClause(
- beginSpec, llvm::omp::Clause::OMPC_partial) == nullptr;
- }
- return false;
-}
-
namespace {
// Helper class to check if a given evaluate::Expr is an array expression.
// This does not check any proper subexpressions of the expression (except
@@ -812,27 +823,6 @@ bool IsTransparentInterveningCode(const parser::ExecutionPartConstruct &x) {
parser::Unwrap<parser::ContinueStmt>(x);
}
-bool IsTransformableLoop(const parser::DoConstruct &loop) {
- return loop.IsDoNormal();
-}
-
-bool IsTransformableLoop(const parser::OpenMPLoopConstruct &omp) {
- if (IsFullUnroll(omp)) {
- return false;
- }
- return IsLoopTransforming(omp.BeginDir().DirId());
-}
-
-bool IsTransformableLoop(const parser::ExecutionPartConstruct &epc) {
- if (auto *loop{parser::Unwrap<parser::DoConstruct>(epc)}) {
- return IsTransformableLoop(*loop);
- }
- if (auto *omp{parser::Unwrap<parser::OpenMPLoopConstruct>(epc)}) {
- return IsTransformableLoop(*omp);
- }
- return false;
-}
-
template <typename T,
typename = std::enable_if_t<std::is_arithmetic_v<llvm::remove_cvref_t<T>>>>
WithReason<T> operator+(const WithReason<T> &a, const WithReason<T> &b) {
@@ -1057,7 +1047,7 @@ LoopSequence::LoopSequence(
std::unique_ptr<LoopSequence::Construct> LoopSequence::createConstructEntry(
const parser::ExecutionPartConstruct &code) {
if (auto *loop{parser::Unwrap<parser::DoConstruct>(code)}) {
- if (allowAllLoops_ || IsTransformableLoop(*loop)) {
+ if (allowAllLoops_ || IsTransformableLoop(code)) {
auto &body{std::get<parser::Block>(loop->t)};
return std::make_unique<Construct>(body, &code);
}
@@ -1126,7 +1116,7 @@ WithReason<int64_t> LoopSequence::calculateLength() const {
}
// TODO: Handle split, apply.
- if (IsFullUnroll(omp)) {
+ if (IsFullUnroll(beginSpec)) {
return {};
}
@@ -1250,10 +1240,10 @@ LoopSequence::Depth LoopSequence::calculateDepths() const {
auto &omp{DEREF(parser::Unwrap<parser::OpenMPLoopConstruct>(*entry_->owner))};
const parser::OmpDirectiveSpecification &beginSpec{omp.BeginDir()};
llvm::omp::Directive dir{beginSpec.DirId()};
- bool isFullUnroll{IsFullUnroll(omp)};
+ bool isFullUnroll{IsFullUnroll(beginSpec)};
// Check full unroll separately.
- if (!isFullUnroll && !IsTransformableLoop(omp)) {
+ if (!isFullUnroll && !IsTransformableLoop(beginSpec)) {
Reason reason;
reason.Say(beginSpec.DirName().source,
"This construct is not a DO-loop or a loop-nest-generating construct"_because_en_US);
@@ -1368,10 +1358,11 @@ static Reason WhyNotWellFormed(
Reason reason;
parser::CharBlock source{*parser::GetSource(badCode)};
if (auto *omp{parser::Unwrap<parser::OpenMPLoopConstruct>(badCode)}) {
- if (IsFullUnroll(*omp)) {
+ const parser::OmpDirectiveSpecification &beginSpec{omp->BeginDir()};
+ if (IsFullUnroll(beginSpec)) {
reason.Say(source, MsgConstructDoesNotResult, "Fully unrolled loop",
isSequence ? "a loop nest or a loop sequence" : "a loop nest");
- } else if (!IsLoopTransforming(omp->BeginDir().DirId())) {
+ } else if (!IsLoopTransforming(beginSpec.DirId())) {
reason.Say(source,
"Only loop-transforming constructs are allowed inside loop constructs"_because_en_US);
}
>From 22d5d5a866b82eff70457312c9b6cf65afb5226c Mon Sep 17 00:00:00 2001
From: Krzysztof Parzyszek <Krzysztof.Parzyszek at amd.com>
Date: Mon, 6 Apr 2026 11:27:49 -0500
Subject: [PATCH 2/3] [flang][OpenMP] Fix subtle bug in
GetAffectedNestDepthWithReason
For constructs that allow COLLAPSE or ORDERED clauses, the function
would return an empty value for the affected depth if none of these
clauses were actually present. What should happen is that the return
value should be 1 without a specific reason.
This bug was not detectable with any source program, since the empty
value caused depth checks to be skipped. Detecting the problem would
require a loop nest with a lower depth than needed that the bug would
cause not to be diagnosed. Since the correct value was 1, such a loop
would need to have a depth of 0 and such a nest cannot be constructed.
Issue: https://github.com/llvm/llvm-project/issues/185287
---
flang/lib/Semantics/openmp-utils.cpp | 23 +++++++++++++++--------
1 file changed, 15 insertions(+), 8 deletions(-)
diff --git a/flang/lib/Semantics/openmp-utils.cpp b/flang/lib/Semantics/openmp-utils.cpp
index 33707f9d0d95b..1d9de49e6cd8e 100644
--- a/flang/lib/Semantics/openmp-utils.cpp
+++ b/flang/lib/Semantics/openmp-utils.cpp
@@ -850,17 +850,24 @@ std::pair<WithReason<int64_t>, bool> GetAffectedNestDepthWithReason(
dir, llvm::omp::Clause::OMPC_ordered, version)};
if (allowsCollapse || allowsOrdered) {
- auto [count, reason]{GetArgumentValueWithReason(
+ auto [ccount, creason]{GetArgumentValueWithReason(
spec, llvm::omp::Clause::OMPC_collapse, version)};
- auto [vo, ro]{GetArgumentValueWithReason(
+ auto [ocount, oreason]{GetArgumentValueWithReason(
spec, llvm::omp::Clause::OMPC_ordered, version)};
- if (vo) {
- if (!count || *count < *vo) {
- count = vo;
- reason = std::move(ro);
- }
+ // Ignore invalid arguments.
+ if (ccount <= 0) {
+ ccount = std::nullopt;
+ creason = Reason();
+ }
+ if (ocount <= 0) {
+ ocount = std::nullopt;
+ oreason = Reason();
+ }
+ if (ccount < ocount) {
+ // `ocount` cannot be std::nullopt here (C++ std guarantee).
+ return {{ocount.value_or(1), std::move(oreason)}, true};
}
- return {{count, std::move(reason)}, true};
+ return {{ccount.value_or(1), std::move(creason)}, true};
}
if (IsLoopTransforming(dir)) {
>From 8bfcd37abeab257ec91c86790a9bf7b1e8b96da3 Mon Sep 17 00:00:00 2001
From: Krzysztof Parzyszek <Krzysztof.Parzyszek at amd.com>
Date: Tue, 7 Apr 2026 07:40:10 -0500
Subject: [PATCH 3/3] Add unit tests for GetAffectedNestDepthWithReason
---
flang/unittests/CMakeLists.txt | 1 +
flang/unittests/Semantics/CMakeLists.txt | 32 +++
flang/unittests/Semantics/OpenMPUtils.cpp | 259 ++++++++++++++++++++++
3 files changed, 292 insertions(+)
create mode 100644 flang/unittests/Semantics/CMakeLists.txt
create mode 100644 flang/unittests/Semantics/OpenMPUtils.cpp
diff --git a/flang/unittests/CMakeLists.txt b/flang/unittests/CMakeLists.txt
index 2d612e58dae24..64cd5c0446474 100644
--- a/flang/unittests/CMakeLists.txt
+++ b/flang/unittests/CMakeLists.txt
@@ -60,3 +60,4 @@ add_subdirectory(Common)
add_subdirectory(Decimal)
add_subdirectory(Evaluate)
add_subdirectory(Frontend)
+add_subdirectory(Semantics)
diff --git a/flang/unittests/Semantics/CMakeLists.txt b/flang/unittests/Semantics/CMakeLists.txt
new file mode 100644
index 0000000000000..ea090b887d717
--- /dev/null
+++ b/flang/unittests/Semantics/CMakeLists.txt
@@ -0,0 +1,32 @@
+set(LLVM_LINK_COMPONENTS
+ ${LLVM_TARGETS_TO_BUILD}
+ Core
+ FrontendOpenACC
+ FrontendOpenMP
+ TargetParser
+)
+
+add_flang_unittest(OpenMPUtilsTests
+ OpenMPUtils.cpp
+)
+
+target_link_libraries(OpenMPUtilsTests
+ PRIVATE
+ flangFrontend
+ flangFrontendTool
+ FortranLower
+ FortranParser
+ FortranSemantics
+ FortranSupport
+ FortranEvaluate
+)
+
+clang_target_link_libraries(OpenMPUtilsTests
+ PRIVATE
+ clangBasic
+)
+
+mlir_target_link_libraries(OpenMPUtilsTests
+ PRIVATE
+ MLIRIR
+)
diff --git a/flang/unittests/Semantics/OpenMPUtils.cpp b/flang/unittests/Semantics/OpenMPUtils.cpp
new file mode 100644
index 0000000000000..32664388ef4d0
--- /dev/null
+++ b/flang/unittests/Semantics/OpenMPUtils.cpp
@@ -0,0 +1,259 @@
+//===- unittests/Semantics/OpenMPUtils.cpp OpenMP utilities tests --------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "flang/Frontend/CompilerInstance.h"
+#include "flang/Frontend/CompilerInvocation.h"
+#include "flang/Frontend/FrontendOptions.h"
+#include "flang/FrontendTool/Utils.h"
+#include "flang/Parser/parse-tree.h"
+#include "flang/Parser/parsing.h"
+#include "flang/Semantics/openmp-utils.h"
+#include "flang/Semantics/semantics.h"
+
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/TargetSelect.h"
+#include "llvm/Support/raw_ostream.h"
+#include "llvm/TargetParser/Host.h"
+#include "llvm/TargetParser/Triple.h"
+
+#include "gtest/gtest.h"
+
+using namespace Fortran;
+using namespace Fortran::frontend;
+
+namespace {
+
+// This is a copy of FrontendActionTest.
+
+class OpenMPUtilsTest : public ::testing::Test {
+protected:
+ // AllSources (which is used to manage files inside every compiler
+ // instance), works with paths. So we need a filename and a path for the
+ // input file.
+ // TODO: We could use `-` for inputFilePath, but then we'd need a way to
+ // write to stdin that's then read by AllSources. Ideally, AllSources should
+ // be capable of reading from any stream.
+ std::string inputFileName;
+ std::string inputFilePath;
+ // The output stream for the input file. Use this to populate the input.
+ std::unique_ptr<llvm::raw_fd_ostream> inputFileOs;
+
+ std::error_code ec;
+
+ CompilerInstance compInst;
+ std::shared_ptr<CompilerInvocation> invoc;
+
+ void SetUp() override {
+ // Generate a unique test file name.
+ const testing::TestInfo *const testInfo =
+ testing::UnitTest::GetInstance()->current_test_info();
+ inputFileName = std::string(testInfo->name()) + "_test-file.f90";
+
+ // Create the input file stream. Note that this stream is populated
+ // separately in every test (i.e. the input is test specific).
+ inputFileOs = std::make_unique<llvm::raw_fd_ostream>(
+ inputFileName, ec, llvm::sys::fs::OF_None);
+ if (ec)
+ FAIL() << "Failed to create the input file";
+
+ // Get the path of the input file.
+ llvm::SmallString<256> cwd;
+ if (std::error_code ec = llvm::sys::fs::current_path(cwd))
+ FAIL() << "Failed to obtain the current working directory";
+ inputFilePath = cwd.c_str();
+ inputFilePath += "/" + inputFileName;
+
+ // Prepare the compiler (CompilerInvocation + CompilerInstance)
+ compInst.createDiagnostics();
+ invoc = std::make_shared<CompilerInvocation>();
+
+ // Set-up default target triple and initialize LLVM Targets so that the
+ // target data layout can be passed to the frontend.
+ invoc->getTargetOpts().triple =
+ llvm::Triple::normalize(llvm::sys::getDefaultTargetTriple());
+ invoc->getLangOpts().OpenMPVersion = 60;
+ llvm::InitializeAllTargets();
+ llvm::InitializeAllTargetMCs();
+
+ compInst.setInvocation(std::move(invoc));
+ compInst.getFrontendOpts().inputs.push_back(
+ FrontendInputFile(inputFilePath, Language::Fortran));
+ compInst.getFrontendOpts().features.Enable(common::LanguageFeature::OpenMP);
+ }
+
+ void TearDown() override {
+ // Clear the input file.
+ llvm::sys::fs::remove(inputFileName);
+
+ // Clear the output files.
+ // Note that these tests use an output buffer (as opposed to an output
+ // file), hence there are no physical output files to delete and
+ // `EraseFiles` is set to `false`. Also, some actions (e.g.
+ // `ParseSyntaxOnly`) don't generated output. In such cases there's no
+ // output to clear and `ClearOutputFile` returns immediately.
+ compInst.clearOutputFiles(/*EraseFiles=*/false);
+ }
+};
+
+TEST_F(OpenMPUtilsTest, AffectedNestDepthNoClauses) {
+ // Populate the input file with the pre-defined input and flush it.
+ *inputFileOs << R"(
+ integer :: i
+ !$omp do
+ do i = 1, 10
+ end do
+ end
+ )";
+ inputFileOs.reset();
+
+ // Set-up the action kind.
+ compInst.getInvocation().getFrontendOpts().programAction = ParseSyntaxOnly;
+
+ // Set-up the output stream for the semantic diagnostics.
+ llvm::SmallVector<char, 256> outputDiagBuffer;
+ std::unique_ptr<llvm::raw_pwrite_stream> outputStream(
+ new llvm::raw_svector_ostream(outputDiagBuffer));
+ compInst.setSemaOutputStream(std::move(outputStream));
+
+ // Execute the action.
+ bool success = executeCompilerInvocation(&compInst);
+
+ std::optional<parser::Program> &parseTree{compInst.getParsing().parseTree()};
+ EXPECT_TRUE(parseTree.has_value());
+
+ if (parseTree) {
+ // clang-format off
+ // The AST for the test program is
+ // Program -> ProgramUnit -> MainProgram
+ // | SpecificationPart
+ // | | ImplicitPart ->
+ // | ExecutionPart -> Block
+ // | | ExecutionPartConstruct -> ExecutableConstruct -> OpenMPConstruct -> OpenMPLoopConstruct
+ // | | | OmpBeginLoopDirective
+ // | | | | OmpDirectiveName -> llvm::omp::Directive = do
+ // | | | | OmpClauseList ->
+ // | | | | Flags = {}
+ // | | | Block
+ // | | | | ExecutionPartConstruct -> ExecutableConstruct -> DoConstruct
+ // | | | | | NonLabelDoStmt
+ // | | | | | | LoopControl -> LoopBounds
+ // | | | | | | | Scalar -> Name = 'i'
+ // | | | | | | | Scalar -> Expr -> LiteralConstant -> IntLiteralConstant = '1'
+ // | | | | | | | Scalar -> Expr -> LiteralConstant -> IntLiteralConstant = '10'
+ // | | | | | Block
+ // | | | | | EndDoStmt ->
+ // | EndProgramStmt ->
+ // clang-format on
+ auto &mainProgram =
+ parser::UnwrapRef<parser::MainProgram>(parseTree->v.front());
+ auto &body = std::get<parser::ExecutionPart>(mainProgram.t).v;
+ auto &omp = parser::UnwrapRef<parser::OpenMPLoopConstruct>(body.front());
+ auto [depth, mustBePerfect] =
+ semantics::omp::GetAffectedNestDepthWithReason(omp.BeginDir(), 60);
+ EXPECT_TRUE(depth.value.has_value());
+ if (depth) {
+ EXPECT_EQ(*depth.value, 1);
+ }
+ }
+ // Validate the expected output.
+ EXPECT_TRUE(success);
+}
+
+TEST_F(OpenMPUtilsTest, AffectedNestDepthCollapse) {
+ // Populate the input file with the pre-defined input and flush it.
+ *inputFileOs << R"(
+ integer :: i, j
+ !$omp do collapse(2)
+ do i = 1, 10
+ do j = 1, 10
+ end do
+ end do
+ end
+ )";
+ inputFileOs.reset();
+
+ // Set-up the action kind.
+ compInst.getInvocation().getFrontendOpts().programAction = ParseSyntaxOnly;
+
+ // Set-up the output stream for the semantic diagnostics.
+ llvm::SmallVector<char, 256> outputDiagBuffer;
+ std::unique_ptr<llvm::raw_pwrite_stream> outputStream(
+ new llvm::raw_svector_ostream(outputDiagBuffer));
+ compInst.setSemaOutputStream(std::move(outputStream));
+
+ // Execute the action.
+ bool success = executeCompilerInvocation(&compInst);
+
+ std::optional<parser::Program> &parseTree{compInst.getParsing().parseTree()};
+ EXPECT_TRUE(parseTree.has_value());
+
+ if (parseTree) {
+ auto &mainProgram =
+ parser::UnwrapRef<parser::MainProgram>(parseTree->v.front());
+ auto &body = std::get<parser::ExecutionPart>(mainProgram.t).v;
+ auto &omp = parser::UnwrapRef<parser::OpenMPLoopConstruct>(body.front());
+ auto [depth, mustBePerfect] =
+ semantics::omp::GetAffectedNestDepthWithReason(omp.BeginDir(), 60);
+ EXPECT_TRUE(depth.value.has_value());
+ if (depth) {
+ EXPECT_EQ(*depth.value, 2);
+ }
+ }
+ // Validate the expected output.
+ EXPECT_TRUE(success);
+}
+
+TEST_F(OpenMPUtilsTest, AffectedNestDepthCollapseOrdered) {
+ // Populate the input file with the pre-defined input and flush it.
+ *inputFileOs << R"(
+ integer :: i, j, k, m
+ !$omp do collapse(2) ordered(3)
+ do i = 1, 10
+ do j = 1, 10
+ do k = 1, 10
+ do m = 1, 10
+ end do
+ end do
+ end do
+ end do
+ end
+ )";
+ inputFileOs.reset();
+
+ // Set-up the action kind.
+ compInst.getInvocation().getFrontendOpts().programAction = ParseSyntaxOnly;
+
+ // Set-up the output stream for the semantic diagnostics.
+ llvm::SmallVector<char, 256> outputDiagBuffer;
+ std::unique_ptr<llvm::raw_pwrite_stream> outputStream(
+ new llvm::raw_svector_ostream(outputDiagBuffer));
+ compInst.setSemaOutputStream(std::move(outputStream));
+
+ // Execute the action.
+ bool success = executeCompilerInvocation(&compInst);
+
+ std::optional<parser::Program> &parseTree{compInst.getParsing().parseTree()};
+ EXPECT_TRUE(parseTree.has_value());
+
+ if (parseTree) {
+ auto &mainProgram =
+ parser::UnwrapRef<parser::MainProgram>(parseTree->v.front());
+ auto &body = std::get<parser::ExecutionPart>(mainProgram.t).v;
+ auto &omp = parser::UnwrapRef<parser::OpenMPLoopConstruct>(body.front());
+ auto [depth, mustBePerfect] =
+ semantics::omp::GetAffectedNestDepthWithReason(omp.BeginDir(), 60);
+ EXPECT_TRUE(depth.value.has_value());
+ if (depth) {
+ EXPECT_EQ(*depth.value, 3);
+ }
+ }
+ // Validate the expected output.
+ EXPECT_TRUE(success);
+}
+
+} // namespace
More information about the flang-commits
mailing list