Repository: stfc/PSyclone (python)

Issue:
The `is_writen/read/accessed_before` from SingleVariableAccessInfo does not respected control flow
This is because it uses the _location in AccessType, which is mostly a syntactic location but with the assignment order switched. But it means what calling these methods will result to bugs if conditions and loops are involved.

We now have the DefinitionUseChain for knowing the proper usage order, and we already have position for syntactic location, so I propose removing these methods and the _location altogether.

Pull request:
(closes #3036) Remove AccessType location and is_accessed_before method

Patch:
diff --git a/doc/developer_guide/dependency.rst b/doc/developer_guide/dependency.rst
index 3ae20cb50..6abf2c2af 100644
--- a/doc/developer_guide/dependency.rst
+++ b/doc/developer_guide/dependency.rst
@@ -344,7 +344,7 @@ AccessInfo
 The class `SingleVariableAccessInfo` uses a list of
 `psyclone.core.AccessInfo` instances to store all
 accesses to a single variable. A new instance of `AccessInfo`
-is appended to the list whenever `add_access_with_location()`
+is appended to the list whenever `add_access()`
 is called.
 
 .. autoclass:: psyclone.core.AccessInfo
@@ -446,51 +446,6 @@ wrapped in an outer loop over all accesses.
 
     Index 'i' is used.
 
-Access Location
----------------
-
-Variable accesses are stored in the order in which they happen. For example,
-an assignment `a=a+1` will store two access for the variable `a`, the
-first one being a READ access, followed by a WRITE access, since this is the
-order in which the accesses are executed.
-Additionally, the function `reference_accesses()` keeps track of the location
-at which the accesses happen. A location is an integer number, starting with 0,
-which is increased for each new statement. This makes it possible to
-compare accesses to variables: if two accesses have the same location value,
-it means the accesses happen in the same statement, for example `a=a+1`:
-the READ and WRITE access to `a` will have the same location number. If on the
-other hand the accesses happen in two separate statements, e.g. `a=b+1; c=a+1`
-then the first access to `a` (and the access to `b`) will have a smaller
-location number than the second access to `a` (and the access to `c`).
-If two statements have consecutive locations, this does not necessarily mean
-that the statements are executed one after another. For example in if-statements
-the statements in the if-body are counted first, then the statements in the
-else-body. It is the responsibility of the user to handle these cases - for
-example by creating separate `VariablesAccessMap` for statements in the if-body
-and for the else-body.
-
-.. note:: When using different instances for an if- and else-body, the first
-    statement of the if-body will
-    have the same location number as the first statement of the else-body. So
-    you can only compare location numbers from the same `VariablesAccessMap`
-    instance. If you merge two instances together, the locations of the merged-in
-    instance will be appropriately increased to follow the locations of the
-    instance to which it is merged.
-
-
-The location number is not exactly a line number - several statements can be
-on one line, which will get different location numbers. And certain lines
-will not have a location number (e.g. comment lines).
-
-As stated above, one instance of `VariablesAccessMap` can be extended by adding
-additional variable information. It is the responsibility of the user to make
-sure the accesses are added in the right order - the `VariablesAccessMap` object
-will always assume accesses happen at the current location, and a call to 
-`next_location()` is required (internally) to increase the location number.
-
-.. note:: It is not possible to add access information about an earlier
-     usage to an existing `VariablesAccessMap` object. 
-
 
 Access Examples
 ---------------
diff --git a/src/psyclone/core/single_variable_access_info.py b/src/psyclone/core/single_variable_access_info.py
index 701d95c42..06b05dfa0 100644
--- a/src/psyclone/core/single_variable_access_info.py
+++ b/src/psyclone/core/single_variable_access_info.py
@@ -40,38 +40,31 @@
 '''This module provides management of variable access information.'''
 
 
+from __future__ import annotations
+from typing import TYPE_CHECKING, Optional
+
 from psyclone.core.access_type import AccessType
 from psyclone.core.component_indices import ComponentIndices
 from psyclone.errors import InternalError
+if TYPE_CHECKING:  # pragma: no cover
+    from psyclone.psyir.nodes import Node
 
 
 class AccessInfo():
-    '''This class stores information about a single access
-    pattern of one variable (e.g. variable is read at a certain location).
-    A location is a number which can be used to compare different accesses
-    (i.e. if one access happens before another). Each consecutive
-    location will have an increasing location number, but read and write
-    accesses in the same statement will have the same location number.
-    If the variable accessed is an array, this class will also store
-    the indices used in the access.
-    Note that the name of the variable is not stored in this class.
-    It is a helper class used in the `SingleVariableAccessInfo` class,
-    which stores all `AccessInfo` objects for a variable, and it stores
-    the name of the variable.
+    ''' This class stores information about an access to a variable (the node
+    where it happens and the type of access, and the index accessed if
+    available).
 
     :param access: the access type.
-    :type access_type: :py:class:`psyclone.core.access_type.AccessType`
-    :param int location: a number used in ordering the accesses.
     :param node: Node in PSyIR in which the access happens.
-    :type node: :py:class:`psyclone.psyir.nodes.Node`
     :param component_indices: indices used in the access, defaults to None.
-    :type component_indices: None, [], a list or a list of lists of \
-        :py:class:`psyclone.psyir.nodes.Node` objects, or an object of type \
-        :py:class:`psyclone.core.component_indices.ComponentIndices`
 
     '''
-    def __init__(self, access_type, location, node, component_indices=None):
-        self._location = location
+    def __init__(
+        self, access_type: AccessType, node: 'Node',
+        component_indices: Optional[list[list['Node']] |
+                                    ComponentIndices] = None
+    ):
         self._access_type = access_type
         self._node = node
         if not isinstance(component_indices, ComponentIndices):
@@ -80,9 +73,7 @@ class AccessInfo():
             self.component_indices = component_indices
 
     def __str__(self):
-        '''Returns a string representation showing the access mode
-        and location, e.g.: WRITE(5).'''
-        return f"{self._access_type}({self._location})"
+        return f"{self._access_type}"
 
     def change_read_to_write(self):
         '''This changes the access mode from READ to WRITE.
@@ -114,7 +105,7 @@ class AccessInfo():
         return self._component_indices
 
     @component_indices.setter
-    def component_indices(self, component_indices):
+    def component_indices(self, component_indices: ComponentIndices):
         '''Sets the indices for this AccessInfo instance. The component_indices
         contains a list of indices for each component of the signature,
         e.g. for `a(i)%b(j,k)%c` the component_indices will be
@@ -122,8 +113,6 @@ class AccessInfo():
         index expression).
 
         :param component_indices: indices used in the access.
-        :type component_indices: \
-            :py:class:`psyclone.core.component_indices.ComponentIndices`
 
         :raises InternalError: if component_indices is not an instance \
             of :py:class:`psyclone.core.component_indices.ComponentIndices`.
@@ -160,14 +149,6 @@ class AccessInfo():
         '''
         return self._access_type not in AccessType.non_data_accesses()
 
-    @property
-    def location(self):
-        ''':returns: the location information for this access.\
-        Please see the Developers' Guide for more information.
-        :rtype: int
-        '''
-        return self._location
-
     @property
     def node(self):
         ''':returns: the PSyIR node at which this access happens.
@@ -205,21 +186,15 @@ class SingleVariableAccessInfo():
     def __init__(self, signature):
         self._signature = signature
         # This is the list of AccessInfo instances for this variable.
-
         self._accesses = []
 
     def __str__(self):
         '''Returns a string representation of this object with the format:
-        var_name:WRITE(2),WRITE(3),READ(5) where the numbers indicate
-        the 'location' of the corresponding access. The location is an
-        integer number that enumerates each statement in a program unit,
-        and can be used to compare if an access is earlier, later or in
-        the same statement as another access.
-
+        var_name:[WRITE,WRITE,READ]
         '''
         all_accesses = ",".join([str(access) for access in self._accesses])
 
-        return f"{self._signature}:{all_accesses}"
+        return f"{self._signature}:[{all_accesses}]"
 
     def __repr__(self):
         return ",".join([str(access) for access in self._accesses])
@@ -337,24 +312,19 @@ class SingleVariableAccessInfo():
         return [access for access in self._accesses
                 if access.access_type in AccessType.all_write_accesses()]
 
-    def add_access_with_location(self, access_type, location, node,
-                                 component_indices):
+    def add_access(
+        self, access_type: AccessType, node: 'Node',
+        component_indices: Optional[list[list['Node']] |
+                                    ComponentIndices] = None
+    ):
         '''Adds access information to this variable.
 
         :param access_type: the type of access (READ, WRITE, ....)
-        :type access_type: \
-            :py:class:`psyclone.core.access_type.AccessType`
-        :param location: location information
-        :type location: int
         :param node: Node in PSyIR in which the access happens.
-        :type node: :py:class:`psyclone.psyir.nodes.Node`
         :param component_indices: indices used for each component of the \
             access.
-        :type component_indices:  \
-            :py:class:`psyclone.core.component_indices.ComponentIndices`
         '''
-        self._accesses.append(AccessInfo(access_type, location, node,
-                                         component_indices))
+        self._accesses.append(AccessInfo(access_type, node, component_indices))
 
     def change_read_to_write(self):
         '''This function is only used when analysing an assignment statement.
@@ -425,83 +395,6 @@ class SingleVariableAccessInfo():
         # The index variable is not used in any index in any access:
         return False
 
-    def is_written_before(self, reference):
-        '''Returns True if this variable is written before the specified
-        reference, and False if not.
-
-        :param reference: the reference at which to stop for access checks.
-        :type reference: :py:class:`psyclone.psyir.nodes.Reference`
-
-        :returns: True if this variable is written before the specified \
-            reference, and False if not.
-        :rtype: bool
-
-        :raises ValueError: if the specified reference is not in the list of \
-            all accesses.
-
-        '''
-        result = False
-
-        for access in self._accesses:
-            if access.node is reference:
-                return result
-            if access.access_type == AccessType.WRITE:
-                result = True
-        raise ValueError(f"Reference not found in 'is_written_before' for "
-                         f"variable '{self.var_name}'.")
-
-    def is_read_before(self, reference):
-        '''Returns True if this variable is read before the specified
-        reference, and False if not.
-
-        :param reference: the reference at which to stop for access checks.
-        :type reference: :py:class:`psyclone.psyir.nodes.Reference`
-
-        :returns: True if this variable is read before the specified \
-            reference, and False if not.
-        :rtype: bool
-
-        :raises ValueError: if the specified reference is not in the list of \
-            all accesses.
-
-        '''
-        result = False
-
-        for access in self._accesses:
-            if access.node is reference:
-                return result
-            if access.access_type == AccessType.READ:
-                result = True
-        raise ValueError(f"Reference not found in 'is_read_before' for "
-                         f"variable '{self.var_name}'.")
-
-    def is_accessed_before(self, reference):
-        '''Returns True if this variable is accessed before the specified
-        reference, and False if not. This is equivalent to testing that
-        'reference' is the very first access, but this function will also
-        verify that 'reference' is indeed in the list of accesses.
-
-        :param reference: the reference at which to stop for access checks.
-        :type reference: :py:class:`psyclone.psyir.nodes.Reference`
-
-        :returns: True if this variable is read before the specified \
-            reference, and False if not.
-        :rtype: bool
-
-        :raises ValueError: if the specified reference is not in the list of \
-            all accesses.
-
-        '''
-
-        result = False
-
-        for access in self._accesses:
-            if access.node is reference:
-                return result
-            result = True
-        raise ValueError(f"Reference not found in 'is_accessed_before' for "
-                         f"variable '{self.var_name}'.")
-
 
 # ---------- Documentation utils -------------------------------------------- #
 # The list of module members that we wish AutoAPI to generate
diff --git a/src/psyclone/core/variables_access_map.py b/src/psyclone/core/variables_access_map.py
index 5bba1f366..e05c0b33e 100644
--- a/src/psyclone/core/variables_access_map.py
+++ b/src/psyclone/core/variables_access_map.py
@@ -50,17 +50,10 @@ from psyclone.errors import InternalError
 
 class VariablesAccessMap(dict):
     ''' This dictionary stores `SingleVariableAccessInfo` instances indexed by
-    their signature. It also maintains 'location' information, which is an
-    integer number that is increased for each new statement. It can be used to
-    easily determine if one access is syntactically before another.
+    their signature.
 
     '''
 
-    def __init__(self):
-        super().__init__()
-        # Stores the current location information
-        self._location = 0
-
     def __str__(self):
         '''Gives a shortened visual representation of all variables
         and their access mode. The output is one of: READ, WRITE, READ+WRITE,
@@ -97,20 +90,6 @@ class VariablesAccessMap(dict):
             output_list.append(f"{signature}: {mode}")
         return ", ".join(output_list)
 
-    @property
-    def location(self):
-        '''Returns the current location of this instance, which is
-        the location at which the next accesses will be stored.
-        See the Developers' Guide for more information.
-
-        :returns: the current location of this object.
-        :rtype: int'''
-        return self._location
-
-    def next_location(self):
-        '''Increases the location number.'''
-        self._location = self._location + 1
-
     def add_access(self, signature, access_type, node, component_indices=None):
         '''Adds access information for the variable with the given signature.
         If the `component_indices` parameter is not an instance of
@@ -172,13 +151,10 @@ class VariablesAccessMap(dict):
                                 f"requires {len(signature)} elements.")
 
         if signature in self:
