[flang-commits] [flang] [flang][semantics] Diagnose numeric storage size on use (PR #220779)

Andre Kuhlenschmidt via flang-commits flang-commits at lists.llvm.org
Tue Sep 15 13:57:19 PDT 2026


https://github.com/akuhlens updated https://github.com/llvm/llvm-project/pull/220779

>From 232772427b412dae86a5b43d03d2b01816a56efb Mon Sep 17 00:00:00 2001
From: Andre Kuhlenschmidt <akuhlenschmi at nvidia.com>
Date: Tue, 1 Sep 2026 11:26:14 -0700
Subject: [PATCH 1/4] [flang][semantics] Diagnose numeric storage size on
 import

---
 flang/lib/Evaluate/fold-integer.cpp           |  7 --
 flang/lib/Semantics/resolve-names.cpp         | 36 ++++++++
 flang/test/Semantics/numeric_storage_size.f90 |  6 +-
 .../Semantics/numeric_storage_size_only.f90   | 92 +++++++++++++++++++
 4 files changed, 132 insertions(+), 9 deletions(-)
 create mode 100644 flang/test/Semantics/numeric_storage_size_only.f90

diff --git a/flang/lib/Evaluate/fold-integer.cpp b/flang/lib/Evaluate/fold-integer.cpp
index c7db4069e3e28..48bef73cccbb8 100644
--- a/flang/lib/Evaluate/fold-integer.cpp
+++ b/flang/lib/Evaluate/fold-integer.cpp
@@ -1456,13 +1456,6 @@ Expr<Type<TypeCategory::Integer, KIND>> FoldIntrinsicFunction(
       auto realBytes{
           context.targetCharacteristics().GetByteSize(TypeCategory::Real,
               context.defaults().GetDefaultKind(TypeCategory::Real))};
-      if (intBytes != realBytes) {
-        // Using the low-level API to bypass the module file check in this case.
-        context.messages().Warn(
-            /*isInModuleFile=*/false, context.languageFeatures(),
-            common::UsageWarning::FoldingValueChecks, *context.moduleFileName(),
-            "NUMERIC_STORAGE_SIZE from ISO_FORTRAN_ENV is not well-defined when default INTEGER and REAL are not consistent due to compiler options"_warn_en_US);
-      }
       return Expr<T>{8 * std::min(intBytes, realBytes)};
     }
   }
diff --git a/flang/lib/Semantics/resolve-names.cpp b/flang/lib/Semantics/resolve-names.cpp
index 17abba65bdae5..ae7c1e2324cbd 100644
--- a/flang/lib/Semantics/resolve-names.cpp
+++ b/flang/lib/Semantics/resolve-names.cpp
@@ -1198,6 +1198,7 @@ class DeclarationVisitor : public ArraySpecVisitor,
   void CheckEquivalenceSets();
   bool CheckNotInBlock(const char *);
   bool NameIsKnownOrIntrinsic(const parser::Name &);
+  void WarnForNumericStorageSize(const parser::Name &, const Symbol &);
   void FinishNamelists();
 
   // Each of these returns a pointer to a resolved Name (i.e. with symbol)
@@ -9756,6 +9757,40 @@ const parser::Name *DeclarationVisitor::ResolveDataRef(
       x.u);
 }
 
