[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