[flang-commits] [flang] [flang][Semantics] Warn on repeated do-variable in nested I/O implied DO (PR #198757)

Caroline Newcombe via flang-commits flang-commits at lists.llvm.org
Tue Jun 16 09:13:11 PDT 2026


https://github.com/cenewcombe updated https://github.com/llvm/llvm-project/pull/198757

>From 30e478dc553f6ad7bd7de0fbb950999ebfe172c9 Mon Sep 17 00:00:00 2001
From: Caroline Newcombe <caroline.newcombe at hpe.com>
Date: Tue, 19 May 2026 16:04:53 -0500
Subject: [PATCH] [flang][Semantics] Warn on repeated do-variable in nested I/O
 implied DO

---
 .../include/flang/Support/Fortran-features.h  |  2 +-
 flang/lib/Semantics/check-do-forall.cpp       | 66 +++++++++++++++++--
 flang/lib/Semantics/check-do-forall.h         |  4 ++
 flang/lib/Support/Fortran-features.cpp        |  1 +
 flang/test/Semantics/io-implied-do01.f90      | 64 ++++++++++++++++++
 5 files changed, 130 insertions(+), 7 deletions(-)
 create mode 100644 flang/test/Semantics/io-implied-do01.f90

diff --git a/flang/include/flang/Support/Fortran-features.h b/flang/include/flang/Support/Fortran-features.h
index a49ecda91603f..ebc6f495e59ba 100644
--- a/flang/include/flang/Support/Fortran-features.h
+++ b/flang/include/flang/Support/Fortran-features.h
@@ -86,7 +86,7 @@ ENUM_CLASS(UsageWarning, Portability, PointerToUndefinable,
     RealConstantWidening, VolatileOrAsynchronousTemporary, UnusedVariable,
     UsedUndefinedVariable, BadValueInDeadCode, AssumedTypeSizeDummy,
     MisplacedIgnoreTKR, NamelistParameter, ImpureFinalInPure,
-    IgnoredNoReallocateLHS, ExperimentalOption)
+    IgnoredNoReallocateLHS, ExperimentalOption, IoImpliedDoIndexConflict)
 
 using LanguageFeatures = EnumSet<LanguageFeature, LanguageFeature_enumSize>;
 using UsageWarnings = EnumSet<UsageWarning, UsageWarning_enumSize>;
diff --git a/flang/lib/Semantics/check-do-forall.cpp b/flang/lib/Semantics/check-do-forall.cpp
index 7502fde40bd35..63ff714b2a6bd 100644
--- a/flang/lib/Semantics/check-do-forall.cpp
+++ b/flang/lib/Semantics/check-do-forall.cpp
@@ -1195,16 +1195,70 @@ static void CheckIoImpliedDoIndex(
   }
 }
 