-            self[signature].add_access_with_location(access_type,
-                                                     self._location, node,
-                                                     component_indices)
+            self[signature].add_access(access_type, node, component_indices)
         else:
             var_info = SingleVariableAccessInfo(signature)
-            var_info.add_access_with_location(access_type, self._location,
-                                              node, component_indices)
+            var_info.add_access(access_type, node, component_indices)
             self[signature] = var_info
 
     @property
@@ -206,9 +182,7 @@ class VariablesAccessMap(dict):
     def update(self, other_access_info):
         ''' Updates this dictionary with the entries in the provided
         VariablesAccessMap. If there are repeated signatures, the provided
-        values are appeneded to the existing sequence of accesses. The
-        'location' property of the provided accesses (values in the dictionary)
-        will be updated to be after the existing entries.
+        values are appended to the existing sequence of accesses.
 
         :param other_access_info: the other VariablesAccessMap instance.
         :type other_access_info: :py:class:`psyclone.core.VariablesAccessMap`
@@ -217,22 +191,15 @@ class VariablesAccessMap(dict):
         for signature in other_access_info.all_signatures:
             var_info = other_access_info[signature]
             for access_info in var_info.all_accesses:
-                new_location = access_info.location + self._location
                 if signature in self:
                     var_info = self[signature]
                 else:
                     var_info = SingleVariableAccessInfo(signature)
                     self[signature] = var_info
 
-                var_info.add_access_with_location(access_info.access_type,
-                                                  new_location,
-                                                  access_info.node,
-                                                  access_info.
-                                                  component_indices)
-        # Increase the current location of this instance by the amount of
-        # locations just merged in
-        # pylint: disable=protected-access
-        self._location = self._location + other_access_info._location
+                var_info.add_access(access_info.access_type,
+                                    access_info.node,
+                                    access_info.component_indices)
 
     def is_called(self, signature: Signature) -> bool:
         '''
