Fortran: fix namelist read for input with comments [PR125095]

Message ID 87385a0d-18df-4839-9947-e8f363458468@gmx.de
State New
Headers
Series Fortran: fix namelist read for input with comments [PR125095] |

Commit Message

Harald Anlauf May 5, 2026, 8:17 p.m. UTC
  Dear all,

namelist input of arrays got confused when there were comments
after value separators.  The obvious fix is to cleanly skip the
comments and eat subsequent whitespace.  The attached conservative
patch fixes this for all basic types.

Tested on x86_64-pc-linux-gnu for gfortran.dg/namelist*.

OK for mainline / backports?

Thanks,
Harald
  

Comments

Jerry D May 5, 2026, 9:57 p.m. UTC | #1
On 5/5/26 1:17 PM, Harald Anlauf wrote:
> Dear all,
> 
> namelist input of arrays got confused when there were comments
> after value separators.  The obvious fix is to cleanly skip the
> comments and eat subsequent whitespace.  The attached conservative
> patch fixes this for all basic types.
> 
> Tested on x86_64-pc-linux-gnu for gfortran.dg/namelist*.
> 
> OK for mainline / backports?
> 
> Thanks,
> Harald
> 

Looks good, mainline and backports.

Much appreciated,

Jerry
  
Harald Anlauf May 6, 2026, 7:06 p.m. UTC | #2
Hi Jerry!

Am 05.05.26 um 11:57 PM schrieb Jerry D:
> On 5/5/26 1:17 PM, Harald Anlauf wrote:
>> Dear all,
>>
>> namelist input of arrays got confused when there were comments
>> after value separators.  The obvious fix is to cleanly skip the
>> comments and eat subsequent whitespace.  The attached conservative
>> patch fixes this for all basic types.
>>
>> Tested on x86_64-pc-linux-gnu for gfortran.dg/namelist*.
>>
>> OK for mainline / backports?
>>
>> Thanks,
>> Harald
>>
> 
> Looks good, mainline and backports.

Thanks for the review!

Pushed as r17-367-g202ca69360af7f to mainline so far.

Harald

> Much appreciated,
> 
> Jerry
>
  

Patch

From 9f981df194cd8dba1d9fe491596cd26232ea13a5 Mon Sep 17 00:00:00 2001
From: Harald Anlauf <anlauf@gmx.de>
Date: Tue, 5 May 2026 22:00:43 +0200
Subject: [PATCH] Fortran: fix namelist read with comments [PR125095]

Namelist input may contain comments (initiated with a "!") after a
separator or in the first nonblank position of a namelist input record.
Skip comments until end of line, and eat leading whitespace in a subsequent
input record.

	PR libfortran/125095

libgfortran/ChangeLog:

	* io/list_read.c (read_logical): Eat comments in namelist read mode.
	(read_integer): Likewise.
	(read_character): Likewise.
	(read_complex): Likewise.
	(read_real): Likewise.

gcc/testsuite/ChangeLog:

	* gfortran.dg/namelist_102.f90: New test.
---
 gcc/testsuite/gfortran.dg/namelist_102.f90 | 248 +++++++++++++++++++++
 libgfortran/io/list_read.c                 |  26 +++
 2 files changed, 274 insertions(+)
 create mode 100644 gcc/testsuite/gfortran.dg/namelist_102.f90