+// F2023 12.6.3p7: The do-variable of an io-implied-do that is in another
+// io-implied-do shall not appear as, nor be associated with, the do-variable
+// of the containing io-implied-do.
+void DoForallChecker::CheckActiveIoImpliedDoVar(const parser::Name &name) {
+  if (!name.symbol) {
+    return;
+  }
+  const Symbol &resolved{ResolveAssociations(*name.symbol)};
+  const EquivalenceSet *equivSet{FindEquivalenceSet(resolved)};
+  for (const Symbol *active : activeIoImpliedDoVars_) {
+    const Symbol &activeResolved{ResolveAssociations(*active)};
+    bool associated{&resolved == &activeResolved};
+    if (!associated && equivSet) {
+      for (const EquivalenceObject &obj : *equivSet) {
+        if (obj.symbol == activeResolved) {
+          associated = true;
+          break;
+        }
+      }
+    }
+    if (associated) {
+      if (name.symbol == active) {
+        context_.Warn(common::UsageWarning::IoImpliedDoIndexConflict,
+            name.source,
+            "I/O implied DO index '%s' appears in an enclosing I/O implied DO loop and should not have the same name"_warn_en_US,
+            name.source);
+      } else {
+        context_.Warn(common::UsageWarning::IoImpliedDoIndexConflict,
+            name.source,
+            "I/O implied DO index '%s' should not be associated with do-variable '%s' of an enclosing I/O implied DO loop"_warn_en_US,
+            name.source, active->name());
+      }
+      break;
+    }
+  }
+  activeIoImpliedDoVars_.insert(name.symbol);
+}
+
+void DoForallChecker::Enter(const parser::OutputImpliedDo &outputImpliedDo) {
+  CheckActiveIoImpliedDoVar(parser::UnwrapRef<parser::Name>(
+      std::get<parser::IoImpliedDoControl>(outputImpliedDo.t).Name()));
+}
+
+void DoForallChecker::Enter(const parser::InputImpliedDo &inputImpliedDo) {
+  CheckActiveIoImpliedDoVar(parser::UnwrapRef<parser::Name>(
+      std::get<parser::IoImpliedDoControl>(inputImpliedDo.t).Name()));
+}
+
 void DoForallChecker::Leave(const parser::OutputImpliedDo &outputImpliedDo) {
-  CheckIoImpliedDoIndex(context_,
-      parser::UnwrapRef<parser::Name>(
-          std::get<parser::IoImpliedDoControl>(outputImpliedDo.t).Name()));
+  const auto &name{parser::UnwrapRef<parser::Name>(
+      std::get<parser::IoImpliedDoControl>(outputImpliedDo.t).Name())};
+  CheckIoImpliedDoIndex(context_, name);
+  if (name.symbol) {
+    activeIoImpliedDoVars_.erase(name.symbol);
+  }
 }
 
 void DoForallChecker::Leave(const parser::InputImpliedDo &inputImpliedDo) {
-  CheckIoImpliedDoIndex(context_,
-      parser::UnwrapRef<parser::Name>(
-          std::get<parser::IoImpliedDoControl>(inputImpliedDo.t).Name()));
+  const auto &name{parser::UnwrapRef<parser::Name>(
+      std::get<parser::IoImpliedDoControl>(inputImpliedDo.t).Name())};
+  CheckIoImpliedDoIndex(context_, name);
+  if (name.symbol) {
+    activeIoImpliedDoVars_.erase(name.symbol);
+  }
 }
 
 void DoForallChecker::Leave(const parser::StatVariable &statVariable) {
diff --git a/flang/lib/Semantics/check-do-forall.h b/flang/lib/Semantics/check-do-forall.h
index c32d4a26a4f45..a36e5ca9812f4 100644
--- a/flang/lib/Semantics/check-do-forall.h
+++ b/flang/lib/Semantics/check-do-forall.h
@@ -55,7 +55,9 @@ class DoForallChecker : public virtual BaseChecker {
   void Leave(const parser::Expr &);
   void Leave(const parser::InquireSpec &);
   void Leave(const parser::IoControlSpec &);
+  void Enter(const parser::OutputImpliedDo &);
   void Leave(const parser::OutputImpliedDo &);
+  void Enter(const parser::InputImpliedDo &);
   void Leave(const parser::InputImpliedDo &);
   void Leave(const parser::StatVariable &);
 
@@ -63,7 +65,9 @@ class DoForallChecker : public virtual BaseChecker {
   SemanticsContext &context_;
   int exprDepth_{0};
   std::list<SemanticsContext::IndexVarKind> nestedWithinConcurrent_;
+  std::set<const Symbol *> activeIoImpliedDoVars_;
 
+  void CheckActiveIoImpliedDoVar(const parser::Name &);
   void SayBadLeave(
       StmtType, const char *enclosingStmt, const ConstructNode &) const;
   void CheckDoConcurrentExit(StmtType, const ConstructNode &) const;
diff --git a/flang/lib/Support/Fortran-features.cpp b/flang/lib/Support/Fortran-features.cpp
index 73bde3ec8d32d..b8d71867da85d 100644
--- a/flang/lib/Support/Fortran-features.cpp
+++ b/flang/lib/Support/Fortran-features.cpp
@@ -215,6 +215,7 @@ LanguageFeatureControl::LanguageFeatureControl() {
   warnUsage_.set(UsageWarning::MisplacedIgnoreTKR);
   warnUsage_.set(UsageWarning::ImpureFinalInPure);
   warnUsage_.set(UsageWarning::IgnoredNoReallocateLHS);
+  warnUsage_.set(UsageWarning::IoImpliedDoIndexConflict);
   warnLanguage_.set(LanguageFeature::OpenMPThreadprivateEquivalence);
   warnLanguage_.set(LanguageFeature::OpenACCMultipleNamesInRoutine);
 }
diff --git a/flang/test/Semantics/io-implied-do01.f90 b/flang/test/Semantics/io-implied-do01.f90
new file mode 100644
index 0000000000000..4347a7241be94
--- /dev/null
+++ b/flang/test/Semantics/io-implied-do01.f90
@@ -0,0 +1,64 @@
+! RUN: %python %S/test_errors.py %s %flang_fc1 -Werror
+!
+! Check that repeated do-variable names in nested io-implied-do are diagnosed.
+! Section 12.6.3 paragraph 7: The do-variable of an io-implied-do that is in
+! another io-implied-do shall not appear as, nor be associated with, the
+! do-variable of the containing io-implied-do.
+
+module m
+  integer :: mk
+end module
+
+program test
+  use m, only: mk, mk_alias => mk
+  implicit none
+  integer :: matrix(10,10), vec(10), cube(5,5,5)
+  integer :: i, j, k, ei
+  equivalence(j, ei)
+
+  do i = 1, 10
+    do j = 1, 10
+      matrix(i,j) = 10*(i - 1) + j
+    end do
+  end do
+  vec = [(i, i=1,10)]
+
+  ! OK: different do-variables
+  write(*,'(10i5)') ((matrix(i,j), j=1,10), i=1,10)
+
+  ! Bad: j repeated in nested io-implied-do
+  !WARNING: I/O implied DO index 'j' appears in an enclosing I/O implied DO loop and should not have the same name [-Wio-implied-do-index-conflict]
+  write(*,'(10i5)') ((matrix(i,j), j=1,10), j=1,10)
+
+  ! Bad: i repeated in nested io-implied-do
+  !WARNING: I/O implied DO index 'i' appears in an enclosing I/O implied DO loop and should not have the same name [-Wio-implied-do-index-conflict]
+  write(*,'(10i5)') ((matrix(i,j), i=1,10), i=1,10)
+
+  ! OK: single (non-nested) io-implied-do
+  write(*,'(10i5)') (vec(j), j=1,10)
+
+  ! OK: sibling (non-nested) implied-DOs reusing the same variable
+  write(*,'(10i5)') (vec(i), i=1,5), (vec(i), i=6,10)
+
+  ! Bad: j repeated in nested io-implied-do (READ)
+  !WARNING: I/O implied DO index 'j' appears in an enclosing I/O implied DO loop and should not have the same name [-Wio-implied-do-index-conflict]
+  read(*,*) ((matrix(i,j), j=1,10), j=1,10)
+
+  ! Bad: ei is associated with j via EQUIVALENCE
+  !WARNING: I/O implied DO index 'ei' should not be associated with do-variable 'j' of an enclosing I/O implied DO loop [-Wio-implied-do-index-conflict]
+  write(*,'(10i5)') ((matrix(i,ei), ei=1,10), j=1,10)
+
+  ! Bad: USE-renamed mk_alias is the same variable as mk
+  !WARNING: I/O implied DO index 'mk_alias' should not be associated with do-variable 'mk' of an enclosing I/O implied DO loop [-Wio-implied-do-index-conflict]
+  write(*,'(10i5)') ((matrix(i,mk_alias), mk_alias=1,10), mk=1,10)
+
+  ! Bad: triple nesting, k repeated at innermost level
+  !WARNING: I/O implied DO index 'k' appears in an enclosing I/O implied DO loop and should not have the same name [-Wio-implied-do-index-conflict]
+  write(*,*) (((cube(i,j,k), k=1,5), j=1,5), k=1,5)
+
+  ! Bad: ASSOCIATE — x is associated with j
+  associate(x => j)
+    !WARNING: I/O implied DO index 'x' should not be associated with do-variable 'j' of an enclosing I/O implied DO loop [-Wio-implied-do-index-conflict]
+    write(*,'(10i5)') ((matrix(i,x), x=1,10), j=1,10)
+  end associate
+end program test



More information about the flang-commits mailing list