Repository: stfc/PSyclone
Pull request #3000: (Closes #2973) Improves handling of pure subroutines in definition use chains for 2973
Author: login: LonelyCat124
Labels: name: under review
Created: 2025-05-20T11:44:06Z | Merged: 2025-05-28T07:44:26Z

Small change that means that pure subroutines are skipped over compared to general calls, but their arguments are just handled directly instead.

One more commit to improve tests is needed.

Linked issue #2973: Add support for PURE subroutines/functions in the definition use chains
Since pure routines are only allowed to change values of their arguments, we can do better than assuming calls are a dependency to all variables if the call is to a pure RoutineSymbol.

Diff:
diff --git a/src/psyclone/psyir/tools/definition_use_chains.py b/src/psyclone/psyir/tools/definition_use_chains.py
index f41ca0a396..54014ca948 100644
--- a/src/psyclone/psyir/tools/definition_use_chains.py
+++ b/src/psyclone/psyir/tools/definition_use_chains.py
@@ -499,6 +499,11 @@ def _compute_forward_uses(self, basic_block_list):
                         # Note that this assumes two symbols are not
                         # aliases of each other.
                         continue
+                    if reference.is_pure:
+                        # Pure subroutines only touch their arguments, so we'll
+                        # catch the arguments that are passed into the call
+                        # later as References.
+                        continue
                     # For now just assume calls are bad if we have a non-local
                     # variable and we treat them as though they were a write.
                     if defs_out is not None:
@@ -759,6 +764,11 @@ def _compute_backward_uses(self, basic_block_list):
                         # Note that this assumes two symbols are not
                         # aliases of each other.
                         continue
+                    if reference.is_pure:
+                        # Pure subroutines only touch their arguments, so we'll
+                        # catch the arguments that are passed into the call
+                        # later as References.
+                        continue
                     # For now just assume calls are bad if we have a non-local
                     # variable and we treat them as though they were a write.
                     if defs_out is not None:
diff --git a/src/psyclone/tests/psyir/tools/definition_use_chains_backward_dependence_test.py b/src/psyclone/tests/psyir/tools/definition_use_chains_backward_dependence_test.py
index e8353ffb27..54f3152def 100644
--- a/src/psyclone/tests/psyir/tools/definition_use_chains_backward_dependence_test.py
+++ b/src/psyclone/tests/psyir/tools/definition_use_chains_backward_dependence_test.py
@@ -38,9 +38,12 @@
 
 import pytest
 from psyclone.psyir.nodes import (
+    Assignment,
+    Call,
+    CodeBlock,
+    Loop,
     Routine,
     Reference,
-    Assignment,
     WhileLoop,
 )
 from psyclone.psyir.tools.definition_use_chains import DefinitionUseChain