diff --git a/src/psyclone/domain/lfric/lfric_builtins.py b/src/psyclone/domain/lfric/lfric_builtins.py
index 362bf7c1b..a925d4702 100644
--- a/src/psyclone/domain/lfric/lfric_builtins.py
+++ b/src/psyclone/domain/lfric/lfric_builtins.py
@@ -251,9 +251,6 @@ class LFRicBuiltIn(BuiltIn, metaclass=abc.ABCMeta):
                     var_accesses.add_access(Signature(name), arg.access, self)
         # Now merge the write access to the end of all other accesses:
         var_accesses.update(written)
-        # Forward location pointer to next index, since this built-in kernel
-        # finishes a statement
-        var_accesses.next_location()
         return var_accesses
 
     def load(self, call, parent=None):
diff --git a/src/psyclone/domain/lfric/lfric_kern.py b/src/psyclone/domain/lfric/lfric_kern.py
index 79fd71f3f..245f84c80 100644
--- a/src/psyclone/domain/lfric/lfric_kern.py
+++ b/src/psyclone/domain/lfric/lfric_kern.py
@@ -151,9 +151,6 @@ class LFRicKern(CodedKern):
         create_arg_list.generate(var_accesses)
 
         var_accesses.update(super().reference_accesses())
-        # Set the current location index to the next location, since after
-        # this kernel a new statement starts.
-        var_accesses.next_location()
         return var_accesses
 
     def load(self, call, parent=None):
diff --git a/src/psyclone/gocean1p0.py b/src/psyclone/gocean1p0.py
index b16308804..f1910f1c9 100644
--- a/src/psyclone/gocean1p0.py
+++ b/src/psyclone/gocean1p0.py
@@ -1067,7 +1067,6 @@ class GOKern(CodedKern):
                                             self, [Reference(symbol_i),
                                                    Reference(symbol_j)])
         var_accesses.update(super().reference_accesses())
-        var_accesses.next_location()
         return var_accesses
 
     @property
diff --git a/src/psyclone/psyGen.py b/src/psyclone/psyGen.py
index 1682e7142..aff4abbc5 100644
--- a/src/psyclone/psyGen.py
+++ b/src/psyclone/psyGen.py
@@ -56,7 +56,7 @@ except ImportError:
     from psyclone.utils import stringify_annotation
 
 from psyclone.configuration import Config, LFRIC_API_NAMES, GOCEAN_API_NAMES