+void DeclarationVisitor::WarnForNumericStorageSize(
+    const parser::Name &name, const Symbol &symbol) {
+  const Symbol *associated{&symbol};
+  while (const auto *host{associated->detailsIf<HostAssocDetails>()}) {
+    associated = &host->symbol();
+  }
+  const auto *use{associated->detailsIf<UseDetails>()};
+  const Symbol &ultimate{symbol.GetUltimate()};
+  const Scope &owner{ultimate.owner()};
+  if (ultimate.name() != "numeric_storage_size" || !owner.IsModule() ||
+      !owner.parent().IsIntrinsicModules() || !owner.GetName() ||
+      owner.GetName().value() != "iso_fortran_env") {
+    return;
+  }
+  const auto &defaults{context().defaultKinds()};
+  const auto &targetCharacteristics{context().targetCharacteristics()};
+  auto intKind{defaults.GetDefaultKind(TypeCategory::Integer)};
+  auto realKind{defaults.GetDefaultKind(TypeCategory::Real)};
+  auto intBytes{
+      targetCharacteristics.GetByteSize(TypeCategory::Integer, intKind)};
+  auto realBytes{
+      targetCharacteristics.GetByteSize(TypeCategory::Real, realKind)};
+  if (intBytes != realBytes) {
+    if (auto *message{context().Warn(common::UsageWarning::FoldingValueChecks,
+            name.source,
+            "NUMERIC_STORAGE_SIZE from ISO_FORTRAN_ENV is not well-defined because compiler options make default INTEGER(KIND=%d) and REAL(KIND=%d) have different storage sizes (%d and %d bytes, respectively)"_warn_en_US,
+            intKind, realKind, intBytes, realBytes)}) {
+      if (use) {
+        message->Attach(use->location(), "USE-associated here"_en_US);
+      }
+    }
+  }
+}
+
 // If implicit types are allowed, ensure name is in the symbol table.
 // Otherwise, report an error if it hasn't been declared.
 const parser::Name *DeclarationVisitor::ResolveName(const parser::Name &name) {
@@ -9768,6 +9803,7 @@ const parser::Name *DeclarationVisitor::ResolveName(const parser::Name &name) {
     if (CheckUseError(name)) {
       return nullptr; // reported an error
     }
+    WarnForNumericStorageSize(name, *symbol);
     NotePossibleBadForwardRef(name);
     symbol->set(Symbol::Flag::ImplicitOrError, false);
     if (IsUplevelReference(*symbol)) {
diff --git a/flang/test/Semantics/numeric_storage_size.f90 b/flang/test/Semantics/numeric_storage_size.f90
index ee11f3b15d5bf..ec6bdc70b116f 100644
--- a/flang/test/Semantics/numeric_storage_size.f90
+++ b/flang/test/Semantics/numeric_storage_size.f90
@@ -13,9 +13,11 @@
 !CHECK-I4: nss = 32_4
 !CHECK-R4: nss = 32_4
 !CHECK-I4-R4: nss = 32_4
-!CHECK-I8: warning: NUMERIC_STORAGE_SIZE from ISO_FORTRAN_ENV is not well-defined when default INTEGER and REAL are not consistent due to compiler options
+!CHECK-I8: warning: NUMERIC_STORAGE_SIZE from ISO_FORTRAN_ENV is not well-defined because compiler options make default INTEGER(KIND=8) and REAL(KIND=4) have different storage sizes (8 and 4 bytes, respectively)
+!CHECK-I8: USE-associated here
 !CHECK-I8: nss = 32_4
-!CHECK-R8: warning: NUMERIC_STORAGE_SIZE from ISO_FORTRAN_ENV is not well-defined when default INTEGER and REAL are not consistent due to compiler options
+!CHECK-R8: warning: NUMERIC_STORAGE_SIZE from ISO_FORTRAN_ENV is not well-defined because compiler options make default INTEGER(KIND=4) and REAL(KIND=8) have different storage sizes (4 and 8 bytes, respectively)
+!CHECK-R8: USE-associated here
 !CHECK-R8: nss = 32_4
 !CHECK-I8-R8: nss = 64_4
 integer, parameter :: nss = numeric_storage_size
diff --git a/flang/test/Semantics/numeric_storage_size_only.f90 b/flang/test/Semantics/numeric_storage_size_only.f90
new file mode 100644
index 0000000000000..e602995b66c89
--- /dev/null
+++ b/flang/test/Semantics/numeric_storage_size_only.f90
@@ -0,0 +1,92 @@
+! RUN: split-file %s %t
+! RUN: %flang_fc1 -fsyntax-only -fdefault-integer-8 -module-dir %t/only-input %t/only-input/test.f90 2>&1 | FileCheck --allow-empty --implicit-check-not=NUMERIC_STORAGE_SIZE %s
+! RUN: %flang_fc1 -fsyntax-only -fdefault-real-8 -module-dir %t/only-input %t/only-input/test.f90 2>&1 | FileCheck --allow-empty --implicit-check-not=NUMERIC_STORAGE_SIZE %s
+! RUN: %flang_fc1 -fsyntax-only -fdefault-integer-8 -module-dir %t/unused %t/unused/test.f90 2>&1 | FileCheck --allow-empty --implicit-check-not=NUMERIC_STORAGE_SIZE %s
+! RUN: %flang_fc1 -fsyntax-only -fdefault-integer-8 -module-dir %t/direct %t/direct/test.f90 2>&1 | FileCheck --check-prefix=TWO --implicit-check-not=NUMERIC_STORAGE_SIZE %s
+! RUN: %flang_fc1 -fsyntax-only -fdefault-integer-8 -module-dir %t/repeated %t/repeated/test.f90 2>&1 | FileCheck --check-prefix=TWO --implicit-check-not=NUMERIC_STORAGE_SIZE %s
+! RUN: %flang_fc1 -fsyntax-only -fdefault-integer-8 -module-dir %t/renamed-twice %t/renamed-twice/test.f90 2>&1 | FileCheck --check-prefix=TWO --implicit-check-not=NUMERIC_STORAGE_SIZE %s
+! RUN: %flang_fc1 -fsyntax-only -fdefault-integer-8 -module-dir %t/homonym %t/homonym/test.f90 2>&1 | FileCheck --allow-empty --implicit-check-not=NUMERIC_STORAGE_SIZE %s
+! RUN: not %flang_fc1 -fsyntax-only -fdefault-integer-8 -module-dir %t/rejected %t/rejected/test.f90 2>&1 | FileCheck --check-prefix=REJECTED --implicit-check-not=NUMERIC_STORAGE_SIZE %s
+! RUN: %flang_fc1 -fsyntax-only -fdefault-integer-8 -module-dir %t/reexport %t/reexport/test.f90 2>&1 | FileCheck --check-prefix=ONE --implicit-check-not=NUMERIC_STORAGE_SIZE %s
+
+! ONE: warning: NUMERIC_STORAGE_SIZE from ISO_FORTRAN_ENV is not well-defined because compiler options make default INTEGER(KIND=8) and REAL(KIND=4) have different storage sizes (8 and 4 bytes, respectively) [-Wfolding-value-checks]
+! ONE: USE-associated here
+! TWO-COUNT-2: warning: NUMERIC_STORAGE_SIZE from ISO_FORTRAN_ENV is not well-defined because compiler options make default INTEGER(KIND=8) and REAL(KIND=4) have different storage sizes (8 and 4 bytes, respectively) [-Wfolding-value-checks]
+! REJECTED: error: Reference to 'numeric_storage_size' is ambiguous
+
+!--- only-input/test.f90
+subroutine only_input_unit
+  use, intrinsic :: iso_fortran_env, only: renamed_input_unit => input_unit
+  implicit none
+  integer :: i
+  i = renamed_input_unit
+end subroutine only_input_unit
+
+!--- unused/test.f90
+subroutine unused_numeric_storage_size
+  use, intrinsic :: iso_fortran_env
+  implicit none
+  integer :: i
+  i = input_unit
+end subroutine unused_numeric_storage_size
+
+!--- direct/test.f90
+subroutine only_numeric_storage_size
+  use, intrinsic :: iso_fortran_env, only: numeric_storage_size
+  implicit none
+  integer, parameter :: nss = numeric_storage_size
+end subroutine only_numeric_storage_size
+
+subroutine renamed_numeric_storage_size
+  use, intrinsic :: iso_fortran_env, only: local_nss => numeric_storage_size
+  implicit none
+  integer, parameter :: nss = local_nss
+end subroutine renamed_numeric_storage_size
+
+!--- repeated/test.f90
+subroutine repeated_same_import
+  use, intrinsic :: iso_fortran_env, only: numeric_storage_size
+  use, intrinsic :: iso_fortran_env, only: numeric_storage_size
+  integer, parameter :: nss = numeric_storage_size + numeric_storage_size
+end subroutine repeated_same_import
+
+!--- renamed-twice/test.f90
+subroutine repeated_renamed_import
+  use, intrinsic :: iso_fortran_env, only: first_nss => numeric_storage_size
+  use, intrinsic :: iso_fortran_env, only: second_nss => numeric_storage_size
+  integer, parameter :: nss = first_nss + second_nss
+end subroutine repeated_renamed_import
+
+!--- homonym/test.f90
+module iso_fortran_env
+  integer, parameter :: numeric_storage_size = 123
+end module iso_fortran_env
+
+subroutine non_intrinsic_homonym
+  use, non_intrinsic :: iso_fortran_env, only: numeric_storage_size
+  implicit none
+  integer, parameter :: nss = numeric_storage_size
+end subroutine non_intrinsic_homonym
+
+!--- rejected/test.f90
+module conflicting_nss
+  integer, parameter :: numeric_storage_size = 456
+end module conflicting_nss
+
+subroutine rejected_intrinsic_import
+  use conflicting_nss, only: numeric_storage_size
+  use, intrinsic :: iso_fortran_env, only: numeric_storage_size
+  ! The ambiguous name is not successfully associated with the intrinsic
+  ! constant, so referencing it diagnoses only the import error.
+  integer, parameter :: nss = numeric_storage_size
+end subroutine rejected_intrinsic_import
+
+!--- reexport/test.f90
+module reexported_nss
+  use, intrinsic :: iso_fortran_env, only: numeric_storage_size
+end module reexported_nss
+
+subroutine import_from_reexport
+  use reexported_nss, only: numeric_storage_size
+  integer, parameter :: nss = numeric_storage_size
+end subroutine import_from_reexport

>From 63dc61dcb87b50fc83d3df8da88ac9eac2365af5 Mon Sep 17 00:00:00 2001
From: Andre Kuhlenschmidt <akuhlenschmi at nvidia.com>
Date: Mon, 14 Sep 2026 13:02:38 -0700
Subject: [PATCH 2/4] [flang][semantics] Diagnose numeric storage size in
 expressions

---
 flang/lib/Semantics/expression.cpp            | 69 +++++++++++++++++++
 flang/lib/Semantics/resolve-names.cpp         | 36 ----------
 .../Semantics/numeric_storage_size_only.f90   | 20 ++++++
 3 files changed, 89 insertions(+), 36 deletions(-)

diff --git a/flang/lib/Semantics/expression.cpp b/flang/lib/Semantics/expression.cpp
index fa78379b7a4d0..ca127e887045f 100644
--- a/flang/lib/Semantics/expression.cpp
+++ b/flang/lib/Semantics/expression.cpp
@@ -1051,6 +1051,72 @@ MaybeExpr ExpressionAnalyzer::Analyze(const parser::BOZLiteralConstant &x) {
 }
 
 // Names and named constants
+static void WarnForNumericStorageSize(
+    semantics::SemanticsContext &context, const parser::Name &name) {
+  const semantics::Symbol *associated{name.symbol};
+  const semantics::UseDetails *use{nullptr};
+  bool isNumericStorageSize{false};
+  while (associated) {
+    if (const auto *host{
+            associated->detailsIf<semantics::HostAssocDetails>()}) {
+      associated = &host->symbol();
+    } else if (const auto *nextUse{
+                   associated->detailsIf<semantics::UseDetails>()}) {
+      if (!use) {
+        use = nextUse;
+      }
+      const semantics::Symbol &used{nextUse->symbol()};
+      const semantics::Symbol &module{semantics::GetUsedModule(*nextUse)};
+      isNumericStorageSize |= used.name() == "numeric_storage_size" &&
+          module.name() == "iso_fortran_env" &&
+          module.attrs().test(semantics::Attr::INTRINSIC);
+      associated = &used;
+    } else {
+      break;
+    }
+  }
+  if (!isNumericStorageSize) {
+    return;
+  }
+  const auto &defaults{context.defaultKinds()};
+  const auto &targetCharacteristics{context.targetCharacteristics()};
+  auto intKind{defaults.GetDefaultKind(TypeCategory::Integer)};
+  auto realKind{defaults.GetDefaultKind(TypeCategory::Real)};
+  auto intBytes{
+      targetCharacteristics.GetByteSize(TypeCategory::Integer, intKind)};
+  auto realBytes{
+      targetCharacteristics.GetByteSize(TypeCategory::Real, realKind)};
+  if (intBytes != realBytes) {
+    if (auto *message{context.messages().Warn(
+            /*isInModuleFile=*/false, context.languageFeatures(),
+            common::UsageWarning::FoldingValueChecks, name.source,
+            "NUMERIC_STORAGE_SIZE from ISO_FORTRAN_ENV is not well-defined because compiler options make default INTEGER(KIND=%d) and REAL(KIND=%d) have different storage sizes (%zu and %zu bytes, respectively)"_warn_en_US,
+            intKind, realKind, intBytes, realBytes)}) {
+      if (use) {
+        message->Attach(use->location(), "USE-associated here"_en_US);
+      }
+    }
+  }
+}
+
+class NumericStorageSizeWarningVisitor {
+public:
+  explicit NumericStorageSizeWarningVisitor(
+      semantics::SemanticsContext &context)
+      : context_{context} {}
+  template <typename A> bool Pre(const A &) { return true; }
+  bool Pre(const parser::Name &name) {
+    if (!context_.HasError(name.symbol)) {
+      WarnForNumericStorageSize(context_, name);
+    }
+    return false;
+  }
+  template <typename A> void Post(const A &) {}
+
+private:
+  semantics::SemanticsContext &context_;
+};
+
 MaybeExpr ExpressionAnalyzer::Analyze(const parser::Name &n) {
   auto restorer{GetContextualMessages().SetLocation(n.source)};
   if (std::optional<int> kind{IsImpliedDo(n.source)}) {
@@ -1060,6 +1126,7 @@ MaybeExpr ExpressionAnalyzer::Analyze(const parser::Name &n) {
   if (context_.HasError(n.symbol)) { // includes case of no symbol
     return std::nullopt;
   } else {
+    WarnForNumericStorageSize(context_, n);
     const Symbol &ultimate{n.symbol->GetUltimate()};
     if (ultimate.has<semantics::TypeParamDetails>()) {
       // A bare reference to a derived type parameter within a parameterized
@@ -4649,6 +4716,8 @@ MaybeExpr ExpressionAnalyzer::Analyze(const parser::Expr &expr) {
   MaybeExpr result;
   if (useSavedTypedExprs_) {
     if (expr.typedExpr) {
+      NumericStorageSizeWarningVisitor visitor{context_};
+      parser::Walk(expr, visitor);
       return expr.typedExpr->v;
     }
     if (!wasIterativelyAnalyzing) {
diff --git a/flang/lib/Semantics/resolve-names.cpp b/flang/lib/Semantics/resolve-names.cpp
index ae7c1e2324cbd..17abba65bdae5 100644
--- a/flang/lib/Semantics/resolve-names.cpp
+++ b/flang/lib/Semantics/resolve-names.cpp
@@ -1198,7 +1198,6 @@ class DeclarationVisitor : public ArraySpecVisitor,
   void CheckEquivalenceSets();
   bool CheckNotInBlock(const char *);
   bool NameIsKnownOrIntrinsic(const parser::Name &);
-  void WarnForNumericStorageSize(const parser::Name &, const Symbol &);
   void FinishNamelists();
 
   // Each of these returns a pointer to a resolved Name (i.e. with symbol)
@@ -9757,40 +9756,6 @@ const parser::Name *DeclarationVisitor::ResolveDataRef(
       x.u);
 }
 
-void DeclarationVisitor::WarnForNumericStorageSize(
-    const parser::Name &name, const Symbol &symbol) {
-  const Symbol *associated{&symbol};
-  while (const auto *host{associated->detailsIf<HostAssocDetails>()}) {
-    associated = &host->symbol();
-  }
-  const auto *use{associated->detailsIf<UseDetails>()};
-  const Symbol &ultimate{symbol.GetUltimate()};
-  const Scope &owner{ultimate.owner()};
-  if (ultimate.name() != "numeric_storage_size" || !owner.IsModule() ||
-      !owner.parent().IsIntrinsicModules() || !owner.GetName() ||
-      owner.GetName().value() != "iso_fortran_env") {
-    return;
-  }
-  const auto &defaults{context().defaultKinds()};
-  const auto &targetCharacteristics{context().targetCharacteristics()};
-  auto intKind{defaults.GetDefaultKind(TypeCategory::Integer)};
-  auto realKind{defaults.GetDefaultKind(TypeCategory::Real)};
-  auto intBytes{
-      targetCharacteristics.GetByteSize(TypeCategory::Integer, intKind)};
-  auto realBytes{
-      targetCharacteristics.GetByteSize(TypeCategory::Real, realKind)};
-  if (intBytes != realBytes) {
-    if (auto *message{context().Warn(common::UsageWarning::FoldingValueChecks,
-            name.source,
-            "NUMERIC_STORAGE_SIZE from ISO_FORTRAN_ENV is not well-defined because compiler options make default INTEGER(KIND=%d) and REAL(KIND=%d) have different storage sizes (%d and %d bytes, respectively)"_warn_en_US,
-            intKind, realKind, intBytes, realBytes)}) {
-      if (use) {
-        message->Attach(use->location(), "USE-associated here"_en_US);
-      }
-    }
-  }
-}
-
 // If implicit types are allowed, ensure name is in the symbol table.
 // Otherwise, report an error if it hasn't been declared.
 const parser::Name *DeclarationVisitor::ResolveName(const parser::Name &name) {
@@ -9803,7 +9768,6 @@ const parser::Name *DeclarationVisitor::ResolveName(const parser::Name &name) {
     if (CheckUseError(name)) {
       return nullptr; // reported an error
     }
-    WarnForNumericStorageSize(name, *symbol);
     NotePossibleBadForwardRef(name);
     symbol->set(Symbol::Flag::ImplicitOrError, false);
     if (IsUplevelReference(*symbol)) {
diff --git a/flang/test/Semantics/numeric_storage_size_only.f90 b/flang/test/Semantics/numeric_storage_size_only.f90
index e602995b66c89..9ebd89e879205 100644
--- a/flang/test/Semantics/numeric_storage_size_only.f90
+++ b/flang/test/Semantics/numeric_storage_size_only.f90
@@ -8,6 +8,8 @@
 ! RUN: %flang_fc1 -fsyntax-only -fdefault-integer-8 -module-dir %t/homonym %t/homonym/test.f90 2>&1 | FileCheck --allow-empty --implicit-check-not=NUMERIC_STORAGE_SIZE %s
 ! RUN: not %flang_fc1 -fsyntax-only -fdefault-integer-8 -module-dir %t/rejected %t/rejected/test.f90 2>&1 | FileCheck --check-prefix=REJECTED --implicit-check-not=NUMERIC_STORAGE_SIZE %s
 ! RUN: %flang_fc1 -fsyntax-only -fdefault-integer-8 -module-dir %t/reexport %t/reexport/test.f90 2>&1 | FileCheck --check-prefix=ONE --implicit-check-not=NUMERIC_STORAGE_SIZE %s
+! RUN: %flang_fc1 -fsyntax-only -fdefault-integer-8 -module-dir %t/separate %t/separate/a.f90 2>&1 | FileCheck --allow-empty --implicit-check-not=NUMERIC_STORAGE_SIZE %s
+! RUN: %flang_fc1 -fsyntax-only -fdefault-integer-8 -I %t/separate -module-dir %t/separate %t/separate/b.f90 2>&1 | FileCheck --check-prefix=ONE --implicit-check-not=NUMERIC_STORAGE_SIZE %s
 
 ! ONE: warning: NUMERIC_STORAGE_SIZE from ISO_FORTRAN_ENV is not well-defined because compiler options make default INTEGER(KIND=8) and REAL(KIND=4) have different storage sizes (8 and 4 bytes, respectively) [-Wfolding-value-checks]
 ! ONE: USE-associated here
@@ -90,3 +92,21 @@ subroutine import_from_reexport
   use reexported_nss, only: numeric_storage_size
   integer, parameter :: nss = numeric_storage_size
 end subroutine import_from_reexport
+
+!--- separate/a.f90
+module separate_parent
+  use, intrinsic :: iso_fortran_env
+  implicit none
+  interface
+    module subroutine separate_foo
+    end subroutine
+  end interface
+end module
+
+!--- separate/b.f90
+submodule (separate_parent) separate_child
+contains
+  module subroutine separate_foo
+    integer, parameter :: nss = numeric_storage_size
+  end subroutine
+end submodule

>From 011a7a11bd295bd772d58e37b2258021de40dd96 Mon Sep 17 00:00:00 2001
From: Andre Kuhlenschmidt <akuhlenschmi at nvidia.com>
Date: Mon, 14 Sep 2026 13:09:58 -0700
Subject: [PATCH 3/4] [flang][semantics] Clarify expression warning paths

---
 flang/lib/Semantics/expression.cpp                 | 5 +++++
 flang/test/Semantics/numeric_storage_size_only.f90 | 9 +++++++++
 2 files changed, 14 insertions(+)

diff --git a/flang/lib/Semantics/expression.cpp b/flang/lib/Semantics/expression.cpp
index ca127e887045f..6d689221b2c6c 100644
--- a/flang/lib/Semantics/expression.cpp
+++ b/flang/lib/Semantics/expression.cpp
@@ -1126,6 +1126,9 @@ MaybeExpr ExpressionAnalyzer::Analyze(const parser::Name &n) {
   if (context_.HasError(n.symbol)) { // includes case of no symbol
     return std::nullopt;
   } else {
+    // Most expression references are diagnosed here. Analyze(Expr) below
+    // performs the same check explicitly when returning a saved typed
+    // expression, since that path does not call Analyze(Name).
     WarnForNumericStorageSize(context_, n);
     const Symbol &ultimate{n.symbol->GetUltimate()};
     if (ultimate.has<semantics::TypeParamDetails>()) {
@@ -4716,6 +4719,8 @@ MaybeExpr ExpressionAnalyzer::Analyze(const parser::Expr &expr) {
   MaybeExpr result;
   if (useSavedTypedExprs_) {
     if (expr.typedExpr) {
+      // Returning a saved typed expression bypasses Analyze(Name), so walk the
+      // original expression to perform its numeric_storage_size checks.
       NumericStorageSizeWarningVisitor visitor{context_};
       parser::Walk(expr, visitor);
       return expr.typedExpr->v;
diff --git a/flang/test/Semantics/numeric_storage_size_only.f90 b/flang/test/Semantics/numeric_storage_size_only.f90
index 9ebd89e879205..2638c4b141fc3 100644
--- a/flang/test/Semantics/numeric_storage_size_only.f90
+++ b/flang/test/Semantics/numeric_storage_size_only.f90
@@ -3,6 +3,7 @@
 ! RUN: %flang_fc1 -fsyntax-only -fdefault-real-8 -module-dir %t/only-input %t/only-input/test.f90 2>&1 | FileCheck --allow-empty --implicit-check-not=NUMERIC_STORAGE_SIZE %s
 ! RUN: %flang_fc1 -fsyntax-only -fdefault-integer-8 -module-dir %t/unused %t/unused/test.f90 2>&1 | FileCheck --allow-empty --implicit-check-not=NUMERIC_STORAGE_SIZE %s
 ! RUN: %flang_fc1 -fsyntax-only -fdefault-integer-8 -module-dir %t/direct %t/direct/test.f90 2>&1 | FileCheck --check-prefix=TWO --implicit-check-not=NUMERIC_STORAGE_SIZE %s
+! RUN: %flang_fc1 -fsyntax-only -fdefault-integer-8 -module-dir %t/nonconstant %t/nonconstant/test.f90 2>&1 | FileCheck --check-prefix=ONE --implicit-check-not=NUMERIC_STORAGE_SIZE %s
 ! RUN: %flang_fc1 -fsyntax-only -fdefault-integer-8 -module-dir %t/repeated %t/repeated/test.f90 2>&1 | FileCheck --check-prefix=TWO --implicit-check-not=NUMERIC_STORAGE_SIZE %s
 ! RUN: %flang_fc1 -fsyntax-only -fdefault-integer-8 -module-dir %t/renamed-twice %t/renamed-twice/test.f90 2>&1 | FileCheck --check-prefix=TWO --implicit-check-not=NUMERIC_STORAGE_SIZE %s
 ! RUN: %flang_fc1 -fsyntax-only -fdefault-integer-8 -module-dir %t/homonym %t/homonym/test.f90 2>&1 | FileCheck --allow-empty --implicit-check-not=NUMERIC_STORAGE_SIZE %s
@@ -45,6 +46,14 @@ subroutine renamed_numeric_storage_size
   integer, parameter :: nss = local_nss
 end subroutine renamed_numeric_storage_size
 
+!--- nonconstant/test.f90
+subroutine nonconstant_subexpression(n)
+  use, intrinsic :: iso_fortran_env, only: numeric_storage_size
+  implicit none
+  integer :: n
+  n = n + 2 * numeric_storage_size
+end subroutine nonconstant_subexpression
+
 !--- repeated/test.f90
 subroutine repeated_same_import
   use, intrinsic :: iso_fortran_env, only: numeric_storage_size

>From 9919ce3cabb54afeea9a02fed43efdc3776d34ce Mon Sep 17 00:00:00 2001
From: Andre Kuhlenschmidt <akuhlenschmi at nvidia.com>
Date: Tue, 15 Sep 2026 13:56:32 -0700
Subject: [PATCH 4/4] [flang][semantics] Clarify numeric storage size types

---
 flang/lib/Semantics/expression.cpp | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/flang/lib/Semantics/expression.cpp b/flang/lib/Semantics/expression.cpp
index 6d689221b2c6c..d9cb21ab226a7 100644
--- a/flang/lib/Semantics/expression.cpp
+++ b/flang/lib/Semantics/expression.cpp
@@ -1080,11 +1080,11 @@ static void WarnForNumericStorageSize(
   }
   const auto &defaults{context.defaultKinds()};
   const auto &targetCharacteristics{context.targetCharacteristics()};
-  auto intKind{defaults.GetDefaultKind(TypeCategory::Integer)};
-  auto realKind{defaults.GetDefaultKind(TypeCategory::Real)};
-  auto intBytes{
+  const int intKind{defaults.GetDefaultKind(TypeCategory::Integer)};
+  const int realKind{defaults.GetDefaultKind(TypeCategory::Real)};
+  const std::size_t intBytes{
       targetCharacteristics.GetByteSize(TypeCategory::Integer, intKind)};
-  auto realBytes{
+  const std::size_t realBytes{
       targetCharacteristics.GetByteSize(TypeCategory::Real, realKind)};
   if (intBytes != realBytes) {
     if (auto *message{context.messages().Warn(



More information about the flang-commits mailing list