[flang-commits] [flang] [flang] Classify intrinsic functions as SIMPLE (PR #223160)

Šárka Holendová via flang-commits flang-commits at lists.llvm.org
Fri Sep 18 12:00:35 PDT 2026


https://github.com/mlir-maiden updated https://github.com/llvm/llvm-project/pull/223160

>From 88c5244ee60079ec6d90429f850b9badb98d4f32 Mon Sep 17 00:00:00 2001
From: Sarka Holendova <sarka.holendova at gmail.com>
Date: Sat, 12 Sep 2026 11:31:23 -0400
Subject: [PATCH 1/2] [flang] Classify intrinsic functions as SIMPLE

Classify standard intrinsic functions as SIMPLE per Fortran 2023 16.1(2).

This change also:
- Classifies extensions as SIMPLE where applicable
- Documents the SIMPLE and non-SIMPLE classifications of extensions in
  Extensions.md
- Updates procedure-interface tests affected by the new SIMPLE classification
- Updates F202X.md to reflect the current SIMPLE implementation status

Part of the SIMPLE procedure support tracked in #221457.
---
 flang/docs/Extensions.md                      | 10 +++++++++
 flang/docs/F202X.md                           | 10 +++++++--
 flang/lib/Evaluate/intrinsics.cpp             | 22 ++++++++++++++++---
 flang/test/Semantics/intrinsics03.f90         |  8 +++----
 flang/test/Semantics/procinterface01.f90      |  4 ++--
 flang/test/Semantics/procinterface02.f90      |  6 ++---
 .../Semantics/simple-intrinsic-functions.f90  | 18 +++++++++++++++
 7 files changed, 64 insertions(+), 14 deletions(-)
 create mode 100644 flang/test/Semantics/simple-intrinsic-functions.f90

diff --git a/flang/docs/Extensions.md b/flang/docs/Extensions.md
index 0b16348074cfc..a9b1c66c6d1a3 100644
--- a/flang/docs/Extensions.md
+++ b/flang/docs/Extensions.md
@@ -570,6 +570,16 @@ end program
   rather than an allocatable, it is interpreted as `ASSOCIATED(p)` with a
   stern warning.
 
+### `SIMPLE` classification of extensions
+
+The following extensions are `SIMPLE`: `IARGC`, `INT2`, `INT8`, `INT_PTR_KIND`, `ISNAN`,
+`IZEXT`, `JZEXT`, `LNBLNK`, `LOC`, `SELECTED_UNSIGNED_KIND`, `SIZEOF`, `UINT`, `UMASKL`,
+and `UMASKR`. All specific intrinsic extensions are also `SIMPLE`.
+
+The following extensions are not classified as `SIMPLE`: `CHDIR`, `DSECNDS`, `ETIME`,
+`FSEEK`, `FTELL`, `GETCWD`, `GETGID`, `GETPID`, `GETUID`, `HOSTNM`, `IRAND`, `MALLOC`,
+`PUTENV`, `RAND`, `RENAME`, `RTC`, `SECNDS`, `SECOND`, `SYSTEM`, `TIME`, `TIMEF`, and `UNLINK`.
+
 ### Extensions supported when enabled by options
 
 * C-style backslash escape sequences in quoted CHARACTER literals
diff --git a/flang/docs/F202X.md b/flang/docs/F202X.md
index 7d0cf26dbaa2c..b9ef4187518c4 100644
--- a/flang/docs/F202X.md
+++ b/flang/docs/F202X.md
@@ -175,8 +175,14 @@ This makes `SIMPLE` a lower-priority feature.
 - `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)
+- Additional `SIMPLE` semantic constraints remain to be implemented (see #221457)
+- F2023 16.1(2): All standard intrinsic functions are classified as `SIMPLE`
+- F2023 16.1(5): `MVBITS`, `SPLIT`, and both forms of `TOKENIZE` are classified
+  as `SIMPLE` intrinsic subroutines
+- F2023 16.1(5): Conditional `SIMPLE` classification of `MOVE_ALLOC` is not yet
+  implemented (planned as follow-up work)
+- Intrinsic module procedures are not yet classified as `SIMPLE`
+- Lowering does not yet propagate the `SIMPLE` attribute to FIR procedure attributes (see #222360)
 
 #### Conditional expressions and actual arguments
 
diff --git a/flang/lib/Evaluate/intrinsics.cpp b/flang/lib/Evaluate/intrinsics.cpp
index abfb48e5ad82b..7c543e6dc0309 100644
--- a/flang/lib/Evaluate/intrinsics.cpp
+++ b/flang/lib/Evaluate/intrinsics.cpp
@@ -19,6 +19,7 @@
 #include "flang/Semantics/scope.h"
 #include "flang/Semantics/tools.h"
 #include "flang/Support/Fortran.h"
+#include "llvm/ADT/StringSet.h"
 #include "llvm/Support/raw_ostream.h"
 #include <algorithm>
 #include <climits>
@@ -1157,6 +1158,14 @@ static const IntrinsicInterface genericIntrinsicFunction[]{
     {"__builtin_numeric_storage_size", {}, DefaultInt},
 };
 
+// Extensions that are not SIMPLE: they have side
+// effects, or their result varies with external state (clock, RNG, file,
+// process/user/group identity). See docs/Extensions.md.
+static const llvm::StringSet<> notSimpleExtensionFunctions{"chdir", "dsecnds",
+    "etime", "fseek", "ftell", "getcwd", "getgid", "getpid", "getuid", "hostnm",
+    "irand", "malloc", "putenv", "rand", "rename", "rtc", "secnds", "second",
+    "system", "time", "timef", "unlink"};
+
 // TODO: Non-standard intrinsic functions
 //  SHIFT,
 //  COMPL, EQV, NEQV, INT8, JINT, JNINT, KNINT,
@@ -2889,9 +2898,14 @@ std::optional<SpecificCall> IntrinsicInterface::Match(
             name, characteristics::Procedure{std::move(dummyArgs), attrs}},
         std::move(rearranged)};
   } else {
-    // TODO: Mark intrinsic functions that are SIMPLE per F2023
-    if (intrinsicClass != IntrinsicClass::impureFunction /* RAND and IRAND */)
+    if (intrinsicClass != IntrinsicClass::impureFunction /* RAND and IRAND */) {
       attrs.set(characteristics::Procedure::Attr::Pure);
+      // F2023 16.1: standard intrinsic functions are SIMPLE. The extensions in
+      // notSimpleExtensionFunctions are excluded because they are not SIMPLE.
+      if (!notSimpleExtensionFunctions.contains(name)) {
+        attrs.set(characteristics::Procedure::Attr::Simple);
+      }
+    }
     characteristics::TypeAndShape typeAndShape{resultType.value(), resultRank};
     characteristics::FunctionResult funcResult{std::move(typeAndShape)};
     characteristics::Procedure chars{
@@ -4129,7 +4143,9 @@ IntrinsicProcTable::Implementation::IsSpecificIntrinsicFunction(
           std::string{specific.dummy[j].keyword}, std::move(dummy));
     }
     characteristics::Procedure::Attrs attrs;
-    attrs.set(characteristics::Procedure::Attr::Pure)
+    // F2023 16.1: specific intrinsic functions are SIMPLE
+    attrs.set(characteristics::Procedure::Attr::Simple)
+        .set(characteristics::Procedure::Attr::Pure)
         .set(characteristics::Procedure::Attr::Elemental);
     characteristics::Procedure chars{
         std::move(fResult), std::move(args), attrs};
diff --git a/flang/test/Semantics/intrinsics03.f90 b/flang/test/Semantics/intrinsics03.f90
index 1a4269868a3d4..801d44a0e97ec 100644
--- a/flang/test/Semantics/intrinsics03.f90
+++ b/flang/test/Semantics/intrinsics03.f90
@@ -3,16 +3,16 @@
 
 program test
   interface
-    pure integer function index1(string, substring)
+    simple integer function index1(string, substring)
       character(*), intent(in) :: string, substring ! ok
     end
-    pure integer function index2(x1, x2)
+    simple integer function index2(x1, x2)
       character(*), intent(in) :: x1, x2 ! ok
     end
-    pure integer function index3(string, substring)
+    simple integer function index3(string, substring)
       character, intent(in) :: string, substring ! not assumed length
     end
-    pure integer function index4(string, substring, back)
+    simple integer function index4(string, substring, back)
       character(*), intent(in) :: string, substring
       logical, optional, intent(in) :: back ! not ok
     end
diff --git a/flang/test/Semantics/procinterface01.f90 b/flang/test/Semantics/procinterface01.f90
index 70f4a889d6809..fecbf593ab6d8 100644
--- a/flang/test/Semantics/procinterface01.f90
+++ b/flang/test/Semantics/procinterface01.f90
@@ -48,7 +48,7 @@ end function tan
  type :: derived1
   !REF: /module1/abstract1
   !DEF: /module1/derived1/p1 NOPASS, POINTER (Function) ProcEntity REAL(4)
-  !DEF: /module1/nested1 PUBLIC, PURE (Function) Subprogram REAL(4)
+  !DEF: /module1/nested1 PUBLIC, SIMPLE (Function) Subprogram REAL(4)
   procedure(abstract1), pointer, nopass :: p1 => nested1
   !REF: /module1/explicit1
   !DEF: /module1/derived1/p2 NOPASS, POINTER (Function) ProcEntity REAL(4)
@@ -81,7 +81,7 @@ end function tan
 
  !REF: /module1/nested1
  !DEF: /module1/nested1/x INTENT(IN) ObjectEntity REAL(4)
- pure real function nested1(x)
+ simple real function nested1(x)
   !REF: /module1/nested1/x
   real, intent(in) :: x
   !DEF: /module1/nested1/nested1 ObjectEntity REAL(4)
diff --git a/flang/test/Semantics/procinterface02.f90 b/flang/test/Semantics/procinterface02.f90
index 6aa5dc13bd9a5..2acfb8ea8a836 100644
--- a/flang/test/Semantics/procinterface02.f90
+++ b/flang/test/Semantics/procinterface02.f90
@@ -1,10 +1,10 @@
 ! RUN: %python %S/test_errors.py %s %flang_fc1 -pedantic
 subroutine foo(A, B, P)
   interface
-    real elemental function foo_elemental(x)
+    simple elemental real function foo_elemental(x)
       real, intent(in) :: x
     end function
-    pure real function foo_pure(x)
+    simple real function foo_simple(x)
       real, intent(in) :: x
     end function
     real function foo_nonelemental(x)
@@ -18,7 +18,7 @@ real function foo_nonelemental(x)
   A = P(B)
   !ERROR: Procedure pointer 'p' associated with incompatible procedure designator 'foo_elemental': incompatible procedure attributes: Elemental
   P => foo_elemental
-  P => foo_pure ! ok
+  P => foo_simple ! ok
   !ERROR: PURE procedure pointer 'p' may not be associated with non-PURE procedure designator 'foo_nonelemental'
   P => foo_nonelemental
 end subroutine
diff --git a/flang/test/Semantics/simple-intrinsic-functions.f90 b/flang/test/Semantics/simple-intrinsic-functions.f90
new file mode 100644
index 0000000000000..85564501b57b9
--- /dev/null
+++ b/flang/test/Semantics/simple-intrinsic-functions.f90
@@ -0,0 +1,18 @@
+! Test that intrinsic functions are classified SIMPLE (F2023 16.1)
+! RUN: %flang_fc1 -fsyntax-only %s
+
+! An intrinsic function is SIMPLE and can target a SIMPLE procedure pointer
+module simple_intrinsic_function
+  implicit none
+  abstract interface
+    simple real function ifc(x)
+      real, intent(in) :: x
+    end function
+  end interface
+contains
+  subroutine test()
+    procedure(ifc), pointer :: sp
+    intrinsic :: sin
+    sp => sin
+  end subroutine
+end module

>From 4aebca6777fbc199e5de7b1cdeb3aa1462ecafcb Mon Sep 17 00:00:00 2001
From: Sarka Holendova <sarka.holendova at gmail.com>
Date: Fri, 18 Sep 2026 14:58:02 -0400
Subject: [PATCH 2/2] [flang] Fix SIMPLE classification for NULL() and static
 init of non-SIMPLE extension set

- Avoid a static constructor for the non-SIMPLE extension set
- Classify NULL() as SIMPLE
- Document the procedure interface compatibility change
---
 flang/docs/ReleaseNotes.md        |  6 ++++++
 flang/lib/Evaluate/intrinsics.cpp | 18 ++++++++----------
 2 files changed, 14 insertions(+), 10 deletions(-)

diff --git a/flang/docs/ReleaseNotes.md b/flang/docs/ReleaseNotes.md
index 76a6b95dd3ecc..20a906bd7e5f8 100644
--- a/flang/docs/ReleaseNotes.md
+++ b/flang/docs/ReleaseNotes.md
@@ -100,6 +100,12 @@ page](https://llvm.org/releases/).
 ## Windows Support
 
 ## Fortran Language Changes in Flang
+- Standard intrinsic functions are now classified as `SIMPLE` per Fortran
+  2023 16.1(2). This may be a breaking change for code that defines its own
+  interface for an intrinsic (for example, to declare a procedure pointer
+  or dummy procedure) using `PURE` rather than `SIMPLE`, since the procedure
+  characteristics will no longer match. Such code will need to update the
+  hand-written interface to specify `SIMPLE`.
 
 ## Build System Changes
 
diff --git a/flang/lib/Evaluate/intrinsics.cpp b/flang/lib/Evaluate/intrinsics.cpp
index 7c543e6dc0309..45ace730f8071 100644
--- a/flang/lib/Evaluate/intrinsics.cpp
+++ b/flang/lib/Evaluate/intrinsics.cpp
@@ -1158,14 +1158,6 @@ static const IntrinsicInterface genericIntrinsicFunction[]{
     {"__builtin_numeric_storage_size", {}, DefaultInt},
 };
 
-// Extensions that are not SIMPLE: they have side
-// effects, or their result varies with external state (clock, RNG, file,
-// process/user/group identity). See docs/Extensions.md.
-static const llvm::StringSet<> notSimpleExtensionFunctions{"chdir", "dsecnds",
-    "etime", "fseek", "ftell", "getcwd", "getgid", "getpid", "getuid", "hostnm",
-    "irand", "malloc", "putenv", "rand", "rename", "rtc", "secnds", "second",
-    "system", "time", "timef", "unlink"};
-
 // TODO: Non-standard intrinsic functions
 //  SHIFT,
 //  COMPL, EQV, NEQV, INT8, JINT, JNINT, KNINT,
@@ -2900,8 +2892,13 @@ std::optional<SpecificCall> IntrinsicInterface::Match(
   } else {
     if (intrinsicClass != IntrinsicClass::impureFunction /* RAND and IRAND */) {
       attrs.set(characteristics::Procedure::Attr::Pure);
-      // F2023 16.1: standard intrinsic functions are SIMPLE. The extensions in
-      // notSimpleExtensionFunctions are excluded because they are not SIMPLE.
+      // F2023 16.1: standard intrinsic functions are SIMPLE. Extensions that
+      // are not SIMPLE: they have side effects, or their result varies with
+      // external state. See docs/Extensions.md.
+      static const llvm::StringSet<> notSimpleExtensionFunctions{"chdir",
+          "dsecnds", "etime", "fseek", "ftell", "getcwd", "getgid", "getpid",
+          "getuid", "hostnm", "irand", "malloc", "putenv", "rand", "rename",
+          "rtc", "secnds", "second", "system", "time", "timef", "unlink"};
       if (!notSimpleExtensionFunctions.contains(name)) {
         attrs.set(characteristics::Procedure::Attr::Simple);
       }
@@ -3179,6 +3176,7 @@ SpecificCall IntrinsicProcTable::Implementation::HandleNull(
   characteristics::Procedure::Attrs attrs;
   attrs.set(characteristics::Procedure::Attr::NullPointer);
   attrs.set(characteristics::Procedure::Attr::Pure);
+  attrs.set(characteristics::Procedure::Attr::Simple);
   arguments.clear();
   return SpecificCall{
       SpecificIntrinsic{"null"s,



More information about the flang-commits mailing list