Hi Jerry,
Jerry D wrote:
The attached log entry and patch (git show) fixes this issue by adding
logic to handle spaces in eat_separators. One or more spaces by
themselves are a valid separator. So in this case we look at the
character following the spaces to see if it is a comma or semicolon.
If so, I change it to the valid separator for the given decimal mode,
point or comma. This allows the comma or semicolon to be interpreted as
a null read on the next effective item in the formatted read.
I chose a permissive approach here that allows reads to proceed when the
input line is mal-formed with an incorrect separator as long as there is
at least one space in front of it.
First: Consider also adding 'PR fortran/105473' to the commit log
as the PRs are closely related, albeit this PR is different-
The patch looks mostly like I would expect, except for decimal='point'
and a ';' which is *not* preceded by a space.
Thanks for working on it.
Regarding the 'except' case:
* * *
If I try your patch with the testcase of at comment 19,
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=114304#c19
→ https://gcc.gnu.org/bugzilla/attachment.cgi?id=57695 ,
I do note that with 'decimal=point', a tailing semicolon is silently
accepted – even if not proceeded by a space.
I think such code is invalid – and you could consider to reject it.
Otherwise, the handling all seems to be in line with the Fortran spec.
i.e. for the following string, I had *expected an error*:
point, isreal = F , testinput = ";"n= 42 ios= 0
point, isreal = F , testinput = "5;"n= 5 ios= 0
point, isreal = T , testinput = "8;"r= 8.00000000 ios= 0
point, isreal = T , testinput = "3.3;"r= 3.29999995 ios= 0
point, isreal = T , testinput = "3,3;"r= 3.00000000 ios= 0
while I think the following is OK (i.e. no error is what I expect) due
to the the space before the ';'.
point, isreal = F , testinput = "7 ;"n= 7 ios= 0
point, isreal = T , testinput = "9 ;"r= 9.00000000 ios= 0
point, isreal = T , testinput = "4.4 ;"r= 4.40000010 ios=0
point, isreal = T , testinput = "9 ;"r= 9.00000000 ios= 0
point, isreal = T , testinput = "4,4 ;"r= 4.00000000 ios= 0
* * *
Looking at the other compilers, ifort, ifx and Flang do issue an error
here. Likewise, g95 seems to yield an error in this case (see below).
I do note that the Lapack testcase that triggered this PR did have such
a code - but it was then changed because g95 did not like it:
https://github.com/Reference-LAPACK/lapack/commit/64e8a7500d817869e5fcde35afd39af8bc7a8086
In terms of gfortran: until recently did accept it (all versions,
including 13+14); it then rejected it due to the change in PR105473 (GCC
14/mainline, backported to 13)– but I now think it rightly did so. With
the current patch, it is accepted again.
* * *
I have attached the modified testcase linked above; consider adding it
as well. - Changes to the one of the attachment:
- I added a few additional (albeit boring) tests
- I added an expected output + error diagnostic.
The testcase assumes an error for ';' as separator (with 'point'),
unless there is a space before it.
[If we want to not diagnose this as vendor extension, we really need to
add a comment to that testcase besides changing valid = .false. to .true.]
Tobias
! { dg-do run }
!
! PR fortran/114304
!
! See also PR fortran/105473
!
! Testing: Does list-directed reading an integer/real allows some non-integer input?
!
! Note: GCC result comments before fix of this PR.
implicit none
call t(.true., 'comma', ';') ! No error shown
call t(.false., 'point', ';') ! /!\ gfortran: no error, others: error
call t(.false., 'comma', ',') ! Error shown
call t(.true., 'point', ',') ! No error shown
call t(.false., 'comma', '.') ! Error shown
call t(.false., 'point', '.') ! Error shown
call t(.false., 'comma', '5.') ! Error shown
call t(.false., 'point', '5.') ! gfortran/flang: Error shown, ifort: no error
call t(.false., 'comma', '5,') ! gfortran: error; others: no error
call t(.true., 'point', '5,') ! No error shown
call t(.true., 'comma', '5;') ! No error shown
call t(.false., 'point', '5;') ! /!\ gfortran: no error shown, others: error
call t(.true., 'comma', '7 .') ! No error shown
call t(.true., 'point', '7 .') ! No error shown
call t(.true., 'comma', '7 ,') ! /!\ gfortran: error; others: no error
call t(.true., 'point', '7 ,') ! No error shown
call t(.true., 'comma', '7 ;') ! No error shown
call t(.true., 'point', '7 ;') ! No error shown
! print *, '---------------'
call t(.false., 'comma', '8.', .true.) ! Error shown
call t(.true., 'point', '8.', .true.) ! gfortran/flang: Error shown, ifort: no error
call t(.true., 'comma', '8,', .true.) ! gfortran: error; others: no error
call t(.true., 'point', '8,', .true.) ! No error shown
call t(.true., 'comma', '8;', .true.) ! No error shown
call t(.false., 'point', '8;', .true.) ! /!\ gfortran: no error shown, others: error
call t(.true., 'comma', '9 .', .true.) ! No error shown
call t(.true., 'point', '9 .', .true.) ! No error shown
call t(.true., 'comma', '9 ,', .true.) ! /!\ gfortran: error; others: no error
call t(.true., 'point', '9 ,', .true.) ! No error shown
call t(.true., 'comma', '9 ;', .true.) ! No error shown
call t(.true., 'point', '9 ;', .true.) ! No error shown
call t(.false., 'comma', '3,3.', .true.) ! Error shown
call t(.false., 'point', '3.3.', .true.) ! Error shown
call t(.false., 'comma', '3,3,', .true.) ! gfortran/flang: no error; ifort: error
call t(.true., 'comma', '3,3;', .true.) ! No error shown
call t(.false., 'point', '3.3;', .true.) ! gfortran/flang: no error; ifort: error
call t(.true., 'comma', '4,4 .', .true.) ! N error shown
call t(.true., 'point', '4.4 .', .true.) ! No error shown
call t(.true., 'comma', '4,4 ,', .true.) ! /!\ gfortran: error; others: no error
call t(.true., 'point', '4.4 ,', .true.) ! No error shown
call t(.true., 'comma', '4,4 ;', .true.) ! No error shown
call t(.true., 'point', '4.4 ;', .true.) ! No error shown
! print *, '---------------'
call t(.true., 'comma', '8', .true.)
call t(.true., 'point', '8', .true.)
call t(.true., 'point', '9 ;', .true.)
call t(.true., 'comma', '3;3.', .true.)
call t(.true., 'point', '3,3.', .true.)
call t(.true., 'comma', '3;3,', .true.)
call t(.true., 'comma', '3;3;', .true.)
call t(.true., 'point', '3,3;', .true.)
call t(.true., 'comma', '4;4 .', .true.)
call t(.true., 'point', '4,4 .', .true.)
call t(.true., 'comma', '4;4 ,', .true.)
call t(.true., 'point', '4,4 ,', .true.)
call t(.true., 'comma', '4;4 ;', .true.)
call t(.true., 'point', '4,4 ;', .true.)
contains
subroutine t(valid, dec, testinput, isreal)
logical, value :: valid
character(len=*) :: dec, testinput
logical, optional :: isreal
logical :: isreal2
integer n,ios
real :: r
r = 42; n = 42
isreal2 = .false.
if (present(isreal)) isreal2 = isreal
if (isreal2) then
read(testinput,*,decimal=dec,iostat=ios) r
if ((valid .and. ios /= 0) .or. (.not.valid .and. ios == 0)) then
print '(*(g0))', valid, ', ', dec,', isreal = ',isreal2,', testinput = "',testinput,'"',', r=',r,' ios=',ios
print *, 'ERROR'
stop 1
end if
else
read(testinput,*,decimal=dec,iostat=ios) n
if ((valid .and. ios /= 0) .or. (.not.valid .and. ios == 0)) then
print '(*(g0))', valid, ', ', dec,', isreal = ',isreal2,', testinput = "',testinput,'"',', n=',n,' ios=',ios
print *, 'ERROR'
stop 1
end if
end if
end
end program