[flang-commits] [flang] [flang] Emit warnings, not errors, for bad subscripts in dead code (PR #174040)
Peter Klausler via flang-commits
flang-commits at lists.llvm.org
Tue Dec 30 15:58:18 PST 2025
https://github.com/klausler created https://github.com/llvm/llvm-project/pull/174040
When semantics is checking expressions in known dead branches of IF constructs, errors should not be fatal; emit warnings instead, and allow them to be disabled.
Fixes https://github.com/llvm/llvm-project/issues/171844.
>From 168d839f93e500c80cb7969fef8f9db329c6d09d Mon Sep 17 00:00:00 2001
From: Peter Klausler <pklausler at nvidia.com>
Date: Tue, 30 Dec 2025 15:51:54 -0800
Subject: [PATCH] [flang] Emit warnings, not errors, for bad subscripts in dead
code
When semantics is checking expressions in known dead branches of
IF constructs, errors should not be fatal; emit warnings instead,
and allow them to be disabled.
Fixes https://github.com/llvm/llvm-project/issues/171844.
---
flang/docs/Extensions.md | 4 +
flang/include/flang/Parser/message.h | 9 +-
flang/include/flang/Semantics/expression.h | 4 +
.../include/flang/Support/Fortran-features.h | 2 +-
flang/lib/Parser/message.cpp | 1 +
flang/lib/Semantics/expression.cpp | 93 +++++++++++++++++--
flang/lib/Support/Fortran-features.cpp | 1 +
flang/test/Semantics/bug171844.f90 | 37 ++++++++
8 files changed, 143 insertions(+), 8 deletions(-)
create mode 100644 flang/test/Semantics/bug171844.f90
diff --git a/flang/docs/Extensions.md b/flang/docs/Extensions.md
index 82822d5a143d2..c3837ce7ce65a 100644
--- a/flang/docs/Extensions.md
+++ b/flang/docs/Extensions.md
@@ -976,6 +976,10 @@ print *, [(j,j=1,10)]
but it can be disabled via `FORT_TRUNCATE_STREAM=0` in the
environment at execution time.
+* Some expression errors, like out-of-range known subscript values,
+ are noted only as warnings when they appear in code known to be
+ dead anyway at compilation time.
+
## De Facto Standard Features
* `EXTENDS_TYPE_OF()` returns `.TRUE.` if both of its arguments have the
diff --git a/flang/include/flang/Parser/message.h b/flang/include/flang/Parser/message.h
index 7c639eff1eeef..c50320513567d 100644
--- a/flang/include/flang/Parser/message.h
+++ b/flang/include/flang/Parser/message.h
@@ -35,6 +35,7 @@ namespace Fortran::parser {
// and severity of a message or attachment.
enum class Severity {
Error, // fatal error that prevents code and module file generation
+ ErrorUnlessDeadCode,
Warning, // likely problem
Portability, // nonstandard or obsolete features
Because, // for AttachTo(), explanatory attachment to support another message
@@ -62,7 +63,9 @@ class MessageFixedText {
return *this;
}
bool IsFatal() const {
- return severity_ == Severity::Error || severity_ == Severity::Todo;
+ return severity_ == Severity::Error ||
+ severity_ == Severity::ErrorUnlessDeadCode ||
+ severity_ == Severity::Todo;
}
static const MessageFixedText endOfFileMessage; // "end of file"_err_en_US
@@ -77,6 +80,10 @@ constexpr MessageFixedText operator""_err_en_US(
const char str[], std::size_t n) {
return MessageFixedText{str, n, Severity::Error};
}
+constexpr MessageFixedText operator""_errUnlessDead_en_US(
+ const char str[], std::size_t n) {
+ return MessageFixedText{str, n, Severity::ErrorUnlessDeadCode};
+}
constexpr MessageFixedText operator""_warn_en_US(
const char str[], std::size_t n) {
return MessageFixedText{str, n, Severity::Warning};
diff --git a/flang/include/flang/Semantics/expression.h b/flang/include/flang/Semantics/expression.h
index 639ef99d2d936..9787accb30d8c 100644
--- a/flang/include/flang/Semantics/expression.h
+++ b/flang/include/flang/Semantics/expression.h
@@ -26,6 +26,7 @@
#include "flang/Support/Fortran.h"
#include <map>
#include <optional>
+#include <stack>
#include <type_traits>
#include <variant>
@@ -428,6 +429,7 @@ class ExpressionAnalyzer {
bool inDataStmtConstant_{false};
bool inStmtFunctionDefinition_{false};
bool iterativelyAnalyzingSubexpressions_{false};
+ bool inDeadCode_{false};
friend class ArgumentAnalyzer;
};
@@ -535,6 +537,8 @@ class ExprChecker {
exprAnalyzer_.set_inWhereBody(InWhereBody());
}
+ bool Pre(const parser::IfConstruct &);
+
bool Pre(const parser::ComponentDefStmt &) {
inComponentDefStmt_ = true;
return true;
diff --git a/flang/include/flang/Support/Fortran-features.h b/flang/include/flang/Support/Fortran-features.h
index ef5c1a84ba3d7..f7f84ce7a33d1 100644
--- a/flang/include/flang/Support/Fortran-features.h
+++ b/flang/include/flang/Support/Fortran-features.h
@@ -79,7 +79,7 @@ ENUM_CLASS(UsageWarning, Portability, PointerToUndefinable,
CompatibleDeclarationsFromDistinctModules, ConstantIsContiguous,
NullActualForDefaultIntentAllocatable, UseAssociationIntoSameNameSubprogram,
HostAssociatedIntentOutInSpecExpr, NonVolatilePointerToVolatile,
- RealConstantWidening, VolatileOrAsynchronousTemporary)
+ RealConstantWidening, VolatileOrAsynchronousTemporary, BadValueInDeadCode)
using LanguageFeatures = EnumSet<LanguageFeature, LanguageFeature_enumSize>;
using UsageWarnings = EnumSet<UsageWarning, UsageWarning_enumSize>;
diff --git a/flang/lib/Parser/message.cpp b/flang/lib/Parser/message.cpp
index cfcd08b0861ef..0ae0e64914afa 100644
--- a/flang/lib/Parser/message.cpp
+++ b/flang/lib/Parser/message.cpp
@@ -258,6 +258,7 @@ std::optional<ProvenanceRange> Message::GetProvenanceRange(
static std::string Prefix(Severity severity) {
switch (severity) {
case Severity::Error:
+ case Severity::ErrorUnlessDeadCode:
return "error: ";
case Severity::Warning:
return "warning: ";
diff --git a/flang/lib/Semantics/expression.cpp b/flang/lib/Semantics/expression.cpp
index 6f5d0bf9eb242..4bbeae997cd79 100644
--- a/flang/lib/Semantics/expression.cpp
+++ b/flang/lib/Semantics/expression.cpp
@@ -349,11 +349,11 @@ static void ValidateSubscriptValue(parser::ContextualMessages &messages,
std::optional<ConstantSubscript> bound;
if (lb && val < *lb) {
msg =
- "%ssubscript %jd is less than lower %sbound %jd for %sdimension %d of array"_err_en_US;
+ "%ssubscript %jd is less than lower %sbound %jd for %sdimension %d of array"_errUnlessDead_en_US;
bound = *lb;
} else if (ub && val > *ub) {
msg =
- "%ssubscript %jd is greater than upper %sbound %jd for %sdimension %d of array"_err_en_US;
+ "%ssubscript %jd is greater than upper %sbound %jd for %sdimension %d of array"_errUnlessDead_en_US;
bound = *ub;
if (dim + 1 == symbol.Rank() && IsDummy(symbol) && *bound == 1) {
// Old-school overindexing of a dummy array isn't fatal when
@@ -1821,8 +1821,8 @@ void ArrayConstructorContext::Push(MaybeExpr &&x) {
}
} else if (dyType->IsUnlimitedPolymorphic()) {
if (!(messageDisplayedSet_ & 8)) {
- exprAnalyzer_.Say("Cannot have an unlimited polymorphic value in an "
- "array constructor"_err_en_US); // C7113
+ exprAnalyzer_.Say(
+ "Cannot have an unlimited polymorphic value in an array constructor"_err_en_US); // C7113
messageDisplayedSet_ |= 8;
}
return;
@@ -2040,8 +2040,7 @@ void ArrayConstructorContext::Add(const parser::AcImpliedDo &impliedDo) {
exprAnalyzer_.RemoveImpliedDo(name);
} else if (!(messageDisplayedSet_ & 0x20)) {
exprAnalyzer_.SayAt(name,
- "Implied DO index '%s' is active in a surrounding implied DO loop "
- "and may not have the same name"_err_en_US,
+ "Implied DO index '%s' is active in a surrounding implied DO loop and may not have the same name"_err_en_US,
name); // C7115
messageDisplayedSet_ |= 0x20;
}
@@ -5372,6 +5371,88 @@ bool ExprChecker::Pre(const parser::DataImpliedDo &ido) {
return false;
}
+// Handle messages from dead code in block IF constructs
+bool ExprChecker::Pre(const parser::IfConstruct &construct) {
+ enum State {
+ NotYetKnownTrue,
+ WasKnownTrue,
+ TrueNow,
+ FalseNow
+ } state{NotYetKnownTrue};
+ auto &messages{exprAnalyzer_.GetContextualMessages()};
+ parser::CharBlock determiner; // source of relevant IF statement
+
+ auto consider{[&](const SomeExpr *expr, parser::CharBlock here) {
+ if (state == TrueNow) {
+ state = WasKnownTrue;
+ } else if (state != WasKnownTrue) {
+ state = NotYetKnownTrue;
+ if (expr) {
+ if (auto known{
+ evaluate::GetScalarConstantValue<evaluate::LogicalResult>(
+ *expr)}) {
+ state = known->IsTrue() ? TrueNow : FalseNow;
+ determiner = here;
+ }
+ }
+ }
+ }};
+ auto doBlock{[&, this](const parser::Block &block) {
+ if ((state == FalseNow || state == WasKnownTrue) && messages.messages()) {
+ parser::Messages inDeadCode;
+ {
+ auto restorer{messages.SetMessages(inDeadCode)};
+ parser::Walk(block, *this);
+ }
+ for (auto &msg : inDeadCode.messages()) {
+ if (msg.severity() == parser::Severity::ErrorUnlessDeadCode) {
+ msg.set_severity(parser::Severity::Warning);
+ msg.set_usageWarning(common::UsageWarning::BadValueInDeadCode);
+ msg.Attach(determiner,
+ "in code known to be dead due to this compile-time IF expression value"_en_US);
+ }
+ }
+ if (context_.ShouldWarn(common::UsageWarning::BadValueInDeadCode) ||
+ inDeadCode.AnyFatalError()) {
+ messages.messages()->Annex(std::move(inDeadCode));
+ }
+ } else {
+ parser::Walk(block, *this);
+ }
+ }};
+
+ const auto &[ifThen, block, elseIfs, maybeElse, endIf]{construct.t};
+ // IF (...) THEN block
+ parser::Walk(ifThen, *this);
+ consider(GetExpr(&context_,
+ std::get<parser::ScalarLogicalExpr>(ifThen.statement.t)),
+ ifThen.source);
+ doBlock(block);
+ // ELSE IF (...) THEN block ...
+ for (const auto &elseIf : elseIfs) {
+ const auto &[stmt, block]{elseIf.t};
+ parser::Walk(stmt, *this);
+ consider(GetExpr(&context_,
+ std::get<parser::ScalarLogicalExpr>(stmt.statement.t)),
+ stmt.source);
+ doBlock(block);
+ }
+ // ELSE block
+ if (maybeElse) {
+ const auto &[stmt, block]{maybeElse->t};
+ parser::Walk(stmt, *this);
+ if (state == TrueNow || state == WasKnownTrue) {
+ state = FalseNow;
+ } else {
+ state = TrueNow;
+ }
+ doBlock(block);
+ }
+ // END IF
+ parser::Walk(endIf, *this);
+ return false;
+}
+
bool ExprChecker::Walk(const parser::Program &program) {
parser::Walk(program, *this);
return !context_.AnyFatalError();
diff --git a/flang/lib/Support/Fortran-features.cpp b/flang/lib/Support/Fortran-features.cpp
index 4a6fb8d75a135..356050f22b443 100644
--- a/flang/lib/Support/Fortran-features.cpp
+++ b/flang/lib/Support/Fortran-features.cpp
@@ -152,6 +152,7 @@ LanguageFeatureControl::LanguageFeatureControl() {
// New warnings, on by default
warnLanguage_.set(LanguageFeature::SavedLocalInSpecExpr);
warnLanguage_.set(LanguageFeature::NullActualForAllocatable);
+ warnUsage_.set(UsageWarning::BadValueInDeadCode);
}
std::optional<LanguageControlFlag> LanguageFeatureControl::FindWarning(
diff --git a/flang/test/Semantics/bug171844.f90 b/flang/test/Semantics/bug171844.f90
new file mode 100644
index 0000000000000..de775c83302ca
--- /dev/null
+++ b/flang/test/Semantics/bug171844.f90
@@ -0,0 +1,37 @@
+! RUN: %flang_fc1 -fsyntax-only %s 2>&1 | FileCheck --check-prefix=CHECK-WARNING %s
+! RUN: %flang_fc1 -fsyntax-only -Wno-bad-value-in-dead-code %s 2>&1 | FileCheck %s
+
+real a(2)
+
+if (.false.) then
+ !CHECK-WARNING::8:12: warning: subscript 3 is greater than upper bound 2 for dimension 1 of array [-Wbad-value-in-dead-code]
+ print *, a(3)
+end if
+
+if (.true.) then
+ !CHECK::13:12: error: subscript 0 is less than lower bound 1 for dimension 1 of array
+ print *, a(0)
+else
+ !CHECK-WARNING::16:12: warning: subscript 0 is less than lower bound 1 for dimension 1 of array [-Wbad-value-in-dead-code]
+ print *, a(0)
+end if
+
+if (.false.) then
+else if (.true.) then
+ !CHECK::22:12: error: subscript 0 is less than lower bound 1 for dimension 1 of array
+ print *, a(0)
+else
+ !CHECK-WARNING::25:12: warning: subscript 0 is less than lower bound 1 for dimension 1 of array [-Wbad-value-in-dead-code]
+ print *, a(0)
+end if
+
+if (.true.) then
+else if (.true.) then
+ !CHECK-WARNING::31:12: warning: subscript -1 is less than lower bound 1 for dimension 1 of array [-Wbad-value-in-dead-code]
+ print *, a(-1)
+else
+ !CHECK-WARNING::34:12: warning: subscript 3 is greater than upper bound 2 for dimension 1 of array [-Wbad-value-in-dead-code]
+ print *, a(3)
+end if
+
+end
More information about the flang-commits
mailing list