diff --git a/gcc/testsuite/gfortran.dg/namelist_102.f90 b/gcc/testsuite/gfortran.dg/namelist_102.f90
new file mode 100644
index 00000000000..66c3809439e
--- /dev/null
+++ b/gcc/testsuite/gfortran.dg/namelist_102.f90
@@ -0,0 +1,248 @@ 
+! { dg-do run }
+! { dg-additional-options "-O2" }
+!
+! PR fortran/125095 - test namelist read with comments
+!
+! Based on testcases by Andy Nelson and Steven G. Kargl
+
+program nmlbug
+  implicit none
+  call test_int
+  call test_real
+  call test_complex
+  call test_logical
+  call test_char
+
+contains
+
+  subroutine test_int
+
+    integer :: array(4), barray(4), carray(4), darray(4)
+
+    namelist/nml1/  array
+    namelist/nml2/ barray
+    namelist/nml3/ carray
+    namelist/nml4/ darray
+
+    open(10,status='scratch')
+    write(10,'(A)') '&nml1'
+    write(10,'(A)') '  array = 1, 2, 3, 4,'
+    write(10,'(A)') '/'
+    write(10,*)
+    write(10,'(A)') '&nml2'
+    write(10,'(A)') '  barray = 5,    ! comment'
+    write(10,'(A)') '           6,'
+    write(10,'(A)') '           7     ! another comment'
+    write(10,'(A)') '           8,'
+    write(10,'(A)') '/'
+    write(10,*)
+    write(10,'(A)') '&nml3'
+    write(10,'(A)') '  carray =  9     ! New comment'
+    write(10,'(A)') '           10'
+    write(10,'(A)') '           11     ! another new comment'
+    write(10,'(A)') '           12'
+    write(10,'(A)') '/'
+    write(10,*)
+    write(10,'(A)') '&nml4'
+    write(10,'(A)') '  darray = 13, 14, 15, 16,'
+    write(10,'(A)') '/'
+    flush(10)
+    rewind(10)
+
+    read(10,nml1)
+    if (any( array /= [ 1,  2,  3,  4])) stop 1
+    read(10,nml2)
+    if (any(barray /= [ 5,  6,  7,  8])) stop 2
+    read(10,nml3)
+    if (any(carray /= [ 9, 10, 11, 12])) stop 3
+    read(10,nml4)
+    if (any(darray /= [13, 14, 15, 16])) stop 4
+    close(10)
+
+  end subroutine test_int
+
+  subroutine test_real
+
+    real :: array(4), barray(4), carray(4), darray(4)
+
+    namelist/nml1/  array
+    namelist/nml2/ barray
+    namelist/nml3/ carray
+    namelist/nml4/ darray
+
+    open(10,status='scratch')
+    write(10,'(A)') '&nml1'
+    write(10,'(A)') '  array = 1, 2, 3, 4,'
+    write(10,'(A)') '/'
+    write(10,*)
+    write(10,'(A)') '&nml2'
+    write(10,'(A)') '  barray = 5,    ! comment'
+    write(10,'(A)') '           6,'
+    write(10,'(A)') '           7     ! another comment'
+    write(10,'(A)') '           8,'
+    write(10,'(A)') '/'
+    write(10,*)
+    write(10,'(A)') '&nml3'
+    write(10,'(A)') '  carray =  9     ! New comment'
+    write(10,'(A)') '           10'
+    write(10,'(A)') '           11     ! another new comment'
+    write(10,'(A)') '           12'
+    write(10,'(A)') '/'
+    write(10,*)
+    write(10,'(A)') '&nml4'
+    write(10,'(A)') '  darray = 13, 14, 15, 16,'
+    write(10,'(A)') '/'
+    flush(10)
+    rewind(10)
+
+    read(10,nml1)
+    if (any( array /= [ 1,  2,  3,  4])) stop 1
+    read(10,nml2)
+    if (any(barray /= [ 5,  6,  7,  8])) stop 2
+    read(10,nml3)
+    if (any(carray /= [ 9, 10, 11, 12])) stop 3
+    read(10,nml4)
+    if (any(darray /= [13, 14, 15, 16])) stop 4
+    close(10)
+
+  end subroutine test_real
+
+  subroutine test_complex
+
+    complex :: array(4), barray(4), carray(4), darray(4)
+
+    namelist/nml1/  array
+    namelist/nml2/ barray
+    namelist/nml3/ carray
+    namelist/nml4/ darray
+
+    open(10,status='scratch')
+    write(10,'(A)') '&nml1'
+    write(10,'(A)') '  array = (1,0), (2,0), (3,0), (4,0),'
+    write(10,'(A)') '/'
+    write(10,*)
+    write(10,'(A)') '&nml2'
+    write(10,'(A)') '  barray = (5,0),    ! comment'
+    write(10,'(A)') '           (6,0),'
+    write(10,'(A)') '           (7,0)     ! another comment'
+    write(10,'(A)') '           (8,0),'
+    write(10,'(A)') '/'
+    write(10,*)
+    write(10,'(A)') '&nml3'
+    write(10,'(A)') '  carray =  (9,0)     ! New comment'
+    write(10,'(A)') '           (10,0)'
+    write(10,'(A)') '           (11,0)     ! another new comment'
+    write(10,'(A)') '           (12,0)'
+    write(10,'(A)') '/'
+    write(10,*)
+    write(10,'(A)') '&nml4'
+    write(10,'(A)') '  darray = (13,0), (14,0), (15,0), (16,0),'
+    write(10,'(A)') '/'
+    flush(10)
+    rewind(10)
+
+    read(10,nml1)
+    if (any( array /= [ 1,  2,  3,  4])) stop 1
+    read(10,nml2)
+    if (any(barray /= [ 5,  6,  7,  8])) stop 2
+    read(10,nml3)
+    if (any(carray /= [ 9, 10, 11, 12])) stop 3
+    read(10,nml4)
+    if (any(darray /= [13, 14, 15, 16])) stop 4
+    close(10)
+
+  end subroutine test_complex
+
+  subroutine test_logical
+
+    logical :: array(4), barray(4), carray(4), darray(4)
+
+    namelist/nml1/  array
+    namelist/nml2/ barray
+    namelist/nml3/ carray
+    namelist/nml4/ darray
+
+    open(10,status='scratch')
+    write(10,'(A)') '&nml1'
+    write(10,'(A)') '  array = T,F,F,T'
+    write(10,'(A)') '/'
+    write(10,*)
+    write(10,'(A)') '&nml2'
+    write(10,'(A)') '  barray = T,    ! comment'
+    write(10,'(A)') '           F,'
+    write(10,'(A)') '           F     ! another comment'
+    write(10,'(A)') '           T,'
+    write(10,'(A)') '/'
+    write(10,*)
+    write(10,'(A)') '&nml3'
+    write(10,'(A)') '  carray =  T     ! New comment'
+    write(10,'(A)') '            F'
+    write(10,'(A)') '            F     ! another new comment'
+    write(10,'(A)') '            T'
+    write(10,'(A)') '/'
+    write(10,*)
+    write(10,'(A)') '&nml4'
+    write(10,'(A)') '  darray =  T,F,F,T'
+    write(10,'(A)') '/'
+    flush(10)
+    rewind(10)
+
+    read(10,nml1)
+    if (any( array .neqv. [ .true.,.false.,.false.,.true. ])) stop 1
+    read(10,nml2)
+    if (any(barray .neqv. [ .true.,.false.,.false.,.true. ])) stop 2
+    read(10,nml3)
+    if (any(carray .neqv. [ .true.,.false.,.false.,.true. ])) stop 3
+    read(10,nml4)
+    if (any(darray .neqv. [ .true.,.false.,.false.,.true. ])) stop 4
+    close(10)
+
+  end subroutine test_logical
+
+  subroutine test_char
+
+    character(8) :: array(4), barray(4), carray(4), darray(4)
+
+    namelist/nml1/  array
+    namelist/nml2/ barray
+    namelist/nml3/ carray
+    namelist/nml4/ darray
+
+    open(10,status='scratch')
+    write(10,'(A)') '&nml1'
+    write(10,'(A)') '  array = "a", "b", "c", "d",'
+    write(10,'(A)') '/'
+    write(10,*)
+    write(10,'(A)') '&nml2'
+    write(10,'(A)') '  barray = "a",    ! comment'
+    write(10,'(A)') '           "b",'
+    write(10,'(A)') '           "c"     ! another comment'
+    write(10,'(A)') '           "d",'
+    write(10,'(A)') '/'
+    write(10,*)
+    write(10,'(A)') '&nml3'
+    write(10,'(A)') '  carray = "a"     ! New comment'
+    write(10,'(A)') '           "b"'
+    write(10,'(A)') '           "c"     ! another new comment'
+    write(10,'(A)') '           "d"'
+    write(10,'(A)') '/'
+    write(10,*)
+    write(10,'(A)') '&nml4'
+    write(10,'(A)') '  darray = "a", "b", "c", "d",'
+    write(10,'(A)') '/'
+    flush(10)
+    rewind(10)
+
+    read(10,nml1)
+    if (any( array /= [ "a", "b", "c", "d" ])) stop 1
+    read(10,nml2)
+    if (any(barray /= [ "a", "b", "c", "d" ])) stop 2
+    read(10,nml3)
+    if (any(carray /= [ "a", "b", "c", "d" ])) stop 3
+    read(10,nml4)
+    if (any(darray /= [ "a", "b", "c", "d" ])) stop 4
+    close(10)
+
+  end subroutine test_char
+
+end program nmlbug
diff --git a/libgfortran/io/list_read.c b/libgfortran/io/list_read.c
index 0d16640a900..7b71cf38719 100644
--- a/libgfortran/io/list_read.c
+++ b/libgfortran/io/list_read.c
@@ -917,6 +917,7 @@  read_logical (st_parameter_dt *dtp, int length)
   if (parse_repeat (dtp))
     return;
 
