[flang-commits] [flang] [Flang] Add parsing and attribute registration for SIMPLE specifier (PR #161285)
Eugene Epshteyn via flang-commits
flang-commits at lists.llvm.org
Tue May 5 06:55:18 PDT 2026
https://github.com/eugeneepshteyn updated https://github.com/llvm/llvm-project/pull/161285
>From 4cbf40e7097935d73df13717843996b7788a087c Mon Sep 17 00:00:00 2001
From: Sarka Holendova <sarka.holendova at gmail.com>
Date: Mon, 29 Sep 2025 23:23:40 +0200
Subject: [PATCH 01/10] [Flang] Add parsing and attribute registration for
SIMPLE procedures
---
flang/include/flang/Evaluate/call.h | 1 +
flang/include/flang/Evaluate/characteristics.h | 5 +++--
flang/include/flang/Evaluate/tools.h | 2 ++
flang/include/flang/Parser/dump-parse-tree.h | 1 +
flang/include/flang/Parser/parse-tree.h | 5 +++--
flang/include/flang/Semantics/attr.h | 2 +-
flang/lib/Evaluate/call.cpp | 14 ++++++++++++++
flang/lib/Evaluate/tools.cpp | 9 +++++++++
flang/lib/Parser/program-parsers.cpp | 3 ++-
flang/lib/Parser/unparse.cpp | 1 +
flang/lib/Semantics/resolve-names.cpp | 1 +
flang/test/Parser/simple-unparse.f90 | 14 ++++++++++++++
flang/test/Parser/simple.f90 | 9 +++++++++
13 files changed, 61 insertions(+), 6 deletions(-)
create mode 100644 flang/test/Parser/simple-unparse.f90
create mode 100644 flang/test/Parser/simple.f90
diff --git a/flang/include/flang/Evaluate/call.h b/flang/include/flang/Evaluate/call.h
index 93e36f64fe591..01b41f36b9977 100644
--- a/flang/include/flang/Evaluate/call.h
+++ b/flang/include/flang/Evaluate/call.h
@@ -220,6 +220,7 @@ struct ProcedureDesignator {
int Rank() const;
bool IsElemental() const;
bool IsPure() const;
+ bool IsSimple() const;
std::optional<Expr<SubscriptInteger>> LEN() const;
llvm::raw_ostream &AsFortran(llvm::raw_ostream &) const;
diff --git a/flang/include/flang/Evaluate/characteristics.h b/flang/include/flang/Evaluate/characteristics.h
index 4cf82e7c14d70..af0d429b6aa3b 100644
--- a/flang/include/flang/Evaluate/characteristics.h
+++ b/flang/include/flang/Evaluate/characteristics.h
@@ -365,8 +365,8 @@ struct FunctionResult {
// 15.3.1
struct Procedure {
- ENUM_CLASS(Attr, Pure, Elemental, BindC, ImplicitInterface, NullPointer,
- NullAllocatable, Subroutine)
+ ENUM_CLASS(Attr, Pure, Simple, Elemental, BindC, ImplicitInterface,
+ NullPointer, NullAllocatable, Subroutine)
using Attrs = common::EnumSet<Attr, Attr_enumSize>;
Procedure(){};
Procedure(FunctionResult &&, DummyArguments &&, Attrs);
@@ -398,6 +398,7 @@ struct Procedure {
bool IsSubroutine() const { return attrs.test(Attr::Subroutine); }
bool IsPure() const { return attrs.test(Attr::Pure); }
+ bool IsSimple() const { return attrs.test(Attr::Simple); }
bool IsElemental() const { return attrs.test(Attr::Elemental); }
bool IsBindC() const { return attrs.test(Attr::BindC); }
bool HasExplicitInterface() const {
diff --git a/flang/include/flang/Evaluate/tools.h b/flang/include/flang/Evaluate/tools.h
index 944494823d8a2..d2d0b69e6337d 100644
--- a/flang/include/flang/Evaluate/tools.h
+++ b/flang/include/flang/Evaluate/tools.h
@@ -1593,6 +1593,8 @@ inline bool IsAlternateEntry(const Symbol *symbol) {
bool IsVariableName(const Symbol &);
bool IsPureProcedure(const Symbol &);
bool IsPureProcedure(const Scope &);
+bool IsSimpleProcedure(const Symbol &);
+bool IsSimpleProcedure(const Scope &);
bool IsExplicitlyImpureProcedure(const Symbol &);
bool IsElementalProcedure(const Symbol &);
bool IsFunction(const Symbol &);
diff --git a/flang/include/flang/Parser/dump-parse-tree.h b/flang/include/flang/Parser/dump-parse-tree.h
index 3002edc0fc5de..0e0f2520e81fb 100644
--- a/flang/include/flang/Parser/dump-parse-tree.h
+++ b/flang/include/flang/Parser/dump-parse-tree.h
@@ -823,6 +823,7 @@ class ParseTreeDumper {
NODE(PrefixSpec, Non_Recursive)
NODE(PrefixSpec, Pure)
NODE(PrefixSpec, Recursive)
+ NODE(PrefixSpec, Simple)
NODE(PrefixSpec, Attributes)
NODE(PrefixSpec, Launch_Bounds)
NODE(PrefixSpec, Cluster_Dims)
diff --git a/flang/include/flang/Parser/parse-tree.h b/flang/include/flang/Parser/parse-tree.h
index dde83e0c71d7c..fa78c3ce0d07b 100644
--- a/flang/include/flang/Parser/parse-tree.h
+++ b/flang/include/flang/Parser/parse-tree.h
@@ -3101,7 +3101,7 @@ struct ProcedureDeclarationStmt {
// R1527 prefix-spec ->
// declaration-type-spec | ELEMENTAL | IMPURE | MODULE |
-// NON_RECURSIVE | PURE | RECURSIVE |
+// NON_RECURSIVE | PURE | RECURSIVE | SIMPLE |
// (CUDA) ATTRIBUTES ( (DEVICE | GLOBAL | GRID_GLOBAL | HOST)... )
// LAUNCH_BOUNDS(expr-list) | CLUSTER_DIMS(expr-list)
struct PrefixSpec {
@@ -3112,11 +3112,12 @@ struct PrefixSpec {
EMPTY_CLASS(Non_Recursive);
EMPTY_CLASS(Pure);
EMPTY_CLASS(Recursive);
+ EMPTY_CLASS(Simple);
WRAPPER_CLASS(Attributes, std::list<common::CUDASubprogramAttrs>);
WRAPPER_CLASS(Launch_Bounds, std::list<ScalarIntConstantExpr>);
WRAPPER_CLASS(Cluster_Dims, std::list<ScalarIntConstantExpr>);
std::variant<DeclarationTypeSpec, Elemental, Impure, Module, Non_Recursive,
- Pure, Recursive, Attributes, Launch_Bounds, Cluster_Dims>
+ Pure, Recursive, Simple, Attributes, Launch_Bounds, Cluster_Dims>
u;
};
diff --git a/flang/include/flang/Semantics/attr.h b/flang/include/flang/Semantics/attr.h
index 76fab5e0c904d..488f325de5887 100644
--- a/flang/include/flang/Semantics/attr.h
+++ b/flang/include/flang/Semantics/attr.h
@@ -25,7 +25,7 @@ ENUM_CLASS(Attr, ABSTRACT, ALLOCATABLE, ASYNCHRONOUS, BIND_C, CONTIGUOUS,
DEFERRED, ELEMENTAL, EXTENDS, EXTERNAL, IMPURE, INTENT_IN, INTENT_INOUT,
INTENT_OUT, INTRINSIC, MODULE, NON_OVERRIDABLE, NON_RECURSIVE, NOPASS,
OPTIONAL, PARAMETER, PASS, POINTER, PRIVATE, PROTECTED, PUBLIC, PURE,
- RECURSIVE, SAVE, TARGET, VALUE, VOLATILE)
+ RECURSIVE, SAVE, SIMPLE, TARGET, VALUE, VOLATILE)
// Set of attributes
class Attrs : public common::EnumSet<Attr, Attr_enumSize> {
diff --git a/flang/lib/Evaluate/call.cpp b/flang/lib/Evaluate/call.cpp
index f77df92a7597a..b179fd7a4a4f5 100644
--- a/flang/lib/Evaluate/call.cpp
+++ b/flang/lib/Evaluate/call.cpp
@@ -158,6 +158,20 @@ bool ProcedureDesignator::IsPure() const {
return false;
}
+bool ProcedureDesignator::IsSimple() const {
+ if (const Symbol *interface{GetInterfaceSymbol()}) {
+ return IsSimpleProcedure(*interface);
+ } else if (const Symbol *symbol{GetSymbol()}) {
+ return IsSimpleProcedure(*symbol);
+ } else if (const auto *intrinsic{std::get_if<SpecificIntrinsic>(&u)}) {
+ return intrinsic->characteristics.value().attrs.test(
+ characteristics::Procedure::Attr::Simple);
+ } else {
+ DIE("ProcedureDesignator::IsSimple(): no case");
+ }
+ return false;
+}
+
const SpecificIntrinsic *ProcedureDesignator::GetSpecificIntrinsic() const {
return std::get_if<SpecificIntrinsic>(&u);
}
diff --git a/flang/lib/Evaluate/tools.cpp b/flang/lib/Evaluate/tools.cpp
index 6357de0fe0d91..72734ccd73086 100644
--- a/flang/lib/Evaluate/tools.cpp
+++ b/flang/lib/Evaluate/tools.cpp
@@ -2197,6 +2197,15 @@ bool IsPureProcedure(const Scope &scope) {
return symbol && IsPureProcedure(*symbol);
}
+bool IsSimpleProcedure(const Symbol &original) {
+ return original.attrs().test(Attr::SIMPLE);
+}
+
+bool IsSimpleProcedure(const Scope &scope) {
+ const Symbol *symbol{scope.GetSymbol()};
+ return symbol && IsSimpleProcedure(*symbol);
+}
+
bool IsExplicitlyImpureProcedure(const Symbol &original) {
// An ENTRY is IMPURE if its containing subprogram is so
return DEREF(GetMainEntry(&original.GetUltimate()))
diff --git a/flang/lib/Parser/program-parsers.cpp b/flang/lib/Parser/program-parsers.cpp
index b26603b5aea45..1b4e0962b79e9 100644
--- a/flang/lib/Parser/program-parsers.cpp
+++ b/flang/lib/Parser/program-parsers.cpp
@@ -534,7 +534,7 @@ TYPE_PARSER(construct<AltReturnSpec>(star >> label))
// R1527 prefix-spec ->
// declaration-type-spec | ELEMENTAL | IMPURE | MODULE |
-// NON_RECURSIVE | PURE | RECURSIVE |
+// NON_RECURSIVE | PURE | RECURSIVE | SIMPLE |
// (CUDA) ATTRIBUTES ( (DEVICE | GLOBAL | GRID_GLOBAL | HOST)... ) |
// LAUNCH_BOUNDS(expr-list) | CLUSTER_DIMS(expr-list)
TYPE_PARSER(withMessage(
@@ -551,6 +551,7 @@ TYPE_PARSER(first(construct<PrefixSpec>(declarationTypeSpec),
construct<PrefixSpec::Non_Recursive>("NON_RECURSIVE"_tok)),
construct<PrefixSpec>(construct<PrefixSpec::Pure>("PURE"_tok)),
construct<PrefixSpec>(construct<PrefixSpec::Recursive>("RECURSIVE"_tok)),
+ construct<PrefixSpec>(construct<PrefixSpec::Simple>("SIMPLE"_tok)),
extension<LanguageFeature::CUDA>(
construct<PrefixSpec>(construct<PrefixSpec::Attributes>(
localRecovery("expected valid ATTRIBUTES specification"_err_en_US,
diff --git a/flang/lib/Parser/unparse.cpp b/flang/lib/Parser/unparse.cpp
index 48a49433d78d0..b90303bb65d8a 100644
--- a/flang/lib/Parser/unparse.cpp
+++ b/flang/lib/Parser/unparse.cpp
@@ -1748,6 +1748,7 @@ class UnparseVisitor {
void Post(const PrefixSpec::Non_Recursive) { Word("NON_RECURSIVE"); }
void Post(const PrefixSpec::Pure) { Word("PURE"); }
void Post(const PrefixSpec::Recursive) { Word("RECURSIVE"); }
+ void Post(const PrefixSpec::Simple) { Word("SIMPLE"); }
void Unparse(const PrefixSpec::Attributes &x) {
Word("ATTRIBUTES("), Walk(x.v), Word(")");
}
diff --git a/flang/lib/Semantics/resolve-names.cpp b/flang/lib/Semantics/resolve-names.cpp
index f39650da81053..1d305e071f02b 100644
--- a/flang/lib/Semantics/resolve-names.cpp
+++ b/flang/lib/Semantics/resolve-names.cpp
@@ -271,6 +271,7 @@ class AttrsVisitor : public virtual BaseVisitor {
HANDLE_ATTR_CLASS(PrefixSpec::Non_Recursive, NON_RECURSIVE)
HANDLE_ATTR_CLASS(PrefixSpec::Pure, PURE)
HANDLE_ATTR_CLASS(PrefixSpec::Recursive, RECURSIVE)
+ HANDLE_ATTR_CLASS(PrefixSpec::Simple, SIMPLE)
HANDLE_ATTR_CLASS(TypeAttrSpec::BindC, BIND_C)
HANDLE_ATTR_CLASS(BindAttr::Deferred, DEFERRED)
HANDLE_ATTR_CLASS(BindAttr::Non_Overridable, NON_OVERRIDABLE)
diff --git a/flang/test/Parser/simple-unparse.f90 b/flang/test/Parser/simple-unparse.f90
new file mode 100644
index 0000000000000..4cc52bfeee6cf
--- /dev/null
+++ b/flang/test/Parser/simple-unparse.f90
@@ -0,0 +1,14 @@
+! RUN: %flang_fc1 -fdebug-unparse-no-sema %s 2>&1 | FileCheck %s
+
+! Test that SIMPLE function specifier is recognized
+! by the parser and the unparser. This test does not
+! exercise semantic checks.
+
+simple function foo()
+ return
+end function
+
+! CHECK: SIMPLE FUNCTION foo()
+! CHECK-NEXT: RETURN
+! CHECK-NEXT: END FUNCTION
+
diff --git a/flang/test/Parser/simple.f90 b/flang/test/Parser/simple.f90
new file mode 100644
index 0000000000000..488909a5550a2
--- /dev/null
+++ b/flang/test/Parser/simple.f90
@@ -0,0 +1,9 @@
+! RUN: %flang_fc1 -fdebug-dump-parse-tree %s | FileCheck %s
+
+! Check that SIMPLE is recognized in the parse tree
+
+simple function foo()
+ return
+end function
+
+! CHECK: Simple
>From 03f09ef87869ed0ac41290d28823c31bea850387 Mon Sep 17 00:00:00 2001
From: Sarka Holendova <sarka.holendova at gmail.com>
Date: Thu, 9 Oct 2025 16:26:51 -0400
Subject: [PATCH 02/10] [flang] Extend SIMPLE specifier support: disallow
PURE+SIMPLE combination and update purity handling
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
- Adds a rule in resolve-names.cpp to diagnose procedures declared with both PURE and SIMPLE attributes (Fortran 2023 C759, C1543).
- Updates IsPureProcedureImpl() in tools.cpp so that SIMPLE implies PURE (Fortran 2023 §15.8).
- Introduces a new LIT test (simple-conflict.f90) verifying diagnostic emission for PURE+SIMPLE conflicts.
---
flang/lib/Evaluate/tools.cpp | 3 +++
flang/lib/Semantics/resolve-names.cpp | 1 +
flang/test/Semantics/simple-conflict.f90 | 12 ++++++++++++
3 files changed, 16 insertions(+)
create mode 100644 flang/test/Semantics/simple-conflict.f90
diff --git a/flang/lib/Evaluate/tools.cpp b/flang/lib/Evaluate/tools.cpp
index 72734ccd73086..e470296fe6f9e 100644
--- a/flang/lib/Evaluate/tools.cpp
+++ b/flang/lib/Evaluate/tools.cpp
@@ -2182,6 +2182,9 @@ static bool IsPureProcedureImpl(
}
return true; // statement function was not found to be impure
}
+ if (symbol.attrs().test(Attr::SIMPLE)) {
+ return true; // SIMPLE implies PURE (Fortran 2023 §15.8)
+ }
return symbol.attrs().test(Attr::PURE) ||
(symbol.attrs().test(Attr::ELEMENTAL) &&
!symbol.attrs().test(Attr::IMPURE));
diff --git a/flang/lib/Semantics/resolve-names.cpp b/flang/lib/Semantics/resolve-names.cpp
index 1d305e071f02b..5af1d31c4a063 100644
--- a/flang/lib/Semantics/resolve-names.cpp
+++ b/flang/lib/Semantics/resolve-names.cpp
@@ -2632,6 +2632,7 @@ bool AttrsVisitor::IsConflictingAttr(Attr attrName) {
HaveAttrConflict(attrName, Attr::INTENT_INOUT, Attr::INTENT_OUT) ||
HaveAttrConflict(attrName, Attr::PASS, Attr::NOPASS) || // C781
HaveAttrConflict(attrName, Attr::PURE, Attr::IMPURE) ||
+ HaveAttrConflict(attrName, Attr::PURE, Attr::SIMPLE) ||
HaveAttrConflict(attrName, Attr::PUBLIC, Attr::PRIVATE) ||
HaveAttrConflict(attrName, Attr::RECURSIVE, Attr::NON_RECURSIVE) ||
HaveAttrConflict(attrName, Attr::INTRINSIC, Attr::EXTERNAL);
diff --git a/flang/test/Semantics/simple-conflict.f90 b/flang/test/Semantics/simple-conflict.f90
new file mode 100644
index 0000000000000..efc14c18e51f5
--- /dev/null
+++ b/flang/test/Semantics/simple-conflict.f90
@@ -0,0 +1,12 @@
+! RUN: not %flang_fc1 -fsyntax-only %s 2>&1 | FileCheck %s
+!
+! Verify that PURE and SIMPLE prefix-specs are mutually exclusive
+
+pure simple subroutine ps()
+end
+! CHECK: error: Attributes 'PURE' and 'SIMPLE' conflict
+
+simple pure function sp()
+end
+! CHECK: error: Attributes 'PURE' and 'SIMPLE' conflict
+
>From 3828aae0cc548feddce1a4cb96fb5f7567f809d9 Mon Sep 17 00:00:00 2001
From: Sarka Holendova <sarka.holendova at gmail.com>
Date: Sat, 24 Jan 2026 18:51:49 -0500
Subject: [PATCH 03/10] [flang] Treat SIMPLE procedures as PURE -
Set PURE implicitly when SIMPLE is specified and add symbol-dump coverage
---
flang/lib/Semantics/resolve-names.cpp | 11 +++++-
flang/test/Semantics/simple-conflict.f90 | 12 -------
.../simple-satisfies-pure-negative.f90 | 36 +++++++++++++++++++
.../test/Semantics/simple-satisfies-pure.f90 | 33 +++++++++++++++++
flang/test/Semantics/simple.f90 | 17 +++++++++
5 files changed, 96 insertions(+), 13 deletions(-)
delete mode 100644 flang/test/Semantics/simple-conflict.f90
create mode 100644 flang/test/Semantics/simple-satisfies-pure-negative.f90
create mode 100644 flang/test/Semantics/simple-satisfies-pure.f90
create mode 100644 flang/test/Semantics/simple.f90
diff --git a/flang/lib/Semantics/resolve-names.cpp b/flang/lib/Semantics/resolve-names.cpp
index 5af1d31c4a063..ee18efcaaf7e3 100644
--- a/flang/lib/Semantics/resolve-names.cpp
+++ b/flang/lib/Semantics/resolve-names.cpp
@@ -708,10 +708,20 @@ class ScopeHandler : public ImplicitRulesVisitor {
void SetExplicitAttr(Symbol &symbol, Attr attr) const {
symbol.attrs().set(attr);
symbol.implicitAttrs().reset(attr);
+
+ // SIMPLE implies PURE; mark PURE as implicit
+ if (attr == Attr::SIMPLE && !symbol.attrs().test(Attr::PURE)) {
+ SetImplicitAttr(symbol, Attr::PURE);
+ }
}
void SetExplicitAttrs(Symbol &symbol, Attrs attrs) const {
symbol.attrs() |= attrs;
symbol.implicitAttrs() &= ~attrs;
+
+ // SIMPLE implies PURE; mark PURE as implicit
+ if (attrs.test(Attr::SIMPLE) && !symbol.attrs().test(Attr::PURE)) {
+ SetImplicitAttr(symbol, Attr::PURE);
+ }
}
void SetImplicitAttr(Symbol &symbol, Attr attr) const {
symbol.attrs().set(attr);
@@ -2632,7 +2642,6 @@ bool AttrsVisitor::IsConflictingAttr(Attr attrName) {
HaveAttrConflict(attrName, Attr::INTENT_INOUT, Attr::INTENT_OUT) ||
HaveAttrConflict(attrName, Attr::PASS, Attr::NOPASS) || // C781
HaveAttrConflict(attrName, Attr::PURE, Attr::IMPURE) ||
- HaveAttrConflict(attrName, Attr::PURE, Attr::SIMPLE) ||
HaveAttrConflict(attrName, Attr::PUBLIC, Attr::PRIVATE) ||
HaveAttrConflict(attrName, Attr::RECURSIVE, Attr::NON_RECURSIVE) ||
HaveAttrConflict(attrName, Attr::INTRINSIC, Attr::EXTERNAL);
diff --git a/flang/test/Semantics/simple-conflict.f90 b/flang/test/Semantics/simple-conflict.f90
deleted file mode 100644
index efc14c18e51f5..0000000000000
--- a/flang/test/Semantics/simple-conflict.f90
+++ /dev/null
@@ -1,12 +0,0 @@
-! RUN: not %flang_fc1 -fsyntax-only %s 2>&1 | FileCheck %s
-!
-! Verify that PURE and SIMPLE prefix-specs are mutually exclusive
-
-pure simple subroutine ps()
-end
-! CHECK: error: Attributes 'PURE' and 'SIMPLE' conflict
-
-simple pure function sp()
-end
-! CHECK: error: Attributes 'PURE' and 'SIMPLE' conflict
-
diff --git a/flang/test/Semantics/simple-satisfies-pure-negative.f90 b/flang/test/Semantics/simple-satisfies-pure-negative.f90
new file mode 100644
index 0000000000000..3bf3d700f0687
--- /dev/null
+++ b/flang/test/Semantics/simple-satisfies-pure-negative.f90
@@ -0,0 +1,36 @@
+! This is the negative/control case for simple-satisfies-pure.f90.
+! It verifies that a procedure which is neither PURE nor SIMPLE is rejected
+! when passed to a dummy argument that requires a PURE procedure.
+! RUN: not %flang_fc1 -fsyntax-only %s 2>&1 | FileCheck %s
+
+module m
+ implicit none
+
+ abstract interface
+ pure integer function iproc(x)
+ integer, intent(in) :: x
+ end function
+ end interface
+
+contains
+
+ ! Neither PURE nor SIMPLE -> must NOT satisfy PURE requirements
+ integer function impure(x)
+ integer, intent(in) :: x
+ impure = x
+ end function
+
+ pure integer function apply_pure(f, x)
+ procedure(iproc) :: f
+ integer, intent(in) :: x
+ apply_pure = f(x)
+ end function
+
+ integer function test()
+ test = apply_pure(impure, 1)
+ end function
+end module
+
+! CHECK: error: Actual procedure argument has interface incompatible with dummy argument 'f='
+! CHECK-SAME: incompatible procedure attributes: Pure
+
diff --git a/flang/test/Semantics/simple-satisfies-pure.f90 b/flang/test/Semantics/simple-satisfies-pure.f90
new file mode 100644
index 0000000000000..d08b758538530
--- /dev/null
+++ b/flang/test/Semantics/simple-satisfies-pure.f90
@@ -0,0 +1,33 @@
+! This test verifies that SIMPLE procedures satisfy PURE requirements.
+! (i.e. anywhere the language requires a PURE procedure, a SIMPLE one is accepted)
+! RUN: %flang_fc1 -fsyntax-only %s
+
+module m
+ implicit none
+
+ abstract interface
+ ! Dummy procedure explicitly requires PURE
+ pure integer function pure_iface(x)
+ integer, intent(in) :: x
+ end function
+ end interface
+
+contains
+
+ ! SIMPLE procedure (should be accepted where PURE is required)
+ simple integer function simple_impl(x)
+ integer, intent(in) :: x
+ simple_impl = x
+ end function
+
+ pure integer function apply_requires_pure(f, x)
+ procedure(pure_iface) :: f
+ integer, intent(in) :: x
+ apply_requires_pure = f(x)
+ end function
+
+ integer function test()
+ test = apply_requires_pure(simple_impl, 1)
+ end function
+end module
+
diff --git a/flang/test/Semantics/simple.f90 b/flang/test/Semantics/simple.f90
new file mode 100644
index 0000000000000..3c29a80843c71
--- /dev/null
+++ b/flang/test/Semantics/simple.f90
@@ -0,0 +1,17 @@
+! RUN: %flang_fc1 -fdebug-dump-symbols %s 2>&1 | FileCheck %s
+
+module m
+contains
+ ! SIMPLE: should end up with both SIMPLE and PURE (PURE implied by SIMPLE)
+ simple subroutine s_simple()
+ end
+
+ ! PURE: should remain PURE, and should not acquire SIMPLE
+ pure subroutine s_pure()
+ end
+end module
+
+! CHECK: s_simple, PUBLIC, PURE, SIMPLE
+! CHECK: s_pure, PUBLIC, PURE
+! CHECK-NOT: s_pure, PUBLIC, PURE, SIMPLE
+
>From bf8d9e754129d0a52543ec45ecc32d54a3283dfc Mon Sep 17 00:00:00 2001
From: Sarka Holendova <sarka.holendova at gmail.com>
Date: Mon, 23 Mar 2026 20:38:12 -0400
Subject: [PATCH 04/10] [flang] Handle SIMPLE propagation and SIMPLE/IMPURE
conflict
Propagate SIMPLE in CharacterizeProcedure() so it is reflected in
procedure characteristics.
Treat SIMPLE as satisfying PURE in procedure compatibility, while
preserving that PURE alone does not satisfy SIMPLE.
Diagnose invalid SIMPLE+IMPURE combinations.
Update simple.f90 to use CHECK-DAG since the symbol dump order is not stable.
---
flang/lib/Evaluate/characteristics.cpp | 4 +++
flang/lib/Semantics/check-declarations.cpp | 6 +++++
.../pure-does-not-satisfy-simple.f90 | 26 +++++++++++++++++++
.../test/Semantics/simple-impure-conflict.f90 | 6 +++++
flang/test/Semantics/simple.f90 | 4 +--
5 files changed, 44 insertions(+), 2 deletions(-)
create mode 100644 flang/test/Semantics/pure-does-not-satisfy-simple.f90
create mode 100644 flang/test/Semantics/simple-impure-conflict.f90
diff --git a/flang/lib/Evaluate/characteristics.cpp b/flang/lib/Evaluate/characteristics.cpp
index 50f5f91fe60d6..63d4c74d553e3 100644
--- a/flang/lib/Evaluate/characteristics.cpp
+++ b/flang/lib/Evaluate/characteristics.cpp
@@ -787,6 +787,7 @@ static std::optional<Procedure> CharacterizeProcedure(
CopyAttrs<Procedure, Procedure::Attr>(symbol, *result,
{
{semantics::Attr::BIND_C, Procedure::Attr::BindC},
+ {semantics::Attr::SIMPLE, Procedure::Attr::Simple},
});
CopyAttrs<Procedure, Procedure::Attr>(DEREF(GetMainEntry(&symbol)), *result,
{
@@ -1331,6 +1332,9 @@ bool Procedure::IsCompatibleWith(const Procedure &actual,
if (!attrs.test(Attr::Pure)) {
actualAttrs.reset(Attr::Pure);
}
+ if (!attrs.test(Attr::Simple)) {
+ actualAttrs.reset(Attr::Simple);
+ }
if (!attrs.test(Attr::Elemental) && specificIntrinsic) {
actualAttrs.reset(Attr::Elemental);
}
diff --git a/flang/lib/Semantics/check-declarations.cpp b/flang/lib/Semantics/check-declarations.cpp
index 4174cf6d1e340..72488ce91e90e 100644
--- a/flang/lib/Semantics/check-declarations.cpp
+++ b/flang/lib/Semantics/check-declarations.cpp
@@ -1594,6 +1594,12 @@ void CheckHelper::CheckSubprogram(
context_.SetError(symbol);
}
}
+ if (symbol.attrs().test(Attr::SIMPLE) &&
+ symbol.attrs().test(Attr::IMPURE)) {
+ messages_.Say(symbol.name(),
+ "A procedure may not have both the SIMPLE and IMPURE attributes"_err_en_US);
+ context_.SetError(symbol);
+ }
if (const Symbol *iface{FindSeparateModuleSubprogramInterface(&symbol)}) {
SubprogramMatchHelper{*this}.Check(symbol, *iface);
}
diff --git a/flang/test/Semantics/pure-does-not-satisfy-simple.f90 b/flang/test/Semantics/pure-does-not-satisfy-simple.f90
new file mode 100644
index 0000000000000..03f41da6ef4b2
--- /dev/null
+++ b/flang/test/Semantics/pure-does-not-satisfy-simple.f90
@@ -0,0 +1,26 @@
+! RUN: not %flang_fc1 -fsyntax-only %s 2>&1 | FileCheck %s
+
+module pure_not_enough_for_simple
+
+ abstract interface
+ simple subroutine simple_proc()
+ end subroutine
+ end interface
+
+contains
+
+ subroutine needs_simple(p)
+ procedure(simple_proc) :: p
+ end subroutine
+
+ pure subroutine pure_only()
+ end subroutine
+
+ subroutine test()
+ call needs_simple(pure_only)
+ end subroutine
+
+end module
+
+! CHECK: incompatible procedure attributes
+! CHECK: Simple
diff --git a/flang/test/Semantics/simple-impure-conflict.f90 b/flang/test/Semantics/simple-impure-conflict.f90
new file mode 100644
index 0000000000000..f94a288636f68
--- /dev/null
+++ b/flang/test/Semantics/simple-impure-conflict.f90
@@ -0,0 +1,6 @@
+! RUN: not %flang_fc1 -fsyntax-only %s 2>&1 | FileCheck %s
+
+simple impure subroutine bug()
+end subroutine
+
+! CHECK: may not have both the SIMPLE and IMPURE attributes
diff --git a/flang/test/Semantics/simple.f90 b/flang/test/Semantics/simple.f90
index 3c29a80843c71..5511c730915ac 100644
--- a/flang/test/Semantics/simple.f90
+++ b/flang/test/Semantics/simple.f90
@@ -11,7 +11,7 @@ pure subroutine s_pure()
end
end module
-! CHECK: s_simple, PUBLIC, PURE, SIMPLE
-! CHECK: s_pure, PUBLIC, PURE
+! CHECK-DAG: s_simple, PUBLIC, PURE, SIMPLE
+! CHECK-DAG: s_pure, PUBLIC, PURE
! CHECK-NOT: s_pure, PUBLIC, PURE, SIMPLE
>From 1a5af3dd4fa59e90190d419a0f24af336fe3d948 Mon Sep 17 00:00:00 2001
From: Sarka Holendova <sarka.holendova at gmail.com>
Date: Mon, 23 Mar 2026 20:58:13 -0400
Subject: [PATCH 05/10] [flang] Fix clang-format in SIMPLE/IMPURE check
---
flang/lib/Semantics/check-declarations.cpp | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/flang/lib/Semantics/check-declarations.cpp b/flang/lib/Semantics/check-declarations.cpp
index 72488ce91e90e..109ea1e4a8c31 100644
--- a/flang/lib/Semantics/check-declarations.cpp
+++ b/flang/lib/Semantics/check-declarations.cpp
@@ -1594,8 +1594,7 @@ void CheckHelper::CheckSubprogram(
context_.SetError(symbol);
}
}
- if (symbol.attrs().test(Attr::SIMPLE) &&
- symbol.attrs().test(Attr::IMPURE)) {
+ if (symbol.attrs().test(Attr::SIMPLE) && symbol.attrs().test(Attr::IMPURE)) {
messages_.Say(symbol.name(),
"A procedure may not have both the SIMPLE and IMPURE attributes"_err_en_US);
context_.SetError(symbol);
>From df34427f4963961080e8145ac0d3c609073f270e Mon Sep 17 00:00:00 2001
From: Sarka Holendova <sarka.holendova at gmail.com>
Date: Sun, 26 Apr 2026 18:46:44 -0400
Subject: [PATCH 06/10] [flang] Address SIMPLE procedure review feedback
- Move SIMPLE=>PURE handling from SetExplicitAttr* to IsPureProcedureImpl
- Derive PURE via query (not stored as a symbol attribute)
- Update IsSimpleProcedure to use GetUltimate/GetMainEntry
- Fix SIMPLE detection for ENTRY and use-associated procedures
- Add use-associated SIMPLE regression test
- Remove outdated symbol-dump test (simple.f90)
- Add current-status section for SIMPLE in F202X.md
- Add TODO for marking intrinsics as SIMPLE in intrinsics.cpp (follow-up PR)
---
flang/docs/F202X.md | 12 +++++++++
flang/lib/Evaluate/intrinsics.cpp | 1 +
flang/lib/Evaluate/tools.cpp | 7 +++--
flang/lib/Semantics/check-declarations.cpp | 1 +
flang/lib/Semantics/resolve-names.cpp | 10 -------
.../test/Semantics/simple-use-associated.f90 | 26 +++++++++++++++++++
flang/test/Semantics/simple.f90 | 17 ------------
7 files changed, 45 insertions(+), 29 deletions(-)
create mode 100644 flang/test/Semantics/simple-use-associated.f90
delete mode 100644 flang/test/Semantics/simple.f90
diff --git a/flang/docs/F202X.md b/flang/docs/F202X.md
index 68747a6cf274f..60c38bd09587f 100644
--- a/flang/docs/F202X.md
+++ b/flang/docs/F202X.md
@@ -167,6 +167,17 @@ I don't expect any codes to rush to change their `PURE` procedures
to be `SIMPLE`, since it buys little and reduces portability.
This makes `SIMPLE` a lower-priority feature.
+### Current status
+
+- Parsing and symbol attribute support for `SIMPLE` procedures: implemented
+- `SIMPLE` is represented in procedure characteristics
+- `SIMPLE` procedures are accepted where PURE procedures are required
+- `PURE` procedures do not satisfy `SIMPLE` requirements
+- `SIMPLE`/`IMPURE` conflicts are diagnosed
+- Use-associated `SIMPLE` procedures are correctly recognized
+- Additional `SIMPLE` semantic constraints remain to be implemented
+- Intrinsic procedures are not yet marked as `SIMPLE` (planned as follow-up work)
+
#### Conditional expressions and actual arguments
Next on the list of "big ticket" items are C-style conditional
@@ -198,6 +209,7 @@ a powerful sum data type as in Haskell or Rust. Unlike the
current `ENUM, BIND(C)` feature, `ENUMERATION TYPE` defines a new
type name and its distinct values.
+
This feature may well be the item requiring the largest patch to
the compiler for its implementation, as it affects parsing,
type checking on assignment and argument association, generic
diff --git a/flang/lib/Evaluate/intrinsics.cpp b/flang/lib/Evaluate/intrinsics.cpp
index 84cd2288fcd0b..5b76af2ac789d 100644
--- a/flang/lib/Evaluate/intrinsics.cpp
+++ b/flang/lib/Evaluate/intrinsics.cpp
@@ -2865,6 +2865,7 @@ std::optional<SpecificCall> IntrinsicInterface::Match(
if (elementalRank > 0) {
attrs.set(characteristics::Procedure::Attr::Elemental);
}
+ // TODO: Mark intrinsic procedures that are SIMPLE per F2023
if (call.isSubroutineCall) {
if (intrinsicClass == IntrinsicClass::pureSubroutine /* MOVE_ALLOC */ ||
intrinsicClass == IntrinsicClass::elementalSubroutine /* MVBITS */) {
diff --git a/flang/lib/Evaluate/tools.cpp b/flang/lib/Evaluate/tools.cpp
index e470296fe6f9e..3edaa5befd27b 100644
--- a/flang/lib/Evaluate/tools.cpp
+++ b/flang/lib/Evaluate/tools.cpp
@@ -2183,7 +2183,7 @@ static bool IsPureProcedureImpl(
return true; // statement function was not found to be impure
}
if (symbol.attrs().test(Attr::SIMPLE)) {
- return true; // SIMPLE implies PURE (Fortran 2023 §15.8)
+ return true; // SIMPLE implies PURE; F2023 15.8
}
return symbol.attrs().test(Attr::PURE) ||
(symbol.attrs().test(Attr::ELEMENTAL) &&
@@ -2201,7 +2201,10 @@ bool IsPureProcedure(const Scope &scope) {
}
bool IsSimpleProcedure(const Symbol &original) {
- return original.attrs().test(Attr::SIMPLE);
+ // An ENTRY is SIMPLE if its containing subprogram is
+ return DEREF(GetMainEntry(&original.GetUltimate()))
+ .attrs()
+ .test(Attr::SIMPLE);
}
bool IsSimpleProcedure(const Scope &scope) {
diff --git a/flang/lib/Semantics/check-declarations.cpp b/flang/lib/Semantics/check-declarations.cpp
index 109ea1e4a8c31..e127e67618b47 100644
--- a/flang/lib/Semantics/check-declarations.cpp
+++ b/flang/lib/Semantics/check-declarations.cpp
@@ -1594,6 +1594,7 @@ void CheckHelper::CheckSubprogram(
context_.SetError(symbol);
}
}
+ // F2023 C1553
if (symbol.attrs().test(Attr::SIMPLE) && symbol.attrs().test(Attr::IMPURE)) {
messages_.Say(symbol.name(),
"A procedure may not have both the SIMPLE and IMPURE attributes"_err_en_US);
diff --git a/flang/lib/Semantics/resolve-names.cpp b/flang/lib/Semantics/resolve-names.cpp
index ee18efcaaf7e3..1d305e071f02b 100644
--- a/flang/lib/Semantics/resolve-names.cpp
+++ b/flang/lib/Semantics/resolve-names.cpp
@@ -708,20 +708,10 @@ class ScopeHandler : public ImplicitRulesVisitor {
void SetExplicitAttr(Symbol &symbol, Attr attr) const {
symbol.attrs().set(attr);
symbol.implicitAttrs().reset(attr);
-
- // SIMPLE implies PURE; mark PURE as implicit
- if (attr == Attr::SIMPLE && !symbol.attrs().test(Attr::PURE)) {
- SetImplicitAttr(symbol, Attr::PURE);
- }
}
void SetExplicitAttrs(Symbol &symbol, Attrs attrs) const {
symbol.attrs() |= attrs;
symbol.implicitAttrs() &= ~attrs;
-
- // SIMPLE implies PURE; mark PURE as implicit
- if (attrs.test(Attr::SIMPLE) && !symbol.attrs().test(Attr::PURE)) {
- SetImplicitAttr(symbol, Attr::PURE);
- }
}
void SetImplicitAttr(Symbol &symbol, Attr attr) const {
symbol.attrs().set(attr);
diff --git a/flang/test/Semantics/simple-use-associated.f90 b/flang/test/Semantics/simple-use-associated.f90
new file mode 100644
index 0000000000000..fbcb55d570775
--- /dev/null
+++ b/flang/test/Semantics/simple-use-associated.f90
@@ -0,0 +1,26 @@
+! RUN: %flang_fc1 -fsyntax-only %s
+
+module m
+contains
+ simple subroutine s()
+ end subroutine
+end module
+
+program p
+ use m
+ implicit none
+
+ call needs_simple(s)
+
+contains
+
+ subroutine needs_simple(p)
+ abstract interface
+ simple subroutine simple_proc()
+ end subroutine
+ end interface
+
+ procedure(simple_proc) :: p
+ end subroutine
+
+end program
diff --git a/flang/test/Semantics/simple.f90 b/flang/test/Semantics/simple.f90
deleted file mode 100644
index 5511c730915ac..0000000000000
--- a/flang/test/Semantics/simple.f90
+++ /dev/null
@@ -1,17 +0,0 @@
-! RUN: %flang_fc1 -fdebug-dump-symbols %s 2>&1 | FileCheck %s
-
-module m
-contains
- ! SIMPLE: should end up with both SIMPLE and PURE (PURE implied by SIMPLE)
- simple subroutine s_simple()
- end
-
- ! PURE: should remain PURE, and should not acquire SIMPLE
- pure subroutine s_pure()
- end
-end module
-
-! CHECK-DAG: s_simple, PUBLIC, PURE, SIMPLE
-! CHECK-DAG: s_pure, PUBLIC, PURE
-! CHECK-NOT: s_pure, PUBLIC, PURE, SIMPLE
-
>From 177ce2575fc8429109b16a9c1a71c4a71ea2f952 Mon Sep 17 00:00:00 2001
From: Sarka Holendova <sarka.holendova at gmail.com>
Date: Sun, 26 Apr 2026 19:33:41 -0400
Subject: [PATCH 07/10] [flang] Use test_errors.py for SIMPLE negative
diagnostic test
---
flang/test/Semantics/simple-satisfies-pure-negative.f90 | 7 ++-----
1 file changed, 2 insertions(+), 5 deletions(-)
diff --git a/flang/test/Semantics/simple-satisfies-pure-negative.f90 b/flang/test/Semantics/simple-satisfies-pure-negative.f90
index 3bf3d700f0687..299874508eb4e 100644
--- a/flang/test/Semantics/simple-satisfies-pure-negative.f90
+++ b/flang/test/Semantics/simple-satisfies-pure-negative.f90
@@ -1,7 +1,7 @@
! This is the negative/control case for simple-satisfies-pure.f90.
! It verifies that a procedure which is neither PURE nor SIMPLE is rejected
! when passed to a dummy argument that requires a PURE procedure.
-! RUN: not %flang_fc1 -fsyntax-only %s 2>&1 | FileCheck %s
+! RUN: %python %S/test_errors.py %s %flang_fc1
module m
implicit none
@@ -27,10 +27,7 @@ pure integer function apply_pure(f, x)
end function
integer function test()
+ !ERROR: Actual procedure argument has interface incompatible with dummy argument 'f=': incompatible procedure attributes: Pure
test = apply_pure(impure, 1)
end function
end module
-
-! CHECK: error: Actual procedure argument has interface incompatible with dummy argument 'f='
-! CHECK-SAME: incompatible procedure attributes: Pure
-
>From f72fa43a56e21da12818dd150bd31cd977aec70c Mon Sep 17 00:00:00 2001
From: Sarka Holendova <sarka.holendova at gmail.com>
Date: Wed, 29 Apr 2026 11:38:50 -0400
Subject: [PATCH 08/10] [flang] Add SIMPLE to mod files and consolidate
semantic tests
- Add SIMPLE to subprogramPrefixAttrs in mod-file.cpp so it is
serialized to and read from module files
- Add modfile test to verify SIMPLE is preserved in .mod files
- Consolidate overlapping semantic tests:
* SIMPLE + IMPURE conflict
* PURE does not satisfy SIMPLE
* Neither PURE nor SIMPLE satisfies PURE
into a single test file
- Remove redundant tests replaced by the consolidated coverage
---
flang/docs/F202X.md | 1 -
flang/lib/Semantics/mod-file.cpp | 2 +-
flang/test/Semantics/modfile-simple.f90 | 93 +++++++++++++++++++
.../pure-does-not-satisfy-simple.f90 | 26 ------
.../test/Semantics/simple-impure-conflict.f90 | 6 --
.../simple-pure-attribute-conflicts.f90 | 61 ++++++++++++
.../simple-satisfies-pure-negative.f90 | 33 -------
7 files changed, 155 insertions(+), 67 deletions(-)
create mode 100644 flang/test/Semantics/modfile-simple.f90
delete mode 100644 flang/test/Semantics/pure-does-not-satisfy-simple.f90
delete mode 100644 flang/test/Semantics/simple-impure-conflict.f90
create mode 100644 flang/test/Semantics/simple-pure-attribute-conflicts.f90
delete mode 100644 flang/test/Semantics/simple-satisfies-pure-negative.f90
diff --git a/flang/docs/F202X.md b/flang/docs/F202X.md
index 60c38bd09587f..e62ff68dd4ba7 100644
--- a/flang/docs/F202X.md
+++ b/flang/docs/F202X.md
@@ -209,7 +209,6 @@ a powerful sum data type as in Haskell or Rust. Unlike the
current `ENUM, BIND(C)` feature, `ENUMERATION TYPE` defines a new
type name and its distinct values.
-
This feature may well be the item requiring the largest patch to
the compiler for its implementation, as it affects parsing,
type checking on assignment and argument association, generic
diff --git a/flang/lib/Semantics/mod-file.cpp b/flang/lib/Semantics/mod-file.cpp
index 3bfe1e144f961..f99cc76bed573 100644
--- a/flang/lib/Semantics/mod-file.cpp
+++ b/flang/lib/Semantics/mod-file.cpp
@@ -664,7 +664,7 @@ void ModFileWriter::PutDECStructure(
// Attributes that may be in a subprogram prefix
static const Attrs subprogramPrefixAttrs{Attr::ELEMENTAL, Attr::IMPURE,
- Attr::MODULE, Attr::NON_RECURSIVE, Attr::PURE, Attr::RECURSIVE};
+ Attr::MODULE, Attr::NON_RECURSIVE, Attr::PURE, Attr::SIMPLE, Attr::RECURSIVE};
static void PutOpenACCDeviceTypeRoutineInfo(
llvm::raw_ostream &os, const OpenACCRoutineDeviceTypeInfo &info) {
diff --git a/flang/test/Semantics/modfile-simple.f90 b/flang/test/Semantics/modfile-simple.f90
new file mode 100644
index 0000000000000..6a6712a88c302
--- /dev/null
+++ b/flang/test/Semantics/modfile-simple.f90
@@ -0,0 +1,93 @@
+! RUN: %python %S/test_modfile.py %s %flang_fc1
+! modfile with subprograms
+
+module m1
+ type :: t
+ end type
+contains
+
+ simple subroutine Ss(x, y) bind(c)
+ logical(1) x
+ intent(inout) y
+ intent(in) x
+ end subroutine
+
+ real function f1() result(x)
+ x = 1.0
+ end function
+
+ function f2(y)
+ complex y
+ f2 = 2.0
+ end function
+
+end
+
+module m2
+contains
+ type(t) function f3(x)
+ use m1
+ integer, parameter :: a = 2
+ type t2(b)
+ integer, kind :: b = a
+ integer :: y
+ end type
+ type(t2) :: x
+ end
+ function f4() result(x)
+ implicit complex(x)
+ end
+end
+
+! Module with a subroutine with alternate returns
+module m3
+contains
+ subroutine altReturn(arg1, arg2, *, *)
+ real :: arg1
+ real :: arg2
+ end subroutine
+end module m3
+
+!Expect: m1.mod
+!module m1
+!type::t
+!end type
+!contains
+!simple subroutine ss(x,y) bind(c)
+!logical(1),intent(in)::x
+!real(4),intent(inout)::y
+!end
+!function f1() result(x)
+!real(4)::x
+!end
+!function f2(y)
+!complex(4)::y
+!real(4)::f2
+!end
+!end
+
+!Expect: m2.mod
+!module m2
+!contains
+!function f3(x)
+! use m1,only:t
+! type::t2(b)
+! integer(4),kind::b=2_4
+! integer(4)::y
+! end type
+! type(t2(b=2_4))::x
+! type(t)::f3
+!end
+!function f4() result(x)
+!complex(4)::x
+!end
+!end
+
+!Expect: m3.mod
+!module m3
+!contains
+!subroutine altreturn(arg1,arg2,*,*)
+!real(4)::arg1
+!real(4)::arg2
+!end
+!end
diff --git a/flang/test/Semantics/pure-does-not-satisfy-simple.f90 b/flang/test/Semantics/pure-does-not-satisfy-simple.f90
deleted file mode 100644
index 03f41da6ef4b2..0000000000000
--- a/flang/test/Semantics/pure-does-not-satisfy-simple.f90
+++ /dev/null
@@ -1,26 +0,0 @@
-! RUN: not %flang_fc1 -fsyntax-only %s 2>&1 | FileCheck %s
-
-module pure_not_enough_for_simple
-
- abstract interface
- simple subroutine simple_proc()
- end subroutine
- end interface
-
-contains
-
- subroutine needs_simple(p)
- procedure(simple_proc) :: p
- end subroutine
-
- pure subroutine pure_only()
- end subroutine
-
- subroutine test()
- call needs_simple(pure_only)
- end subroutine
-
-end module
-
-! CHECK: incompatible procedure attributes
-! CHECK: Simple
diff --git a/flang/test/Semantics/simple-impure-conflict.f90 b/flang/test/Semantics/simple-impure-conflict.f90
deleted file mode 100644
index f94a288636f68..0000000000000
--- a/flang/test/Semantics/simple-impure-conflict.f90
+++ /dev/null
@@ -1,6 +0,0 @@
-! RUN: not %flang_fc1 -fsyntax-only %s 2>&1 | FileCheck %s
-
-simple impure subroutine bug()
-end subroutine
-
-! CHECK: may not have both the SIMPLE and IMPURE attributes
diff --git a/flang/test/Semantics/simple-pure-attribute-conflicts.f90 b/flang/test/Semantics/simple-pure-attribute-conflicts.f90
new file mode 100644
index 0000000000000..26ddca086c560
--- /dev/null
+++ b/flang/test/Semantics/simple-pure-attribute-conflicts.f90
@@ -0,0 +1,61 @@
+! RUN: %python %S/test_errors.py %s %flang_fc1
+
+! --- CASE 1: SIMPLE + IMPURE conflict
+!ERROR: A procedure may not have both the SIMPLE and IMPURE attributes
+simple impure subroutine bug()
+end subroutine
+
+! --- CASE 2: PURE does NOT satisfy SIMPLE
+module pure_not_enough_for_simple
+ abstract interface
+ simple subroutine simple_proc()
+ end subroutine
+ end interface
+
+contains
+
+ subroutine needs_simple(p)
+ procedure(simple_proc) :: p
+ end subroutine
+
+ pure subroutine pure_only()
+ end subroutine
+
+ subroutine test()
+ !ERROR: Actual procedure argument has interface incompatible with dummy argument 'p=': incompatible procedure attributes: Simple
+ call needs_simple(pure_only)
+ end subroutine
+end module
+
+! --- CASE 3: Neither PURE nor SIMPLE does NOT satisfy PURE
+module m
+ implicit none
+
+ abstract interface
+ pure integer function iproc(x)
+ integer, intent(in) :: x
+ end function
+ end interface
+
+contains
+
+ ! Neither PURE nor SIMPLE -> must NOT satisfy PURE requirements
+ integer function impure(x)
+ integer, intent(in) :: x
+ impure = x
+ end function
+
+ pure integer function apply_pure(f, x)
+ procedure(iproc) :: f
+ integer, intent(in) :: x
+ apply_pure = f(x)
+ end function
+
+ integer function test()
+ !ERROR: Actual procedure argument has interface incompatible with dummy argument 'f=': incompatible procedure attributes: Pure
+ test = apply_pure(impure, 1)
+ end function
+end module
+
+
+
diff --git a/flang/test/Semantics/simple-satisfies-pure-negative.f90 b/flang/test/Semantics/simple-satisfies-pure-negative.f90
deleted file mode 100644
index 299874508eb4e..0000000000000
--- a/flang/test/Semantics/simple-satisfies-pure-negative.f90
+++ /dev/null
@@ -1,33 +0,0 @@
-! This is the negative/control case for simple-satisfies-pure.f90.
-! It verifies that a procedure which is neither PURE nor SIMPLE is rejected
-! when passed to a dummy argument that requires a PURE procedure.
-! RUN: %python %S/test_errors.py %s %flang_fc1
-
-module m
- implicit none
-
- abstract interface
- pure integer function iproc(x)
- integer, intent(in) :: x
- end function
- end interface
-
-contains
-
- ! Neither PURE nor SIMPLE -> must NOT satisfy PURE requirements
- integer function impure(x)
- integer, intent(in) :: x
- impure = x
- end function
-
- pure integer function apply_pure(f, x)
- procedure(iproc) :: f
- integer, intent(in) :: x
- apply_pure = f(x)
- end function
-
- integer function test()
- !ERROR: Actual procedure argument has interface incompatible with dummy argument 'f=': incompatible procedure attributes: Pure
- test = apply_pure(impure, 1)
- end function
-end module
>From fbe1cca065160f523a2885f921a18ef09aeb51ca Mon Sep 17 00:00:00 2001
From: Sarka Holendova <sarka.holendova at gmail.com>
Date: Wed, 29 Apr 2026 12:02:41 -0400
Subject: [PATCH 09/10] [flang] Apply clang-format to mod-file.cpp
---
flang/lib/Semantics/mod-file.cpp | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/flang/lib/Semantics/mod-file.cpp b/flang/lib/Semantics/mod-file.cpp
index f99cc76bed573..974e88caebc9f 100644
--- a/flang/lib/Semantics/mod-file.cpp
+++ b/flang/lib/Semantics/mod-file.cpp
@@ -664,7 +664,8 @@ void ModFileWriter::PutDECStructure(
// Attributes that may be in a subprogram prefix
static const Attrs subprogramPrefixAttrs{Attr::ELEMENTAL, Attr::IMPURE,
- Attr::MODULE, Attr::NON_RECURSIVE, Attr::PURE, Attr::SIMPLE, Attr::RECURSIVE};
+ Attr::MODULE, Attr::NON_RECURSIVE, Attr::PURE, Attr::SIMPLE,
+ Attr::RECURSIVE};
static void PutOpenACCDeviceTypeRoutineInfo(
llvm::raw_ostream &os, const OpenACCRoutineDeviceTypeInfo &info) {
>From 95600cdcf847fbf45333f36ac481f29b0c3c932f Mon Sep 17 00:00:00 2001
From: Sarka Holendova <sarka.holendova at gmail.com>
Date: Sat, 2 May 2026 12:09:58 -0400
Subject: [PATCH 10/10] [flang] Extend SIMPLE attribute test coverage
- Consolidate simple-satisfies-pure.f90 and simple-use-associated.f90
into simple-positive.f90
- Add coverage for:
* simple pure / pure simple
* simple impure / impure simple conflicts
* SIMPLE functions returning non-trivial CHARACTER results
* SIMPLE separate module procedures (module/simple order)
- Update modfile tests
---
flang/test/Parser/simple-unparse.f90 | 3 +-
flang/test/Parser/simple.f90 | 3 +-
flang/test/Semantics/modfile-simple.f90 | 60 +++++----
flang/test/Semantics/simple-positive.f90 | 119 ++++++++++++++++++
.../simple-pure-attribute-conflicts.f90 | 24 ++--
.../test/Semantics/simple-satisfies-pure.f90 | 33 -----
.../test/Semantics/simple-use-associated.f90 | 26 ----
7 files changed, 159 insertions(+), 109 deletions(-)
create mode 100644 flang/test/Semantics/simple-positive.f90
delete mode 100644 flang/test/Semantics/simple-satisfies-pure.f90
delete mode 100644 flang/test/Semantics/simple-use-associated.f90
diff --git a/flang/test/Parser/simple-unparse.f90 b/flang/test/Parser/simple-unparse.f90
index 4cc52bfeee6cf..a9a90e99d4428 100644
--- a/flang/test/Parser/simple-unparse.f90
+++ b/flang/test/Parser/simple-unparse.f90
@@ -1,8 +1,7 @@
-! RUN: %flang_fc1 -fdebug-unparse-no-sema %s 2>&1 | FileCheck %s
-
! Test that SIMPLE function specifier is recognized
! by the parser and the unparser. This test does not
! exercise semantic checks.
+! RUN: %flang_fc1 -fdebug-unparse-no-sema %s 2>&1 | FileCheck %s
simple function foo()
return
diff --git a/flang/test/Parser/simple.f90 b/flang/test/Parser/simple.f90
index 488909a5550a2..64ef72c8cdc33 100644
--- a/flang/test/Parser/simple.f90
+++ b/flang/test/Parser/simple.f90
@@ -1,6 +1,5 @@
-! RUN: %flang_fc1 -fdebug-dump-parse-tree %s | FileCheck %s
-
! Check that SIMPLE is recognized in the parse tree
+! RUN: %flang_fc1 -fdebug-dump-parse-tree %s | FileCheck %s
simple function foo()
return
diff --git a/flang/test/Semantics/modfile-simple.f90 b/flang/test/Semantics/modfile-simple.f90
index 6a6712a88c302..06525787bcf0f 100644
--- a/flang/test/Semantics/modfile-simple.f90
+++ b/flang/test/Semantics/modfile-simple.f90
@@ -1,52 +1,24 @@
+! Test modfile emission for subprograms, including SIMPLE procedure attributes
! RUN: %python %S/test_modfile.py %s %flang_fc1
-! modfile with subprograms
+! Module with SIMPLE subprograms and function result declarations
module m1
type :: t
end type
contains
-
simple subroutine Ss(x, y) bind(c)
logical(1) x
intent(inout) y
intent(in) x
end subroutine
-
real function f1() result(x)
x = 1.0
end function
-
function f2(y)
complex y
f2 = 2.0
end function
-
-end
-
-module m2
-contains
- type(t) function f3(x)
- use m1
- integer, parameter :: a = 2
- type t2(b)
- integer, kind :: b = a
- integer :: y
- end type
- type(t2) :: x
- end
- function f4() result(x)
- implicit complex(x)
- end
-end
-
-! Module with a subroutine with alternate returns
-module m3
-contains
- subroutine altReturn(arg1, arg2, *, *)
- real :: arg1
- real :: arg2
- end subroutine
-end module m3
+end module m1
!Expect: m1.mod
!module m1
@@ -66,6 +38,23 @@ end module m3
!end
!end
+! Module using m1 with local derived types and implicit typing
+module m2
+contains
+ type(t) function f3(x)
+ use m1
+ integer, parameter :: a = 2
+ type t2(b)
+ integer, kind :: b = a
+ integer :: y
+ end type
+ type(t2) :: x
+ end
+ function f4() result(x)
+ implicit complex(x)
+ end function
+end module m2
+
!Expect: m2.mod
!module m2
!contains
@@ -83,6 +72,15 @@ end module m3
!end
!end
+! Alternate returns; ensure modfile emission unaffected by SIMPLE
+module m3
+contains
+ subroutine altReturn(arg1, arg2, *, *)
+ real :: arg1
+ real :: arg2
+ end subroutine
+end module m3
+
!Expect: m3.mod
!module m3
!contains
diff --git a/flang/test/Semantics/simple-positive.f90 b/flang/test/Semantics/simple-positive.f90
new file mode 100644
index 0000000000000..277fc39a78b7f
--- /dev/null
+++ b/flang/test/Semantics/simple-positive.f90
@@ -0,0 +1,119 @@
+! Test accepted SIMPLE procedure cases.
+! RUN: %flang_fc1 -fsyntax-only %s
+
+! SIMPLE procedures satisfy PURE requirements.
+module simple_satisfies_pure
+ implicit none
+ abstract interface
+ pure integer function pure_iface(x)
+ integer, intent(in) :: x
+ end function
+ end interface
+contains
+ simple integer function simple_impl(x)
+ integer, intent(in) :: x
+ simple_impl = x
+ end function
+
+ pure integer function apply_requires_pure(f, x)
+ procedure(pure_iface) :: f
+ integer, intent(in) :: x
+ apply_requires_pure = f(x)
+ end function
+
+ integer function test()
+ test = apply_requires_pure(simple_impl, 1)
+ end function
+end module
+
+! Use-associated SIMPLE procedures are recognized.
+module simple_use_assoc_m
+contains
+ simple subroutine s()
+ end subroutine
+end module
+
+program simple_use_assoc_p
+ use simple_use_assoc_m
+ implicit none
+ call needs_simple(s)
+contains
+ subroutine needs_simple(p)
+ abstract interface
+ simple subroutine simple_proc()
+ end subroutine
+ end interface
+ procedure(simple_proc) :: p
+ end subroutine
+end program
+
+! SIMPLE functions returning non-trivial CHARACTER results.
+module simple_character_result
+contains
+ simple function fixed_len_character() result(res)
+ character(len=5) :: res
+ res = "hello"
+ end function
+
+ simple function dependent_len_character(x) result(res)
+ character(*), intent(in) :: x
+ character(len=len(x)) :: res
+ res = x
+ end function
+
+ subroutine test()
+ character(len=3) :: a
+ character(len=7) :: b
+ a = dependent_len_character("abc")
+ b = dependent_len_character("testing")
+ end subroutine
+end module
+
+! SIMPLE and PURE may both appear in either order.
+module simple_pure_order
+contains
+ simple pure subroutine simple_pure_sub()
+ end subroutine
+
+ pure simple subroutine pure_simple_sub()
+ end subroutine
+
+ subroutine test()
+ call simple_pure_sub()
+ call pure_simple_sub()
+ end subroutine
+end module
+
+! SIMPLE separate module function is accepted.
+module m_smf
+ interface
+ simple module function f(x) result(res)
+ integer, intent(in) :: x
+ integer :: res
+ end function
+ end interface
+end module
+
+submodule(m_smf) sm_smf
+contains
+ module procedure f
+ res = x
+ end procedure
+end submodule
+
+! MODULE SIMPLE function prefix order is accepted.
+module m_msf
+ interface
+ module simple function g(x) result(res)
+ integer, intent(in) :: x
+ integer :: res
+ end function
+ end interface
+end module
+
+submodule(m_msf) sm_msf
+contains
+ module procedure g
+ res = x
+ end procedure
+end submodule
diff --git a/flang/test/Semantics/simple-pure-attribute-conflicts.f90 b/flang/test/Semantics/simple-pure-attribute-conflicts.f90
index 26ddca086c560..bcc7a665fefbf 100644
--- a/flang/test/Semantics/simple-pure-attribute-conflicts.f90
+++ b/flang/test/Semantics/simple-pure-attribute-conflicts.f90
@@ -1,3 +1,5 @@
+! Test invalid SIMPLE procedure attribute combinations and procedure argument
+! compatibility checks.
! RUN: %python %S/test_errors.py %s %flang_fc1
! --- CASE 1: SIMPLE + IMPURE conflict
@@ -5,57 +7,49 @@
simple impure subroutine bug()
end subroutine
-! --- CASE 2: PURE does NOT satisfy SIMPLE
+! --- CASE 2: IMPURE + SIMPLE conflict
+!ERROR: A procedure may not have both the SIMPLE and IMPURE attributes
+impure simple subroutine bug2()
+end subroutine
+
+! --- CASE 3: PURE does NOT satisfy SIMPLE
module pure_not_enough_for_simple
abstract interface
simple subroutine simple_proc()
end subroutine
end interface
-
contains
-
subroutine needs_simple(p)
procedure(simple_proc) :: p
end subroutine
-
pure subroutine pure_only()
end subroutine
-
subroutine test()
!ERROR: Actual procedure argument has interface incompatible with dummy argument 'p=': incompatible procedure attributes: Simple
call needs_simple(pure_only)
end subroutine
end module
-! --- CASE 3: Neither PURE nor SIMPLE does NOT satisfy PURE
+! --- CASE 4: Neither PURE nor SIMPLE does NOT satisfy PURE
module m
implicit none
-
abstract interface
pure integer function iproc(x)
integer, intent(in) :: x
end function
end interface
-
contains
-
- ! Neither PURE nor SIMPLE -> must NOT satisfy PURE requirements
integer function impure(x)
integer, intent(in) :: x
impure = x
end function
-
pure integer function apply_pure(f, x)
procedure(iproc) :: f
integer, intent(in) :: x
apply_pure = f(x)
end function
-
integer function test()
!ERROR: Actual procedure argument has interface incompatible with dummy argument 'f=': incompatible procedure attributes: Pure
test = apply_pure(impure, 1)
end function
end module
-
-
-
diff --git a/flang/test/Semantics/simple-satisfies-pure.f90 b/flang/test/Semantics/simple-satisfies-pure.f90
deleted file mode 100644
index d08b758538530..0000000000000
--- a/flang/test/Semantics/simple-satisfies-pure.f90
+++ /dev/null
@@ -1,33 +0,0 @@
-! This test verifies that SIMPLE procedures satisfy PURE requirements.
-! (i.e. anywhere the language requires a PURE procedure, a SIMPLE one is accepted)
-! RUN: %flang_fc1 -fsyntax-only %s
-
-module m
- implicit none
-
- abstract interface
- ! Dummy procedure explicitly requires PURE
- pure integer function pure_iface(x)
- integer, intent(in) :: x
- end function
- end interface
-
-contains
-
- ! SIMPLE procedure (should be accepted where PURE is required)
- simple integer function simple_impl(x)
- integer, intent(in) :: x
- simple_impl = x
- end function
-
- pure integer function apply_requires_pure(f, x)
- procedure(pure_iface) :: f
- integer, intent(in) :: x
- apply_requires_pure = f(x)
- end function
-
- integer function test()
- test = apply_requires_pure(simple_impl, 1)
- end function
-end module
-
diff --git a/flang/test/Semantics/simple-use-associated.f90 b/flang/test/Semantics/simple-use-associated.f90
deleted file mode 100644
index fbcb55d570775..0000000000000
--- a/flang/test/Semantics/simple-use-associated.f90
+++ /dev/null
@@ -1,26 +0,0 @@
-! RUN: %flang_fc1 -fsyntax-only %s
-
-module m
-contains
- simple subroutine s()
- end subroutine
-end module
-
-program p
- use m
- implicit none
-
- call needs_simple(s)
-
-contains
-
- subroutine needs_simple(p)
- abstract interface
- simple subroutine simple_proc()
- end subroutine
- end interface
-
- procedure(simple_proc) :: p
- end subroutine
-
-end program
More information about the flang-commits
mailing list