Repository: stfc/PSyclone (python)
Issue: NemoOuterArrayRange2LoopTrans bug when array-index expression includes array reference
Processing a reference of the form `this(jp)%that(:, map(ij) - np, :)` causes the apply method of `NemoOuterArrayRange2LoopTrans` to fail. The culprit is:

     lhs_array_ref = node.lhs.walk(ArrayMixin, stop_type=Range)[-1]

where we're using the `walk` to find the 'deepest' array reference (i.e. allowing for an access to a member of a derived type). However, that `walk` ends up giving us `map(ij)` in this case which is wrong.
Pull request description: (Closes #2492) nemoouterarrayrange bug
Bug fix for the named transformation (required when an array reference itself uses an array reference in an index expression) and also a minor fix to the `utils.py` file in `examples/nemo/scripts` that prevents us attempting to specialise the same Symbol more than once. (Found while processing latest NEMO source.)


Patch:
diff --git a/changelog b/changelog
index 32198feb7..c8add54a3 100644
--- a/changelog
+++ b/changelog
@@ -147,6 +147,8 @@
 	53) PR #2439 for #2105. Use maxval2loop and atomics to parallelise
 	NEMO stpctl.f90.
 
+	54) PR #2493 for #2492. Fix bug in NemoOuterArrayRange2LoopTrans
+
 release 2.4.0 29th of September 2023
 
 	1) PR #1758 for #1741. Splits the PSyData read functionality into a
diff --git a/examples/nemo/scripts/utils.py b/examples/nemo/scripts/utils.py
index b00c44ae8..23fa5eb87 100755
--- a/examples/nemo/scripts/utils.py
+++ b/examples/nemo/scripts/utils.py
@@ -120,7 +120,9 @@ def enhance_tree_information(schedule):
                         ArrayType.Extent.ATTRIBUTE]))
         elif reference.symbol.name == "sbc_dcy":
             # The parser gets this wrong, it is a Call not an Array access
-            reference.symbol.specialise(RoutineSymbol)
+            if not isinstance(reference.symbol, RoutineSymbol):
+                # We haven't already specialised this Symbol.
+                reference.symbol.specialise(RoutineSymbol)
             call = Call(reference.symbol)
             for child in reference.children:
                 call.addchild(child.detach())
diff --git a/src/psyclone/domain/nemo/transformations/nemo_outerarrayrange2loop_trans.py b/src/psyclone/domain/nemo/transformations/nemo_outerarrayrange2loop_trans.py
index 9d70779bc..3c0a818c8 100644
--- a/src/psyclone/domain/nemo/transformations/nemo_outerarrayrange2loop_trans.py
+++ b/src/psyclone/domain/nemo/transformations/nemo_outerarrayrange2loop_trans.py
@@ -113,7 +113,8 @@ class NemoOuterArrayRange2LoopTrans(ArrayRange2LoopTrans):
         self.validate(node)
 
         # Get deepest array in LHS (excluding inside Ranges)
-        lhs_array_ref = node.lhs.walk(ArrayMixin, stop_type=Range)[-1]
+        deepest_range = node.lhs.walk(Range, stop_type=Range)[-1]
+        lhs_array_ref = deepest_range.parent
         index = lhs_array_ref.get_outer_range_index()
         nemo_arrayrange2loop = NemoArrayRange2LoopTrans()
         nemo_arrayrange2loop.apply(lhs_array_ref.children[index])


Test patch:
diff --git a/src/psyclone/tests/domain/nemo/transformations/nemo_outerarrayrange2loop_trans_test.py b/src/psyclone/tests/domain/nemo/transformations/nemo_outerarrayrange2loop_trans_test.py
index 62ca5c627..3cf2d1359 100644
--- a/src/psyclone/tests/domain/nemo/transformations/nemo_outerarrayrange2loop_trans_test.py
+++ b/src/psyclone/tests/domain/nemo/transformations/nemo_outerarrayrange2loop_trans_test.py
@@ -138,6 +138,24 @@ def test_apply_with_structures(fortran_reader, fortran_writer):
     assert ("ptab(jf)%pt2d(jpi,idx_1,idx) = "
             "ptab(jf)%pt2d(jpim1,idx_1,idx)" in result)
 
+    # Array index expression includes an array reference.
+    psyir = fortran_reader.psyir_from_source('''
+    subroutine test
+        use my_variables
+        integer, parameter :: jf = 3, jpi = 3, jpim1 = 1
+        ptab(jf)%pt2d(:,map(jf)+1,:) = ptab(jf)%pt2d(:,3,:)
+    end subroutine test
+    ''')
+    assignment = psyir.walk(Assignment)[0]
+    trans.apply(assignment)
+    result = fortran_writer(assignment)
+    assert ("ptab(jf)%pt2d(:,map(jf) + 1,idx) = ptab(jf)%pt2d(:,3,idx)"
+            in result)
+    trans.apply(assignment)
+    result = fortran_writer(assignment)
+    assert ("ptab(jf)%pt2d(idx_1,map(jf) + 1,idx) = ptab(jf)%pt2d(idx_1,3,idx)"
+            in result)
+
 
 def test_apply_calls_validate():
     '''Check that the apply() method calls the validate method.'''