-from psyclone.core import AccessType, VariablesAccessMap
+from psyclone.core import AccessType
 from psyclone.errors import GenerationError, InternalError, FieldNotFoundError
 from psyclone.parse.algorithm import BuiltInCall
 from psyclone.psyir.backend.fortran import FortranWriter
@@ -1019,18 +1019,6 @@ class Kern(Statement):
         return (self.coloured_name(colour) + " " + self.name +
                 "(" + self.arguments.names + ")")
 
-    def reference_accesses(self) -> VariablesAccessMap:
-        '''
-        :returns: a map of all the symbol accessed inside this node, the
-            keys are Signatures (unique identifiers to a symbol and its
-            structure acccessors) and the values are SingleVariableAccessInfo
-            (a sequence of AccessTypes).
-
-        '''
-        var_accesses = super().reference_accesses()
-        var_accesses.next_location()
-        return var_accesses
-
     @property
     def is_reduction(self):
         '''
diff --git a/src/psyclone/psyir/nodes/assignment.py b/src/psyclone/psyir/nodes/assignment.py
index 23c7bff76..2c39b9c9c 100644
--- a/src/psyclone/psyir/nodes/assignment.py
+++ b/src/psyclone/psyir/nodes/assignment.py
@@ -209,7 +209,6 @@ class Assignment(Statement):
         # location otherwise, but the order is still important)
         rhs_accesses = self.rhs.reference_accesses()
         rhs_accesses.update(lhs_accesses)
-        rhs_accesses.next_location()
         return rhs_accesses
 
     @property
diff --git a/src/psyclone/psyir/nodes/call.py b/src/psyclone/psyir/nodes/call.py
index 79169d4f7..6e561fb97 100644
--- a/src/psyclone/psyir/nodes/call.py
+++ b/src/psyclone/psyir/nodes/call.py
@@ -340,8 +340,6 @@ class Call(Statement, DataNode):
                 # an impure routine in which case any arguments to that Call
                 # will have READWRITE access.)
                 var_accesses.update(arg.reference_accesses())
-        # Make sure that the next statement will be on the next location
-        var_accesses.next_location()
         return var_accesses
 
     @property
diff --git a/src/psyclone/psyir/nodes/if_block.py b/src/psyclone/psyir/nodes/if_block.py
index 4a7acf7ce..011b30024 100644
--- a/src/psyclone/psyir/nodes/if_block.py
+++ b/src/psyclone/psyir/nodes/if_block.py
@@ -190,11 +190,8 @@ class IfBlock(Statement):
 
         '''
         var_accesses = self.condition.reference_accesses()
-        var_accesses.next_location()
         var_accesses.update(self.if_body.reference_accesses())
-        var_accesses.next_location()
 
         if self.else_body:
             var_accesses.update(self.else_body.reference_accesses())
-            var_accesses.next_location()
         return var_accesses
diff --git a/src/psyclone/psyir/nodes/loop.py b/src/psyclone/psyir/nodes/loop.py
index 0e087a996..bc1c72333 100644
--- a/src/psyclone/psyir/nodes/loop.py
+++ b/src/psyclone/psyir/nodes/loop.py
@@ -509,11 +509,9 @@ class Loop(Statement):
             var_accesses.update(self.start_expr.reference_accesses())
             var_accesses.update(self.stop_expr.reference_accesses())
             var_accesses.update(self.step_expr.reference_accesses())
-            var_accesses.next_location()
 
         for child in self.loop_body.children:
             var_accesses.update(child.reference_accesses())
