[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