+next:
   c = safe_tolower (next_char (dtp));
   l_push_char (dtp, c);
   switch (c)
@@ -961,6 +962,9 @@  read_logical (st_parameter_dt *dtp, int length)
     case '!':
       if (!dtp->u.p.namelist_mode)
         goto bad_logical;
+      eat_line (dtp);
+      eat_spaces (dtp);
+      goto next;
 
     CASE_SEPARATORS:
     case EOF:
@@ -1076,6 +1080,7 @@  read_integer (st_parameter_dt *dtp, int length, bt type)
   int c, negative;
   negative = 0;
 
+next:
   c = next_char (dtp);
   switch (c)
     {
@@ -1091,6 +1096,9 @@  read_integer (st_parameter_dt *dtp, int length, bt type)
     case '!':
       if (!dtp->u.p.namelist_mode)
         goto bad_integer;
+      eat_line (dtp);
+      eat_spaces (dtp);
+      goto next;
 
     CASE_SEPARATORS:		/* Single null.  */
       unget_char (dtp, c);
@@ -1260,6 +1268,7 @@  read_character (st_parameter_dt *dtp, int length __attribute__ ((unused)))
 
   quote = ' ';			/* Space means no quote character.  */
 
+next:
   if ((c = next_char (dtp)) == EOF)
     goto eof;
   if (c == ';')
@@ -1284,6 +1293,15 @@  read_character (st_parameter_dt *dtp, int length __attribute__ ((unused)))
       quote = c;
       goto get_string;
 
+    case '!':
+      if (dtp->u.p.namelist_mode)
+	{
+	  eat_line (dtp);
+	  eat_spaces (dtp);
+	  goto next;
+	}
+      /* Fall through...  */
+
     default:
       if (dtp->u.p.namelist_mode)
 	{
@@ -1703,6 +1721,7 @@  read_complex (st_parameter_dt *dtp, void *dest, int kind, size_t size)
   if (parse_repeat (dtp))
     return;
 
+next:
   c = next_char (dtp);
   switch (c)
     {
@@ -1712,6 +1731,9 @@  read_complex (st_parameter_dt *dtp, void *dest, int kind, size_t size)
     case '!':
       if (!dtp->u.p.namelist_mode)
 	goto bad_complex;
+      eat_line (dtp);
+      eat_spaces (dtp);
+      goto next;
 
     CASE_SEPARATORS:
     case EOF:
@@ -1813,6 +1835,7 @@  read_real (st_parameter_dt *dtp, void *dest, int length)
 
   seen_dp = 0;
 
+next:
   c = next_char (dtp);
   if (dtp->u.p.current_unit->decimal_status == DECIMAL_COMMA)
     {
@@ -1844,6 +1867,9 @@  read_real (st_parameter_dt *dtp, void *dest, int length)
     case '!':
       if (!dtp->u.p.namelist_mode)
 	goto bad_real;
+      eat_line (dtp);
+      eat_spaces (dtp);
+      goto next;
 
     CASE_SEPARATORS:
       unget_char (dtp, c);		/* Single null.  */
-- 
2.51.0