[flang-commits] [flang] [flang] Emit warnings, not errors, for bad subscripts in dead code (PR #174040)

via flang-commits flang-commits at lists.llvm.org
Tue Dec 30 15:58:50 PST 2025


llvmbot wrote:


<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-flang-parser

@llvm/pr-subscribers-flang-semantics

Author: Peter Klausler (klausler)

<details>
<summary>Changes</summary>

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.

---
Full diff: https://github.com/llvm/llvm-project/pull/174040.diff


8 Files Affected:

- (modified) flang/docs/Extensions.md (+4) 
- (modified) flang/include/flang/Parser/message.h (+8-1) 
- (modified) flang/include/flang/Semantics/expression.h (+4) 
- (modified) flang/include/flang/Support/Fortran-features.h (+1-1) 
- (modified) flang/lib/Parser/message.cpp (+1) 
- (modified) flang/lib/Semantics/expression.cpp (+87-6) 
- (modified) flang/lib/Support/Fortran-features.cpp (+1) 
- (added) flang/test/Semantics/bug171844.f90 (+37) 


``````````diff
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

``````````

</details>


https://github.com/llvm/llvm-project/pull/174040


More information about the flang-commits mailing list