@@ -137,24 +140,25 @@ def test_definition_use_chain_find_backward_accesses_basic_example(
     routine = psyir.walk(Routine)[0]
     # Creating use chain for the a in a = 2
     chains = DefinitionUseChain(
-        routine.children[9].lhs, [routine]
+        routine.walk(Assignment)[8].lhs, [routine]
     )
     reaches = chains.find_backward_accesses()
     # We find 2 results
     # the a in e = a**3
     # The call bar(c, b) as a isn't local and we can't guarantee its behaviour.
     assert len(reaches) == 2
-    assert reaches[0] is routine.children[8].rhs.children[0]
-    assert reaches[1] is routine.children[6]
+    assert reaches[0] is routine.walk(Assignment)[7].rhs.children[0]
+    assert reaches[1] is routine.walk(Call)[1]
 
     # Create use chain for c in b = c + d
-    chains = DefinitionUseChain(routine.children[5].rhs.children[0], [routine])
+    chains = DefinitionUseChain(routine.walk(Assignment)[5].rhs.children[0],
+                                [routine])
     reaches = chains.find_backward_accesses()
     # We should find 2 results
     # C = d * a
     # d = C + 2.0
-    assert reaches[0] is routine.children[4].rhs.children[0]
-    assert reaches[1] is routine.children[3].lhs
+    assert reaches[0] is routine.walk(Assignment)[4].rhs.children[0]
+    assert reaches[1] is routine.walk(Assignment)[3].lhs
     assert len(reaches) == 2
 
 
@@ -172,15 +176,15 @@ def test_definition_use_chain_find_backward_accesses_assignment(
     psyir = fortran_reader.psyir_from_source(code)
     routine = psyir.walk(Routine)[0]
     # Start chain from A = a * a
-    chains = DefinitionUseChain(routine.children[1].lhs)
+    chains = DefinitionUseChain(routine.walk(Assignment)[1].lhs)
     reaches = chains.find_backward_accesses()
     # We should find 3 results, both 3 references in
     # a = A * A
     # and A = 1
     assert len(reaches) == 3
-    assert reaches[0] is routine.children[1].rhs.children[1]
-    assert reaches[1] is routine.children[1].rhs.children[0]
-    assert reaches[2] is routine.children[0].lhs
+    assert reaches[0] is routine.walk(Assignment)[1].rhs.children[1]
+    assert reaches[1] is routine.walk(Assignment)[1].rhs.children[0]
+    assert reaches[2] is routine.walk(Assignment)[0].lhs
 
 
 def test_definition_use_chain_find_backward_accesses_ifelse_example(
@@ -204,19 +208,19 @@ def test_definition_use_chain_find_backward_accesses_ifelse_example(
     psyir = fortran_reader.psyir_from_source(code)
     routine = psyir.walk(Routine)[0]
     # Start the chain from b = A + d.
-    chains = DefinitionUseChain(routine.children[3].rhs.children[0])
+    chains = DefinitionUseChain(routine.walk(Assignment)[4].rhs.children[0])
     reaches = chains.find_backward_accesses()
     # TODO #2760 For now the if statement doesn't kill the accesses,
     # even though it will always be written to.
     assert len(reaches) == 4
-    assert reaches[2] is routine.children[1].rhs.children[0]
-    assert reaches[1] is routine.children[2].if_body.children[0].lhs
-    assert reaches[0] is routine.children[2].else_body.children[0].lhs
-    assert reaches[3] is routine.children[0].lhs
+    assert reaches[0] is routine.walk(Assignment)[3].lhs
+    assert reaches[1] is routine.walk(Assignment)[2].lhs
+    assert reaches[2] is routine.walk(Assignment)[1].rhs.children[0]
+    assert reaches[3] is routine.walk(Assignment)[0].lhs
 
     # Also check that a = 4 backward access is not a = 3.
-    a_3 = routine.children[2].if_body.children[0].lhs
-    a_4 = routine.children[2].else_body.children[0].lhs
+    a_3 = routine.walk(Assignment)[2].lhs
+    a_4 = routine.walk(Assignment)[3].lhs
     chains = DefinitionUseChain(a_4)
     reaches = chains.find_backward_accesses()
     assert len(reaches) == 2
@@ -245,7 +249,7 @@ def test_definition_use_chain_find_backward_accesses_loop_example(
     routine = psyir.walk(Routine)[0]
     # Start the chain from A = a + i.
     chains = DefinitionUseChain(
-        routine.children[1].loop_body.children[0].lhs
+        routine.walk(Assignment)[1].lhs
     )
     reaches = chains.find_backward_accesses()
     # We should have 4? reaches
@@ -255,15 +259,15 @@ def test_definition_use_chain_find_backward_accesses_loop_example(
     # Last is A = 1
     assert len(reaches) == 4
     assert (
-        reaches[0] is routine.children[1].loop_body.children[1].rhs.children[0]
+        reaches[0] is routine.walk(Assignment)[2].rhs.children[0]
     )
     assert (
-        reaches[2] is routine.children[1].loop_body.children[0].rhs.children[0]
+        reaches[1] is routine.walk(Assignment)[1].lhs
     )
     assert (
-        reaches[1] is routine.children[1].loop_body.children[0].lhs
+        reaches[2] is routine.walk(Assignment)[1].rhs.children[0]
     )
-    assert reaches[3] is routine.children[0].lhs
+    assert reaches[3] is routine.walk(Assignment)[0].lhs
 
     # Check if we access a loop variable
     code = """
@@ -281,13 +285,13 @@ def test_definition_use_chain_find_backward_accesses_loop_example(
     routine = psyir.walk(Routine)[0]
     # Start the chain from I = 1231.
     chains = DefinitionUseChain(
-        routine.children[1].lhs
+        routine.walk(Assignment)[2].lhs
     )
     reaches = chains.find_backward_accesses()
     # We should have 1 reaches
     # It should be the loop
     assert len(reaches) == 1
-    assert reaches[0] is routine.children[0]
+    assert reaches[0] is routine.walk(Loop)[0]
 
 
 def test_definition_use_chain_find_backward_accesses_while_loop_example(
@@ -312,12 +316,12 @@ def test_definition_use_chain_find_backward_accesses_while_loop_example(
     reaches = chains.find_backward_accesses()
 
     assert len(reaches) == 4
-    assert reaches[0] is routine.children[2].condition.children[0]
+    assert reaches[0] is routine.walk(WhileLoop)[0].condition.children[0]
+    assert reaches[1] is routine.walk(Assignment)[2].lhs
     assert (
-        reaches[2] is routine.children[2].loop_body.children[0].rhs.children[0]
+        reaches[2] is routine.walk(Assignment)[2].rhs.children[0]
     )
-    assert reaches[1] is routine.children[2].loop_body.children[0].lhs
-    assert reaches[3] is routine.children[1].lhs
+    assert reaches[3] is routine.walk(Assignment)[1].lhs
 
 
 def test_definition_use_chain_backward_accesses_nested_loop_example(
@@ -341,7 +345,7 @@ def test_definition_use_chain_backward_accesses_nested_loop_example(
     routine = psyir.walk(Routine)[0]
     # Start the chain from b = b + A.
     loops = routine.walk(WhileLoop)
-    chains = DefinitionUseChain(loops[1].loop_body.children[0].rhs.children[1])
+    chains = DefinitionUseChain(loops[1].walk(Assignment)[0].rhs.children[1])
     reaches = chains.find_backward_accesses()
     # TODO #2760 The backwards accesses should not continue past a = a + 3 as
     # to reach the b = b + a statement we must have passed through the
@@ -350,9 +354,9 @@ def test_definition_use_chain_backward_accesses_nested_loop_example(
     # then the A = 1
     assert len(reaches) == 4
     assert reaches[0] is loops[0].condition.children[0]
-    assert reaches[1] is loops[1].loop_body.children[0].rhs.children[1]
-    assert reaches[2] is loops[0].loop_body.children[0].lhs
-    assert reaches[3] is routine.children[1].lhs
+    assert reaches[1] is loops[1].walk(Assignment)[0].rhs.children[1]
+    assert reaches[2] is loops[0].walk(Assignment)[0].lhs
+    assert reaches[3] is routine.walk(Assignment)[1].lhs
 
 
 def test_definition_use_chain_find_backward_accesses_structure_example(
@@ -370,10 +374,10 @@ def test_definition_use_chain_find_backward_accesses_structure_example(
 
     psyir = fortran_reader.psyir_from_source(code)
     routine = psyir.walk(Routine)[0]
-    chains = DefinitionUseChain(routine.children[2].lhs)
+    chains = DefinitionUseChain(routine.walk(Assignment)[2].lhs)
     reaches = chains.find_backward_accesses()
     assert len(reaches) == 1
-    assert reaches[0] is routine.children[0].lhs
+    assert reaches[0] is routine.walk(Assignment)[0].lhs
 
 
 def test_definition_use_chain_find_backward_accesses_no_control_flow_example(
@@ -389,10 +393,10 @@ def test_definition_use_chain_find_backward_accesses_no_control_flow_example(
     end subroutine"""
     psyir = fortran_reader.psyir_from_source(code)
     routine = psyir.walk(Routine)[0]
-    chains = DefinitionUseChain(routine.children[0].lhs)
+    chains = DefinitionUseChain(routine.walk(Assignment)[0].lhs)
     reaches = chains.find_backward_accesses()
     assert len(reaches) == 1
-    assert reaches[0] is routine.children[0].rhs.children[0]
+    assert reaches[0] is routine.walk(Assignment)[0].rhs.children[0]
 
 
 def test_definition_use_chain_find_backward_accesses_codeblock(
@@ -409,10 +413,10 @@ def test_definition_use_chain_find_backward_accesses_codeblock(
     end subroutine"""
     psyir = fortran_reader.psyir_from_source(code)
     routine = psyir.walk(Routine)[0]
-    chains = DefinitionUseChain(routine.children[2].lhs)
+    chains = DefinitionUseChain(routine.walk(Assignment)[1].lhs)
     reaches = chains.find_backward_accesses()
     assert len(reaches) == 1
-    assert reaches[0] is routine.children[1]
+    assert reaches[0] is routine.walk(CodeBlock)[0]
 
 
 def test_definition_use_chain_find_backward_accesses_codeblock_and_call_nlocal(
@@ -430,10 +434,11 @@ def test_definition_use_chain_find_backward_accesses_codeblock_and_call_nlocal(
     end subroutine"""
     psyir = fortran_reader.psyir_from_source(code)
     routine = psyir.walk(Routine)[0]
-    chains = DefinitionUseChain(routine.children[2].rhs.children[0])
+    chains = DefinitionUseChain(routine.walk(Assignment)[0].rhs.children[0])
     reaches = chains.find_backward_accesses()
     assert len(reaches) == 1
-    assert reaches[0] is routine.children[1].children[1]
+    # Result is the argument of the call
+    assert reaches[0] is routine.walk(Call)[0].children[1]
 
 
 def test_definition_use_chain_find_backward_accesses_codeblock_and_call_cflow(
@@ -454,11 +459,11 @@ def test_definition_use_chain_find_backward_accesses_codeblock_and_call_cflow(
     end subroutine"""
     psyir = fortran_reader.psyir_from_source(code)
     routine = psyir.walk(Routine)[0]
-    chains = DefinitionUseChain(routine.children[2].rhs.children[0])
+    chains = DefinitionUseChain(routine.walk(Assignment)[0].rhs.children[0])
     reaches = chains.find_backward_accesses()
     assert len(reaches) == 2
-    assert reaches[0] is routine.children[1].if_body.children[1].children[1]
-    assert reaches[1] is routine.children[0]
+    assert reaches[0] is routine.walk(Call)[1].children[1]
+    assert reaches[1] is routine.walk(Call)[0]
 
 
 def test_definition_use_chain_find_backward_accesses_codeblock_and_call_local(
@@ -477,10 +482,10 @@ def test_definition_use_chain_find_backward_accesses_codeblock_and_call_local(
     end subroutine"""
     psyir = fortran_reader.psyir_from_source(code)
     routine = psyir.walk(Routine)[0]
-    chains = DefinitionUseChain(routine.children[2].rhs.children[0])
+    chains = DefinitionUseChain(routine.walk(Assignment)[0].rhs.children[0])
     reaches = chains.find_backward_accesses()
     assert len(reaches) == 1
-    assert reaches[0] is routine.children[1]
+    assert reaches[0] is routine.walk(CodeBlock)[0]
 
 
 def test_definition_use_chain_find_backward_accesses_call_and_codeblock_nlocal(
@@ -498,10 +503,10 @@ def test_definition_use_chain_find_backward_accesses_call_and_codeblock_nlocal(
     end subroutine"""
     psyir = fortran_reader.psyir_from_source(code)
     routine = psyir.walk(Routine)[0]
-    chains = DefinitionUseChain(routine.children[2].lhs)
+    chains = DefinitionUseChain(routine.walk(Assignment)[0].lhs)
     reaches = chains.find_backward_accesses()
     assert len(reaches) == 1
-    assert reaches[0] is routine.children[1]
+    assert reaches[0] is routine.walk(Call)[0]
 
 
 def test_definition_use_chains_goto_statement(
@@ -517,7 +522,7 @@ def test_definition_use_chains_goto_statement(
     end subroutine"""
     psyir = fortran_reader.psyir_from_source(code)
     routine = psyir.walk(Routine)[0]
-    chains = DefinitionUseChain(routine.children[1].lhs)
+    chains = DefinitionUseChain(routine.walk(Assignment)[0].lhs)
     with pytest.raises(NotImplementedError) as excinfo:
         chains.find_backward_accesses()
     assert ("DefinitionUseChains can't handle code containing GOTO statements"
@@ -546,15 +551,15 @@ def test_definition_use_chains_exit_statement(
     routine = psyir.walk(Routine)[0]
     # Start the chain from a = A +i.
     chains = DefinitionUseChain(
-        routine.children[1].loop_body.children[0].rhs.children[0]
+        routine.walk(Assignment)[1].rhs.children[0]
     )
     reaches = chains.find_backward_accesses()
     # We should have 2 reaches
     # First is A = a + i
     # Second is A = 1
     assert len(reaches) == 2
-    assert reaches[0] is routine.children[1].loop_body.children[0].lhs
-    assert reaches[1] is routine.children[0].lhs
+    assert reaches[0] is routine.walk(Assignment)[1].lhs
+    assert reaches[1] is routine.walk(Assignment)[0].lhs
     pytest.xfail(reason="Issue #2760: DefinitionUseChains should not search "
                         "again in a loop when there is a guaranteed exit "
                         "statement")
@@ -583,15 +588,15 @@ def test_definition_use_chains_cycle_statement(
     routine = psyir.walk(Routine)[0]
     # Start the chain from a = A +i.
     chains = DefinitionUseChain(
-        routine.children[1].loop_body.children[0].rhs.children[0]
+        routine.walk(Assignment)[1].rhs.children[0]
     )
     reaches = chains.find_backward_accesses()
     # We should have 2 reaches
     # A = b * 4
     # A = 1
     assert len(reaches) == 2
-    assert reaches[0] is routine.children[1].loop_body.children[1].lhs
-    assert reaches[1] is routine.children[0].lhs
+    assert reaches[0] is routine.walk(Assignment)[2].lhs
+    assert reaches[1] is routine.walk(Assignment)[0].lhs
 
 
 def test_definition_use_chains_return_statement(
@@ -617,17 +622,15 @@ def test_definition_use_chains_return_statement(
     routine = psyir.walk(Routine)[0]
     # Start the chain from a = A +i.
     chains = DefinitionUseChain(
-        routine.children[1].loop_body.children[0].rhs.children[0]
+        routine.walk(Assignment)[1].rhs.children[0]
     )
     reaches = chains.find_backward_accesses()
     # We should have 2 reaches
     # A = b * 4
     # A = 1
     assert len(reaches) == 2
-    assert reaches[1] is routine.children[0].lhs
-    # Search backwards in loop isn't working correctly - we're finding
-    # the LHS of a = a + i instead of A = b + 4
-    assert reaches[0] is routine.children[1].loop_body.children[1].lhs
+    assert reaches[0] is routine.walk(Assignment)[2].lhs
+    assert reaches[1] is routine.walk(Assignment)[0].lhs
 
 
 def test_definition_use_chains_backward_accesses_multiple_routines(
@@ -651,7 +654,7 @@ def test_definition_use_chains_backward_accesses_multiple_routines(
     psyir = fortran_reader.psyir_from_source(code)
     routine = psyir.walk(Routine)[1]
     chains = DefinitionUseChain(
-        routine.children[0].rhs
+        routine.walk(Assignment)[0].rhs
     )
     reaches = chains.find_backward_accesses()
     assert len(reaches) == 0
@@ -678,15 +681,15 @@ def test_definition_use_chains_backward_accesses_nonassign_reference_in_loop(
     psyir = fortran_reader.psyir_from_source(code)
     routine = psyir.walk(Routine)[0]
     chains = DefinitionUseChain(
-        routine.children[1].loop_body.children[1].children[1]
+        routine.walk(Call)[0].children[1]
     )
     reaches = chains.find_backward_accesses()
     # TODO #2760 The backwards accesses should not continue past a = a + i
     # when searching backwards in the loop, or to a = 1
     assert len(reaches) == 3
-    assert reaches[0] is routine.children[1].loop_body.children[1].children[1]
-    assert reaches[1] is routine.children[1].loop_body.children[0].lhs
-    assert reaches[2] is routine.children[0].lhs
+    assert reaches[0] is routine.walk(Call)[0].children[1]
+    assert reaches[1] is routine.walk(Assignment)[1].lhs
+    assert reaches[2] is routine.walk(Assignment)[0].lhs
 
 
 def test_definition_use_chains_backward_accesses_empty_schedules(
@@ -711,13 +714,13 @@ def test_definition_use_chains_backward_accesses_empty_schedules(
     psyir = fortran_reader.psyir_from_source(code)
     routine = psyir.walk(Routine)[0]
     chains = DefinitionUseChain(
-            routine.children[4].lhs
+            routine.walk(Assignment)[1].lhs
     )
     reaches = chains.find_backward_accesses()
     assert len(reaches) == 3
-    assert reaches[0] is routine.children[4].rhs.children[1]
-    assert reaches[1] is routine.children[4].rhs.children[0]
-    assert reaches[2] is routine.children[0].lhs
+    assert reaches[0] is routine.walk(Assignment)[1].rhs.children[1]
+    assert reaches[1] is routine.walk(Assignment)[1].rhs.children[0]
+    assert reaches[2] is routine.walk(Assignment)[0].lhs
 
 
 def test_definition_use_chains_backward_accesses_inquiry_func(
@@ -738,7 +741,46 @@ def test_definition_use_chains_backward_accesses_inquiry_func(
     psyir = fortran_reader.psyir_from_source(code)
     routine = psyir.walk(Routine)[0]
     chains = DefinitionUseChain(
-            routine.children[1].lhs
+            routine.walk(Assignment)[1].lhs
     )
     reaches = chains.find_backward_accesses()
     assert len(reaches) == 0
+
+
+def test_definition_use_chain_find_backward_accesses_pure_call(
+    fortran_reader,
+):
+    """Functionality test for the find_backward_accesses routine. This
+    tests the behaviour for a pure subroutine call."""
+    code = """
+    pure subroutine y(in)
+        integer :: in
+        in = in + 1
+    end subroutine y
+
+    subroutine x(a, b)
+    integer :: a, b
+    a = 2
+    call y(b)
+    a = a + 2
+    b = 1
+    end subroutine"""
+    psyir = fortran_reader.psyir_from_source(code)
+    routine = psyir.walk(Routine)[1]
+    # Find the a in the rhs of the second assignment
+    assign2 = routine.walk(Assignment)[1]
+    rhs_a = assign2.rhs.children[0]
+    chains = DefinitionUseChain(rhs_a)
+    reaches = chains.find_backward_accesses()
+    assert len(reaches) == 1
+    # Result is lhs of the first assignment
+    lhs_assign1 = routine.walk(Assignment)[0].lhs
+    assert reaches[0] is lhs_assign1
+
+    # Get the lhs of the b = 1 assignment
+    lhs_assign3 = routine.walk(Assignment)[2].lhs
+    chains = DefinitionUseChain(lhs_assign3)
+    reaches = chains.find_backward_accesses()
+    assert len(reaches) == 1
+    # We should find the argument in the pure subroutine call
+    assert reaches[0] is routine.walk(Call)[0].children[1]
diff --git a/src/psyclone/tests/psyir/tools/definition_use_chains_forward_dependence_test.py b/src/psyclone/tests/psyir/tools/definition_use_chains_forward_dependence_test.py
index 1e4982b992..6a31dccb61 100644
--- a/src/psyclone/tests/psyir/tools/definition_use_chains_forward_dependence_test.py
+++ b/src/psyclone/tests/psyir/tools/definition_use_chains_forward_dependence_test.py
@@ -39,11 +39,14 @@
 import pytest
 from psyclone.psyir.nodes import (
     Assignment,
+    Call,
+    CodeBlock,
     IfBlock,
     Loop,
     Reference,
     Routine,
     Node,
+    OMPParallelDirective,
     WhileLoop,
 )
 from psyclone.transformations import OMPParallelTrans
@@ -189,7 +192,7 @@ def test_definition_use_chain_is_basic_block(fortran_reader):
     psyir = fortran_reader.psyir_from_source(code)
     par_trans.apply(psyir.walk(Routine)[0].children[:])
     reference = psyir.walk(Reference)[0]
-    parallel = psyir.walk(Routine)[0].children[0]
+    parallel = psyir.walk(OMPParallelDirective)[0]
     duc = DefinitionUseChain(reference, control_flow_region=[parallel])
     assert not duc.is_basic_block
 
@@ -384,6 +387,18 @@ def test_definition_use_chain_find_basic_blocks(fortran_reader):
     assert len(blocks[0]) == 1
     assert blocks[0][0] is routine.children[0].children[0]
 
+    # Add another assignment before the RegionDirective
+    assign_copy = psyir.walk(Assignment)[0].copy()
+    psyir.walk(Routine)[0].children.insert(0, assign_copy)
+    cfn, blocks = duc._find_basic_blocks(routine.children[:])
+    assert len(cfn) == 2
+    assert cfn[0] is None
+    assert len(blocks) == 2
+    assert len(blocks[0]) == 1
+    assert blocks[0][0] is routine.walk(Assignment)[0]
+    assert len(blocks[1]) == 1
+    assert blocks[1][0] is routine.walk(OMPParallelDirective)[0].dir_body
+
 
 def test_definition_use_chain_find_forward_accesses_basic_example(
     fortran_reader,
@@ -423,33 +438,33 @@ def test_definition_use_chain_find_forward_accesses_basic_example(
     )
     reaches = chains.find_forward_accesses()
     # We find 3 results
-    # the a in e = a**2
-    # the a in c = d * a
+    # the a in e = a**2 (assignment 2)
+    # the a in c = d * a (assignment 4)
     # The call bar(c, b) as a isn't local and we can't guarantee its behaviour.
     assert len(reaches) == 3
-    assert reaches[0] is routine.children[1].rhs.children[0]
-    assert reaches[1] is routine.children[4].rhs.children[1]
-    assert reaches[2] is routine.children[6]
+    assert reaches[0] is routine.walk(Assignment)[1].rhs.children[0]
+    assert reaches[1] is routine.walk(Assignment)[4].rhs.children[1]
+    assert reaches[2] is routine.walk(Call)[1]
 
     # Create use chain for d in d = c + 2.0
     chains = DefinitionUseChain(routine.children[3].lhs, [routine])
     reaches = chains.find_forward_accesses()
     # We should find 2 results
-    # c = D * a
-    # b = c + D
-    assert reaches[0] is routine.children[4].rhs.children[0]
-    assert reaches[1] is routine.children[5].rhs.children[1]
+    # c = D * a (Assignment 5)
+    # b = c + D (Assignment 6)
+    assert reaches[0] is routine.walk(Assignment)[4].rhs.children[0]
+    assert reaches[1] is routine.walk(Assignment)[5].rhs.children[1]
     assert len(reaches) == 2
 
-    # Create use chain for c in c = d * a
-    chains = DefinitionUseChain(routine.children[4].lhs, [routine])
+    # Create use chain for c in c = d * a (Assignment 5)
+    chains = DefinitionUseChain(routine.walk(Assignment)[4].lhs, [routine])
     reaches = chains.find_forward_accesses()
     # 2 results:
-    # b = C + d
-    # call bar(c, d)
+    # b = C + d (Assignment 6)
+    # call bar(c, d) (The second Call)
     assert len(reaches) == 2
-    assert reaches[0] is routine.children[5].rhs.children[0]
-    assert reaches[1] is routine.children[6].arguments[0]
+    assert reaches[0] is routine.walk(Assignment)[5].rhs.children[0]
+    assert reaches[1] is routine.walk(Call)[1].arguments[0]
 
 
 def test_definition_use_chain_find_forward_accesses_assignment(
@@ -471,9 +486,10 @@ def test_definition_use_chain_find_forward_accesses_assignment(
     # We should find 3 results, all 3 references in
     # A = A * A
     assert len(reaches) == 3
-    assert reaches[0] is routine.children[1].rhs.children[0]
-    assert reaches[1] is routine.children[1].rhs.children[1]
-    assert reaches[2] is routine.children[1].lhs
+    assignment = routine.walk(Assignment)[1]
+    assert reaches[0] is assignment.rhs.children[0]
+    assert reaches[1] is assignment.rhs.children[1]
+    assert reaches[2] is assignment.lhs
 
 
 def test_definition_use_chain_find_forward_accesses_ifelse_example(
@@ -502,10 +518,10 @@ def test_definition_use_chain_find_forward_accesses_ifelse_example(
     # TODO #2760 For now the if statement doesn't kill the accesses,
     # even though it will always be written to.
     assert len(reaches) == 4
-    assert reaches[0] is routine.children[1].rhs.children[0]
-    assert reaches[1] is routine.children[2].if_body.children[0].lhs
-    assert reaches[2] is routine.children[2].else_body.children[0].lhs
-    assert reaches[3] is routine.children[3].rhs.children[0]
+    assert reaches[0] is routine.walk(Assignment)[1].rhs.children[0]
+    assert reaches[1] is routine.walk(Assignment)[2].lhs
+    assert reaches[2] is routine.walk(Assignment)[3].lhs
+    assert reaches[3] is routine.walk(Assignment)[4].rhs.children[0]
 
     # Also check that a = 3 forward access is not a = 4.
     a_3 = routine.children[2].if_body.children[0].lhs
@@ -545,10 +561,10 @@ def test_definition_use_chain_find_forward_accesses_loop_example(
     # Second is c = a + b
     assert len(reaches) == 3
     assert (
-        reaches[0] is routine.children[1].loop_body.children[0].rhs.children[0]
+        reaches[0] is routine.walk(Assignment)[1].rhs.children[0]
     )
-    assert reaches[1] is routine.children[1].loop_body.children[0].lhs
-    assert reaches[2] is routine.children[2].rhs.children[0]
+    assert reaches[1] is routine.walk(Assignment)[1].lhs
+    assert reaches[2] is routine.walk(Assignment)[3].rhs.children[0]
 
     # Check if we access a loop variable
     code = """
@@ -573,7 +589,7 @@ def test_definition_use_chain_find_forward_accesses_loop_example(
     # We should have 1 reaches
     # It should be the loop
     assert len(reaches) == 1
-    assert reaches[0] is routine.children[1]
+    assert reaches[0] is routine.walk(Loop)[0]
 
 
 def test_definition_use_chain_find_forward_accesses_while_loop_example(
@@ -594,15 +610,15 @@ def test_definition_use_chain_find_forward_accesses_while_loop_example(
     psyir = fortran_reader.psyir_from_source(code)
     routine = psyir.walk(Routine)[0]
     # Start the chain from A = a + 3.
-    chains = DefinitionUseChain(routine.children[2].loop_body.children[0].lhs)
+    chains = DefinitionUseChain(routine.walk(Assignment)[2].lhs)
     reaches = chains.find_forward_accesses()
 
     assert len(reaches) == 3
-    assert reaches[0] is routine.children[2].condition.children[0]
+    assert reaches[0] is routine.walk(WhileLoop)[0].condition.children[0]
     assert (
-        reaches[1] is routine.children[2].loop_body.children[0].rhs.children[0]
+        reaches[1] is routine.walk(Assignment)[2].rhs.children[0]
     )
-    assert reaches[2] is routine.children[2].loop_body.children[0].lhs
+    assert reaches[2] is routine.walk(Assignment)[2].lhs
 
 
 def test_definition_use_chain_foward_accesses_nested_loop_example(
@@ -630,9 +646,9 @@ def test_definition_use_chain_foward_accesses_nested_loop_example(
     reaches = chains.find_forward_accesses()
     # Results should be A = A + 3 and the a < i condition
     assert reaches[0] is loops[0].condition.children[0]
-    assert reaches[1] is loops[0].loop_body.children[0].rhs.children[0]
-    assert reaches[2] is loops[0].loop_body.children[0].lhs
-    assert reaches[3] is loops[1].loop_body.children[0].rhs.children[1]
+    assert reaches[1] is loops[0].walk(Assignment)[0].rhs.children[0]
+    assert reaches[2] is loops[0].walk(Assignment)[0].lhs
+    assert reaches[3] is loops[1].walk(Assignment)[0].rhs.children[1]
     assert len(reaches) == 4
 
 
@@ -651,10 +667,10 @@ def test_definition_use_chain_find_forward_accesses_structure_example(
 
     psyir = fortran_reader.psyir_from_source(code)
     routine = psyir.walk(Routine)[0]
-    chains = DefinitionUseChain(routine.children[0].lhs)
+    chains = DefinitionUseChain(routine.walk(Assignment)[0].lhs)
     reaches = chains.find_forward_accesses()
     assert len(reaches) == 1
-    assert reaches[0] is routine.children[2].lhs
+    assert reaches[0] is routine.walk(Assignment)[2].lhs
 
 
 def test_definition_use_chain_find_forward_accesses_no_control_flow_example(
@@ -670,10 +686,10 @@ def test_definition_use_chain_find_forward_accesses_no_control_flow_example(
     end subroutine"""
     psyir = fortran_reader.psyir_from_source(code)
     routine = psyir.walk(Routine)[0]
-    chains = DefinitionUseChain(routine.children[0].rhs.children[0])
+    chains = DefinitionUseChain(routine.walk(Assignment)[0].rhs.children[0])
     reaches = chains.find_forward_accesses()
     assert len(reaches) == 1
-    assert reaches[0] is routine.children[0].lhs
+    assert reaches[0] is routine.walk(Assignment)[0].lhs
 
 
 def test_definition_use_chain_find_forward_accesses_no_control_flow_example2(
@@ -691,10 +707,10 @@ def test_definition_use_chain_find_forward_accesses_no_control_flow_example2(
     end subroutine"""
     psyir = fortran_reader.psyir_from_source(code)
     routine = psyir.walk(Routine)[0]
-    chains = DefinitionUseChain(routine.children[0].rhs.children[0])
+    chains = DefinitionUseChain(routine.walk(Assignment)[0].rhs.children[0])
     reaches = chains.find_forward_accesses()
     assert len(reaches) == 1
-    assert reaches[0] is routine.children[0].lhs
+    assert reaches[0] is routine.walk(Assignment)[0].lhs
 
 
 def test_definition_use_chain_find_forward_accesses_codeblock(
@@ -711,10 +727,10 @@ def test_definition_use_chain_find_forward_accesses_codeblock(
     end subroutine"""
     psyir = fortran_reader.psyir_from_source(code)
     routine = psyir.walk(Routine)[0]
-    chains = DefinitionUseChain(routine.children[0].lhs)
+    chains = DefinitionUseChain(routine.walk(Assignment)[0].lhs)
     reaches = chains.find_forward_accesses()
     assert len(reaches) == 1
-    assert reaches[0] is routine.children[1]
+    assert reaches[0] is routine.walk(CodeBlock)[0]
 
 
 def test_definition_use_chain_find_forward_accesses_codeblock_and_call_nlocal(
@@ -732,10 +748,10 @@ def test_definition_use_chain_find_forward_accesses_codeblock_and_call_nlocal(
     end subroutine"""
     psyir = fortran_reader.psyir_from_source(code)
     routine = psyir.walk(Routine)[0]
-    chains = DefinitionUseChain(routine.children[0].lhs)
+    chains = DefinitionUseChain(routine.walk(Assignment)[0].lhs)
     reaches = chains.find_forward_accesses()
     assert len(reaches) == 1
-    assert reaches[0] is routine.children[1]
+    assert reaches[0] is routine.walk(CodeBlock)[0]
 
 
 def test_definition_use_chain_find_forward_accesses_codeblock_and_call_cflow(
@@ -756,11 +772,11 @@ def test_definition_use_chain_find_forward_accesses_codeblock_and_call_cflow(
     end subroutine"""
     psyir = fortran_reader.psyir_from_source(code)
     routine = psyir.walk(Routine)[0]
-    chains = DefinitionUseChain(routine.children[0].lhs)
+    chains = DefinitionUseChain(routine.walk(Assignment)[0].lhs)
     reaches = chains.find_forward_accesses()
     assert len(reaches) == 2
-    assert reaches[0] is routine.children[1].if_body.children[0]
-    assert reaches[1] is routine.children[2]
+    assert reaches[0] is routine.walk(CodeBlock)[0]
+    assert reaches[1] is routine.walk(Call)[1]
 
 
 def test_definition_use_chain_find_forward_accesses_codeblock_and_call_local(
@@ -779,10 +795,10 @@ def test_definition_use_chain_find_forward_accesses_codeblock_and_call_local(
     end subroutine"""
     psyir = fortran_reader.psyir_from_source(code)
     routine = psyir.walk(Routine)[0]
-    chains = DefinitionUseChain(routine.children[0].lhs)
+    chains = DefinitionUseChain(routine.walk(Assignment)[0].lhs)
     reaches = chains.find_forward_accesses()
     assert len(reaches) == 1
-    assert reaches[0] is routine.children[1]
+    assert reaches[0] is routine.walk(CodeBlock)[0]
 
 
 def test_definition_use_chain_find_forward_accesses_call_and_codeblock_nlocal(
@@ -800,10 +816,10 @@ def test_definition_use_chain_find_forward_accesses_call_and_codeblock_nlocal(
     end subroutine"""
     psyir = fortran_reader.psyir_from_source(code)
     routine = psyir.walk(Routine)[0]
-    chains = DefinitionUseChain(routine.children[0].lhs)
+    chains = DefinitionUseChain(routine.walk(Assignment)[0].lhs)
     reaches = chains.find_forward_accesses()
     assert len(reaches) == 1
-    assert reaches[0] is routine.children[1]
+    assert reaches[0] is routine.walk(Call)[0]
 
 
 def test_definition_use_chains_goto_statement(
@@ -819,7 +835,7 @@ def test_definition_use_chains_goto_statement(
     end subroutine"""
     psyir = fortran_reader.psyir_from_source(code)
     routine = psyir.walk(Routine)[0]
-    chains = DefinitionUseChain(routine.children[0].lhs)
+    chains = DefinitionUseChain(routine.walk(Assignment)[0].lhs)
     with pytest.raises(NotImplementedError) as excinfo:
         chains.find_forward_accesses()
     assert ("DefinitionUseChains can't handle code containing GOTO statements"
@@ -848,7 +864,7 @@ def test_definition_use_chains_exit_statement(
     routine = psyir.walk(Routine)[0]
     # Start the chain from A = a +i.
     chains = DefinitionUseChain(
-        routine.children[1].loop_body.children[0].lhs
+        routine.walk(Assignment)[1].lhs
     )
     reaches = chains.find_forward_accesses()
     # We should have 3 reaches
@@ -856,10 +872,10 @@ def test_definition_use_chains_exit_statement(
     # Second is c = a + b
     assert len(reaches) == 3
     assert (
-        reaches[0] is routine.children[1].loop_body.children[0].rhs.children[0]
+        reaches[0] is routine.walk(Assignment)[1].rhs.children[0]
     )
-    assert reaches[1] is routine.children[1].loop_body.children[0].lhs
-    assert reaches[2] is routine.children[2].rhs.children[0]
+    assert reaches[1] is routine.walk(Assignment)[1].lhs
+    assert reaches[2] is routine.walk(Assignment)[3].rhs.children[0]
     pytest.xfail(reason="Issue #2760: DefinitionUseChains should not search "
                         "again in a loop when there is a guaranteed exit "
                         "statement")
@@ -888,7 +904,7 @@ def test_definition_use_chains_cycle_statement(
     routine = psyir.walk(Routine)[0]
     # Start the chain from A = a +i.
     chains = DefinitionUseChain(
-        routine.children[1].loop_body.children[0].lhs
+        routine.walk(Assignment)[1].lhs
     )
     reaches = chains.find_forward_accesses()
     # We should have 4 reaches
@@ -897,11 +913,11 @@ def test_definition_use_chains_cycle_statement(
     # Then c = A + b
     assert len(reaches) == 4
     assert (
-        reaches[0] is routine.children[1].loop_body.children[0].rhs.children[0]
+        reaches[0] is routine.walk(Assignment)[1].rhs.children[0]
     )
-    assert reaches[1] is routine.children[1].loop_body.children[0].lhs
-    assert reaches[2] is routine.children[1].loop_body.children[1].lhs
-    assert reaches[3] is routine.children[2].rhs.children[0]
+    assert reaches[1] is routine.walk(Assignment)[1].lhs
+    assert reaches[2] is routine.walk(Assignment)[2].lhs
+    assert reaches[3] is routine.walk(Assignment)[4].rhs.children[0]
 
 
 def test_definition_use_chains_return_statement(
@@ -927,7 +943,7 @@ def test_definition_use_chains_return_statement(
     routine = psyir.walk(Routine)[0]
     # Start the chain from A = a +i.
     chains = DefinitionUseChain(
-        routine.children[1].loop_body.children[0].lhs
+        routine.walk(Assignment)[1].lhs
     )
     reaches = chains.find_forward_accesses()
     # We should have 4 reaches
@@ -936,11 +952,11 @@ def test_definition_use_chains_return_statement(
     # Then c = A + b
     assert len(reaches) == 4
     assert (
-        reaches[0] is routine.children[1].loop_body.children[0].rhs.children[0]
+        reaches[0] is routine.walk(Assignment)[1].rhs.children[0]
     )
-    assert reaches[1] is routine.children[1].loop_body.children[0].lhs
-    assert reaches[2] is routine.children[1].loop_body.children[1].lhs
-    assert reaches[3] is routine.children[2].rhs.children[0]
+    assert reaches[1] is routine.walk(Assignment)[1].lhs
+    assert reaches[2] is routine.walk(Assignment)[2].lhs
+    assert reaches[3] is routine.walk(Assignment)[4].rhs.children[0]
 
 
 def test_definition_use_chains_forward_accesses_multiple_routines(
@@ -964,7 +980,7 @@ def test_definition_use_chains_forward_accesses_multiple_routines(
     psyir = fortran_reader.psyir_from_source(code)
     routine = psyir.walk(Routine)[0]
     chains = DefinitionUseChain(
-        routine.children[0].lhs
+        routine.walk(Assignment)[0].lhs
     )
     reaches = chains.find_forward_accesses()
     assert len(reaches) == 0
@@ -992,13 +1008,13 @@ def test_definition_use_chains_forward_accesses_empty_schedules(
     psyir = fortran_reader.psyir_from_source(code)
     routine = psyir.walk(Routine)[0]
     chains = DefinitionUseChain(
-            routine.children[0].lhs
+            routine.walk(Assignment)[0].lhs
     )
     reaches = chains.find_forward_accesses()
     assert len(reaches) == 3
-    assert reaches[0] is routine.children[4].rhs.children[0]
-    assert reaches[1] is routine.children[4].rhs.children[1]
-    assert reaches[2] is routine.children[4].lhs
+    assert reaches[0] is routine.walk(Assignment)[1].rhs.children[0]
+    assert reaches[1] is routine.walk(Assignment)[1].rhs.children[1]
+    assert reaches[2] is routine.walk(Assignment)[1].lhs
 
 
 def test_definition_use_chains_backward_accesses_inquiry_func(
@@ -1018,7 +1034,7 @@ def test_definition_use_chains_backward_accesses_inquiry_func(
     """
     psyir = fortran_reader.psyir_from_source(code)
     routine = psyir.walk(Routine)[0]
-    chains = DefinitionUseChain(routine.children[0].lhs)
+    chains = DefinitionUseChain(routine.walk(Assignment)[0].lhs)
     reaches = chains.find_forward_accesses()
     assert len(reaches) == 0
 
@@ -1043,10 +1059,50 @@ def test_definition_use_chains_multiple_ancestor_loops(
         end do
     end subroutine test"""
     psyir = fortran_reader.psyir_from_source(code)
-    loops = psyir.walk(Loop)
-    chains = DefinitionUseChain(loops[2].loop_body.children[0].lhs)
+    routine = psyir.walk(Routine)[0]
+    chains = DefinitionUseChain(routine.walk(Assignment)[1].lhs)
     reaches = chains.find_forward_accesses()
     assert len(reaches) == 3
-    assert reaches[0] is loops[0].loop_body.children[0].lhs
-    assert reaches[1] is loops[1].loop_body.children[0].lhs
-    assert reaches[2] is loops[2].loop_body.children[0].lhs
+    assert reaches[0] is routine.walk(Assignment)[0].lhs
+    assert reaches[1] is routine.walk(Assignment)[1].lhs
+    assert reaches[2] is routine.walk(Assignment)[2].lhs
+
+
+def test_definition_use_chain_find_forward_accesses_pure_call(
+    fortran_reader,
+):
+    """Functionality test for the find_forward_accesses routine. This
+    tests the behaviour for a pure subrotuine call."""
+    code = """
+    pure subroutine y(in)
+        integer :: in
+        in = in + 1
+    end subroutine y
+    subroutine x(a, b)
+    integer :: a, b
+    a = 2
+    b = 1
+    call y(b)
+    a = a + 2
+    end subroutine"""
+    psyir = fortran_reader.psyir_from_source(code)
+    routine = psyir.walk(Routine)[1]
+    # Start from the lhs of the first assignment
+    lhs_assign1 = routine.walk(Assignment)[0].lhs
+    chains = DefinitionUseChain(lhs_assign1)
+    reaches = chains.find_forward_accesses()
+    assert len(reaches) == 2
+    # Should find the a in the rhs of a = a + 2 and the lhs.
+    rhs_assign3 = routine.walk(Assignment)[2].rhs.children[0]
+    lhs_assign3 = routine.walk(Assignment)[2].lhs
+    assert reaches[0] is rhs_assign3
+    assert reaches[1] is lhs_assign3
+
+    # Start from lhs of b = 1
+    lhs_assign2 = routine.walk(Assignment)[1].lhs
+    chains = DefinitionUseChain(lhs_assign2)
+    reaches = chains.find_forward_accesses()
+    assert len(reaches) == 1
+    # result is first argument of the pure subroutine call
+    argument = routine.walk(Call)[0].children[1]
+    assert reaches[0] is argument


Reviews:
user: login: arporter
body: Thanks Aidan. Took a while to convince myself that we can do this (and not merge the check with IntrinsicCall) but I now agree with the implementation.
In checking coverage, I found that the (existing) implementation is not fully covered by the associated tests so have requested that you take a look.
Apart from that it's just a bit of tidying and comments to make the tests easier to understand.
state: CHANGES_REQUESTED
author_association: MEMBER
submitted_at: 2025-05-22T08:57:41Z
user: login: LonelyCat124
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-22T09:51:48Z
user: login: LonelyCat124
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-22T09:52:03Z
user: login: LonelyCat124
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-22T09:53:23Z
user: login: LonelyCat124
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-22T10:03:17Z
user: login: LonelyCat124
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-22T10:12:47Z
user: login: LonelyCat124
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-22T10:37:00Z
user: login: arporter
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-22T12:20:51Z
user: login: arporter
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-22T12:23:03Z
user: login: arporter
body: Those tests are much nicer now, thanks Aidan.
Since this needs to wait behind a couple of other PRs and then needs the integration testing done, we could take the opportunity to improve some of the other tests if you like. If there are other things to do then I'm equally happy to leave it for now.

If you could just delete the `print` statements from the new test code you've added that would be great.
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-22T12:29:39Z
user: login: arporter
body: Changes all look good - thanks for the additional improvements.
Annoyingly the LFRic integration tests are broken so this needs to wait until they are fixed so that I can double-check they are OK with this PR.
state: APPROVED
author_association: MEMBER
submitted_at: 2025-05-27T12:01:58Z

Review comments:
diff_hunk: @@ -742,3 +742,34 @@ def test_definition_use_chains_backward_accesses_inquiry_func(
     )
     reaches = chains.find_backward_accesses()
     assert len(reaches) == 0
+
+
+def test_definition_use_chain_find_backward_accesses_pure_call(
+    fortran_reader,
+):
+    """Functionality test for the find_backward_accesses routine. This
+    tests the behaviour for a pure subrotuine call."""
+    code = """
+    pure subroutine y(in)
+        integer :: in
+        in = in + 1
+    end subroutine y
+
+    subroutine x(a, b)
+    integer :: a, b
+    a = 2
+    call y(b)
+    a = a + 2
+    b = 1
+    end subroutine"""
+    psyir = fortran_reader.psyir_from_source(code)
+    routine = psyir.walk(Routine)[1]
+    chains = DefinitionUseChain(routine.children[2].rhs.children[0])
path: src/psyclone/tests/psyir/tools/definition_use_chains_backward_dependence_test.py
user: login: arporter
body: More robust (and readable) to do a `walk`, e.g.
```python
assign = routine.walk(Assignment)[1]
chains = DefinitionUseChain(assign.rhs.children[0])
```
created_at: 2025-05-22T08:49:38Z
author_association: MEMBER
diff_hunk: @@ -742,3 +742,34 @@ def test_definition_use_chains_backward_accesses_inquiry_func(
     )
     reaches = chains.find_backward_accesses()
     assert len(reaches) == 0
+
+
+def test_definition_use_chain_find_backward_accesses_pure_call(
+    fortran_reader,
+):
+    """Functionality test for the find_backward_accesses routine. This
+    tests the behaviour for a pure subrotuine call."""
path: src/psyclone/tests/psyir/tools/definition_use_chains_backward_dependence_test.py
user: login: arporter
body: "subroutine"
created_at: 2025-05-22T08:49:47Z
author_association: MEMBER
diff_hunk: @@ -742,3 +742,34 @@ def test_definition_use_chains_backward_accesses_inquiry_func(
     )
     reaches = chains.find_backward_accesses()
     assert len(reaches) == 0
+
+
+def test_definition_use_chain_find_backward_accesses_pure_call(
+    fortran_reader,
+):
+    """Functionality test for the find_backward_accesses routine. This
+    tests the behaviour for a pure subrotuine call."""
+    code = """
+    pure subroutine y(in)
+        integer :: in
+        in = in + 1
+    end subroutine y
+
+    subroutine x(a, b)
+    integer :: a, b
+    a = 2
+    call y(b)
+    a = a + 2
+    b = 1
+    end subroutine"""
+    psyir = fortran_reader.psyir_from_source(code)
+    routine = psyir.walk(Routine)[1]
+    chains = DefinitionUseChain(routine.children[2].rhs.children[0])
+    reaches = chains.find_backward_accesses()
+    assert len(reaches) == 1
+    assert reaches[0] is routine.children[0].lhs
path: src/psyclone/tests/psyir/tools/definition_use_chains_backward_dependence_test.py
user: login: arporter
body: Could you add comments to save the reader having to work out what e.g. `routine.children[1].children[1]` is (and/or use a walk to simplify).
created_at: 2025-05-22T08:51:50Z
author_association: MEMBER
diff_hunk: @@ -759,6 +764,11 @@ def _compute_backward_uses(self, basic_block_list):
                         # Note that this assumes two symbols are not
                         # aliases of each other.
path: src/psyclone/psyir/tools/definition_use_chains.py
user: login: arporter
body: Not strictly this PR but:
```
$ pytest --cov psyclone.psyir.tools.definition_use_chains --cov-report term-missing psyir/tools/definition_use_chains_*test.py
---------- coverage: platform linux, python 3.13.0-final-0 -----------
Name                                                                            Stmts   Miss  Cover   Missing
------------------------------------------------------------------------------------------------------------------
/home/me/Projects/PSyclone/src/psyclone/psyir/tools/definition_use_chains.py     417      3    99%   658-660
------------------------------------------------------------------------------------------------------------------
TOTAL                                                                                  417      3    99%
```
created_at: 2025-05-22T08:54:41Z
author_association: MEMBER
diff_hunk: @@ -1050,3 +1050,34 @@ def test_definition_use_chains_multiple_ancestor_loops(
     assert reaches[0] is loops[0].loop_body.children[0].lhs
     assert reaches[1] is loops[1].loop_body.children[0].lhs
     assert reaches[2] is loops[2].loop_body.children[0].lhs
+
+
+def test_definition_use_chain_find_forward_accesses_pure_call(
+    fortran_reader,
+):
+    """Functionality test for the find_forward_accesses routine. This
+    tests the behaviour for a pure subrotuine call."""
+    code = """
+    pure subroutine y(in)
+        integer :: in
+        in = in + 1
+    end subroutine y
+    subroutine x(a, b)
+    integer :: a, b
+    a = 2
+    b = 1
+    call y(b)
+    a = a + 2
+    end subroutine"""
+    psyir = fortran_reader.psyir_from_source(code)
+    routine = psyir.walk(Routine)[1]
+    chains = DefinitionUseChain(routine.children[0].lhs)
path: src/psyclone/tests/psyir/tools/definition_use_chains_forward_dependence_test.py
user: login: arporter
body: Comments and/or walks to help me understand what's being checked please.
created_at: 2025-05-22T08:56:05Z
author_association: MEMBER
diff_hunk: @@ -742,3 +742,34 @@ def test_definition_use_chains_backward_accesses_inquiry_func(
     )
     reaches = chains.find_backward_accesses()
     assert len(reaches) == 0
+
+
+def test_definition_use_chain_find_backward_accesses_pure_call(
+    fortran_reader,
+):
+    """Functionality test for the find_backward_accesses routine. This
+    tests the behaviour for a pure subrotuine call."""
+    code = """
+    pure subroutine y(in)
+        integer :: in
+        in = in + 1
+    end subroutine y
+
+    subroutine x(a, b)
+    integer :: a, b
+    a = 2
+    call y(b)
+    a = a + 2
+    b = 1
+    end subroutine"""
+    psyir = fortran_reader.psyir_from_source(code)
+    routine = psyir.walk(Routine)[1]
+    chains = DefinitionUseChain(routine.children[2].rhs.children[0])
path: src/psyclone/tests/psyir/tools/definition_use_chains_backward_dependence_test.py
user: login: LonelyCat124
body: Done
created_at: 2025-05-22T09:51:47Z
author_association: COLLABORATOR
diff_hunk: @@ -742,3 +742,34 @@ def test_definition_use_chains_backward_accesses_inquiry_func(
     )
     reaches = chains.find_backward_accesses()
     assert len(reaches) == 0
+
+
+def test_definition_use_chain_find_backward_accesses_pure_call(
+    fortran_reader,
+):
+    """Functionality test for the find_backward_accesses routine. This
+    tests the behaviour for a pure subrotuine call."""
path: src/psyclone/tests/psyir/tools/definition_use_chains_backward_dependence_test.py
user: login: LonelyCat124
body: Fixed
created_at: 2025-05-22T09:52:03Z
author_association: COLLABORATOR
diff_hunk: @@ -742,3 +742,34 @@ def test_definition_use_chains_backward_accesses_inquiry_func(
     )
     reaches = chains.find_backward_accesses()
     assert len(reaches) == 0
+
+
+def test_definition_use_chain_find_backward_accesses_pure_call(
+    fortran_reader,
+):
+    """Functionality test for the find_backward_accesses routine. This
+    tests the behaviour for a pure subrotuine call."""
+    code = """
+    pure subroutine y(in)
+        integer :: in
+        in = in + 1
+    end subroutine y
+
+    subroutine x(a, b)
+    integer :: a, b
+    a = 2
+    call y(b)
+    a = a + 2
+    b = 1
+    end subroutine"""
+    psyir = fortran_reader.psyir_from_source(code)
+    routine = psyir.walk(Routine)[1]
+    chains = DefinitionUseChain(routine.children[2].rhs.children[0])
+    reaches = chains.find_backward_accesses()
+    assert len(reaches) == 1
+    assert reaches[0] is routine.children[0].lhs
path: src/psyclone/tests/psyir/tools/definition_use_chains_backward_dependence_test.py
user: login: LonelyCat124
body: Done
created_at: 2025-05-22T09:53:22Z
author_association: COLLABORATOR
diff_hunk: @@ -1050,3 +1050,34 @@ def test_definition_use_chains_multiple_ancestor_loops(
     assert reaches[0] is loops[0].loop_body.children[0].lhs
     assert reaches[1] is loops[1].loop_body.children[0].lhs
     assert reaches[2] is loops[2].loop_body.children[0].lhs
+
+
+def test_definition_use_chain_find_forward_accesses_pure_call(
+    fortran_reader,
+):
+    """Functionality test for the find_forward_accesses routine. This
+    tests the behaviour for a pure subrotuine call."""
+    code = """
+    pure subroutine y(in)
+        integer :: in
+        in = in + 1
+    end subroutine y
+    subroutine x(a, b)
+    integer :: a, b
+    a = 2
+    b = 1
+    call y(b)
+    a = a + 2
+    end subroutine"""
+    psyir = fortran_reader.psyir_from_source(code)
+    routine = psyir.walk(Routine)[1]
+    chains = DefinitionUseChain(routine.children[0].lhs)
path: src/psyclone/tests/psyir/tools/definition_use_chains_forward_dependence_test.py
user: login: LonelyCat124
body: Done
created_at: 2025-05-22T10:03:17Z
author_association: COLLABORATOR
diff_hunk: @@ -759,6 +764,11 @@ def _compute_backward_uses(self, basic_block_list):
                         # Note that this assumes two symbols are not
                         # aliases of each other.
path: src/psyclone/psyir/tools/definition_use_chains.py
user: login: LonelyCat124
body: Not sure how that missing coverage slipped through, I've added something for it now.
created_at: 2025-05-22T10:12:47Z
author_association: COLLABORATOR
diff_hunk: @@ -759,6 +764,11 @@ def _compute_backward_uses(self, basic_block_list):
                         # Note that this assumes two symbols are not
                         # aliases of each other.
path: src/psyclone/psyir/tools/definition_use_chains.py
user: login: LonelyCat124
body: Ah codecov.io seemed to think that has coverage on head somehow, how strange. Its covered now but I guess something else in the code hit that.
created_at: 2025-05-22T10:37:00Z
author_association: COLLABORATOR
diff_hunk: @@ -759,6 +764,11 @@ def _compute_backward_uses(self, basic_block_list):
                         # Note that this assumes two symbols are not
                         # aliases of each other.
path: src/psyclone/psyir/tools/definition_use_chains.py
user: login: arporter
body: Yes, it will be something else in the test suite. The older the code, the poorer we are at achieving this but it does make coverage checking much easier when you're developing.
created_at: 2025-05-22T12:20:51Z
author_association: MEMBER
diff_hunk: @@ -742,3 +742,34 @@ def test_definition_use_chains_backward_accesses_inquiry_func(
     )
     reaches = chains.find_backward_accesses()
     assert len(reaches) == 0
+
+
+def test_definition_use_chain_find_backward_accesses_pure_call(
+    fortran_reader,
+):
+    """Functionality test for the find_backward_accesses routine. This
+    tests the behaviour for a pure subrotuine call."""
+    code = """
+    pure subroutine y(in)
+        integer :: in
+        in = in + 1
+    end subroutine y
+
+    subroutine x(a, b)
+    integer :: a, b
+    a = 2
+    call y(b)
+    a = a + 2
+    b = 1
+    end subroutine"""
+    psyir = fortran_reader.psyir_from_source(code)
+    routine = psyir.walk(Routine)[1]
+    chains = DefinitionUseChain(routine.children[2].rhs.children[0])
path: src/psyclone/tests/psyir/tools/definition_use_chains_backward_dependence_test.py
user: login: arporter
body: Great, that's much easier on my brain, thanks :-)
created_at: 2025-05-22T12:23:03Z
author_association: MEMBER

PR comments:
user: login: codecov[bot]
created_at: 2025-05-20T12:01:45Z
body: ## [Codecov](https://app.codecov.io/gh/stfc/PSyclone/pull/3000?dropdown=coverage&src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=stfc) Report
All modified and coverable lines are covered by tests :white_check_mark:
> Project coverage is 99.91%. Comparing base [(`f908a27`)](https://app.codecov.io/gh/stfc/PSyclone/commit/f908a27612dbfcc54c4de4a13f339602da49106b?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=stfc) to head [(`ab1a195`)](https://app.codecov.io/gh/stfc/PSyclone/commit/ab1a195b1c05dd29d5014f40bb58dc289c9ce3f5?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=stfc).
> Report is 11 commits behind head on master.

<details><summary>Additional details and impacted files</summary>


```diff
@@           Coverage Diff           @@
##           master    #3000   +/-   ##
=======================================
  Coverage   99.91%   99.91%           
=======================================
  Files         365      365           
  Lines       51899    51903    +4     
=======================================
+ Hits        51853    51857    +4     
  Misses         46       46           
```

</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/stfc/PSyclone/pull/3000?dropdown=coverage&src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=stfc).   
:loudspeaker: Have feedback on the report? [Share it here](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=stfc).

<details><summary> :rocket: New features to boost your workflow: </summary>

- :snowflake: [Test Analytics](https://docs.codecov.com/docs/test-analytics): Detect flaky tests, report on failures, and find test suite problems.
</details>
author_association: NONE
user: login: LonelyCat124
created_at: 2025-05-22T10:37:28Z
body: @arporter Ready for another look now. If you want me to refactor all the definition use chains tests to be clearer let me know and we can do that here.
author_association: COLLABORATOR
user: login: LonelyCat124
created_at: 2025-05-22T13:38:51Z
body: @arporter I rewrote the tests to be more readable with `walk` wherever I thought it made sense. Functionality hasn't changed from these changes.
author_association: COLLABORATOR
user: login: arporter
created_at: 2025-05-28T07:46:33Z
body: (Integration tests were green.)
author_association: MEMBER