[flang-commits] [flang] [flang] Diagnose a labeled DO whose terminating statement is not in its block (PR #230484)

Eugene Epshteyn via flang-commits flang-commits at lists.llvm.org
Fri Oct 9 05:53:31 PDT 2026


https://github.com/eugeneepshteyn created https://github.com/llvm/llvm-project/pull/230484

CanonicalizeDo turns a labeled DO statement and the statements up to its
terminating statement into a DO construct, but it only matches statements in
the same block. When the terminating statement is in another block, the
labeled DO statement was silently left unconverted. This happens when the
terminating statement is inside an OpenACC or OpenMP construct but is not
the last statement of the construct's block, or when the DO statement is
inside a construct and the terminating statement follows it. Label analysis
accepts these cases, so the only diagnostic was an internal error for the
DO variable, which was never resolved:

```fortran
do 100 k = 1, m
!$acc parallel
!$acc loop
do 100 i = 1, n
  a(i) = a(i) + k
100 end do
a(1) = 0.0
!$acc end parallel
```

```console
error: Internal: no symbol found for 'k'
```

Report "Label '100' is not in DO loop scope" at the DO statement instead,
as label analysis does when the terminating statement is inside a BLOCK
construct.

Assisted-by: AI


>From 1bbea0704bc974fb35caa399162c45cd7bc30663 Mon Sep 17 00:00:00 2001
From: Eugene Epshteyn <eepshteyn at nvidia.com>
Date: Thu, 8 Oct 2026 20:30:25 -0700
Subject: [PATCH] [flang] Diagnose a labeled DO whose terminating statement is
 not in its block

CanonicalizeDo turns a labeled DO statement and the statements up to its
terminating statement into a DO construct, but it only matches statements in
the same block. When the terminating statement is in another block, the
labeled DO statement was silently left unconverted. This happens when the
terminating statement is inside an OpenACC or OpenMP construct but is not
the last statement of the construct's block, or when the DO statement is
inside a construct and the terminating statement follows it. Label analysis
accepts these cases, so the only diagnostic was an internal error for the
DO variable, which was never resolved:

  do 100 k = 1, m
  !$acc parallel
  !$acc loop
  do 100 i = 1, n
    a(i) = a(i) + k
  100 end do
  a(1) = 0.0
  !$acc end parallel

  error: Internal: no symbol found for 'k'

Report "Label '100' is not in DO loop scope" at the DO statement instead,
as label analysis does when the terminating statement is inside a BLOCK
construct.
---
 flang/lib/Semantics/canonicalize-do.cpp | 22 ++++++-
 flang/lib/Semantics/canonicalize-do.h   |  3 +-
 flang/lib/Semantics/semantics.cpp       |  2 +-
 flang/test/Semantics/label21.f90        | 78 +++++++++++++++++++++++++
 4 files changed, 100 insertions(+), 5 deletions(-)
 create mode 100644 flang/test/Semantics/label21.f90

diff --git a/flang/lib/Semantics/canonicalize-do.cpp b/flang/lib/Semantics/canonicalize-do.cpp
index ba4aeca1b08349..d0e3ed183f4554 100644
--- a/flang/lib/Semantics/canonicalize-do.cpp
+++ b/flang/lib/Semantics/canonicalize-do.cpp
@@ -7,6 +7,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "canonicalize-do.h"
+#include "flang/Parser/message.h"
 #include "flang/Parser/openmp-utils.h"
 #include "flang/Parser/parse-tree-visitor.h"
 #include "flang/Parser/tools.h"
@@ -20,6 +21,8 @@ class CanonicalizationOfDoLoops {
   };
 
 public:
+  explicit CanonicalizationOfDoLoops(Messages &messages)
+      : messages_{messages} {}
   template <typename T> bool Pre(T &) { return true; }
   template <typename T> void Post(T &) {}
   void Post(Block &block) {
@@ -107,6 +110,17 @@ class CanonicalizationOfDoLoops {
             executableConstruct->u);
       }
     }
+    // The terminating statement of a remaining labeled DO statement is not in
+    // this block, e.g. it is inside a construct but is not the last statement
+    // of the construct's block, or the DO statement is inside a construct and
+    // the terminating statement follows it.
+    for (const LabelInfo &info : stack) {
+      const auto &labelDo{std::get<Statement<common::Indirection<LabelDoStmt>>>(
+          std::get<ExecutableConstruct>(info.iter->u).u)};
+      messages_.Say(labelDo.source,
+          "Label '%u' is not in DO loop scope"_err_en_US,
+          static_cast<unsigned>(info.label));
+    }
   }
 
 private:
@@ -170,12 +184,14 @@ class CanonicalizationOfDoLoops {
         },
         omp.u);
   }