-            var_accesses.next_location()
         return var_accesses
 
     def independent_iterations(self,
diff --git a/src/psyclone/psyir/nodes/while_loop.py b/src/psyclone/psyir/nodes/while_loop.py
index 79b9f247c..efc994112 100644
--- a/src/psyclone/psyir/nodes/while_loop.py
+++ b/src/psyclone/psyir/nodes/while_loop.py
@@ -152,7 +152,5 @@ class WhileLoop(Statement):
         '''
         # The first child is the loop condition - all variables are read-only
         var_accesses = self.condition.reference_accesses()
-        var_accesses.next_location()
         var_accesses.update(self.loop_body.reference_accesses())
-        var_accesses.next_location()
         return var_accesses
diff --git a/src/psyclone/psyir/transformations/hoist_trans.py b/src/psyclone/psyir/transformations/hoist_trans.py
index 41ae1de08..dcd85421d 100644
--- a/src/psyclone/psyir/transformations/hoist_trans.py
+++ b/src/psyclone/psyir/transformations/hoist_trans.py
@@ -46,6 +46,7 @@ from psyclone.psyir.nodes import (
     Loop, Assignment, Schedule, Call, CodeBlock)
 from psyclone.psyir.transformations.transformation_error \
     import TransformationError
+from psyclone.psyir.tools.definition_use_chains import DefinitionUseChain
 
 
 class HoistTrans(Transformation):
@@ -226,13 +227,15 @@ class HoistTrans(Transformation):
                                           f"('{written_sig}') that is both "
                                           f"read and written.")
 
-            # Check if the variable is written or read before the first
-            # access in the statement to be hoisted:
+            # Check if any of the written variables could be used inside the
+            # loop before the statement that we are hoisting, this could be
+            # after the statement if there are conditional control flows.
             written_node = accesses_in_statement[0].node
-            # Get all access to that variable in the whole loop before the
-            # first write access that is to be hoisted:
             accesses_in_loop = all_loop_vars[written_sig]
-            if accesses_in_loop.is_accessed_before(written_node):
+            chains = DefinitionUseChain(
+                written_node, parent_loop.children[:]
+            )
+            if chains.find_backward_accesses():
                 code = statement.debug_string().strip()
                 raise TransformationError(f"The statement '{code}' can't be "
                                           f"hoisted as variable "


Test patch:
diff --git a/src/psyclone/tests/core/single_variable_access_info_test.py b/src/psyclone/tests/core/single_variable_access_info_test.py
index 31832b07f..1f58e5536 100644
--- a/src/psyclone/tests/core/single_variable_access_info_test.py
+++ b/src/psyclone/tests/core/single_variable_access_info_test.py
@@ -51,15 +51,13 @@ from psyclone.psyir.symbols import DataSymbol, INTEGER_TYPE, Symbol
 def test_access_info():
     '''Test the AccessInfo class.
     '''
-    location = 12
-    access_info = AccessInfo(AccessType.READ, location, Node())
+    access_info = AccessInfo(AccessType.READ, Node())
     assert access_info.access_type == AccessType.READ
-    assert access_info.location == location
     assert access_info.component_indices.indices_lists == [[]]
     assert not access_info.is_array()
-    assert str(access_info) == "READ(12)"
+    assert str(access_info) == "READ"
     access_info.change_read_to_write()
-    assert str(access_info) == "WRITE(12)"
+    assert str(access_info) == "WRITE"
     assert access_info.access_type == AccessType.WRITE
     with pytest.raises(InternalError) as err:
         access_info.change_read_to_write()
@@ -72,21 +70,18 @@ def test_access_info():
     assert access_info.component_indices == component_indices
     assert access_info.is_array()
 
-    access_info = AccessInfo(AccessType.UNKNOWN, location, Node())
+    access_info = AccessInfo(AccessType.UNKNOWN, Node())
     assert access_info.access_type == AccessType.UNKNOWN
-    assert access_info.location == location
     assert access_info.component_indices.indices_lists == [[]]
 
-    access_info = AccessInfo(AccessType.UNKNOWN, location, Node(),
+    access_info = AccessInfo(AccessType.UNKNOWN, Node(),
                              [["i", "j"]])
     assert access_info.access_type == AccessType.UNKNOWN
-    assert access_info.location == location
     assert access_info.component_indices.indices_lists == [["i", "j"]]
 
-    access_info = AccessInfo(AccessType.UNKNOWN, location, Node(),
+    access_info = AccessInfo(AccessType.UNKNOWN, Node(),
                              ComponentIndices([["i", "j"]]))
     assert access_info.access_type == AccessType.UNKNOWN
-    assert access_info.location == location
     assert access_info.component_indices.indices_lists == [["i", "j"]]
 
 
@@ -94,21 +89,17 @@ def test_access_info():
 def test_access_info_exceptions():
     '''Test that the right exceptions are raised.
     '''
-    location = 12
     with pytest.raises(InternalError) as err:
-        _ = AccessInfo(AccessType.READ, location, Node(),
-                       component_indices=123)
+        _ = AccessInfo(AccessType.READ, Node(), component_indices=123)
     assert "Index object in ComponentIndices constructor must be None, " \
            "a list or list of lists, got '123'" in str(err.value)
 
     with pytest.raises(InternalError) as err:
-        _ = AccessInfo(AccessType.READ, location, Node(),
-                       component_indices=[[], 123])
+        _ = AccessInfo(AccessType.READ, Node(), component_indices=[[], 123])
     assert "ComponentIndices: Invalid list parameter '[[], 123]'" \
         in str(err.value)
 
-    location = 1
-    access_info = AccessInfo(AccessType.READ, location, Node())
+    access_info = AccessInfo(AccessType.READ, Node())
     with pytest.raises(InternalError) as err:
         access_info.component_indices = 123
     assert "The component_indices object in the setter of AccessInfo must " \
@@ -119,17 +110,16 @@ def test_access_info_description():
     '''
     Test for the description() method of AccessInfo.
     '''
-    location = 12
-    # When associated location is not a Statement.
-    ainfo = AccessInfo(AccessType.READ, location, Node())
+    # When associated node is not a Statement.
+    ainfo = AccessInfo(AccessType.READ, Node())
     assert "< node[] >" in ainfo.description.lower()
     # When it is a Statement.
-    ainfo = AccessInfo(AccessType.READ, location, Return())
+    ainfo = AccessInfo(AccessType.READ, Return())
     assert "return" in ainfo.description.lower()
     # When it is a Symbol.
     osym = Symbol("something")
     asym = DataSymbol("test", INTEGER_TYPE, initial_value=Reference(osym))
-    ainfo = AccessInfo(AccessType.INQUIRY, location, asym)
+    ainfo = AccessInfo(AccessType.INQUIRY, asym)
     assert ("definition of symbol 'test: datasymbol<scalar" in
             ainfo.description.lower())
 
@@ -142,7 +132,7 @@ def test_variable_access_info():
 
     vam = SingleVariableAccessInfo(Signature("var_name"))
     assert vam.var_name == "var_name"
-    assert str(vam) == "var_name:"
+    assert str(vam) == "var_name:[]"
     assert vam.is_written() is False
     assert vam.is_written_first() is False
     assert vam.is_read() is False
@@ -152,11 +142,9 @@ def test_variable_access_info():
     assert vam.all_write_accesses == []
     assert vam.signature == Signature("var_name")
 
-    vam.add_access_with_location(AccessType.INQUIRY, 3, Node(),
-                                 component_indices=None)
-    vam.add_access_with_location(AccessType.READ, 2, Node(),
-                                 component_indices=None)
-    assert str(vam) == "var_name:INQUIRY(3),READ(2)"
+    vam.add_access(AccessType.INQUIRY, Node(), component_indices=None)
+    vam.add_access(AccessType.READ, Node(), component_indices=None)
+    assert str(vam) == "var_name:[INQUIRY,READ]"
     assert vam.is_read()
     assert vam.is_read_only()
     assert vam.all_read_accesses == [vam[1]]
@@ -185,16 +173,14 @@ def test_variable_access_info():
 
     # Add a READ access - we should not be able to
     # change this read to write as there's already a WRITE access.
-    vam.add_access_with_location(AccessType.READ, 1, Node(),
-                                 component_indices=None)
+    vam.add_access(AccessType.READ, Node(), component_indices=None)
     with pytest.raises(InternalError) as err:
         vam.change_read_to_write()
     assert ("Variable 'var_name' has a 'WRITE' access. change_read_to_write() "
             "expects only inquiry accesses and a single 'READ' access."
             in str(err.value))
     # And make sure the variable is not read_only if a write is added
-    vam.add_access_with_location(AccessType.WRITE, 3, Node(),
-                                 component_indices=None)
+    vam.add_access(AccessType.WRITE, Node(), component_indices=None)
     assert vam.is_read_only() is False
     assert vam.all_read_accesses == [vam[2]]
     assert vam.all_write_accesses == [vam[1], vam[3]]
@@ -212,11 +198,10 @@ def test_variable_access_info_is_array(fortran_reader):
     '''
     vam = SingleVariableAccessInfo(Signature("var_name"))
     # Add non array-like access:
-    vam.add_access_with_location(AccessType.READ, 1, Node,
-                                 component_indices=None)
+    vam.add_access(AccessType.READ, Node(), component_indices=None)
     assert not vam.is_array()
     # Add array access:
-    vam.add_access_with_location(AccessType.READ, 1, Node(), [[Node()]])
+    vam.add_access(AccessType.READ, Node(), [[Node()]])
     assert vam.is_array()
 
     # Get some real nodes:
@@ -233,8 +218,7 @@ def test_variable_access_info_is_array(fortran_reader):
     ref_i = rhs.children[0]
 
     vam = SingleVariableAccessInfo(Signature("b"))
-    vam.add_access_with_location(AccessType.READ, 1, rhs,
-                                 ComponentIndices([ref_i]))
+    vam.add_access(AccessType.READ, rhs, ComponentIndices([ref_i]))
 
     # Check that the access to "b[i]" is considered an array
     # when testing for access using "i"
@@ -256,107 +240,24 @@ def test_variable_access_info_read_write():
     assert vam.has_read_write() is False
     assert vam.is_written_first() is False
 
-    # Add a READ and WRITE access at the same location, and make sure it
-    # is not reported as READWRITE access
+    # Add a READ and WRITE access and make sure it is not reported as
+    # READWRITE access
     node = Node()
-    vam.add_access_with_location(AccessType.READ, 2, node,
-                                 component_indices=None)
+    vam.add_access(AccessType.READ, node, component_indices=None)
     assert vam[0].node == node
-    assert vam[0].location == 2
     # Test a single read access:
     assert vam.is_written_first() is False
-    vam.add_access_with_location(AccessType.WRITE, 2, Node(),
-                                 component_indices=None)
+    vam.add_access(AccessType.WRITE, Node(), component_indices=None)
     assert vam.has_read_write() is False
     # This tests a read-then-write access:
     assert vam.is_written_first() is False
 
-    vam.add_access_with_location(AccessType.READWRITE, 2, Node(),
-                                 component_indices=None)
+    vam.add_access(AccessType.READWRITE, Node(), component_indices=None)
     assert vam.has_read_write()
 
     # Create a new instance, and add only one READWRITE access:
     vam = SingleVariableAccessInfo(Signature("var_name"))
-    vam.add_access_with_location(AccessType.READWRITE, 2, Node(),
-                                 component_indices=None)
+    vam.add_access(AccessType.READWRITE, Node(), component_indices=None)
     assert vam.has_read_write()
     assert vam.is_read()
     assert vam.is_written()
-
-
-# -----------------------------------------------------------------------------
-def test_is_written_before():
-    '''Tests that the 'is_written_before' function works as expected.
-
-    '''
-    var_sig = Signature("a")
-    accesses = SingleVariableAccessInfo(var_sig)
-    node1 = Node()
-    accesses.add_access_with_location(AccessType.READ, 1, node1, None)
-    node2 = Node()
-    accesses.add_access_with_location(AccessType.WRITE, 2, node2, None)
-    node3 = Node()
-    accesses.add_access_with_location(AccessType.WRITE, 3, node3, None)
-
-    assert accesses.is_written_before(node1) is False
-    assert accesses.is_written_before(node2) is False
-    assert accesses.is_written_before(node3) is True
-
-    with pytest.raises(ValueError) as err:
-        accesses.is_written_before(Node())
-    assert ("Reference not found in 'is_written_before' for variable "
-            "'a'" in str(err.value))
-
-
-# -----------------------------------------------------------------------------
-def test_is_read_before():
-    '''Tests that the 'is_read_before' function works as expected.
-
-    '''
-    var_sig = Signature("a")
-    accesses = SingleVariableAccessInfo(var_sig)
-    node1 = Node()
-    accesses.add_access_with_location(AccessType.WRITE, 1, node1, None)
-    node2 = Node()
-    accesses.add_access_with_location(AccessType.READ, 1, node2, None)
-    node3 = Node()
-    accesses.add_access_with_location(AccessType.WRITE, 1, node3, None)
-
-    assert accesses.is_read_before(node1) is False
-    assert accesses.is_read_before(node2) is False
-    assert accesses.is_read_before(node3) is True
-
-    with pytest.raises(ValueError) as err:
-        accesses.is_read_before(Node())
-    assert ("Reference not found in 'is_read_before' for variable 'a'."
-            in str(err.value))
-
-
-# -----------------------------------------------------------------------------
-def test_is_accessed_before():
-    '''Tests that the 'is_accessed_before' function works as expected.
-
-    '''
-    # First check a write access before the specified node:
-    var_sig = Signature("a")
-    accesses = SingleVariableAccessInfo(var_sig)
-    node1 = Node()
-    accesses.add_access_with_location(AccessType.WRITE, 1, node1, None)
-    node2 = Node()
-    accesses.add_access_with_location(AccessType.READ, 2, node2, None)
-
-    assert accesses.is_accessed_before(node1) is False
-    assert accesses.is_accessed_before(node2) is True
-
-    # Now test a read access before the specified node:
-    accesses = SingleVariableAccessInfo(var_sig)
-    accesses.add_access_with_location(AccessType.READ, 1, node1, None)
-    accesses.add_access_with_location(AccessType.READ, 2, node2, None)
-
-    assert accesses.is_accessed_before(node1) is False
-    assert accesses.is_accessed_before(node2) is True
-
-    with pytest.raises(ValueError) as err:
-        accesses.is_accessed_before(Node())
-    assert ("Reference not found in 'is_accessed_before' for variable 'a'."
-            in str(err.value))
diff --git a/src/psyclone/tests/core/variables_access_map_test.py b/src/psyclone/tests/core/variables_access_map_test.py
index 3b9f36b1f..7b13db030 100644
--- a/src/psyclone/tests/core/variables_access_map_test.py
+++ b/src/psyclone/tests/core/variables_access_map_test.py
@@ -57,10 +57,8 @@ def test_variables_access_map():
     var_accesses.add_access(Signature("written"), AccessType.WRITE, node2)
     assert str(var_accesses) == "read: READ, written: WRITE"
 
-    var_accesses.next_location()
     node = Node()
     var_accesses.add_access(Signature("written"), AccessType.WRITE, node)
-    var_accesses.next_location()
     var_accesses.add_access(Signature("read_written"), AccessType.WRITE, node)
     var_accesses.add_access(Signature("read_written"), AccessType.READ, node)
     assert str(var_accesses) == "read: READ, read_written: READ+WRITE, "\
@@ -70,11 +68,6 @@ def test_variables_access_map():
                                                     Signature("read_written")])
     all_accesses = var_accesses[Signature("read")].all_accesses
     assert all_accesses[0].node == node1
