[flang-commits] [flang] [llvm] [flang] Implement 'F_C_STRING' library function (Fortran 2023) (PR #174474)

Caroline Newcombe via flang-commits flang-commits at lists.llvm.org
Tue Jan 6 09:37:36 PST 2026


https://github.com/cenewcombe updated https://github.com/llvm/llvm-project/pull/174474

>From e071e86d4414e754d9acb8000a27ffd92d39b9f2 Mon Sep 17 00:00:00 2001
From: Caroline Newcombe <caroline.newcombe at hpe.com>
Date: Wed, 17 Dec 2025 15:04:20 -0600
Subject: [PATCH 1/2] [flang] Implement 'F_C_STRING' library function (Fortran
 2023)

---
 flang-rt/lib/runtime/CMakeLists.txt   |  1 +
 flang/module/iso_c_binding.f90        | 16 ++++++-
 flang/test/Integration/f_c_string.f90 | 60 +++++++++++++++++++++++++++
 3 files changed, 76 insertions(+), 1 deletion(-)
 create mode 100644 flang/test/Integration/f_c_string.f90

diff --git a/flang-rt/lib/runtime/CMakeLists.txt b/flang-rt/lib/runtime/CMakeLists.txt
index 7fa8c2cb95417..d18f24e6c786a 100644
--- a/flang-rt/lib/runtime/CMakeLists.txt
+++ b/flang-rt/lib/runtime/CMakeLists.txt
@@ -75,6 +75,7 @@ set(supported_sources
 # List of source not used for GPU offloading.
 set(host_sources
   ${FLANG_SOURCE_DIR}/module/iso_fortran_env_impl.f90
+  ${FLANG_SOURCE_DIR}/module/iso_c_binding.f90
   command.cpp
   complex-powi.cpp
   complex-reduction.c
diff --git a/flang/module/iso_c_binding.f90 b/flang/module/iso_c_binding.f90
index 8e3f78cea51b7..34e7b6280f54d 100644
--- a/flang/module/iso_c_binding.f90
+++ b/flang/module/iso_c_binding.f90
@@ -29,7 +29,7 @@ module iso_c_binding
   private
 
   public :: c_associated, c_funloc, c_funptr, c_f_pointer, c_loc, &
-    c_null_funptr, c_null_ptr, c_ptr, c_sizeof, &
+    c_null_funptr, c_null_ptr, c_ptr, c_sizeof, f_c_string, &
     operator(==), operator(/=)
 
   ! Table 18.2 (in clause 18.3.1)
@@ -145,4 +145,18 @@ subroutine c_f_procpointer(cptr, fptr)
     ! TODO: implement
   end subroutine c_f_procpointer
 
+  ! F_C_STRING - Convert Fortran string to C null-terminated string
+  ! Fortran 2023 standard intrinsic
+  pure function f_c_string(string, asis) result(res)
+    character(kind=c_char, len=*), intent(in) :: string
+    logical, optional, intent(in) :: asis
+    character(kind=c_char, len=:), allocatable :: res
+
+    if (present(asis) .and. asis) then
+      res = string // c_null_char
+    else
+      res = trim(string) // c_null_char
+    end if
+  end function f_c_string
+
 end module iso_c_binding
diff --git a/flang/test/Integration/f_c_string.f90 b/flang/test/Integration/f_c_string.f90
new file mode 100644
index 0000000000000..79ac10556a740
--- /dev/null
+++ b/flang/test/Integration/f_c_string.f90
@@ -0,0 +1,60 @@
+! RUN: %flang %s -o %t && %t | FileCheck %s
+! Test F_C_STRING library function
+
+program test_f_c_string
+  use iso_c_binding
+  implicit none
+  
+  character(len=20) :: str
+  character(len=:), allocatable :: result
+  logical :: flag
+  
+  ! Test 1: Basic trimming
+  str = 'hello     '
+  result = f_c_string(str(1:10))
+  ! CHECK: Test 1: 6
+  print '(A,I0)', 'Test 1: ', len(result)
+  if (result /= 'hello' // c_null_char) error stop 'Test 1 failed'
+  
+  ! Test 2: ASIS=.TRUE. (keep blanks)
+  result = f_c_string(str(1:10), .true.)
+  ! CHECK: Test 2: 11
+  print '(A,I0)', 'Test 2: ', len(result)
+  if (result /= 'hello     ' // c_null_char) error stop 'Test 2 failed'
+  
+  ! Test 3: ASIS=.FALSE. (explicit trim)
+  result = f_c_string(str(1:10), .false.)
+  ! CHECK: Test 3: 6
+  print '(A,I0)', 'Test 3: ', len(result)
+  if (result /= 'hello' // c_null_char) error stop 'Test 3 failed'
+  
+  ! Test 4: Variable ASIS
+  flag = .true.
+  str = 'abc   '
+  result = f_c_string(str(1:6), flag)
+  ! CHECK: Test 4: 7
+  print '(A,I0)', 'Test 4: ', len(result)
+  if (len(result) /= 7) error stop 'Test 4 failed'
+  
+  flag = .false.
+  result = f_c_string(str(1:6), flag)
+  ! CHECK: Test 5: 4
+  print '(A,I0)', 'Test 5: ', len(result)
+  if (len(result) /= 4) error stop 'Test 5 failed'
+  
+  ! Test 6: Empty (all blanks)
+  result = f_c_string('     ')
+  ! CHECK: Test 6: 1
+  print '(A,I0)', 'Test 6: ', len(result)
+  if (result /= c_null_char) error stop 'Test 6 failed'
+  
+  ! Test 7: Internal blanks preserved
+  result = f_c_string('a b c   ')
+  ! CHECK: Test 7: 6
+  print '(A,I0)', 'Test 7: ', len(result)
+  if (result /= 'a b c' // c_null_char) error stop 'Test 7 failed'
+  
+  ! CHECK: PASS
+  print *, 'PASS'
+  
+end program test_f_c_string

>From 9d061994b4648e8fa5c3da64d384c0dadbc4ebb6 Mon Sep 17 00:00:00 2001
From: Caroline Newcombe <caroline.newcombe at hpe.com>
Date: Tue, 6 Jan 2026 11:37:21 -0600
Subject: [PATCH 2/2] Swap the integration test for semantics checking, due to
 testing limitations

---
 flang/test/Integration/f_c_string.f90 | 60 ---------------------------
 flang/test/Semantics/f_c_string.f90   | 39 +++++++++++++++++
 2 files changed, 39 insertions(+), 60 deletions(-)
 delete mode 100644 flang/test/Integration/f_c_string.f90
 create mode 100644 flang/test/Semantics/f_c_string.f90

diff --git a/flang/test/Integration/f_c_string.f90 b/flang/test/Integration/f_c_string.f90
deleted file mode 100644
index 79ac10556a740..0000000000000
--- a/flang/test/Integration/f_c_string.f90
+++ /dev/null
@@ -1,60 +0,0 @@
-! RUN: %flang %s -o %t && %t | FileCheck %s
-! Test F_C_STRING library function
-
-program test_f_c_string
-  use iso_c_binding
-  implicit none
-  
-  character(len=20) :: str
-  character(len=:), allocatable :: result
-  logical :: flag
-  
-  ! Test 1: Basic trimming
-  str = 'hello     '
-  result = f_c_string(str(1:10))
-  ! CHECK: Test 1: 6
-  print '(A,I0)', 'Test 1: ', len(result)
-  if (result /= 'hello' // c_null_char) error stop 'Test 1 failed'
-  
-  ! Test 2: ASIS=.TRUE. (keep blanks)
-  result = f_c_string(str(1:10), .true.)
-  ! CHECK: Test 2: 11
-  print '(A,I0)', 'Test 2: ', len(result)
-  if (result /= 'hello     ' // c_null_char) error stop 'Test 2 failed'
-  
-  ! Test 3: ASIS=.FALSE. (explicit trim)
-  result = f_c_string(str(1:10), .false.)
-  ! CHECK: Test 3: 6
-  print '(A,I0)', 'Test 3: ', len(result)
-  if (result /= 'hello' // c_null_char) error stop 'Test 3 failed'
-  
-  ! Test 4: Variable ASIS
-  flag = .true.
-  str = 'abc   '
-  result = f_c_string(str(1:6), flag)
-  ! CHECK: Test 4: 7
-  print '(A,I0)', 'Test 4: ', len(result)
-  if (len(result) /= 7) error stop 'Test 4 failed'
-  
-  flag = .false.
-  result = f_c_string(str(1:6), flag)
-  ! CHECK: Test 5: 4
-  print '(A,I0)', 'Test 5: ', len(result)
-  if (len(result) /= 4) error stop 'Test 5 failed'
-  
-  ! Test 6: Empty (all blanks)
-  result = f_c_string('     ')
-  ! CHECK: Test 6: 1
-  print '(A,I0)', 'Test 6: ', len(result)
-  if (result /= c_null_char) error stop 'Test 6 failed'
-  
-  ! Test 7: Internal blanks preserved
-  result = f_c_string('a b c   ')
-  ! CHECK: Test 7: 6
-  print '(A,I0)', 'Test 7: ', len(result)
-  if (result /= 'a b c' // c_null_char) error stop 'Test 7 failed'
-  
-  ! CHECK: PASS
-  print *, 'PASS'
-  
-end program test_f_c_string
diff --git a/flang/test/Semantics/f_c_string.f90 b/flang/test/Semantics/f_c_string.f90
new file mode 100644
index 0000000000000..172cbc49c7c8c
--- /dev/null
+++ b/flang/test/Semantics/f_c_string.f90
@@ -0,0 +1,39 @@
+! RUN: %python %S/test_errors.py %s %flang_fc1
+! Test semantic checking of F_C_STRING from ISO_C_BINDING
+
+program test
+  use iso_c_binding
+  implicit none
+  
+  character(len=20) :: str
+  character(len=:), allocatable :: result
+  logical :: flag
+  integer :: n
+  
+  ! Valid usages
+  result = f_c_string('hello')
+  result = f_c_string(str)
+  result = f_c_string(str, .true.)
+  result = f_c_string(str, .false.)
+  result = f_c_string(str, flag)
+  result = f_c_string(string=str)
+  result = f_c_string(string=str, asis=.true.)
+  result = f_c_string(asis=.false., string=str)
+  
+  ! Invalid: missing required argument
+  !ERROR: missing mandatory 'string=' argument
+  result = f_c_string()
+  
+  ! Invalid: too many arguments
+  !ERROR: No intrinsic or generic 'f_c_string' matches the actual arguments
+  result = f_c_string(str, .true., .false.)
+  
+  ! Invalid: non-character first argument
+  !ERROR: No intrinsic or generic 'f_c_string' matches the actual arguments
+  result = f_c_string(n)
+  
+  ! Invalid: non-logical second argument
+  !ERROR: No intrinsic or generic 'f_c_string' matches the actual arguments
+  result = f_c_string(str, n)
+  
+end program



More information about the flang-commits mailing list