stfc / PSyclone

Domain-specific compiler and code transformation system for Finite Difference/Volume/Element Earth-system models in Fortran
BSD 3-Clause "New" or "Revised" License
104 stars 28 forks source link

PSyclone incorrectly converting `DO` statement to `Do j = ` loop resulting in compiler issue #2203

Closed LonelyCat124 closed 1 year ago

LonelyCat124 commented 1 year ago

Socrates error again, the file in question is make_block_1.f90

Inside the original file, there is this code loop:

  IF (l_range) THEN
    i = 0
    DO
      WRITE(iu_stdout, '(A)') &
        'Enter units followed by lower and upper limits and increment:'
      DO
      .... some EXIT condition
     END DO
      range_bands = NINT( (range_high - range_low) / range_inc )
      IF (i + range_bands > SpBasic%n_band) THEN
        WRITE(iu_err, '(a)') 'Specified range exceeds number of bands.'
        CYCLE
      END IF
      DO j = 1, range_bands
        i = i + 1
        ....
      END DO
      IF (i == SpBasic%n_band) EXIT
    END DO

As far as I understand it, this should be something like:

do while (.true.)
      WRITE(iu_stdout, '(A)') &
        'Enter units followed by lower and upper limits and increment:'
      do while(.true.)
     ... some exit condition
     end do
   ....
   do j = 1, range_bands, 1
      i = i + 1
......

However, the output from PSyclone goes very wrong, and instead starts with: do j = 1, range_bands, 1, which is one of the inner loops - not sure what is causing this but it seems to be confused.

This bug also occurs for make_block_17.f90, read_solar_spectrum.f90, map_sub_bands_mod.f90, tidy_90.f90, corr_k_single.f90, conjugate_gradient_90.f90, exponent_fit_90.f90, optimal_k.f90, read_pt_line_90.f90, set_g_point_90.f90, decompose_phf_90.f90, scatter_average_90.f90

LonelyCat124 commented 1 year ago

Standalone test

def test_fail_case(fortran_reader, fortran_writer):
    code = '''subroutine test_subroutine
    integer :: j, iu_stdout, range_bands, i

    i = 0
    DO
      WRITE(iu_stdout, '(A)') &
        'Enter units followed by lower and upper limits and increment:'
      DO
        EXIT
      END DO
      range_bands = 3
      if (range_bands + i > 3 .and. range_bands + i < 15) then
        CYCLE
      end if
      do j = 1, range_bands
        i = i + 1
      end do
      if (i > 15) then
        EXIT
      end if
  end do

  end subroutine'''

    psyir = fortran_reader.psyir_from_source(code)
    pass_through = fortran_writer(psyir)
    assert pass_through.count("do j = 1, range_bands, 1") == 1

Test fails with 2==1 assertion.

LonelyCat124 commented 1 year ago

Ok, this looks like a bug in the frontend. I think this will happen any time we come across a while loop containing a do i = ... loop due to this code ctrl = walk(node.content, Fortran2003.Loop_Control) being called on all loops, but will find the first valid child's loop control if possible.

I think again this may be a very simple fix that I have tried, and I will try for the whole test/example suite and if so, formalise the test and make a PR.