-    written_accesses = var_accesses[Signature("written")].all_accesses
-    assert written_accesses[0].location == 0
-    assert written_accesses[1].location == 1
-    # Check that the location pointer is pointing to the next statement:
-    assert var_accesses.location == 2
 
     # Create a new instance
     var_accesses2 = VariablesAccessMap()
@@ -121,7 +114,7 @@ def test_variables_access_map_errors():
 # -----------------------------------------------------------------------------
 def test_component_indices_auto_extension():
     '''To make it more convenient for the user certain combinations of
-    signature and component_indices in the add_location call will
+    signature and component_indices in the add_access call will
     automatically add empty indices to the component_indices. For example.
     adding "ssh_fld%grid%tmask" with indices ["i", "j"] will automatically
     create component_indices like [[], [], ["i", "j"]].
@@ -159,7 +152,6 @@ def test_variables_access_map_update():
     node = Node()
     var_accesses1.add_access(Signature("b"), AccessType.READ, node)
     var_accesses1.add_access(Signature("a"), AccessType.WRITE, node)
-    var_accesses1.next_location()
     var_accesses1.add_access(Signature("d"), AccessType.READ, node)
     var_accesses1.add_access(Signature("c"), AccessType.WRITE, node)
     c_accesses = var_accesses1[Signature("c")]
@@ -171,38 +163,12 @@ def test_variables_access_map_update():
     var_accesses2 = VariablesAccessMap()
     var_accesses2.add_access(Signature("f"), AccessType.READ, node)
     var_accesses2.add_access(Signature("e"), AccessType.WRITE, node)
-    var_accesses2.next_location()
     var_accesses2.add_access(Signature("h"), AccessType.READ, node)
     var_accesses2.add_access(Signature("g"), AccessType.WRITE, node)
 
     # Now merge the second instance into the first one
     var_accesses1.update(var_accesses2)
 
-    # The e=f access pattern should have the same location
-    # as the c=d (since there is no next_location after
-    # adding the b=a access):
-    c_accesses = var_accesses1[Signature("c")]
-    e_accesses = var_accesses1[Signature("e")]
-    assert c_accesses[0].access_type == AccessType.WRITE
-    assert e_accesses[0].access_type == AccessType.WRITE
-    assert c_accesses[0].location == e_accesses[0].location
-
-    # Test that the g=h part has a higher location than the
-    # c=d data. This makes sure that update() increases the
-    # location number of accesses when merging.
-    c_accesses = var_accesses1[Signature("c")]
-    g_accesses = var_accesses1[Signature("g")]
-    h_accesses = var_accesses1[Signature("h")]
-    assert c_accesses[0].location < g_accesses[0].location
-    assert g_accesses[0].location == h_accesses[0].location
-
-    # Also make sure that the access location was properly increased
-    # Originally we had locations 0,1. Then we merged accesses with
-    # location 0,1 in - the one at 0 is merged with the current 1,
-    # and the new location 1 increases the current location from
-    # 1 to 2:
-    assert var_accesses1.location == 2
-
 
 # -----------------------------------------------------------------------------
 def test_constructor(fortran_reader):
diff --git a/src/psyclone/tests/dependency_test.py b/src/psyclone/tests/dependency_test.py
index b64876e29..0f4e1d5d8 100644
--- a/src/psyclone/tests/dependency_test.py
+++ b/src/psyclone/tests/dependency_test.py
@@ -156,7 +156,6 @@ def test_if_statement(fortran_reader):
     assert len(q_accesses) == 2
     assert q_accesses[0].access_type == AccessType.READ
     assert q_accesses[1].access_type == AccessType.WRITE
-    assert q_accesses[0].location < q_accesses[1].location
 
 
 def test_do_loop(fortran_reader):
@@ -309,72 +308,6 @@ def test_lfric_kern_cma_args():
                 == AccessType.READ)
 
 
-def test_location(fortran_reader):
-    '''Test if the location assignment is working, esp. if each new statement
-    gets a new location, but accesses in the same statement have the same
-    location.
-    '''
-
-    psyir = fortran_reader.psyir_from_source(
-        '''program test_prog
-             integer :: a, b, i, ji, jj, n, x
-             real :: p(5), q(5), r(5), s(5,5), t(5,5)
-             a = b
-             if (a .eq. b) then
-                p(i) = q(i)
-             else
-               q(i) = r(i)
-             endif
-             a = b
-             do jj=1, n
-                do ji=1, 10
-                   s(ji, jj)=t(ji, jj)+1
-                enddo
-             enddo
-             a = b
-             x = x + 1
-          end program test_prog''')
-    schedule = psyir.children[0]
-
-    var_accesses = schedule.reference_accesses()
-    # Test accesses for a:
-    a_accesses = var_accesses[Signature("a")].all_accesses
-    assert a_accesses[0].location == 0
-    assert a_accesses[1].location == 1
-    assert a_accesses[2].location == 6
-    assert a_accesses[3].location == 12
-
-    # b should have the same locations as a:
-    b_accesses = var_accesses[Signature("b")].all_accesses
-    assert len(a_accesses) == len(b_accesses)
-    for (index, access) in enumerate(a_accesses):
-        assert b_accesses[index].location == access.location
-
-    q_accesses = var_accesses[Signature("q")].all_accesses
-    assert q_accesses[0].location == 2
-    assert q_accesses[1].location == 4
-
-    # Test jj for the loop statement. Note that 'jj' has one read and
-    # one write access for the DO statement
-    jj_accesses = var_accesses[Signature("jj")].all_accesses
-    assert jj_accesses[0].location == 7
-    assert jj_accesses[1].location == 7
-    assert jj_accesses[2].location == 9
-    assert jj_accesses[3].location == 9
-
-    ji_accesses = var_accesses[Signature("ji")].all_accesses
-    assert ji_accesses[0].location == 8
-    assert ji_accesses[1].location == 8
-    assert ji_accesses[2].location == 9
-    assert ji_accesses[3].location == 9
-
-    # Verify that x=x+1 shows the READ access before the write access
-    x_accesses = var_accesses[Signature("x")].all_accesses    # x=x+1
-    assert x_accesses[0].access_type == AccessType.READ
-    assert x_accesses[1].access_type == AccessType.WRITE
-    assert x_accesses[0].location == x_accesses[1].location
-
-
 def test_user_defined_variables(fortran_reader):
     ''' Test reading and writing to user defined variables.
     '''
diff --git a/src/psyclone/tests/domain/lfric/kern_call_arg_list_test.py b/src/psyclone/tests/domain/lfric/kern_call_arg_list_test.py
index 5191f9e3a..bc23cc415 100644
--- a/src/psyclone/tests/domain/lfric/kern_call_arg_list_test.py
+++ b/src/psyclone/tests/domain/lfric/kern_call_arg_list_test.py
@@ -235,10 +235,10 @@ def test_kerncallarglist_mesh_properties(fortran_writer):
                              "map_w1: READ, ndf_w1: READ, nfaces_re_h: "
                              "READ, nlayers_f1: READ, undf_w1: READ")
     # Tests that multiple reads are reported as expected:
-    assert str(var_info[Signature("cell")]) == "cell:READ(0),READ(0)"
-    assert str(var_info[Signature("colour")]) == "colour:READ(0),READ(0)"
-    assert str(var_info[Signature("cmap")]) == "cmap:READ(0),READ(0)"
-    assert str(var_info[Signature("adjacent_face")]) == "adjacent_face:READ(0)"
+    assert str(var_info[Signature("cell")]) == "cell:[READ,READ]"
+    assert str(var_info[Signature("colour")]) == "colour:[READ,READ]"
+    assert str(var_info[Signature("cmap")]) == "cmap:[READ,READ]"
+    assert str(var_info[Signature("adjacent_face")]) == "adjacent_face:[READ]"
 
     assert create_arg_list._arglist == [
         'nlayers_f1', 'a', 'f1_data', 'ndf_w1', 'undf_w1',
@@ -403,9 +403,9 @@ def test_kerncallarglist_bcs_operator(fortran_writer):
     # Also check that the structure access is correctly converted
     # into a 2-component signature:
     sig = Signature(("op_a_proxy", "ncell_3d"))
-    assert str(access_info[sig]) == "op_a_proxy%ncell_3d:READ(0)"
+    assert str(access_info[sig]) == "op_a_proxy%ncell_3d:[READ]"
     assert (str(access_info[Signature("op_a_local_stencil")]) ==
-            "op_a_local_stencil:READWRITE(0)")
+            "op_a_local_stencil:[READWRITE]")
 
 
 def test_kerncallarglist_mixed_precision():
diff --git a/src/psyclone/tests/psyir/nodes/call_test.py b/src/psyclone/tests/psyir/nodes/call_test.py
index 7e7c1942e..c53fa7082 100644
--- a/src/psyclone/tests/psyir/nodes/call_test.py
+++ b/src/psyclone/tests/psyir/nodes/call_test.py
@@ -374,8 +374,6 @@ def test_call_reference_accesses():
     # A call with an argument passed by value.
     call1 = Call.create(rsym, [Literal("1", INTEGER_TYPE)])
     var_info = call1.reference_accesses()
-    # Check that the current location number is increased after the call:
-    assert var_info._location == 1
     # The Routine symbol is not considered 'read'.
     assert not var_info.is_read(Signature("trillian"))
     assert var_info.is_called(Signature("trillian"))
diff --git a/src/psyclone/tests/psyir/tools/call_tree_utils_test.py b/src/psyclone/tests/psyir/tools/call_tree_utils_test.py
index 7ab90dbdc..b08d25de6 100644
--- a/src/psyclone/tests/psyir/tools/call_tree_utils_test.py
+++ b/src/psyclone/tests/psyir/tools/call_tree_utils_test.py
@@ -519,13 +519,13 @@ def test_module_info_generic_interfaces():
     # be reported once (even though it is used in both functions), and
     # each variable specific to the two functions:
     expected = set([("reference", "g_mod", Signature("module_var_1"),
-                     'module_var_1:READ(0)'),
+                     'module_var_1:[READ]'),
                     ("reference", "g_mod", Signature("module_var_2"),
-                     'module_var_2:READ(0)'),
+                     'module_var_2:[READ]'),
                     ("reference", "g_mod", Signature("module_var"),
-                     'module_var:READ(0)'),
+                     'module_var:[READ]'),
                     ("reference", "g_mod", Signature("module_var"),
-                     'module_var:WRITE(0)')])
+                     'module_var:[WRITE]')])
     # Convert the access info to a string for easy comparison:
     assert (set((i[0], i[1], i[2], str(i[3])) for i in all_non_locals) ==
             expected)