+
+  Messages &messages_;
 };
 
-bool CanonicalizeDo(Program &program) {
-  CanonicalizationOfDoLoops canonicalizationOfDoLoops;
+bool CanonicalizeDo(Messages &messages, Program &program) {
+  CanonicalizationOfDoLoops canonicalizationOfDoLoops{messages};
   Walk(program, canonicalizationOfDoLoops);
-  return true;
+  return !messages.AnyFatalError();
 }
 
 } // namespace Fortran::parser
diff --git a/flang/lib/Semantics/canonicalize-do.h b/flang/lib/Semantics/canonicalize-do.h
index bbdad8cedc23ac..962c6132e5edd0 100644
--- a/flang/lib/Semantics/canonicalize-do.h
+++ b/flang/lib/Semantics/canonicalize-do.h
@@ -13,7 +13,8 @@
 // logically nested) into the more structured DoConstruct (explicitly nested)
 namespace Fortran::parser {
 struct Program;
-bool CanonicalizeDo(Program &program);
+class Messages;
+bool CanonicalizeDo(Messages &messages, Program &program);
 } // namespace Fortran::parser
 
 #endif // FORTRAN_SEMANTICS_CANONICALIZE_DO_H_
diff --git a/flang/lib/Semantics/semantics.cpp b/flang/lib/Semantics/semantics.cpp
index 224f5bcb1c18ab..bcc96bb651bafd 100644
--- a/flang/lib/Semantics/semantics.cpp
+++ b/flang/lib/Semantics/semantics.cpp
@@ -846,7 +846,7 @@ bool Semantics::Perform() {
     }
   }
   if (!(AnalyzeLabels(context_, program_) &&
-          parser::CanonicalizeDo(program_) && // force line break
+          parser::CanonicalizeDo(context_.messages(), program_) &&
           CanonicalizeAcc(context_.messages(), program_) &&
           CanonicalizeOmp(context_, program_) && CanonicalizeCUDA(program_) &&
           PerformStatementSemantics(context_, program_) &&
diff --git a/flang/test/Semantics/label21.f90 b/flang/test/Semantics/label21.f90
new file mode 100644
index 00000000000000..39ed6fd42c1e0e
--- /dev/null
+++ b/flang/test/Semantics/label21.f90
@@ -0,0 +1,78 @@
+! RUN: %python %S/test_errors.py %s %flang -fopenacc -fopenmp
+
+! A labeled DO statement whose terminating statement is not in the same block
+! cannot be converted into a DO construct.
+
+subroutine outer_ends_inside_acc(a, n, m)
+  integer :: n, m, i, k
+  real :: a(n)
+  !ERROR: Label '100' is not in DO loop scope
+  do 100 k = 1, m
+  !$acc parallel
+  !$acc loop
+  do 100 i = 1, n
+    a(i) = a(i) + k
+100 end do
+  a(1) = 0.0
+  !$acc end parallel
+end subroutine
+
+subroutine outer_ends_inside_omp(a, n, m)
+  integer :: n, m, i, k
+  real :: a(n)
+  !ERROR: Label '200' is not in DO loop scope
+  do 200 k = 1, m
+  !$omp parallel
+  do 200 i = 1, n
+    a(i) = a(i) + k
+200 continue
+  a(1) = 0.0
+  !$omp end parallel
+end subroutine
+
+subroutine starts_inside_block(a, n)
+  integer :: n, i
+  real :: a(n)
+  block
+  !ERROR: Label '300' is not in DO loop scope
+  do 300 i = 1, n
+    a(i) = 0
+  end block
+300 continue
+end subroutine
+
+subroutine starts_inside_acc(a, n)
+  integer :: n, i
+  real :: a(n)
+  !$acc parallel
+  !ERROR: Label '400' is not in DO loop scope
+  do 400 i = 1, n
+    a(i) = 0
+  !$acc end parallel
+400 continue
+end subroutine
+
+! A labeled DO statement terminated by the last statement of the block of an
+! OpenACC or OpenMP construct encloses the construct.
+
+subroutine outer_ends_last_in_acc(a, n, m)
+  integer :: n, m, i, k
+  real :: a(n)
+  do 500 k = 1, m
+  !$acc data copy(a)
+  do 500 i = 1, n
+    a(i) = a(i) + k
+500 continue
+  !$acc end data
+end subroutine
+
+subroutine outer_ends_last_in_omp(a, n, m)
+  integer :: n, m, i, k
+  real :: a(n)
+  do 600 k = 1, m
+  !$omp parallel
+  do 600 i = 1, n
+    a(i) = a(i) + k
+600 continue
+  !$omp end parallel
+end subroutine



More information about the flang-commits mailing list