Repository: stfc/PSyclone

Issue:
PSyIR loses initial values in structures
When code such as the following is  translated to PSyIR

```
type mytype
  integer :: a = 10
end mytype
```
 the initial value gets lost. This is because the PSyIR `StructureType` that represents `mytype` has an `add` method that takes and stores the name, the datatype and the visibility. It actually uses the symbol reader to read the information so it is all there we just don't store it. Assuming the current way is the correct way of doing things I can add an initial value to the `add` method. However, before doing this it seems an obvious question as to why we are not storing the symbol itself here. @sergisiso and @arporter, do you have any thoughts on this?

Discussion:

Patch:
diff --git a/changelog b/changelog
index 28513762f..a4b195464 100644
--- a/changelog
+++ b/changelog
@@ -1,9 +1,12 @@
-
-	1) PR #2199 for #2189. fix bugs with missing maps in enter data
+	1) PR #2199 for #2189. Fix bugs with missing maps in enter data
 	directive.
 
 	2) PR #2269 for #2268. Rename DynInvokes to LFRicInvokes and move
 	to own module.
+	
+	3) PR #2255 for #2237. The derived type intial values are not lost
+	anymore, and the "extends" or "abstract" keywords cause a
+	UnknownFortranType.
 
 release 2.4.0 29th of September 2023
 
diff --git a/examples/psyir/create_structure_types.py b/examples/psyir/create_structure_types.py
index afe95dbf6..7d0a14bdc 100644
--- a/examples/psyir/create_structure_types.py
+++ b/examples/psyir/create_structure_types.py
@@ -64,8 +64,9 @@ SCALAR_TYPE = ScalarType(ScalarType.Intrinsic.REAL, REAL_KIND)
 
 # Derived-type definition in container
 GRID_TYPE = StructureType.create([
-    ("dx", SCALAR_TYPE, Symbol.Visibility.PUBLIC),
-    ("dy", SCALAR_TYPE, Symbol.Visibility.PUBLIC)])
+    ("dx", SCALAR_TYPE, Symbol.Visibility.PUBLIC, None),
+    ("dy", SCALAR_TYPE, Symbol.Visibility.PUBLIC,
+     Literal("1.0", SCALAR_TYPE))])
 GRID_TYPE_SYMBOL = DataTypeSymbol("grid_type", GRID_TYPE)
 CONTAINER_SYMBOL_TABLE.add(GRID_TYPE_SYMBOL)
 
@@ -81,11 +82,11 @@ SYMBOL_TABLE.add(DTYPE_SYMBOL)
 
 # Create the definition of the 'field_type'
 FIELD_TYPE_DEF = StructureType.create(
-    [("data", ArrayType(SCALAR_TYPE, [10]), Symbol.Visibility.PUBLIC),
-     ("grid", GRID_TYPE_SYMBOL, Symbol.Visibility.PUBLIC),
+    [("data", ArrayType(SCALAR_TYPE, [10]), Symbol.Visibility.PUBLIC, None),
+     ("grid", GRID_TYPE_SYMBOL, Symbol.Visibility.PUBLIC, None),
      ("sub_meshes", ArrayType(GRID_TYPE_SYMBOL, [3]),
-      Symbol.Visibility.PUBLIC),
-     ("flag", INTEGER4_TYPE, Symbol.Visibility.PUBLIC)])
+      Symbol.Visibility.PUBLIC, None),
+     ("flag", INTEGER4_TYPE, Symbol.Visibility.PUBLIC, None)])
 FIELD_TYPE_SYMBOL = DataTypeSymbol("field_type", FIELD_TYPE_DEF)
 CONTAINER_SYMBOL_TABLE.add(FIELD_TYPE_SYMBOL)
 
diff --git a/external/FortCL b/external/FortCL
index d516ed01e..401148e4b 160000
--- a/external/FortCL
+++ b/external/FortCL
@@ -1,1 +1,1 @@
-Subproject commit d516ed01ea23565bfc4f531a795d2c7a2a57fe50
+Subproject commit 401148e4b6d6efdd4d0157123b118ed07d831446
diff --git a/src/psyclone/domain/common/transformations/kernel_module_inline_trans.py b/src/psyclone/domain/common/transformations/kernel_module_inline_trans.py
index 3b164ff40..5d401320f 100644
--- a/src/psyclone/domain/common/transformations/kernel_module_inline_trans.py
+++ b/src/psyclone/domain/common/transformations/kernel_module_inline_trans.py
@@ -72,6 +72,7 @@ class KernelModuleInlineTrans(Transformation):
     def __str__(self):
         return "Inline a kernel subroutine into the PSy module"
 
+    # pylint: disable=too-many-branches
     def validate(self, node, options=None):
         '''
         Checks that the supplied node is a Kernel and that it is possible to
@@ -174,9 +175,16 @@ class KernelModuleInlineTrans(Transformation):
 
     @staticmethod
     def _prepare_code_to_inline(code_to_inline):
-        ''' Prepare the PSyIR tree to inline by bringing in to the subroutine
+        '''Prepare the PSyIR tree to inline by bringing in to the subroutine
         all referenced symbols so that the implementation is self contained.
 
+        TODO #2271 will improve this method and could potentially
+        avoid the need for debug_string() within get_kernel_schedule()
+        in dynamo0.3.py. Sergi suggests that we may be missing the
+        traversal of the declaration init expressions here and that
+        might solve the problem. I'm not so sure and explain why in
+        get_kernel_schedule() but still referencing this issue.
+
         :param code_to_inline: the subroutine to module-inline.
         :type code_to_inline: :py:class:`psyclone.psyir.node.Routine`
 
diff --git a/src/psyclone/dynamo0p3.py b/src/psyclone/dynamo0p3.py
index bf3b72a8f..0d2556058 100644
--- a/src/psyclone/dynamo0p3.py
+++ b/src/psyclone/dynamo0p3.py
@@ -8052,6 +8052,25 @@ class DynKern(CodedKern):
 
         # Get the PSyIR Kernel Schedule(s)
         routines = Fparser2Reader().get_routine_schedules(self.name, self.ast)
+        for routine in routines:
+            # If one of the symbols is not declared in a routine then
+            # this is only picked up when writing out the routine
+            # (raising a VisitorError), so we check here so that
+            # invalid code is not inlined. We use debug_string() to
+            # minimise the overhead.
+
+            # TODO #2271 could potentially avoid the need for
+            # debug_string() within. Sergi suggests that we may be
+            # missing the traversal of the declaration init
+            # expressions and that might solve the problem. I'm not so
+            # sure as we are talking about unknown symbols that will
+            # only be resolved in the back-end (or not). If I am right
+            # then one option would be to use the FortranWriter, but
+            # that would be bigger overhead, or perhaps just the
+            # declarations part of FortranWriter if that is possible.
+            # Also see TODO issue #2336 which captures the specific
+            # problem in LFRic that this fixes.
+            routine.debug_string()
 
         if len(routines) == 1:
             sched = routines[0]
@@ -9277,12 +9296,7 @@ class DynKernelArgument(KernelArgument):
             if self.precision:
                 # Ensure any associated precision symbol is in the table.
                 symbol_table.add_lfric_precision_symbol(self.precision)
-            try:
-                lit = reader.psyir_from_expression(self.name, symbol_table)
-            except SymbolError as err:
-                raise InternalError(
-                    f"Unexpected literal expression '{self.name}' when "
-                    f"processing kernel '{self.call.name}'.") from err
+            lit = reader.psyir_from_expression(self.name, symbol_table)
 
             # Sanity check that the resulting expression is a literal.
             if lit.walk(Reference):
diff --git a/src/psyclone/psyir/backend/fortran.py b/src/psyclone/psyir/backend/fortran.py
index 3165e4abe..50cfea828 100644
--- a/src/psyclone/psyir/backend/fortran.py
+++ b/src/psyclone/psyir/backend/fortran.py
@@ -51,7 +51,9 @@ from psyclone.psyir.nodes import (
 from psyclone.psyir.symbols import (
     ArgumentInterface, ArrayType, ContainerSymbol, DataSymbol, DataTypeSymbol,
     DeferredType, RoutineSymbol, ScalarType, Symbol, IntrinsicSymbol,
-    SymbolTable, UnknownFortranType, UnknownType, UnresolvedInterface)
+    SymbolTable, UnknownFortranType, UnknownType, UnresolvedInterface,
+    StructureType)
+
 
 # Mapping from PSyIR types to Fortran data types. Simply reverse the
 # map from the frontend, removing the special case of "double
@@ -595,7 +597,10 @@ class FortranWriter(LanguageWriter):
         result += f" :: {symbol.name}"
 
         # Specify initialisation expression
-        if isinstance(symbol, DataSymbol) and symbol.initial_value:
+        if (isinstance(symbol, StructureType.ComponentType) and
+                symbol.initial_value):
+            result += " = " + self._visit(symbol.initial_value)
+        elif isinstance(symbol, DataSymbol) and symbol.initial_value:
             if not symbol.is_static:
                 raise VisitorError(
                     f"{type(symbol).__name__} '{symbol.name}' has an initial "
diff --git a/src/psyclone/psyir/frontend/fortran.py b/src/psyclone/psyir/frontend/fortran.py
index 375466355..2fd90bbaa 100644
--- a/src/psyclone/psyir/frontend/fortran.py
+++ b/src/psyclone/psyir/frontend/fortran.py
@@ -31,7 +31,7 @@
 # POSSIBILITY OF SUCH DAMAGE.
 # -----------------------------------------------------------------------------
 # Author: S. Siso, STFC Daresbury Lab
-# Modifications: A. R. Porter and N. Nobre, STFC Daresbury Lab
+# Modifications: A. R. Porter, N. Nobre and R. W. Ford, STFC Daresbury Lab
 # -----------------------------------------------------------------------------
 
 ''' This module provides the PSyIR Fortran front-end.'''
@@ -43,8 +43,8 @@ from fparser.two.parser import ParserFactory
 from fparser.two.symbol_table import SYMBOL_TABLES
 from fparser.two.utils import NoMatchError
 from psyclone.psyir.frontend.fparser2 import Fparser2Reader
-from psyclone.psyir.nodes import Schedule, Assignment
-from psyclone.psyir.symbols import SymbolError, SymbolTable
+from psyclone.psyir.nodes import Schedule, Assignment, Routine
+from psyclone.psyir.symbols import SymbolTable
 
 
 class FortranReader():
@@ -100,23 +100,22 @@ class FortranReader():
         return psyir
 
     def psyir_from_expression(self, source_code, symbol_table):
-        '''
-        Generate the PSyIR tree for the supplied Fortran expression.
-        Any symbols referenced in the expression must be present in the
-        supplied table (or one of its ancestors).
+        '''Generate the PSyIR tree for the supplied Fortran statement. The
+        symbol table is expected to provide all symbols found in the
+        expression.
 
         :param str source_code: text of the expression to be parsed.
-        :param symbol_table: the SymbolTable in which to search for any \
-                             symbols that are encountered.
+        :param symbol_table: the SymbolTable in which to search for any
+            symbols that are encountered.
+        :type symbol_table: :py.class:`psyclone.psyir.symbols.SymbolTable`
+
         :returns: PSyIR representing the provided Fortran expression.
         :rtype: :py:class:`psyclone.psyir.nodes.Node`
 
         :raises TypeError: if no valid SymbolTable is supplied.
-        :raises ValueError: if the supplied source does not represent a \
+        :raises ValueError: if the supplied source does not represent a
             Fortran expression.
-        :raises SymbolError: if the expression references a symbol which \
-            cannot be found in the symbol table (and there is no way for it \
-            to be brought into scope).
+
         '''
         if not isinstance(symbol_table, SymbolTable):
             raise TypeError(f"Must be supplied with a valid SymbolTable but "
@@ -136,38 +135,29 @@ class FortranReader():
         # new Schedule and therefore stealing it from its original scope.
         # pylint: disable=protected-access
         fake_parent = Schedule()
+        # pylint: disable=protected-access
         fake_parent._symbol_table = symbol_table
         fake_parent.addchild(Assignment())
-
-        try:
-            # Process the expression, giving the Assignment we've just
-            # created as the parent.
-            self._processor.process_nodes(fake_parent[0], [parse_tree])
-        except SymbolError as err:
-            raise SymbolError(f"Expression '{source_code}' contains symbols "
-                              f"which are not present in any symbol table and "
-                              f"there are no wildcard imports which might be "
-                              f"bringing them into scope.") from err
+        self._processor.process_nodes(fake_parent[0], [parse_tree])
         return fake_parent[0].children[0].detach()
 
     def psyir_from_statement(self, source_code, symbol_table):
-        '''
-        Generate the PSyIR tree for the supplied Fortran statement.
-        Any symbols referenced in the statement must be present in the supplied
-        table (or one of its ancestors).
+        '''Generate the PSyIR tree for the supplied Fortran statement. The
+        symbolt table is expected to provide all symbols found in the
+        statement.
 
         :param str source_code: text of the statement to be parsed.
-        :param symbol_table: the SymbolTable in which to search for any \
-                             symbols that are encountered.
+        :param symbol_table: the SymbolTable in which to search for any
+            symbols that are encountered.
+        :type symbol_table: :py.class:`psyclone.psyir.symbols.SymbolTable`
+
         :returns: PSyIR representing the provided Fortran statement.
         :rtype: :py:class:`psyclone.psyir.nodes.Node`
 
         :raises TypeError: if no valid SymbolTable is supplied.
-        :raises ValueError: if the supplied source does not represent a \
+        :raises ValueError: if the supplied source does not represent a
             Fortran statement.
-        :raises SymbolError: if the expression references a symbol which \
-            cannot be found in the symbol table (and there is no way for it \
-            to be brought into scope).
+
         '''
         if not isinstance(symbol_table, SymbolTable):
             raise TypeError(f"Must be supplied with a valid SymbolTable but "
@@ -184,17 +174,19 @@ class FortranReader():
         # Create a fake sub-tree connected to the supplied symbol table so
         # that we can process the statement and lookup any symbols that it
         # references.
-        fake_parent = Schedule(symbol_table=symbol_table.deep_copy())
-
         try:
-            # Process the statement, giving the Schedule we've just
-            # created as the parent.
-            self._processor.process_nodes(fake_parent, exec_part.children)
-        except SymbolError as err:
-            raise SymbolError(
-                f"Statement '{source_code}' contains symbols which are not "
-                f"present in any symbol table and there are no wildcard"
-                f"imports which might be bringing them into scope.") from err
+            routine_symbol = symbol_table.lookup_with_tag("own_routine_symbol")
+            routine_name = routine_symbol.name
+        except KeyError:
+            routine_name = "dummy"
+        fake_parent = Routine.create(
+            routine_name, SymbolTable(), [])
+        # pylint: disable=protected-access
+        fake_parent._symbol_table = symbol_table
+
+        # Process the statement, giving the Routine we've just
+        # created as the parent.
+        self._processor.process_nodes(fake_parent, exec_part.children)
         return fake_parent[0].detach()
 
     def psyir_from_file(self, file_path, free_form=True):
diff --git a/src/psyclone/psyir/frontend/fparser2.py b/src/psyclone/psyir/frontend/fparser2.py
index f4e340e26..e5ce54207 100644
--- a/src/psyclone/psyir/frontend/fparser2.py
+++ b/src/psyclone/psyir/frontend/fparser2.py
@@ -224,23 +224,22 @@ def _first_type_match(nodelist, typekind):
 
 def _find_or_create_unresolved_symbol(location, name, scope_limit=None,
                                       **kargs):
-    '''Returns the symbol with the name 'name' from a symbol table
-    associated with this node or one of its ancestors.  If a symbol is found
-    and the `symbol_type` keyword argument is supplied then the type of the
-    existing symbol is compared with the specified type. If it is not already
-    an instance of this type, then the symbol is specialised (in place).
-
-    If the symbol is not found and there are no ContainerSymbols with
-    wildcard imports and no interfaces with unknown content then an
-    exception is raised. However, if there are one or more
-    ContainerSymbols with wildcard imports (which could therefore be
-    bringing the symbol into scope) or one or more interfaces with
-    unknown content then a new Symbol with the specified visibility
-    but of unknown interface is created and inserted in the most local
-    SymbolTable that has such an import. The scope_limit variable
-    further limits the symbol table search so that the search through
-    ancestor nodes stops when the scope_limit node is reached
-    i.e. ancestors of the scope_limit node are not searched.
+    '''Returns the symbol with the given 'name' from a symbol table
+    associated with the 'location' node or one of its ancestors. If a
+    symbol is found then the type of the existing symbol is compared
+    with the specified 'symbol_type' parameter (passed as part of
+    '**kargs'). If it is not already an instance of this type, then
+    the symbol is specialised (in place).
+
+    If the symbol is not found then a new Symbol with the specified
+    visibility but of unresolved interface is created and inserted in the
+    most local SymbolTable that has a Routine or Container node as
+    parent.
+
+    The scope_limit variable further limits the symbol table search so
+    that the search through ancestor nodes stops when the scope_limit
+    node is reached i.e. ancestors of the scope_limit node are not
+    searched.
 
     :param location: PSyIR node from which to operate.
     :type location: :py:class:`psyclone.psyir.nodes.Node`
@@ -260,9 +259,6 @@ def _find_or_create_unresolved_symbol(location, name, scope_limit=None,
     :raises TypeError: if the supplied scope_limit is not a Node.
     :raises ValueError: if the supplied scope_limit node is not an
         ancestor of the supplied node.
-    :raises SymbolError: if no matching symbol is found and there are
-        no ContainerSymbols from which it might be brought into scope
-        or unknown interfaces which might contain its declaration.
 
     '''
     if not isinstance(location, Node):
@@ -294,71 +290,37 @@ def _find_or_create_unresolved_symbol(location, name, scope_limit=None,
                 f"_find_or_create_unresolved_symbol() is not an ancestor of "
                 f"this node '{location}'.")
 
-    # Keep a reference to the most local SymbolTable with a wildcard
-    # import in case we need to create a Symbol.
-    first_symbol_table = None
-    test_node = location
-
-    # Iterate over ancestor Nodes of this Node.
-    while test_node:
-        # For simplicity, test every Node for the existence of a
-        # SymbolTable (rather than checking for the particular
-        # Node types which we know to have SymbolTables).
-        if hasattr(test_node, 'symbol_table'):
-            # This Node does have a SymbolTable.
-            symbol_table = test_node.symbol_table
-            try:
-                # If the name matches a Symbol in this SymbolTable then
-                # return the Symbol (after specialising it, if necessary).
-                sym = symbol_table.lookup(name, scope_limit=test_node)
-                if "symbol_type" in kargs:
-                    expected_type = kargs.pop("symbol_type")
-                    if not isinstance(sym, expected_type):
-                        # The caller specified a sub-class so we need to
-                        # specialise the existing symbol.
-                        sym.specialise(expected_type, **kargs)
-                return sym
-            except KeyError:
-                # The supplied name does not match any Symbols in
-                # this SymbolTable. Does this SymbolTable have any
-                # wildcard imports?
-                if first_symbol_table is None:
-                    for csym in symbol_table.containersymbols:
-                        if csym.wildcard_import:
-                            first_symbol_table = symbol_table
-                            break
-
-        if test_node is scope_limit:
-            # The ancestor scope/top-level Node has been reached and
-            # nothing has matched.
-            break
-
-        # Move on to the next ancestor.
-        test_node = test_node.parent
-
-    if first_symbol_table:
-        # No symbol found but there are one or more Containers from which
-        # it may be being brought into scope. Therefore create a generic
-        # Symbol with a UnresolvedInterface and add it to the most
-        # local SymbolTable with a wildcard import.
-        return first_symbol_table.new_symbol(
-                name, interface=UnresolvedInterface(), **kargs)
-
-    # Are there any interfaces that might be hiding the symbol declaration?
-    symbol_table = location.scope.symbol_table
     try:
-        _ = symbol_table.lookup(
-            "_psyclone_internal_interface", scope_limit=scope_limit)
-        # There is an unknown interface so add this symbol.
-        return location.scope.symbol_table.new_symbol(
-            name, interface=UnresolvedInterface(), **kargs)
+        sym = location.scope.symbol_table.lookup(name, scope_limit=scope_limit)
+        if "symbol_type" in kargs:
+            expected_type = kargs.pop("symbol_type")
+            if not isinstance(sym, expected_type):
+                # The caller specified a sub-class so we need to
+                # specialise the existing symbol.
+                sym.specialise(expected_type, **kargs)
+        return sym
     except KeyError:
         pass
 
+    # find the closest ancestor symbol table attached to a Routine or
+    # Container node. We don't want to add to a Schedule node as in
+    # some situations PSyclone assumes symbols are declared within
+    # Routine or Container symbol tables due to its Fortran provenance
+    # (but should probably not!). We also have cases when the whole
+    # tree has not been built so the symbol table is not connected to
+    # a node.
+    symbol_table = location.scope.symbol_table
+    while symbol_table.node and not isinstance(
+            symbol_table.node, (Routine, Container)):
+        symbol_table = symbol_table.parent_symbol_table()
+
     # All requested Nodes have been checked but there has been no
-    # match and there are no wildcard imports or unknown interfaces so
-    # raise an exception.
-    raise SymbolError(f"No Symbol found for name '{name}'.")
+    # match. Add it to the symbol table as an unresolved symbol in any
+    # case as, for example, it might be declared later, or the
+    # declaration may be hidden (perhaps in a codeblock), or it may be
+    # imported with a wildcard import.
+    return symbol_table.new_symbol(
+        name, interface=UnresolvedInterface(), **kargs)
 
 
 def _find_or_create_psyclone_internal_cmp(node):
@@ -1244,13 +1206,8 @@ class Fparser2Reader():
         # Parse the declarations if it has any
         for child in module.children:
             if isinstance(child, Fortran2003.Specification_Part):
-                try:
-                    self.process_declarations(new_container, child.children,
-                                              [], visibility_map)
-                except SymbolError as err:
-                    raise SymbolError(
-                        f"Error when generating Container for module "
-                        f"'{mod_name}': {err.args[0]}") from err
+                self.process_declarations(new_container, child.children,
+                                          [], visibility_map)
                 break
 
         return new_container
@@ -1896,6 +1853,14 @@ class Fparser2Reader():
                 raise GenerationError(
                     f"SAVE and PARAMETER attributes are not compatible but "
                     f"found:\n {decl}")
+
+            # Now we've checked for save and parameter existing
+            # together, we can allow parameter without save and set it
+            # to the same interface as save.
+            if has_constant_value and interface is None:
+                # We have a parameter so should set its interface to static.
+                interface = StaticInterface()
+
             if allocatable and has_constant_value:
                 raise GenerationError(
                     f"ALLOCATABLE and PARAMETER attributes are not compatible "
@@ -1990,19 +1955,32 @@ class Fparser2Reader():
             tag = None
             try:
                 sym = symbol_table.lookup(sym_name, scope_limit=scope)
-                if sym is symbol_table.lookup_with_tag("own_routine_symbol"):
-                    # In case it is its own function routine symbol, Fortran
-                    # will declare it inside the function as a DataSymbol.
-                    # Remove the RoutineSymbol in order to free the exact name
-                    # for the DataSymbol.
-                    symbol_table.remove(sym)
-                    # And trigger the exception path but keeping the same tag
-                    tag = "own_routine_symbol"
-                    raise KeyError
-                if not sym.is_unresolved:
-                    raise SymbolError(
-                        f"Symbol '{sym_name}' already present in SymbolTable "
-                        f"with a defined interface ({sym.interface}).")
+                # pylint: disable=unidiomatic-typecheck
+                if type(sym) == Symbol:
+                    # This was a generic symbol. We now know what it is
+                    sym.specialise(DataSymbol, datatype=datatype,
+                                   visibility=visibility,
+                                   interface=interface,
+                                   is_constant=has_constant_value,
+                                   initial_value=init_expr)
+                else:
+                    if sym is symbol_table.lookup_with_tag(
+                            "own_routine_symbol"):
+                        # In case it is its own function routine
+                        # symbol, Fortran will declare it inside the
+                        # function as a DataSymbol.  Remove the
+                        # RoutineSymbol in order to free the exact
+                        # name for the DataSymbol.
+                        symbol_table.remove(sym)
+                        # And trigger the exception path but keeping
+                        # the same tag
+                        tag = "own_routine_symbol"
+                        raise KeyError
+                    if not sym.is_unresolved:
+                        raise SymbolError(
+                            f"Symbol '{sym_name}' already present in "
+                            f"SymbolTable with a defined interface "
+                            f"({sym.interface}).")
             except KeyError:
                 try:
                     sym = DataSymbol(sym_name, datatype,
@@ -2109,6 +2087,15 @@ class Fparser2Reader():
         # Populate this StructureType by processing the components of
         # the derived type
         try:
+            # We don't support derived-types with additional
+            # attributes e.g. "extends" or "abstract". Note, we do
+            # support public/private attributes but these are stored
+            # as Access_Spec, not Type_Attr_Spec.
+            derived_type_stmt = decl.children[0]
+            if walk(derived_type_stmt, Fortran2003.Type_Attr_Spec):
+                raise NotImplementedError(
+                    "Derived-type definition contains unsupported attributes.")
+
             # We don't yet support derived-type definitions with a CONTAINS
             # section.
             contains = walk(decl, Fortran2003.Contains_Stmt)
@@ -2123,7 +2110,8 @@ class Fparser2Reader():
                 self._process_decln(parent, local_table, child)
             # Convert from Symbols to type information
             for symbol in local_table.symbols:
-                dtype.add(symbol.name, symbol.datatype, symbol.visibility)
+                dtype.add(symbol.name, symbol.datatype, symbol.visibility,
+                          symbol.initial_value)
 
             # Update its type with the definition we've found
             tsymbol.datatype = dtype
@@ -2475,19 +2463,10 @@ class Fparser2Reader():
             # into scope by an unqualified use statement.
             for name, vis in visibility_map.items():
                 if name not in parent.symbol_table:
-                    try:
-                        # If a suitable unqualified use statement is found then
-                        # this call creates a Symbol and inserts it in the
-                        # appropriate symbol table.
-                        _find_or_create_unresolved_symbol(parent, name,
-                                                          visibility=vis)
-                    except SymbolError as err:
-                        # Improve the error message with context-specific info
-                        raise SymbolError(
-                            f"'{name}' is listed in an accessibility "
-                            f"statement as being '{vis}' but failed to find a "
-                            f"declaration or possible import (use) of this "
-                            f"symbol.") from err
+                    # This call creates a Symbol and inserts it in the
+                    # appropriate symbol table.
+                    _find_or_create_unresolved_symbol(parent, name,
+                                                      visibility=vis)
         try:
             arg_symbols = []
             # Ensure each associated symbol has the correct interface info.
@@ -3005,22 +2984,15 @@ class Fparser2Reader():
             self.process_nodes(parent=loop_body, nodes=node.content[1:-1])
             return loop
 
-        # Second element of items member of Loop Control is itself a tuple
-        # containing:
-        #   Loop variable, [start value expression, end value expression, step
-        #   expression]
-        # Loop variable will be an instance of Fortran2003.Name
+        # Second element of items member of Loop Control is itself a
+        # tuple containing: Loop variable, [start value expression,
+        # end value expression, step expression] Loop variable will be
+        # an instance of Fortran2003.Name and will be an integer.
         loop_var = str(ctrl[0].items[1][0])
         variable_name = str(loop_var)
-        try:
-            data_symbol = _find_or_create_unresolved_symbol(
-                parent, variable_name, symbol_type=DataSymbol,
-                datatype=DeferredType())
-        except SymbolError as err:
-            raise InternalError(
-                f"Loop-variable name '{variable_name}' is not declared and "
-                f"there are no unqualified use statements. This is currently "
-                f"unsupported.") from err
+        data_symbol = _find_or_create_unresolved_symbol(
+            parent, variable_name, symbol_type=DataSymbol,
+            datatype=default_integer_type())
         # The loop node is created with the _create_loop factory method as some
         # APIs require a specialised loop node type.
         loop = self._create_loop(parent, data_symbol)
diff --git a/src/psyclone/psyir/nodes/loop.py b/src/psyclone/psyir/nodes/loop.py
index 6c3db9b18..26e8ab3ef 100644
--- a/src/psyclone/psyir/nodes/loop.py
+++ b/src/psyclone/psyir/nodes/loop.py
@@ -130,18 +130,23 @@ class Loop(Statement):
             scalar integer.
 
         '''
+        try:
+            variable_name = f"'{variable.name}'"
+        except AttributeError:
+            variable_name = "property"
         if not isinstance(variable, DataSymbol):
             raise GenerationError(
-                f"variable property in Loop class should be a DataSymbol but "
-                f"found '{type(variable).__name__}'.")
+                f"variable {variable_name} in Loop class should be a "
+                f"DataSymbol but found '{type(variable).__name__}'.")
         if not isinstance(variable.datatype, ScalarType):
             raise GenerationError(
-                f"variable property in Loop class should be a ScalarType but "
-                f"found '{type(variable.datatype).__name__}'.")
+                f"variable {variable_name} in Loop class should be a "
+                f"ScalarType but found '{type(variable.datatype).__name__}'.")
         if variable.datatype.intrinsic != ScalarType.Intrinsic.INTEGER:
             raise GenerationError(
-                f"variable property in Loop class should be a scalar integer "
-                f"but found '{variable.datatype.intrinsic.name}'.")
+                f"variable {variable_name} in Loop class should be a "
+                f"scalar integer but found "
+                f"'{variable.datatype.intrinsic.name}'.")
 
     @staticmethod
     def _validate_child(position, child):
diff --git a/src/psyclone/psyir/symbols/datatypes.py b/src/psyclone/psyir/symbols/datatypes.py
index 88e7405d0..c11b72536 100644
--- a/src/psyclone/psyir/symbols/datatypes.py
+++ b/src/psyclone/psyir/symbols/datatypes.py
@@ -700,8 +700,8 @@ class StructureType(DataType):
     '''
     # Each member of a StructureType is represented by a ComponentType
     # (named tuple).
-    ComponentType = namedtuple("ComponentType", ["name", "datatype",
-                                                 "visibility"])
+    ComponentType = namedtuple("ComponentType", [
+        "name", "datatype", "visibility", "initial_value"])
 
     def __init__(self):
         self._components = OrderedDict()
@@ -715,7 +715,7 @@ class StructureType(DataType):
         Creates a StructureType from the supplied list of properties.
 
         :param components: the name, type and visibility of each component.
-        :type components: list of 3-tuples
+        :type components: list of 4-tuples
 
         :returns: the new type object.
         :rtype: :py:class:`psyclone.psyir.symbols.StructureType`
@@ -723,12 +723,12 @@ class StructureType(DataType):
         '''
         stype = StructureType()
         for component in components:
-            if len(component) != 3:
+            if len(component) != 4:
                 raise TypeError(
-                    f"Each component must be specified using a 3-tuple of "
-                    f"(name, type, visibility) but found a tuple with "
-                    f"{len(component)} members: {component}")
-            stype.add(component[0], component[1], component[2])
+                    f"Each component must be specified using a 4-tuple of "
+                    f"(name, type, visibility, initial_value) but found a "
+                    f"tuple with {len(component)} members: {component}")
+            stype.add(*component)
         return stype
 
     @property
@@ -739,21 +739,28 @@ class StructureType(DataType):
         '''
         return self._components
 
-    def add(self, name, datatype, visibility):
+    def add(self, name, datatype, visibility, initial_value):
         '''
         Create a component with the supplied attributes and add it to
         this StructureType.
 
         :param str name: the name of the new component.
         :param datatype: the type of the new component.
-        :type datatype: :py:class:`psyclone.psyir.symbols.DataType` or \
-                        :py:class:`psyclone.psyir.symbols.DataTypeSymbol`
+        :type datatype: :py:class:`psyclone.psyir.symbols.DataType` |
+            :py:class:`psyclone.psyir.symbols.DataTypeSymbol`
         :param visibility: whether this component is public or private.
         :type visibility: :py:class:`psyclone.psyir.symbols.Symbol.Visibility`
+        :param initial_value: the initial value of the new component.
+        :type initial_value: Optional[
+            :py:class:`psyclone.psyir.nodes.DataNode`]
 
         :raises TypeError: if any of the supplied values are of the wrong type.
 
         '''
+        # This import must be placed here to avoid circular
+        # dependencies.
+        # pylint: disable=import-outside-toplevel
+        from psyclone.psyir.nodes import DataNode
         if not isinstance(name, str):
             raise TypeError(
                 f"The name of a component of a StructureType must be a 'str' "
@@ -774,8 +781,15 @@ class StructureType(DataType):
                 f"Error attempting to add component '{name}' - a "
                 f"StructureType definition cannot be recursive - i.e. it "
                 f"cannot contain components with the same type as itself.")
+        if (initial_value is not None and
+                not isinstance(initial_value, DataNode)):
+            raise TypeError(
+                f"The initial value of a component of a StructureType must "
+                f"be None or an instance of 'DataNode', but got "
+                f"'{type(initial_value).__name__}'.")
 
-        self._components[name] = self.ComponentType(name, datatype, visibility)
+        self._components[name] = self.ComponentType(
+            name, datatype, visibility, initial_value)
 
     def lookup(self, name):
         '''
diff --git a/src/psyclone/psyir/symbols/symbol_table.py b/src/psyclone/psyir/symbols/symbol_table.py
index 3af263bcb..f3c2e630e 100644
--- a/src/psyclone/psyir/symbols/symbol_table.py
+++ b/src/psyclone/psyir/symbols/symbol_table.py
@@ -1396,6 +1396,43 @@ class SymbolTable():
                                             symbol, symbol_target):
                     continue
 
+                # Determine if there is an Unresolved Symbol in a
+                # descendent symbol table that matches the name of the
+                # symbol we are importing and if so, move it to this
+                # symbol table if a symbol with the same name does not
+                # already exist in this symbol table.
+
+                # There are potential issues with this approach and
+                # with the routine in general which are captured in
+                # issue #2331. Issue #2271 may also help/fix some or
+                # all of the problems too.
+
+                # Import here to avoid circular dependencies
+                # pylint: disable=import-outside-toplevel
+                from psyclone.psyir.nodes import ScopingNode, Reference
+                for scoping_node in self.node.walk(ScopingNode):
+                    symbol_table = scoping_node.symbol_table
+                    if symbol.name in symbol_table:
+                        test_symbol = symbol_table.lookup(symbol.name)
+                        # pylint: disable=unidiomatic-typecheck
+                        if (type(test_symbol) == Symbol
+                                and test_symbol.is_unresolved):
+                            # No wildcard imports in this symbol table
+                            if not [sym for sym in
+                                    symbol_table.containersymbols if
+                                    sym.wildcard_import]:
+                                symbol_table.remove(test_symbol)
+                                if test_symbol.name not in self:
+                                    self.add(test_symbol)
+                                else:
+                                    for ref in symbol_table.node.walk(
+                                            Reference):
+                                        if SymbolTable._has_same_name(
+                                                ref.symbol, symbol):
+                                            mod_symbol = self.lookup(
+                                                symbol.name)
+                                            ref.symbol = mod_symbol
+
                 # This Symbol matches the name of a symbol in the current table
                 if symbol.name in self:
 


Test patch:
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 0ab5ab207..317df78f5 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
@@ -511,8 +511,9 @@ def test_kerncallarglist_scalar_literal(fortran_writer):
     args[3]._intrinsic_type = "invalid"
     with pytest.raises(InternalError) as err:
         create_arg_list.scalar(args[3])
-    assert ("Unexpected literal expression 'invalid' when "
-            "processing kernel 'testkern_qr_code'" in str(err.value))
+    assert ("Expected argument 'invalid' to kernel 'testkern_qr_code' "
+            "to be a literal but the created PSyIR contains one or more "
+            "References." in str(err.value))
 
 
 def test_indirect_dofmap(fortran_writer):
diff --git a/src/psyclone/tests/dynamo0p3_test.py b/src/psyclone/tests/dynamo0p3_test.py
index bb53b1518..b1e504c32 100644
--- a/src/psyclone/tests/dynamo0p3_test.py
+++ b/src/psyclone/tests/dynamo0p3_test.py
@@ -1473,11 +1473,6 @@ def test_dynkernelargument_psyir_expression(monkeypatch):
     assert isinstance(psyir, BinaryOperation)
     assert psyir.operator == BinaryOperation.Operator.ADD
     # Test that we catch invalid literal expressions
-    monkeypatch.setattr(kern.arguments.args[1], "_name", "missing")
-    with pytest.raises(InternalError) as err:
-        _ = kern.arguments.args[1].psyir_expression()
-    assert ("Unexpected literal expression 'missing' when processing kernel "
-            "'inc_x_powint_n" in str(err.value))
     monkeypatch.setattr(kern.arguments.args[1], "_name", "3.0 + f1")
     with pytest.raises(InternalError) as err:
         _ = kern.arguments.args[1].psyir_expression()
diff --git a/src/psyclone/tests/psyir/backend/fortran_gen_decls_test.py b/src/psyclone/tests/psyir/backend/fortran_gen_decls_test.py
index 875f554be..c38c17af5 100644
--- a/src/psyclone/tests/psyir/backend/fortran_gen_decls_test.py
+++ b/src/psyclone/tests/psyir/backend/fortran_gen_decls_test.py
@@ -135,7 +135,7 @@ def test_gen_decls(fortran_writer):
     local_variable = DataSymbol("local", INTEGER_TYPE)
     symbol_table.add(local_variable)
     dtype = StructureType.create([
-        ("flag", INTEGER_TYPE, Symbol.Visibility.PUBLIC)])
+        ("flag", INTEGER_TYPE, Symbol.Visibility.PUBLIC, None)])
     dtype_variable = DataTypeSymbol("field", dtype)
     symbol_table.add(dtype_variable)
     grid_type = DataTypeSymbol("grid_type", DeferredType(),
diff --git a/src/psyclone/tests/psyir/backend/fortran_test.py b/src/psyclone/tests/psyir/backend/fortran_test.py
index cadd1bc74..3be76f9b6 100644
--- a/src/psyclone/tests/psyir/backend/fortran_test.py
+++ b/src/psyclone/tests/psyir/backend/fortran_test.py
@@ -362,8 +362,8 @@ def test_gen_typedecl_validation(fortran_writer, monkeypatch):
             "'UnknownType'" in str(err.value))
     # Symbol with an invalid visibility
     dtype = StructureType.create([
-        ("flag", INTEGER_TYPE, Symbol.Visibility.PUBLIC),
-        ("secret", INTEGER_TYPE, Symbol.Visibility.PRIVATE)])
+        ("flag", INTEGER_TYPE, Symbol.Visibility.PUBLIC, None),
+        ("secret", INTEGER_TYPE, Symbol.Visibility.PRIVATE, None)])
     tsymbol = DataTypeSymbol("my_type", dtype)
     tsymbol._visibility = "wrong"
     with pytest.raises(InternalError) as err:
@@ -433,15 +433,15 @@ def test_gen_typedecl(fortran_writer):
     tsymbol = DataTypeSymbol("grid_type", DeferredType())
     dtype = StructureType.create([
         # Scalar integer
-        ("flag", INTEGER_TYPE, Symbol.Visibility.PUBLIC),
+        ("flag", INTEGER_TYPE, Symbol.Visibility.PUBLIC, None),
         # Private, scalar integer
-        ("secret", INTEGER_TYPE, Symbol.Visibility.PRIVATE),
+        ("secret", INTEGER_TYPE, Symbol.Visibility.PRIVATE, None),
         # Static array
-        ("matrix", atype, Symbol.Visibility.PUBLIC),
+        ("matrix", atype, Symbol.Visibility.PUBLIC, None),
         # Allocatable array
-        ("data", dynamic_atype, Symbol.Visibility.PUBLIC),
+        ("data", dynamic_atype, Symbol.Visibility.PUBLIC, None),
         # Derived type
-        ("grid", tsymbol, Symbol.Visibility.PRIVATE)])
+        ("grid", tsymbol, Symbol.Visibility.PRIVATE, None)])
     tsymbol = DataTypeSymbol("my_type", dtype)
     assert (fortran_writer.gen_typedecl(tsymbol) ==
             "type, public :: my_type\n"
@@ -2005,3 +2005,27 @@ def test_fw_keeps_symbol_renaming(fortran_writer, fortran_reader):
     psyir = fortran_reader.psyir_from_source(code)
     output = fortran_writer(psyir)
     assert "b_mod_name=>a_mod_name" in output
+
+
+def test_componenttype_initialisation(fortran_reader, fortran_writer):
+    '''Test that initial values are output for a StructureType which
+    contains types that have initial values.
+
+    '''
+    test_code = (
+        "module test_mod\n"
+        "    type :: my_type\n"
+        "      integer :: i = 1\n"
+        "      integer :: j\n"
+        "    end type my_type\n"
+        "end module test_mod\n")
+    psyir = fortran_reader.psyir_from_source(test_code)
+    sym_table = psyir.children[0].symbol_table
+    symbol = sym_table.lookup("my_type")
+    assert isinstance(symbol.datatype, StructureType)
+    result = fortran_writer(psyir)
+    assert (
+        "  type, public :: my_type\n"
+        "    integer, public :: i = 1\n"
+        "    integer, public :: j\n"
+        "  end type my_type\n" in result)
diff --git a/src/psyclone/tests/psyir/backend/language_writer_test.py b/src/psyclone/tests/psyir/backend/language_writer_test.py
index 785432cf4..678d61430 100644
--- a/src/psyclone/tests/psyir/backend/language_writer_test.py
+++ b/src/psyclone/tests/psyir/backend/language_writer_test.py
@@ -1,7 +1,7 @@
 # -----------------------------------------------------------------------------
 # BSD 3-Clause License
 #
-# Copyright (c) 2021, Science and Technology Facilities Council.
+# Copyright (c) 2021-2023, Science and Technology Facilities Council.
 # All rights reserved.
 #
 # Redistribution and use in source and binary forms, with or without
@@ -123,14 +123,14 @@ def test_lw_structureref(fortran_writer):
     ''' Test the LanguageWriter support for StructureReference
     using the FortranWriter as instance. '''
     region_type = StructureType.create([
-        ("nx", INTEGER_TYPE, Symbol.Visibility.PUBLIC),
-        ("ny", INTEGER_TYPE, Symbol.Visibility.PUBLIC)])
+        ("nx", INTEGER_TYPE, Symbol.Visibility.PUBLIC, None),
+        ("ny", INTEGER_TYPE, Symbol.Visibility.PUBLIC, None)])
     region_type_sym = DataTypeSymbol("grid_type", region_type)
     region_array_type = ArrayType(region_type_sym, [2, 2])
     grid_type = StructureType.create([
-        ("dx", INTEGER_TYPE, Symbol.Visibility.PUBLIC),
-        ("area", region_type_sym, Symbol.Visibility.PUBLIC),
-        ("levels", region_array_type, Symbol.Visibility.PUBLIC)])
+        ("dx", INTEGER_TYPE, Symbol.Visibility.PUBLIC, None),
+        ("area", region_type_sym, Symbol.Visibility.PUBLIC, None),
+        ("levels", region_array_type, Symbol.Visibility.PUBLIC, None)])
     grid_type_sym = DataTypeSymbol("grid_type", grid_type)
     grid_var = DataSymbol("grid", grid_type_sym)
     grid_ref = StructureReference.create(grid_var, ['area', 'nx'])
@@ -158,13 +158,13 @@ def test_lw_arrayofstructuresmember(fortran_writer):
     ''' Test the LanguageWriter support for ArrayOfStructuresMember
     using the FortranWriter. '''
     region_type = StructureType.create([
-        ("nx", INTEGER_TYPE, Symbol.Visibility.PUBLIC),
-        ("ny", INTEGER_TYPE, Symbol.Visibility.PUBLIC)])
+        ("nx", INTEGER_TYPE, Symbol.Visibility.PUBLIC, None),
+        ("ny", INTEGER_TYPE, Symbol.Visibility.PUBLIC, None)])
     region_type_sym = DataTypeSymbol("grid_type", region_type)
     region_array_type = ArrayType(region_type_sym, [2, 2])
     # The grid type contains an array of region-type structures
     grid_type = StructureType.create([
-        ("levels", region_array_type, Symbol.Visibility.PUBLIC)])
+        ("levels", region_array_type, Symbol.Visibility.PUBLIC, None)])
     grid_type_sym = DataTypeSymbol("grid_type", grid_type)
     grid_var = DataSymbol("grid", grid_type_sym)
     # Reference to an element of an array that is a structure
@@ -191,7 +191,7 @@ def test_lw_arrayofstructuresref(fortran_writer):
     ''' Test the LanguageWriter support for ArrayOfStructuresReference
     using the FortranWriter as instance. '''
     grid_type = StructureType.create([
-        ("dx", INTEGER_TYPE, Symbol.Visibility.PUBLIC)])
+        ("dx", INTEGER_TYPE, Symbol.Visibility.PUBLIC, None)])
     grid_type_sym = DataTypeSymbol("grid_type", grid_type)
     grid_array_type = ArrayType(grid_type_sym, [10])
     grid_var = DataSymbol("grid", grid_array_type)
@@ -218,13 +218,13 @@ def test_lw_arrayofstructuresref(fortran_writer):
 def test_member_node(fortran_writer):
     '''Explicitly test the member_node function.'''
     region_type = StructureType.create([
-        ("nx", INTEGER_TYPE, Symbol.Visibility.PUBLIC),
-        ("ny", INTEGER_TYPE, Symbol.Visibility.PUBLIC)])
+        ("nx", INTEGER_TYPE, Symbol.Visibility.PUBLIC, None),
+        ("ny", INTEGER_TYPE, Symbol.Visibility.PUBLIC, None)])
     region_type_sym = DataTypeSymbol("grid_type", region_type)
     region_array_type = ArrayType(region_type_sym, [2, 2])
     # The grid type contains an array of region-type structures
     grid_type = StructureType.create([
-        ("levels", region_array_type, Symbol.Visibility.PUBLIC)])
+        ("levels", region_array_type, Symbol.Visibility.PUBLIC, None)])
     grid_type_sym = DataTypeSymbol("grid_type", grid_type)
     grid_var = DataSymbol("grid", grid_type_sym)
     # Reference to a member of a structure that is an element of an array
diff --git a/src/psyclone/tests/psyir/frontend/fortran_test.py b/src/psyclone/tests/psyir/frontend/fortran_test.py
index fe02010a7..9aeb120b3 100644
--- a/src/psyclone/tests/psyir/frontend/fortran_test.py
+++ b/src/psyclone/tests/psyir/frontend/fortran_test.py
@@ -41,10 +41,11 @@ import pytest
 from fparser.two import Fortran2003
 from psyclone.psyir.frontend.fortran import FortranReader
 from psyclone.psyir.frontend.fparser2 import Fparser2Reader
-from psyclone.psyir.nodes import Routine, FileContainer, UnaryOperation, \
-    BinaryOperation, Literal, Assignment, CodeBlock, IntrinsicCall
-from psyclone.psyir.symbols import SymbolTable, DataSymbol, \
-    ScalarType, SymbolError, ContainerSymbol, DeferredType
+from psyclone.psyir.nodes import (
+    Routine, FileContainer, UnaryOperation, BinaryOperation, Literal,
+    Assignment, CodeBlock, IntrinsicCall)
+from psyclone.psyir.symbols import (
+    SymbolTable, DataSymbol, ScalarType, DeferredType)
 
 
 # The 'contiguous' keyword is just valid with Fortran 2008
@@ -112,6 +113,7 @@ def test_fortran_psyir_from_expression(fortran_reader):
     psyir = fortran_reader.psyir_from_expression("3.0", table)
     assert isinstance(psyir, Literal)
     assert psyir.value == "3.0"
+
     psyir = fortran_reader.psyir_from_expression("-3.0 + 1.0", table)
     assert isinstance(psyir, BinaryOperation)
     assert psyir.operator == BinaryOperation.Operator.ADD
@@ -119,33 +121,27 @@ def test_fortran_psyir_from_expression(fortran_reader):
     assert psyir.children[0].operator == UnaryOperation.Operator.MINUS
     assert isinstance(psyir.children[0].children[0], Literal)
     assert psyir.children[0].children[0].value == "3.0"
+
     psyir = fortran_reader.psyir_from_expression("ABS(-3.0)", table)
     assert isinstance(psyir, IntrinsicCall)
     assert psyir.intrinsic == IntrinsicCall.Intrinsic.ABS
     assert isinstance(psyir.children[0], UnaryOperation)
     assert psyir.children[0].operator == UnaryOperation.Operator.MINUS
     assert isinstance(psyir.children[0].children[0], Literal)
-    # With kind specified by a kind parameter. Add a ContainerSymbol with a
-    # wildcard import so there's some way that 'r_def' can be brought into
-    # scope.
-    csym = table.new_symbol("kind_params_mod", symbol_type=ContainerSymbol)
-    csym.wildcard_import = True
-    fortran_reader.psyir_from_expression("3.0_r_def", table)
-    assert "r_def" in table
+
     psyir = fortran_reader.psyir_from_expression("3.0_r_def", table)
     assert isinstance(psyir, Literal)
     assert isinstance(psyir.datatype, ScalarType)
     assert isinstance(psyir.datatype.precision, DataSymbol)
-    assert psyir.datatype.precision is table.lookup("r_def")
-    # Symbol not found in supplied table
-    with pytest.raises(SymbolError) as err:
-        fortran_reader.psyir_from_expression("3.0 + a", SymbolTable())
-    assert ("Expression '3.0 + a' contains symbols which are not present in "
-            "any symbol table and there are no" in str(err.value))
-    # Now use the table with the container that has the wildcard import
+    symbol = table.lookup("r_def")
+    assert isinstance(symbol, DataSymbol)
+    assert psyir.datatype.precision is symbol
+
     psyir = fortran_reader.psyir_from_expression("3.0 + a", table)
     assert isinstance(psyir, BinaryOperation)
-    assert "a" in table
+    a_symbol = psyir.children[1].symbol
+    a_symbol_table = table.lookup("a")
+    assert a_symbol is a_symbol_table
 
 
 def test_fortran_psyir_from_expression_invalid(fortran_reader):
@@ -165,10 +161,8 @@ def test_fortran_psyir_from_expression_invalid(fortran_reader):
     assert ("Must be supplied with a valid SymbolTable but got 'NoneType'" in
             str(err.value))
     table = SymbolTable()
-    with pytest.raises(SymbolError) as err:
-        fortran_reader.psyir_from_expression("return", table)
-    assert ("Expression 'return' contains symbols which are not present" in
-            str(err.value))
+    # OK
+    fortran_reader.psyir_from_expression("return", table)
     with pytest.raises(ValueError) as err:
         fortran_reader.psyir_from_expression("a = b", table)
     assert "not represent a Fortran expression: 'a = b'" in str(err.value)
@@ -206,10 +200,8 @@ def test_psyir_from_statement_invalid(fortran_reader):
         fortran_reader.psyir_from_statement("blah", table)
     assert ("Supplied source does not represent a Fortran statement: 'blah'"
             in str(err.value))
-    with pytest.raises(SymbolError) as err:
-        fortran_reader.psyir_from_statement("a=b", table)
-    assert ("Statement 'a=b' contains symbols which are not present in any "
-            "symbol table" in str(err.value))
+    # OK
+    fortran_reader.psyir_from_statement("a=b", table)
 
 
 def test_fortran_psyir_from_file(fortran_reader, tmpdir_factory):
diff --git a/src/psyclone/tests/psyir/frontend/fparser2_container_test.py b/src/psyclone/tests/psyir/frontend/fparser2_container_test.py
index 2132b0cb8..bae8116b5 100644
--- a/src/psyclone/tests/psyir/frontend/fparser2_container_test.py
+++ b/src/psyclone/tests/psyir/frontend/fparser2_container_test.py
@@ -1,7 +1,7 @@
 # -----------------------------------------------------------------------------
 # BSD 3-Clause License
 #
-# Copyright (c) 2020-2021, Science and Technology Facilities Council.
+# Copyright (c) 2020-2023, Science and Technology Facilities Council.
 # All rights reserved.
 #
 # Redistribution and use in source and binary forms, with or without
@@ -32,6 +32,7 @@
 # POSSIBILITY OF SUCH DAMAGE.
 # -----------------------------------------------------------------------------
 # Author: A. R. Porter, STFC Daresbury Lab
+# Modified: R. W. Ford, STFC Daresbury Lab
 # -----------------------------------------------------------------------------
 
 ''' Performs py.test tests on the container generation in the fparser2 PSyIR
@@ -46,7 +47,7 @@ from fparser.two import Fortran2003
 from psyclone.psyir.frontend.fparser2 import Fparser2Reader
 from psyclone.errors import InternalError, GenerationError
 from psyclone.psyir.nodes import Container, KernelSchedule
-from psyclone.psyir.symbols import Symbol, RoutineSymbol, SymbolError
+from psyclone.psyir.symbols import Symbol, RoutineSymbol
 
 
 def test_generate_container(parser):
@@ -135,10 +136,12 @@ def test_generate_container_routine_names(parser):
     assert isinstance(sub, RoutineSymbol)
 
 
-def test_access_stmt_no_unqualified_use_error(parser):
-    ''' Check that we raise the expected error if an undeclared symbol is
-    listed in an access statement and there are no unqualified use
-    statements to bring it into scope. '''
+def test_access_stmt_no_unqualified_use(parser):
+    '''Check that no error is raised if an undeclared symbol is listed in
+    an access statement and there are no unqualified use statements to
+    bring it into scope. This is now the job of the back-end.
+
+    '''
     processor = Fparser2Reader()
     reader = FortranStringReader(
         "module modulename\n"
@@ -147,10 +150,7 @@ def test_access_stmt_no_unqualified_use_error(parser):
         "public var3\n"
         "end module modulename")
     fparser2spec = parser(reader)
-    with pytest.raises(SymbolError) as err:
-        processor.generate_container(fparser2spec)
-    assert ("module 'modulename': 'var3' is listed in an accessibility "
-            "statement as being" in str(err.value))
+    processor.generate_container(fparser2spec)
 
 
 def test_default_public_container(parser):
diff --git a/src/psyclone/tests/psyir/frontend/fparser2_derived_type_test.py b/src/psyclone/tests/psyir/frontend/fparser2_derived_type_test.py
index 4dccea0b2..aee5683be 100644
--- a/src/psyclone/tests/psyir/frontend/fparser2_derived_type_test.py
+++ b/src/psyclone/tests/psyir/frontend/fparser2_derived_type_test.py
@@ -32,6 +32,7 @@
 # POSSIBILITY OF SUCH DAMAGE.
 # -----------------------------------------------------------------------------
 # Authors: A. R. Porter, N. Nobre and S. Siso, STFC Daresbury Lab
+# Modified: R. W. Ford, STFC Daresbury Lab
 # -----------------------------------------------------------------------------
 
 ''' Performs py.test tests on the fparser2 PSyIR front-end support for
@@ -125,17 +126,16 @@ def test_deferred_derived_type(type_name):
 
 @pytest.mark.usefixtures("f2008_parser")
 def test_missing_derived_type():
-    ''' Check that the fronted raises an error if it encounters a variable
-    of a derived type that cannot be resolved. '''
+    '''Check that the fronted does not raise an error if it encounters a
+    variable of a derived type that cannot be resolved. This is the
+    back-end's responsibility.
+
+    '''
     fake_parent = KernelSchedule("dummy_schedule")
     processor = Fparser2Reader()
     reader = FortranStringReader("type(my_type) :: var")
     fparser2spec = Fortran2003.Specification_Part(reader)
-    # This should raise an error because there's no Container from which
-    # the definition of 'my_type' can be brought into scope.
-    with pytest.raises(SymbolError) as err:
-        processor.process_declarations(fake_parent, fparser2spec.content, [])
-    assert "No Symbol found for name 'my_type'" in str(err.value)
+    processor.process_declarations(fake_parent, fparser2spec.content, [])
 
 
 @pytest.mark.parametrize("type_name", ["my_type", "MY_TYPE", "mY_type"])
diff --git a/src/psyclone/tests/psyir/frontend/fparser2_do_handler_test.py b/src/psyclone/tests/psyir/frontend/fparser2_do_handler_test.py
index d9f12376e..04ab28bdd 100644
--- a/src/psyclone/tests/psyir/frontend/fparser2_do_handler_test.py
+++ b/src/psyclone/tests/psyir/frontend/fparser2_do_handler_test.py
@@ -1,7 +1,7 @@
 # -----------------------------------------------------------------------------
 # BSD 3-Clause License
 #
-# Copyright (c) 2020-2021, Science and Technology Facilities Council.
+# Copyright (c) 2020-2023, Science and Technology Facilities Council.
 # All rights reserved.
 #
 # Redistribution and use in source and binary forms, with or without
@@ -32,18 +32,19 @@
 # POSSIBILITY OF SUCH DAMAGE.
 # -----------------------------------------------------------------------------
 # Authors: A. R. Porter and N. Nobre, STFC Daresbury Lab
-# Modified A. B. G. Chalk, STFC Datesbury Lab
+# Modified A. B. G. Chalk, STFC Daresbury Lab
+# Modified R. W. Ford, STFC Daresbury Lab
 
 ''' Module containing pytest tests for the handling of the DO
 construct in the PSyIR fparser2 frontend. '''
 
 
-import pytest
 from fparser.two import Fortran2003
 
-from psyclone.errors import InternalError
-from psyclone.psyir.nodes import Assignment, BinaryOperation, CodeBlock, \
-                                 Literal, Loop, Routine, Schedule, WhileLoop
+from psyclone.psyir.nodes import (
+    Assignment, BinaryOperation, CodeBlock, Literal, Loop, Routine, Schedule,
+    WhileLoop)
+from psyclone.psyir.symbols import DataSymbol, ScalarType
 from psyclone.tests.utilities import Compile
 
 
@@ -169,8 +170,12 @@ END PROGRAM my_test'''
 
 
 def test_undeclared_loop_var(fortran_reader):
-    '''Check that the do handler raises the expected error if an undeclared
-    loop variable is encountered.
+    '''Check that the do handler defaults the loop var to an integer if
+    the declaration of the loop variable can't be found. This may be
+    because it is declared in another module or in some code that
+    can't be parsed by the parser. One result of this is that a loop
+    variable that really is not declared will be accepted by PSyclone
+    and determined to be an integer.
 
     '''
     code = '''
@@ -179,10 +184,12 @@ def test_undeclared_loop_var(fortran_reader):
         end do
       end subroutine test
     '''
-    with pytest.raises(InternalError) as err:
-        _ = fortran_reader.psyir_from_source(code)
-    assert ("Loop-variable name 'i' is not declared and there are no "
-            "unqualified use statements" in str(err.value))
+    result = fortran_reader.psyir_from_source(code)
+    i_var = result.children[0].symbol_table.lookup("i")
+    assert isinstance(i_var, DataSymbol)
+    assert isinstance(i_var.datatype, ScalarType)
+    assert i_var.datatype.intrinsic is ScalarType.Intrinsic.INTEGER
+    assert i_var.datatype.precision is ScalarType.Precision.UNDEFINED
 
 
 def test_do_inside_while(fortran_reader, fortran_writer, tmpdir):
diff --git a/src/psyclone/tests/psyir/frontend/fparser2_find_or_create_symbol_test.py b/src/psyclone/tests/psyir/frontend/fparser2_find_or_create_symbol_test.py
index 8029ca07f..aee729e8e 100644
--- a/src/psyclone/tests/psyir/frontend/fparser2_find_or_create_symbol_test.py
+++ b/src/psyclone/tests/psyir/frontend/fparser2_find_or_create_symbol_test.py
@@ -1,7 +1,7 @@
 # -----------------------------------------------------------------------------
 # BSD 3-Clause License
 #
-# Copyright (c) 2021, Science and Technology Facilities Council.
+# Copyright (c) 2021-2023, Science and Technology Facilities Council.
 # All rights reserved.
 #
 # Redistribution and use in source and binary forms, with or without
@@ -32,6 +32,7 @@
 # POSSIBILITY OF SUCH DAMAGE.
 # -----------------------------------------------------------------------------
 # Author: A. R. Porter, STFC Daresbury Lab
+# Modified: R. W. Ford, STFC Daresbury Lab
 # -----------------------------------------------------------------------------
 
 ''' Performs py.test tests for the _find_or_create_unresolved_symbol routine
@@ -43,11 +44,12 @@ import pytest
 from fparser.common.readfortran import FortranFileReader
 from psyclone.psyGen import PSyFactory, Kern
 from psyclone.psyir.frontend.fparser2 import _find_or_create_unresolved_symbol
-from psyclone.psyir.nodes import Reference, Container, Assignment, Literal, \
-    KernelSchedule, BinaryOperation
-from psyclone.psyir.symbols import Symbol, DataSymbol, SymbolError, \
-    SymbolTable, REAL_TYPE, ContainerSymbol, ScalarType, UnresolvedInterface, \
-    RoutineSymbol, NoType
+from psyclone.psyir.nodes import (
+    Reference, Container, Assignment, Literal, KernelSchedule,
+    BinaryOperation)
+from psyclone.psyir.symbols import (
+    Symbol, DataSymbol, SymbolTable, REAL_TYPE, ScalarType,
+    UnresolvedInterface, RoutineSymbol, NoType)
 from psyclone.tests.utilities import get_invoke
 
 
@@ -74,14 +76,6 @@ def test_find_or_create_unresolved_symbol():
         field_old, field_old.name, scope_limit=kernel_schedule), DataSymbol)
     assert field_old.symbol.name == field_old.name
 
-    # Symbol in KernelSchedule SymbolTable with parent scope, so
-    # the symbol should not be found as we limit the scope to the
-    # immediate parent of the reference
-    with pytest.raises(SymbolError) as excinfo:
-        _ = _find_or_create_unresolved_symbol(field_old, field_old.name,
-                                              scope_limit=field_old.parent)
-    assert "No Symbol found for name 'field_old'." in str(excinfo.value)
-
     # Symbol in Container SymbolTable
     alpha = references[6]
     assert alpha.name == "alpha"
@@ -92,14 +86,6 @@ def test_find_or_create_unresolved_symbol():
     assert (_find_or_create_unresolved_symbol(alpha, alpha.name) in
             container.symbol_table.symbols)
 
-    # Symbol in Container SymbolTable with KernelSchedule scope, so
-    # the symbol should not be found as we limit the scope to the
-    # kernel so do not search the container symbol table.
-    with pytest.raises(SymbolError) as excinfo:
-        _ = _find_or_create_unresolved_symbol(alpha, alpha.name,
-                                              scope_limit=kernel_schedule)
-    assert "No Symbol found for name 'alpha'." in str(excinfo.value)
-
     # Symbol in Container SymbolTable with Container scope
     assert (_find_or_create_unresolved_symbol(
         alpha, alpha.name, scope_limit=container).name == alpha.name)
@@ -132,8 +118,8 @@ def test_find_or_create_unresolved_symbol():
         alpha, "very_private", visibility=Symbol.Visibility.PRIVATE)
     assert sym.name == "very_private"
     assert sym.visibility == Symbol.Visibility.PRIVATE
-    assert sym is container.symbol_table.lookup("very_private",
-                                                scope_limit=container)
+    assert sym is container.children[0].symbol_table.lookup(
+        "very_private", scope_limit=container)
 
 
 def test_find_or_create_unresolved_symbol_2():
@@ -149,20 +135,14 @@ def test_find_or_create_unresolved_symbol_2():
     xref = Reference(xvar)
     assign = Assignment.create(xref, Literal("1.0", REAL_TYPE))
     kernel1.addchild(assign)
-    # We have no wildcard imports so there can be no symbol named 'undefined'
-    with pytest.raises(SymbolError) as err:
-        _ = _find_or_create_unresolved_symbol(assign, "undefined")
-    assert "No Symbol found for name 'undefined'" in str(err.value)
+    # OK to add
+    orig_symbol = _find_or_create_unresolved_symbol(assign, "undefined")
     # We should be able to find the 'tmp' symbol in the parent Container
     sym = _find_or_create_unresolved_symbol(assign, "tmp")
     assert sym.datatype.intrinsic == ScalarType.Intrinsic.REAL
-    # Add a wildcard import to the SymbolTable of the KernelSchedule
-    new_container = ContainerSymbol("some_mod")
-    new_container.wildcard_import = True
-    kernel1.symbol_table.add(new_container)
-    # Symbol not in any container but we do have wildcard imports so we
-    # get a new symbol back
+    # OK to find
     new_symbol = _find_or_create_unresolved_symbol(assign, "undefined")
+    assert orig_symbol is new_symbol
     assert new_symbol.name == "undefined"
     assert isinstance(new_symbol.interface, UnresolvedInterface)
     # pylint: disable=unidiomatic-typecheck
@@ -171,9 +151,49 @@ def test_find_or_create_unresolved_symbol_2():
     assert kernel1.symbol_table.lookup("undefined") is new_symbol
 
 
+def test_find_or_create_unresolved_scope_limit():
+    '''Test that the implemented works as expected when scope_limit is
+    provided.
+
+    '''
+    kernel = KernelSchedule.create("mod_1", SymbolTable(), [])
+    container = Container.create("container_name", SymbolTable(),
+                                 [kernel])
+    xvar = DataSymbol("x", REAL_TYPE)
+    xref = Reference(xvar)
+    assign = Assignment.create(xref, Literal("1.0", REAL_TYPE))
+    kernel.addchild(assign)
+
+    # Add x to the kernel symbol table as no symbol exists
+    xsymbol = _find_or_create_unresolved_symbol(assign, "x")
+    assert "x" in kernel.symbol_table
+    xsymbol = kernel.symbol_table.lookup("x")
+    # pylint: disable=unidiomatic-typecheck
+    assert type(xsymbol) is Symbol
+
+    # Move x to the container symbol table
+    kernel.symbol_table.remove(xsymbol)
+    container.symbol_table.add(xsymbol)
+    assert "x" in container.symbol_table
+    assert "x" not in kernel.symbol_table
+
+    # Add "x" ("x_1") to the kernel symbol table as we limit the scope to
+    # kernel. However, to avoid conficts with the pre-existing
+    # variables "x_1" in created.
+    xsymbol = _find_or_create_unresolved_symbol(
+        assign, "x", scope_limit=kernel)
+    assert "x" in container.symbol_table
+    assert "x_1" in kernel.symbol_table
+    assert (container.symbol_table.lookup("x") !=
+            kernel.symbol_table.lookup("x_1"))
+    assert kernel.symbol_table.lookup("x_1") == xsymbol
+
+
 def test_nemo_find_container_symbol(parser):
-    ''' Check that find_or_create_symbol() works for the NEMO API when the
-    searched-for symbol is declared in the parent module. '''
+    '''Check that find_or_create_symbol() works for the NEMO API when the
+    searched-for symbol is declared in the parent module.
+
+    '''
     reader = FortranFileReader(
         os.path.join(
             os.path.dirname(os.path.dirname(os.path.dirname(
diff --git a/src/psyclone/tests/psyir/frontend/fparser2_literals_test.py b/src/psyclone/tests/psyir/frontend/fparser2_literals_test.py
index a73ddc0cd..d9cc23a6c 100644
--- a/src/psyclone/tests/psyir/frontend/fparser2_literals_test.py
+++ b/src/psyclone/tests/psyir/frontend/fparser2_literals_test.py
@@ -38,15 +38,19 @@
     PSyIR front-end '''
 
 import pytest
+
 from fparser.common.readfortran import FortranStringReader
 from fparser.two import Fortran2003
+
+from psyclone.errors import InternalError, GenerationError
 from psyclone.psyir.frontend import fparser2
 from psyclone.psyir.frontend.fparser2 import Fparser2Reader, \
     get_literal_precision
-from psyclone.psyir.symbols import (ScalarType, DataSymbol, INTEGER_TYPE,
-                                    UnknownFortranType, ContainerSymbol)
-from psyclone.psyir.nodes import Node, Literal, CodeBlock, Schedule, Assignment
-from psyclone.errors import InternalError
+from psyclone.psyir.nodes import (
+    Node, Literal, CodeBlock, Schedule, Assignment, Routine)
+from psyclone.psyir.symbols import (
+    ScalarType, DataSymbol, INTEGER_TYPE, UnknownFortranType,
+    SymbolTable)
 
 
 @pytest.mark.parametrize("code, dtype",
@@ -154,11 +158,7 @@ def test_handling_literal_precision_1(value, dprecision, intrinsic):
         code = f"x={value}_{dprecision}"
     reader = FortranStringReader(code)
     astmt = Fortran2003.Assignment_Stmt(reader)
-    fake_parent = Schedule()
-    # Add a wildcard import so that there's somewhere for the precision
-    # symbols to have come from.
-    fake_parent.symbol_table.add(ContainerSymbol("amod",
-                                                 wildcard_import=True))
+    fake_parent = Routine.create("fake", SymbolTable(), [])
     # Ensure the symbol table has an entry for "x"
     fake_parent.symbol_table.add(
         DataSymbol("x", ScalarType(ScalarType.Intrinsic.INTEGER, 4)))
@@ -280,7 +280,6 @@ def test_handling_invalid_logic_literal():
     TODO #754 fix test so that 'disable_declaration_check' fixture is not
     required.
     '''
-    from psyclone.errors import GenerationError
     reader = FortranStringReader("x = .true.")
     astmt = Fortran2003.Assignment_Stmt(reader)
     astmt.items[2].items = ('invalid', None)
diff --git a/src/psyclone/tests/psyir/frontend/fparser2_part_ref_test.py b/src/psyclone/tests/psyir/frontend/fparser2_part_ref_test.py
index cf1e2d369..d96d3f1f2 100644
--- a/src/psyclone/tests/psyir/frontend/fparser2_part_ref_test.py
+++ b/src/psyclone/tests/psyir/frontend/fparser2_part_ref_test.py
@@ -1,7 +1,7 @@
 # -----------------------------------------------------------------------------
 # BSD 3-Clause License
 #
-# Copyright (c) 2022, Science and Technology Facilities Council.
+# Copyright (c) 2022-2023, Science and Technology Facilities Council.
 # All rights reserved.
 #
 # Redistribution and use in source and binary forms, with or without
@@ -48,25 +48,6 @@ from psyclone.psyir.symbols import SymbolError, DataSymbol, ScalarType, \
     INTEGER_TYPE, RoutineSymbol, ArrayType
 
 
-@pytest.mark.usefixtures("f2008_parser")
-def test_handling_part_ref_error():
-    '''Test that the expected exception is raised when the parsed symbol
-    is not found in the symbol table.
-
-    '''
-    reader = FortranStringReader("x(2)=1")
-    fparser2part_ref = Execution_Part.match(reader)[0][0]
-
-    fake_parent = KernelSchedule('kernel')
-    processor = Fparser2Reader()
-
-    # If one of the ancestors has a symbol table then process_nodes()
-    # checks that the symbol is declared.
-    with pytest.raises(SymbolError) as error:
-        processor.process_nodes(fake_parent, [fparser2part_ref])
-    assert "No Symbol found for name 'x'." in str(error.value)
-
-
 @pytest.mark.usefixtures("f2008_parser")
 def test_handling_part_ref():
     '''Test that fparser2 Part_Ref is converted to the expected PSyIR
diff --git a/src/psyclone/tests/psyir/frontend/fparser2_test.py b/src/psyclone/tests/psyir/frontend/fparser2_test.py
index 3004257b4..d82141d4f 100644
--- a/src/psyclone/tests/psyir/frontend/fparser2_test.py
+++ b/src/psyclone/tests/psyir/frontend/fparser2_test.py
@@ -65,7 +65,8 @@ from psyclone.psyir.symbols import (
     DataSymbol, ContainerSymbol, SymbolTable, ArgumentInterface,
     SymbolError, ScalarType, ArrayType, INTEGER_TYPE, REAL_TYPE,
     UnknownFortranType, DeferredType, Symbol, UnresolvedInterface,
-    ImportInterface, BOOLEAN_TYPE, StaticInterface, UnknownInterface)
+    ImportInterface, BOOLEAN_TYPE, StaticInterface, UnknownInterface,
+    StructureType, DataTypeSymbol)
 
 # pylint: disable=too-many-statements
 
@@ -2047,12 +2048,6 @@ def test_handling_name():
     fake_parent = KernelSchedule('kernel')
     processor = Fparser2Reader()
 
-    # If one of the ancestors has a symbol table then process_nodes()
-    # checks that the symbol is declared.
-    with pytest.raises(SymbolError) as error:
-        processor.process_nodes(fake_parent, [fparser2name])
-    assert "No Symbol found for name 'x'." in str(error.value)
-
     fake_parent.symbol_table.add(DataSymbol('x', INTEGER_TYPE))
     processor.process_nodes(fake_parent, [fparser2name])
     assert len(fake_parent.children) == 1
@@ -2773,22 +2768,24 @@ def test_named_and_wildcard_use_var(f2008_parser):
         ''')
     prog = f2008_parser(reader)
     psy = PSyFactory(api="nemo").create(prog)
-    # We should have an entry for "a_var" in the Container symbol table
-    # due to the access in "test_sub1". The Container is the first child
-    # of the FileContainer node.
+    # We should not have an entry for "a_var" in the Container symbol
+    # table as we don't know whether the access in "test_sub1" comes
+    # from the wildcard import ("some_mod"). The Container is the
+    # first child of the FileContainer node.
     container = psy.container.children[0]
-    avar1 = container.symbol_table.lookup("a_var")
+    assert "a_var" not in container.symbol_table
+    # There should be an entry for "a_var" in the symbol table for the
+    # "test_sub1" routine as we do not yet know where it is declared.
+    routine = container.children[0]
+    avar1 = routine.symbol_table.lookup("a_var")
     # It must be a generic Symbol since we don't know anything about it
     # pylint: disable=unidiomatic-typecheck
     assert type(avar1) is Symbol
-    # There should be no entry for "a_var" in the symbol table for the
-    # "test_sub1" routine as it is not declared there.
-    schedule = psy.invokes.invoke_list[0].schedule
-    assert "a_var" not in schedule.symbol_table
+
     # There should be another, distinct entry for "a_var" in the symbol table
     # for "test_sub2" as it has a use statement that imports it.
-    schedule = psy.invokes.invoke_list[1].schedule
-    avar2 = schedule.symbol_table.lookup("a_var")
+    routine = container.children[1]
+    avar2 = routine.symbol_table.lookup("a_var")
     assert type(avar2) is Symbol
     assert avar2 is not avar1
 
@@ -2945,3 +2942,234 @@ def test_declarations_with_initialisations_errors(parser):
     with pytest.raises(ValueError) as err:
         _ = processor.get_routine_schedules("a", ast)
     assert "error to propagate" in str(err.value)
+
+
+def test_structures(fortran_reader, fortran_writer):
+    '''Test that Fparser2Reader parses Fortran types correctly when there
+    is a type declaration with one of the members being initialised,
+    when there is a type declaration with an additional attribute
+    (e.g. one that extends an existing type), when there is a type
+    declaration that contains procedures and when there is a type
+    declaration that both has an additional attribute and contains
+    procedures.
+
+    '''
+    # derived-type with initial value (StructureType)
+    test_code = (
+        "module test_mod\n"
+        "    type, private :: my_type\n"
+        "      integer :: i = 1\n"
+        "      integer :: j\n"
+        "    end type my_type\n"
+        "end module test_mod\n")
+    psyir = fortran_reader.psyir_from_source(test_code)
+    sym_table = psyir.children[0].symbol_table
+    symbol = sym_table.lookup("my_type")
+    assert isinstance(symbol, DataTypeSymbol)
+    assert isinstance(symbol.datatype, StructureType)
+    result = fortran_writer(psyir)
+    assert (
+        "  type, private :: my_type\n"
+        "    integer, public :: i = 1\n"
+        "    integer, public :: j\n"
+        "  end type my_type\n" in result)
+
+    # type that extends another type (UnknownFortranType)
+    test_code = (
+        "module test_mod\n"
+        "    use kernel_mod, only : kernel_type\n"
+        "    type, extends(kernel_type) :: my_type\n"
+        "      integer :: i = 1\n"
+        "    end type my_type\n"
+        "end module test_mod\n")
+    psyir = fortran_reader.psyir_from_source(test_code)
+    sym_table = psyir.children[0].symbol_table
+    symbol = sym_table.lookup("my_type")
+    assert isinstance(symbol, DataTypeSymbol)
+    assert isinstance(symbol.datatype, UnknownFortranType)
+    result = fortran_writer(psyir)
+    assert (
+        "  type, extends(kernel_type), public :: my_type\n"
+        "  INTEGER :: i = 1\n"
+        "END TYPE my_type\n" in result)
+
+    # type that contains a procedure (UnknownFortranType)
+    test_code = (
+        "module test_mod\n"
+        "    type :: test_type\n"
+        "      integer :: i = 1\n"
+        "      contains\n"
+        "      procedure, nopass :: test_code\n"
+        "    end type test_type\n"
+        "    contains\n"
+        "    subroutine test_code()\n"
+        "    end subroutine\n"
+        "end module test_mod\n")
+    psyir = fortran_reader.psyir_from_source(test_code)
+    sym_table = psyir.children[0].symbol_table
+    symbol = sym_table.lookup("test_type")
+    assert isinstance(symbol, DataTypeSymbol)
+    assert isinstance(symbol.datatype, UnknownFortranType)
+    result = fortran_writer(psyir)
+    assert (
+        "  type, public :: test_type\n"
+        "  INTEGER :: i = 1\n"
+        "  CONTAINS\n"
+        "  PROCEDURE, NOPASS :: test_code\n"
+        "END TYPE test_type\n" in result)
+
+    # type that creates an abstract type and contains a procedure
+    # (UnknownFortranType)
+    test_code = (
+        "module test_mod\n"
+        "    type, abstract, private :: test_type\n"
+        "      integer :: i = 1\n"
+        "      contains\n"
+        "      procedure, nopass :: test_code\n"
+        "    end type test_type\n"
+        "    contains\n"
+        "    subroutine test_code()\n"
+        "    end subroutine\n"
+        "end module test_mod\n")
+    psyir = fortran_reader.psyir_from_source(test_code)
+    sym_table = psyir.children[0].symbol_table
+    symbol = sym_table.lookup("test_type")
+    assert isinstance(symbol, DataTypeSymbol)
+    assert isinstance(symbol.datatype, UnknownFortranType)
+    result = fortran_writer(psyir)
+    assert (
+        "  type, abstract, private :: test_type\n"
+        "  INTEGER :: i = 1\n"
+        "  CONTAINS\n"
+        "  PROCEDURE, NOPASS :: test_code\n"
+        "END TYPE test_type\n" in result)
+
+
+def test_structures_constants(fortran_reader, fortran_writer):
+    '''Test that Fparser2Reader parses Fortran types correctly when there
+    is a type declaration with one of the members being initialised
+    with constants that are declared outside of the type.
+
+    '''
+    test_code = (
+        "module test_mod\n"
+        "    integer, parameter :: N = 1, M = 2\n"
+        "    type, private :: my_type\n"
+        "      integer :: i = N + M\n"
+        "      integer :: j\n"
+        "    end type my_type\n"
+        "end module test_mod\n")
+    psyir = fortran_reader.psyir_from_source(test_code)
+    sym_table = psyir.children[0].symbol_table
+    n_symbol = sym_table.lookup("n")
+    assert isinstance(n_symbol.interface, StaticInterface)
+    m_symbol = sym_table.lookup("m")
+    assert isinstance(m_symbol.interface, StaticInterface)
+    symbol = sym_table.lookup("my_type")
+    assert isinstance(symbol, DataTypeSymbol)
+    assert isinstance(symbol.datatype, StructureType)
+    i_symbol = symbol.datatype.lookup("i")
+    n_reference = i_symbol.initial_value.children[0]
+    assert n_reference.symbol is n_symbol
+    m_reference = i_symbol.initial_value.children[1]
+    assert m_reference.symbol is m_symbol
+    result = fortran_writer(psyir)
+    assert ("  integer, parameter, public :: N = 1\n"
+            "  integer, parameter, public :: M = 2\n"
+            "  type, private :: my_type\n"
+            "    integer, public :: i = N + M\n"
+            "    integer, public :: j\n"
+            "  end type my_type\n" in result)
+
+
+def test_structures_constant_scope(fortran_reader, fortran_writer):
+    '''Test that Fparser2Reader parses Fortran types correctly when there
+    is a type declaration with one of the members being initialised
+    with constants that are declared outside of the type within a
+    different symbol table.
+
+    '''
+    test_code = (
+        "module test_mod\n"
+        "  integer, parameter :: N = 1, M = 2\n"
+        "  contains\n"
+        "  subroutine test_code()\n"
+        "    type, private :: my_type\n"
+        "      integer :: i = N + M\n"
+        "      integer :: j\n"
+        "    end type my_type\n"
+        "  end subroutine\n"
+        "end module test_mod\n")
+    psyir = fortran_reader.psyir_from_source(test_code)
+    sym_table = psyir.children[0].symbol_table
+    n_symbol = sym_table.lookup("n")
+    assert isinstance(n_symbol.interface, StaticInterface)
+    m_symbol = sym_table.lookup("m")
+    assert isinstance(m_symbol.interface, StaticInterface)
+    sym_table = psyir.children[0].children[0].symbol_table
+    symbol = sym_table.lookup("my_type")
+    assert isinstance(symbol, DataTypeSymbol)
+    assert isinstance(symbol.datatype, StructureType)
+    i_symbol = symbol.datatype.lookup("i")
+    n_reference = i_symbol.initial_value.children[0]
+    assert n_reference.symbol is n_symbol
+    m_reference = i_symbol.initial_value.children[1]
+    assert m_reference.symbol is m_symbol
+    result = fortran_writer(psyir)
+    assert (
+        "module test_mod\n"
+        "  implicit none\n"
+        "  integer, parameter, public :: n = 1\n"
+        "  integer, parameter, public :: m = 2\n"
+        "  public\n\n"
+        "  contains\n"
+        "  subroutine test_code()\n"
+        "    type :: my_type\n"
+        "      integer :: i = n + m\n"
+        "      integer :: j\n"
+        "    end type my_type\n\n\n"
+        "  end subroutine test_code\n\n"
+        "end module test_mod" in result)
+
+
+def test_structures_constant_use(fortran_reader, fortran_writer):
+    '''Test that Fparser2Reader parses Fortran types correctly when there
+    is a type declaration with one of the members being initialised
+    with constants that are declared outside of the type within a
+    different symbol table and there is a wildcard use statement.
+
+    '''
+    test_code = (
+        "module test_mod\n"
+        "  use wildcard\n"
+        "  contains\n"
+        "  subroutine test_code()\n"
+        "    integer, parameter :: N = 1, M = 2\n"
+        "    type :: my_type\n"
+        "      integer :: i = N + M\n"
+        "      integer :: j\n"
+        "    end type my_type\n"
+        "  end subroutine\n"
+        "end module test_mod\n")
+    psyir = fortran_reader.psyir_from_source(test_code)
+    sym_table = psyir.children[0].children[0].symbol_table
+    n_symbol = sym_table.lookup("n")
+    assert isinstance(n_symbol.interface, StaticInterface)
+    m_symbol = sym_table.lookup("m")
+    assert isinstance(m_symbol.interface, StaticInterface)
+    symbol = sym_table.lookup("my_type")
+    assert isinstance(symbol, DataTypeSymbol)
+    assert isinstance(symbol.datatype, StructureType)
+    i_symbol = symbol.datatype.lookup("i")
+    n_reference = i_symbol.initial_value.children[0]
+    assert n_reference.symbol is n_symbol
+    m_reference = i_symbol.initial_value.children[1]
+    assert m_reference.symbol is m_symbol
+    result = fortran_writer(psyir)
+    assert (
+        "    integer, parameter :: N = 1\n"
+        "    integer, parameter :: M = 2\n"
+        "    type :: my_type\n"
+        "      integer :: i = N + M\n"
+        "      integer :: j\n"
+        "    end type my_type\n" in result)
diff --git a/src/psyclone/tests/psyir/frontend/fparser2_where_handler_test.py b/src/psyclone/tests/psyir/frontend/fparser2_where_handler_test.py
index 23df20107..ff2122370 100644
--- a/src/psyclone/tests/psyir/frontend/fparser2_where_handler_test.py
+++ b/src/psyclone/tests/psyir/frontend/fparser2_where_handler_test.py
@@ -304,13 +304,13 @@ def test_where_within_loop(fortran_reader):
     # Check that we have symbols for the two arrays
     mymod = psyir.children[0]
     assert isinstance(mymod, Container)
-    assert "var" in mymod.symbol_table
-    assert "var2" in mymod.symbol_table
-    assert isinstance(mymod.symbol_table.lookup("var").interface,
+    sub = mymod.children[0]
+    assert "var" in sub.symbol_table
+    assert "var2" in sub.symbol_table
+    assert isinstance(sub.symbol_table.lookup("var").interface,
                       UnresolvedInterface)
-    assert isinstance(mymod.symbol_table.lookup("var2").interface,
+    assert isinstance(sub.symbol_table.lookup("var2").interface,
                       UnresolvedInterface)
-    sub = mymod.children[0]
     assert isinstance(sub, Routine)
     assert isinstance(sub[0], Loop)
     assert sub[0].variable.name == "jl"
diff --git a/src/psyclone/tests/psyir/nodes/array_mixin_test.py b/src/psyclone/tests/psyir/nodes/array_mixin_test.py
index 41a1e43a0..b8aa050f2 100644
--- a/src/psyclone/tests/psyir/nodes/array_mixin_test.py
+++ b/src/psyclone/tests/psyir/nodes/array_mixin_test.py
@@ -379,7 +379,7 @@ def test_aref_to_aos_bound_expression():
 
     '''
     sgrid_type = StructureType.create(
-        [("ID", INTEGER_TYPE, Symbol.Visibility.PUBLIC)])
+        [("ID", INTEGER_TYPE, Symbol.Visibility.PUBLIC, None)])
     sgrid_type_sym = DataTypeSymbol("subgrid_type", sgrid_type)
     sym = DataSymbol("subgrids", ArrayType(sgrid_type_sym, [(3, 10)]))
     lbound = IntrinsicCall.create(IntrinsicCall.Intrinsic.LBOUND,
@@ -432,14 +432,14 @@ def test_member_get_bound_expression(fortran_writer):
     a2d = ArrayType(REAL_TYPE, [2, (2, 8)])
     # Structure that contains "map" which is a 2D array.
     stypedef = StructureType.create(
-        [("map", a2d, Symbol.Visibility.PUBLIC)])
+        [("map", a2d, Symbol.Visibility.PUBLIC, None)])
     stypedefsym = DataTypeSymbol("map_type", stypedef)
     # Structure containing a structure of stypedef and an array of such
     # structures.
     stypedef2 = StructureType.create(
-        [("grid", stypedef, Symbol.Visibility.PUBLIC),
+        [("grid", stypedef, Symbol.Visibility.PUBLIC, None),
          ("subgrids", ArrayType(stypedefsym, [3, (2, 6)]),
-          Symbol.Visibility.PUBLIC)])
+          Symbol.Visibility.PUBLIC, None)])
     ssym = DataSymbol("var", stypedef2)
     sref = StructureReference.create(ssym,
                                      ["grid",
diff --git a/src/psyclone/tests/psyir/nodes/array_of_structures_reference_test.py b/src/psyclone/tests/psyir/nodes/array_of_structures_reference_test.py
index 436d921e4..9b0a8c782 100644
--- a/src/psyclone/tests/psyir/nodes/array_of_structures_reference_test.py
+++ b/src/psyclone/tests/psyir/nodes/array_of_structures_reference_test.py
@@ -65,11 +65,13 @@ def make_component_symbol():
 
     '''
     region_type = symbols.StructureType.create([
-        ("startx", symbols.INTEGER_TYPE, symbols.Symbol.Visibility.PUBLIC)])
+        ("startx", symbols.INTEGER_TYPE, symbols.Symbol.Visibility.PUBLIC,
+         None)])
     region_type_symbol = symbols.DataTypeSymbol("region_type", region_type)
     grid_type = symbols.StructureType.create([
-        ("nx", symbols.INTEGER_TYPE, symbols.Symbol.Visibility.PUBLIC),
-        ("region", region_type_symbol, symbols.Symbol.Visibility.PUBLIC)])
+        ("nx", symbols.INTEGER_TYPE, symbols.Symbol.Visibility.PUBLIC, None),
+        ("region", region_type_symbol, symbols.Symbol.Visibility.PUBLIC,
+         None)])
     grid_type_symbol = symbols.DataTypeSymbol("grid_type", grid_type)
     grid_array_type = symbols.ArrayType(grid_type_symbol, [5])
     ssym = symbols.DataSymbol("grid", grid_array_type)
@@ -160,7 +162,8 @@ def test_ast_str():
     ''' Test that the __str__ method of the StructureReference class works OK
     when we have an ArrayOfStructuresReference. '''
     grid_type = symbols.StructureType.create([
-        ("nx", symbols.INTEGER_TYPE, symbols.Symbol.Visibility.PUBLIC)])
+        ("nx", symbols.INTEGER_TYPE, symbols.Symbol.Visibility.PUBLIC,
+         None)])
     grid_type_symbol = symbols.DataTypeSymbol("grid_type", grid_type)
     grid_array_type = symbols.ArrayType(grid_type_symbol, [5])
     ssym = symbols.DataSymbol("grid", grid_array_type)
@@ -175,7 +178,8 @@ def test_ast_is_array():
     ''' Test that an ArrayOfStructuresReference is marked as being an array.
     '''
     grid_type = symbols.StructureType.create([
-        ("nx", symbols.INTEGER_TYPE, symbols.Symbol.Visibility.PUBLIC)])
+        ("nx", symbols.INTEGER_TYPE, symbols.Symbol.Visibility.PUBLIC,
+         None)])
     grid_type_symbol = symbols.DataTypeSymbol("grid_type", grid_type)
     grid_array_type = symbols.ArrayType(grid_type_symbol, [5])
     ssym = symbols.DataSymbol("grid", grid_array_type)
@@ -195,8 +199,8 @@ def test_asr_datatype():
     atype = symbols.ArrayType(symbols.REAL_TYPE,
                               [nodes.Reference(ndofs), nodes.Reference(ndofs)])
     grid_type = symbols.StructureType.create([
-        ("nx", symbols.INTEGER_TYPE, symbols.Symbol.Visibility.PUBLIC),
-        ("data", atype, symbols.Symbol.Visibility.PUBLIC)])
+        ("nx", symbols.INTEGER_TYPE, symbols.Symbol.Visibility.PUBLIC, None),
+        ("data", atype, symbols.Symbol.Visibility.PUBLIC, None)])
     grid_type_symbol = symbols.DataTypeSymbol("grid_type", grid_type)
     grid_array_type = symbols.ArrayType(grid_type_symbol, [5])
     ssym = symbols.DataSymbol("grid", grid_array_type)
diff --git a/src/psyclone/tests/psyir/nodes/assignment_test.py b/src/psyclone/tests/psyir/nodes/assignment_test.py
index 1a62cd61b..474307e35 100644
--- a/src/psyclone/tests/psyir/nodes/assignment_test.py
+++ b/src/psyclone/tests/psyir/nodes/assignment_test.py
@@ -153,14 +153,15 @@ def test_is_array_assignment():
 
     # Check when lhs consists of various forms of structure access
     grid_type = StructureType.create([
-        ("dx", REAL_SINGLE_TYPE, Symbol.Visibility.PUBLIC),
-        ("dy", REAL_SINGLE_TYPE, Symbol.Visibility.PUBLIC)])
+        ("dx", REAL_SINGLE_TYPE, Symbol.Visibility.PUBLIC, None),
+        ("dy", REAL_SINGLE_TYPE, Symbol.Visibility.PUBLIC, None)])
     grid_type_symbol = DataTypeSymbol("grid_type", grid_type)
     # Create the definition of the 'field_type', contains array of grid_types
     field_type_def = StructureType.create(
-        [("data", ArrayType(REAL_SINGLE_TYPE, [10]), Symbol.Visibility.PUBLIC),
+        [("data", ArrayType(REAL_SINGLE_TYPE, [10]), Symbol.Visibility.PUBLIC,
+          None),
          ("sub_meshes", ArrayType(grid_type_symbol, [3]),
-          Symbol.Visibility.PUBLIC)])
+          Symbol.Visibility.PUBLIC, None)])
     field_type_symbol = DataTypeSymbol("field_type", field_type_def)
     field_symbol = DataSymbol("wind", field_type_symbol)
 
@@ -310,8 +311,8 @@ def test_is_not_array_assignment():
 
     # lhs is a scalar member of a structure
     grid_type = StructureType.create([
-        ("dx", REAL_SINGLE_TYPE, Symbol.Visibility.PUBLIC),
-        ("dy", REAL_SINGLE_TYPE, Symbol.Visibility.PUBLIC)])
+        ("dx", REAL_SINGLE_TYPE, Symbol.Visibility.PUBLIC, None),
+        ("dy", REAL_SINGLE_TYPE, Symbol.Visibility.PUBLIC, None)])
     grid_type_symbol = DataTypeSymbol("grid_type", grid_type)
     grid_sym = DataSymbol("grid", grid_type_symbol)
     assignment = Assignment.create(StructureReference.create(grid_sym, ["dx"]),
diff --git a/src/psyclone/tests/psyir/nodes/loop_test.py b/src/psyclone/tests/psyir/nodes/loop_test.py
index 2d87a2795..a2d25b39a 100644
--- a/src/psyclone/tests/psyir/nodes/loop_test.py
+++ b/src/psyclone/tests/psyir/nodes/loop_test.py
@@ -390,13 +390,13 @@ def test_check_variable():
     array_symbol = DataSymbol("my_array", array_type)
     with pytest.raises(GenerationError) as info:
         Loop._check_variable(array_symbol)
-    assert ("variable property in Loop class should be a ScalarType but "
+    assert ("variable 'my_array' in Loop class should be a ScalarType but "
             "found 'ArrayType'." in str(info.value))
 
     scalar_symbol = DataSymbol("my_array", REAL_TYPE)
     with pytest.raises(GenerationError) as info:
         Loop._check_variable(scalar_symbol)
-    assert ("variable property in Loop class should be a scalar integer but "
+    assert ("variable 'my_array' in Loop class should be a scalar integer but "
             "found 'REAL'." in str(info.value))
 
     scalar_symbol = DataSymbol("my_array", INTEGER_TYPE)
diff --git a/src/psyclone/tests/psyir/nodes/structure_member_test.py b/src/psyclone/tests/psyir/nodes/structure_member_test.py
index caedc213a..d7408999b 100644
--- a/src/psyclone/tests/psyir/nodes/structure_member_test.py
+++ b/src/psyclone/tests/psyir/nodes/structure_member_test.py
@@ -1,7 +1,7 @@
 # -----------------------------------------------------------------------------
 # BSD 3-Clause License
 #
-# Copyright (c) 2020-2022, Science and Technology Facilities Council.
+# Copyright (c) 2020-2023, Science and Technology Facilities Council.
 # All rights reserved.
 #
 # Redistribution and use in source and binary forms, with or without
@@ -59,16 +59,16 @@ def create_structure_symbol(table):
 
     '''
     region_type = symbols.StructureType.create([
-        ("nx", symbols.INTEGER_TYPE, symbols.Symbol.Visibility.PUBLIC),
-        ("ny", symbols.INTEGER_TYPE, symbols.Symbol.Visibility.PUBLIC),
+        ("nx", symbols.INTEGER_TYPE, symbols.Symbol.Visibility.PUBLIC, None),
+        ("ny", symbols.INTEGER_TYPE, symbols.Symbol.Visibility.PUBLIC, None),
         ("domain", symbols.DataTypeSymbol("dom_type", symbols.DeferredType()),
-         symbols.Symbol.Visibility.PUBLIC)])
+         symbols.Symbol.Visibility.PUBLIC, None)])
     region_type_sym = symbols.DataTypeSymbol("grid_type", region_type)
     region_array_type = symbols.ArrayType(region_type_sym, [2, 2])
     grid_type = symbols.StructureType.create([
-        ("dx", symbols.INTEGER_TYPE, symbols.Symbol.Visibility.PUBLIC),
-        ("area", region_type_sym, symbols.Symbol.Visibility.PUBLIC),
-        ("levels", region_array_type, symbols.Symbol.Visibility.PUBLIC)])
+        ("dx", symbols.INTEGER_TYPE, symbols.Symbol.Visibility.PUBLIC, None),
+        ("area", region_type_sym, symbols.Symbol.Visibility.PUBLIC, None),
+        ("levels", region_array_type, symbols.Symbol.Visibility.PUBLIC, None)])
     grid_type_sym = symbols.DataTypeSymbol("grid_type", grid_type)
     grid_var = symbols.DataSymbol("grid", grid_type_sym)
     table.add(grid_type_sym)
diff --git a/src/psyclone/tests/psyir/nodes/structure_reference_test.py b/src/psyclone/tests/psyir/nodes/structure_reference_test.py
index b9189c8fb..257984e7c 100644
--- a/src/psyclone/tests/psyir/nodes/structure_reference_test.py
+++ b/src/psyclone/tests/psyir/nodes/structure_reference_test.py
@@ -63,15 +63,17 @@ def test_struc_ref_init():
 def test_struc_ref_create():
     ''' Tests for the create method. '''
     region_type = symbols.StructureType.create([
-        ("startx", symbols.INTEGER_TYPE, symbols.Symbol.Visibility.PUBLIC)])
+        ("startx", symbols.INTEGER_TYPE, symbols.Symbol.Visibility.PUBLIC,
+         None)])
     region_type_symbol = symbols.DataTypeSymbol("region_type", region_type)
     grid_type = symbols.StructureType.create([
-        ("nx", symbols.INTEGER_TYPE, symbols.Symbol.Visibility.PUBLIC),
-        ("region", region_type_symbol, symbols.Symbol.Visibility.PRIVATE),
+        ("nx", symbols.INTEGER_TYPE, symbols.Symbol.Visibility.PUBLIC, None),
+        ("region", region_type_symbol, symbols.Symbol.Visibility.PRIVATE,
+         None),
         ("sub_grids", symbols.ArrayType(region_type_symbol, [3]),
-         symbols.Symbol.Visibility.PUBLIC),
+         symbols.Symbol.Visibility.PUBLIC, None),
         ("data", symbols.ArrayType(symbols.REAL_TYPE, [10, 10]),
-         symbols.Symbol.Visibility.PUBLIC)])
+         symbols.Symbol.Visibility.PUBLIC, None)])
     grid_type_symbol = symbols.DataTypeSymbol("grid_type", grid_type)
     ssym = symbols.DataSymbol("grid", grid_type_symbol)
     # Reference to scalar member of structure
@@ -140,7 +142,8 @@ def test_struc_ref_create_errors():
     assert ("one or more structure 'members' that are being accessed but "
             "got an empty list for symbol 'grid'" in str(err.value))
     grid_type = symbols.StructureType.create([
-        ("nx", symbols.INTEGER_TYPE, symbols.Symbol.Visibility.PUBLIC)])
+        ("nx", symbols.INTEGER_TYPE, symbols.Symbol.Visibility.PUBLIC,
+         None)])
     tsymbol_known = symbols.DataTypeSymbol("grid_type", grid_type)
     with pytest.raises(TypeError) as err:
         _ = nodes.StructureReference.create(
@@ -161,7 +164,8 @@ def test_struc_ref_create_errors():
 def test_struc_ref_validate_child():
     ''' Tests for the _validate_child method. '''
     grid_type = symbols.StructureType.create([
-        ("nx", symbols.INTEGER_TYPE, symbols.Symbol.Visibility.PUBLIC)])
+        ("nx", symbols.INTEGER_TYPE, symbols.Symbol.Visibility.PUBLIC,
+         None)])
     grid_type_symbol = symbols.DataTypeSymbol("grid_type", grid_type)
     ssym = symbols.DataSymbol("grid", grid_type_symbol)
     # Reference to scalar member of structure
@@ -181,7 +185,7 @@ def test_struc_ref_validate_child():
 def test_struc_ref_str():
     ''' Test the __str__ method of StructureReference. '''
     grid_type = symbols.StructureType.create([
-        ("nx", symbols.INTEGER_TYPE, symbols.Symbol.Visibility.PUBLIC)])
+        ("nx", symbols.INTEGER_TYPE, symbols.Symbol.Visibility.PUBLIC, None)])
     grid_type_symbol = symbols.DataTypeSymbol("grid_type", grid_type)
     ssym = symbols.DataSymbol("grid", grid_type_symbol)
     # Reference to scalar member of structure
@@ -209,7 +213,7 @@ def test_reference_accesses():
 def test_struc_ref_semantic_nav():
     ''' Test the 'member' property of the StructureReference. '''
     grid_type = symbols.StructureType.create([
-        ("nx", symbols.INTEGER_TYPE, symbols.Symbol.Visibility.PUBLIC)])
+        ("nx", symbols.INTEGER_TYPE, symbols.Symbol.Visibility.PUBLIC, None)])
     grid_type_symbol = symbols.DataTypeSymbol("grid_type", grid_type)
     ssym = symbols.DataSymbol("grid", grid_type_symbol)
     # Reference to scalar member of structure
@@ -228,16 +232,17 @@ def test_struc_ref_datatype():
     '''Test the datatype() method of StructureReference.'''
     atype = symbols.ArrayType(symbols.REAL_TYPE, [10, 8])
     rtype = symbols.StructureType.create([
-        ("gibber", symbols.BOOLEAN_TYPE, symbols.Symbol.Visibility.PUBLIC)])
+        ("gibber", symbols.BOOLEAN_TYPE, symbols.Symbol.Visibility.PUBLIC,
+         None)])
     # TODO #1031. Currently cannot create an array of StructureTypes
     # directly - have to have a DataTypeSymbol.
     rtype_sym = symbols.DataTypeSymbol("gibber_type", rtype)
     artype = symbols.ArrayType(rtype_sym, [10, 3])
     grid_type = symbols.StructureType.create([
-        ("nx", symbols.INTEGER_TYPE, symbols.Symbol.Visibility.PUBLIC),
-        ("data", atype, symbols.Symbol.Visibility.PRIVATE),
-        ("roger", rtype, symbols.Symbol.Visibility.PUBLIC),
-        ("titty", artype, symbols.Symbol.Visibility.PUBLIC)])
+        ("nx", symbols.INTEGER_TYPE, symbols.Symbol.Visibility.PUBLIC, None),
+        ("data", atype, symbols.Symbol.Visibility.PRIVATE, None),
+        ("roger", rtype, symbols.Symbol.Visibility.PUBLIC, None),
+        ("titty", artype, symbols.Symbol.Visibility.PUBLIC, None)])
     # Symbol with type defined by StructureType
     ssym0 = symbols.DataSymbol("grid", grid_type)
     # Reference to scalar member of structure
@@ -305,8 +310,9 @@ def test_structure_reference_deferred_type():
         symbols.UnknownFortranType(
             "type(atype), dimension(10,8), pointer :: aptr"), [10, 8])
     grid_type = symbols.StructureType.create([
-        ("mesh", symbols.DeferredType(), symbols.Symbol.Visibility.PUBLIC),
-        ("aptr", atype, symbols.Symbol.Visibility.PUBLIC)])
+        ("mesh", symbols.DeferredType(), symbols.Symbol.Visibility.PUBLIC,
+         None),
+        ("aptr", atype, symbols.Symbol.Visibility.PUBLIC, None)])
     grid_type_symbol = symbols.DataTypeSymbol("grid_type", grid_type)
     ssym = symbols.DataSymbol("grid", grid_type_symbol)
     # Structure of deferred type
diff --git a/src/psyclone/tests/psyir/symbols/datatype_test.py b/src/psyclone/tests/psyir/symbols/datatype_test.py
index f88b9e9c0..b4be1b57e 100644
--- a/src/psyclone/tests/psyir/symbols/datatype_test.py
+++ b/src/psyclone/tests/psyir/symbols/datatype_test.py
@@ -337,7 +337,7 @@ def test_arraytype_datatypesymbol_only():
     type of StructureType. (This limitation is the subject of #1031.) '''
     with pytest.raises(NotImplementedError) as err:
         _ = ArrayType(StructureType.create(
-            [("nx", INTEGER_TYPE, Symbol.Visibility.PUBLIC)]),
+            [("nx", INTEGER_TYPE, Symbol.Visibility.PUBLIC, None)]),
                       [5])
     assert ("When creating an array of structures, the type of those "
             "structures must be supplied as a DataTypeSymbol but got a "
@@ -644,26 +644,37 @@ def test_structure_type():
     stype = StructureType()
     assert str(stype) == "StructureType<>"
     assert not stype.components
-    stype.add("flag", INTEGER_TYPE, Symbol.Visibility.PUBLIC)
+    stype.add("flag", INTEGER_TYPE, Symbol.Visibility.PUBLIC, None)
     flag = stype.lookup("flag")
+    assert not flag.initial_value
     assert isinstance(flag, StructureType.ComponentType)
+    stype.add("flag2", INTEGER_TYPE, Symbol.Visibility.PUBLIC,
+              Literal("1", INTEGER_TYPE))
+    flag2 = stype.lookup("flag2")
+    assert isinstance(flag2, StructureType.ComponentType)
+    assert flag2.initial_value.value == "1"
     with pytest.raises(TypeError) as err:
-        stype.add(1, "hello", "hello")
+        stype.add(1, "hello", "hello", None)
     assert ("name of a component of a StructureType must be a 'str' but got "
             "'int'" in str(err.value))
     with pytest.raises(TypeError) as err:
-        stype.add("hello", "hello", "hello")
+        stype.add("hello", "hello", "hello", None)
     assert ("type of a component of a StructureType must be a 'DataType' "
             "or 'DataTypeSymbol' but got 'str'" in str(err.value))
     with pytest.raises(TypeError) as err:
-        stype.add("hello", INTEGER_TYPE, "hello")
+        stype.add("hello", INTEGER_TYPE, "hello", None)
     assert ("visibility of a component of a StructureType must be an instance "
             "of 'Symbol.Visibility' but got 'str'" in str(err.value))
+    with pytest.raises(TypeError) as err:
+        stype.add("hello", INTEGER_TYPE, Symbol.Visibility.PUBLIC, "Hello")
+    assert ("The initial value of a component of a StructureType must be "
+            "None or an instance of 'DataNode', but got 'str'."
+            in str(err.value))
     with pytest.raises(KeyError):
         stype.lookup("missing")
     # Cannot have a recursive type definition
     with pytest.raises(TypeError) as err:
-        stype.add("hello", stype, Symbol.Visibility.PUBLIC)
+        stype.add("hello", stype, Symbol.Visibility.PUBLIC, None)
     assert ("attempting to add component 'hello' - a StructureType definition "
             "cannot be recursive" in str(err.value))
 
@@ -673,52 +684,65 @@ def test_create_structuretype():
     # One member will have its type defined by a DataTypeSymbol
     tsymbol = DataTypeSymbol("my_type", DeferredType())
     stype = StructureType.create([
-        ("fred", INTEGER_TYPE, Symbol.Visibility.PUBLIC),
-        ("george", REAL_TYPE, Symbol.Visibility.PRIVATE),
-        ("barry", tsymbol, Symbol.Visibility.PUBLIC)])
+        ("fred", INTEGER_TYPE, Symbol.Visibility.PUBLIC, None),
+        ("george", REAL_TYPE, Symbol.Visibility.PRIVATE,
+         Literal("1.0", REAL_TYPE)),
+        ("barry", tsymbol, Symbol.Visibility.PUBLIC, None)])
     assert len(stype.components) == 3
     george = stype.lookup("george")
     assert isinstance(george, StructureType.ComponentType)
     assert george.name == "george"
     assert george.datatype == REAL_TYPE
     assert george.visibility == Symbol.Visibility.PRIVATE
+    assert george.initial_value.value == "1.0"
     barry = stype.lookup("barry")
     assert isinstance(barry, StructureType.ComponentType)
     assert barry.datatype is tsymbol
     assert barry.visibility == Symbol.Visibility.PUBLIC
+    assert not barry.initial_value
     with pytest.raises(TypeError) as err:
         StructureType.create([
-            ("fred", INTEGER_TYPE, Symbol.Visibility.PUBLIC),
+            ("fred", INTEGER_TYPE, Symbol.Visibility.PUBLIC, None),
             ("george", Symbol.Visibility.PRIVATE)])
-    assert ("Each component must be specified using a 3-tuple of (name, "
-            "type, visibility) but found a tuple with 2 members: ("
-            "'george', " in str(err.value))
+    assert ("Each component must be specified using a 4-tuple of (name, "
+            "type, visibility, initial_value) but found a tuple with 2 "
+            "members: ('george', " in str(err.value))
 
 
 def test_structuretype_eq():
     '''Test the equality operator of StructureType.'''
     stype = StructureType.create([
-        ("nancy", INTEGER_TYPE, Symbol.Visibility.PUBLIC),
-        ("peggy", REAL_TYPE, Symbol.Visibility.PRIVATE)])
+        ("nancy", INTEGER_TYPE, Symbol.Visibility.PUBLIC, None),
+        ("peggy", REAL_TYPE, Symbol.Visibility.PRIVATE,
+         Literal("1.0", REAL_TYPE))])
     assert stype == StructureType.create([
-        ("nancy", INTEGER_TYPE, Symbol.Visibility.PUBLIC),
-        ("peggy", REAL_TYPE, Symbol.Visibility.PRIVATE)])
+        ("nancy", INTEGER_TYPE, Symbol.Visibility.PUBLIC, None),
+        ("peggy", REAL_TYPE, Symbol.Visibility.PRIVATE,
+         Literal("1.0", REAL_TYPE))])
     # Something that is not a StructureType
     assert stype != NoType()
     # Component with a different name.
     assert stype != StructureType.create([
-        ("nancy", INTEGER_TYPE, Symbol.Visibility.PUBLIC),
-        ("roger", REAL_TYPE, Symbol.Visibility.PRIVATE)])
+        ("nancy", INTEGER_TYPE, Symbol.Visibility.PUBLIC, None),
+        ("roger", REAL_TYPE, Symbol.Visibility.PRIVATE,
+         Literal("1.0", REAL_TYPE))])
     # Component with a different type.
     assert stype != StructureType.create([
-        ("nancy", INTEGER_TYPE, Symbol.Visibility.PUBLIC),
-        ("peggy", INTEGER_TYPE, Symbol.Visibility.PRIVATE)])
+        ("nancy", INTEGER_TYPE, Symbol.Visibility.PUBLIC, None),
+        ("peggy", INTEGER_TYPE, Symbol.Visibility.PRIVATE,
+         Literal("1.0", REAL_TYPE))])
     # Component with a different visibility.
     assert stype != StructureType.create([
-        ("nancy", INTEGER_TYPE, Symbol.Visibility.PUBLIC),
-        ("peggy", REAL_TYPE, Symbol.Visibility.PUBLIC)])
+        ("nancy", INTEGER_TYPE, Symbol.Visibility.PUBLIC, None),
+        ("peggy", REAL_TYPE, Symbol.Visibility.PUBLIC,
+         Literal("1.0", REAL_TYPE))])
+    # Component wth a different initialisation
+    assert stype != StructureType.create([
+        ("nancy", INTEGER_TYPE, Symbol.Visibility.PUBLIC, None),
+        ("peggy", REAL_TYPE, Symbol.Visibility.PRIVATE, None)])
     # Different number of components.
     assert stype != StructureType.create([
-        ("nancy", INTEGER_TYPE, Symbol.Visibility.PUBLIC),
-        ("peggy", REAL_TYPE, Symbol.Visibility.PRIVATE),
-        ("roger", INTEGER_TYPE, Symbol.Visibility.PUBLIC)])
+        ("nancy", INTEGER_TYPE, Symbol.Visibility.PUBLIC, None),
+        ("peggy", REAL_TYPE, Symbol.Visibility.PRIVATE,
+         Literal("1.0", REAL_TYPE)),
+        ("roger", INTEGER_TYPE, Symbol.Visibility.PUBLIC, None)])
diff --git a/src/psyclone/tests/psyir/symbols/symbol_table_test.py b/src/psyclone/tests/psyir/symbols/symbol_table_test.py
index d288adb97..e6e2f6be9 100644
--- a/src/psyclone/tests/psyir/symbols/symbol_table_test.py
+++ b/src/psyclone/tests/psyir/symbols/symbol_table_test.py
@@ -1320,7 +1320,7 @@ def test_datatypesymbols():
     sym_table = SymbolTable()
     assert sym_table.datatypesymbols == []
     region_type = StructureType.create([
-        ("startx", INTEGER_TYPE, Symbol.Visibility.PUBLIC)])
+        ("startx", INTEGER_TYPE, Symbol.Visibility.PUBLIC, None)])
     region_sym = DataTypeSymbol("region_type", region_type)
     sym_table.add(region_sym)
     # Add other symbol types
@@ -2033,6 +2033,8 @@ end module gold'''
             "CodeBlock:\nWRITE(*, *) my_var" in str(err.value))
 
 
+# resolve_imports
+
 def test_resolve_imports(fortran_reader, tmpdir, monkeypatch):
     ''' Tests that the SymbolTable resolve_imports method works as expected
     when importing symbol information from external containers and respects
@@ -2482,6 +2484,270 @@ def test_resolve_imports_parent_scope(fortran_reader, tmpdir, monkeypatch):
     assert new_sym.interface.container_symbol.name == "a_mod"
 
 
+def test_resolve_imports_from_child_symtab(
+        fortran_reader, tmpdir, monkeypatch):
+    '''Check that when an unresolved symbol is declared in a subroutine,
+    resolve imports can resolve it from a parent module as long as
+    there are no wildcard imports in the subroutine.
+
+    '''
+    # Set up include_path to import the proper modules
+    monkeypatch.setattr(Config.get(), '_include_paths', [str(tmpdir)])
+    filename = os.path.join(str(tmpdir), "a_mod.f90")
+    with open(filename, "w", encoding='UTF-8') as module:
+        module.write('''
+        module a_mod
+            integer :: some_var
+        end module a_mod
+        ''')
+    psyir = fortran_reader.psyir_from_source('''
+        module b_mod
+            use a_mod
+        contains
+          subroutine my_sub()
+            some_var = 0.0
+          end subroutine
+        end module b_mod
+        ''')
+    mod = psyir.children[0]
+    subroutine = psyir.walk(Routine)[0]
+    assert "some_var" not in mod.symbol_table
+    assert "some_var" in subroutine.symbol_table
+    symbol = subroutine.symbol_table.lookup("some_var")
+    # pylint: disable=unidiomatic-typecheck
+    assert type(symbol) is Symbol
+    mod.symbol_table.resolve_imports()
+    assert "some_var" not in subroutine.symbol_table
+    assert "some_var" in mod.symbol_table
+    symbol = mod.symbol_table.lookup("some_var")
+    # pylint: disable=unidiomatic-typecheck
+    assert type(symbol) is DataSymbol
+    assert isinstance(symbol.interface, ImportInterface)
+    assert symbol.interface.container_symbol.name == "a_mod"
+
+
+def test_resolve_imports_from_child_symtab_uft(
+        fortran_reader, tmpdir, monkeypatch):
+    '''Check that when an unresolved symbol is declared in a subroutine,
+    resolve imports can resolve it from a parent module as an
+    UnknownFortranType as long as there are no wildcard imports in the
+    subroutine.
+
+    '''
+    # Set up include_path to import the proper modules
+    monkeypatch.setattr(Config.get(), '_include_paths', [str(tmpdir)])
+    filename = os.path.join(str(tmpdir), "a_mod.f90")
+    with open(filename, "w", encoding='UTF-8') as module:
+        module.write('''
+        module a_mod
+            integer, save, pointer :: some_var
+        end module a_mod
+        ''')
+    psyir = fortran_reader.psyir_from_source('''
+        module b_mod
+            use a_mod
+        contains
+          subroutine my_sub()
+            some_var = 0.0
+          end subroutine
+        end module b_mod
+        ''')
+    mod = psyir.children[0]
+    subroutine = psyir.walk(Routine)[0]
+    assert "some_var" not in mod.symbol_table
+    assert "some_var" in subroutine.symbol_table
+    symbol = subroutine.symbol_table.lookup("some_var")
+    # pylint: disable=unidiomatic-typecheck
+    assert type(symbol) is Symbol
+    mod.symbol_table.resolve_imports()
+    assert "some_var" not in subroutine.symbol_table
+    assert "some_var" in mod.symbol_table
+    symbol = mod.symbol_table.lookup("some_var")
+    # pylint: disable=unidiomatic-typecheck
+    assert type(symbol) is DataSymbol
+    assert isinstance(symbol.datatype, UnknownFortranType)
+    assert isinstance(symbol.interface, ImportInterface)
+    assert symbol.interface.container_symbol.name == "a_mod"
+
+
+def test_resolve_imports_from_child_symtabs(
+        fortran_reader, tmpdir, monkeypatch):
+    '''Check that when an unresolved symbol is declared in more than one
+    subroutine, resolve imports can resolve it from a parent module as
+    long as there are no wildcard imports in the subroutine. We also
+    need to check that references to the new symbol still work when we
+    remove (rather than move) the original symbol.
+
+    '''
+    # Set up include_path to import the proper modules
+    monkeypatch.setattr(Config.get(), '_include_paths', [str(tmpdir)])
+    filename = os.path.join(str(tmpdir), "a_mod.f90")
+    with open(filename, "w", encoding='UTF-8') as module:
+        module.write('''
+        module a_mod
+            integer :: some_var
+        end module a_mod
+        ''')
+    psyir = fortran_reader.psyir_from_source('''
+        module b_mod
+            use a_mod
+        contains
+          subroutine my_sub1()
+            some_var = 0.0
+          end subroutine
+          subroutine my_sub2()
+            some_var = 0.0
+          end subroutine
+        end module b_mod
+        ''')
+    mod = psyir.children[0]
+    assert "some_var" not in mod.symbol_table
+    for subroutine in psyir.walk(Routine):
+        assert "some_var" in subroutine.symbol_table
+        symbol = subroutine.symbol_table.lookup("some_var")
+        # pylint: disable=unidiomatic-typecheck
+        assert type(symbol) is Symbol
+    mod.symbol_table.resolve_imports()
+    for subroutine in psyir.walk(Routine):
+        assert "some_var" not in subroutine.symbol_table
+    assert "some_var" in mod.symbol_table
+    symbol = mod.symbol_table.lookup("some_var")
+    # pylint: disable=unidiomatic-typecheck
+    assert type(symbol) is DataSymbol
+    assert isinstance(symbol.interface, ImportInterface)
+    assert symbol.interface.container_symbol.name == "a_mod"
+
+    # Check that all References to the symbol have been updated,
+    # i.e. that all References reference the new symbol. This has to
+    # be dealt with by the implementation when the symbol we want to
+    # reference already exists in the module symbol table and we want
+    # to remove the symbol from a subroutine symbol table.
+    some_var_symbol = mod.symbol_table.lookup("some_var")
+    for reference in psyir.walk(Reference):
+        assert reference.symbol is some_var_symbol
+
+
+def test_resolve_imports_from_child_symtabs_utf(
+        fortran_reader, tmpdir, monkeypatch):
+    '''Check that when an unresolved symbol is declared in more than one
+    subroutine, resolve imports can resolve it from a parent module
+    where it is declared as an UnknownFortranType, as long as there
+    are no wildcard imports in the subroutine.  We also need to check
+    that references to the new symbol still work when we remove
+    (rather than move) the original symbol.
+
+    '''
+    # Set up include_path to import the proper modules
+    monkeypatch.setattr(Config.get(), '_include_paths', [str(tmpdir)])
+    filename = os.path.join(str(tmpdir), "a_mod.f90")
+    with open(filename, "w", encoding='UTF-8') as module:
+        module.write('''
+        module a_mod
+            integer, save, pointer :: some_var
+        end module a_mod
+        ''')
+    psyir = fortran_reader.psyir_from_source('''
+        module b_mod
+            use a_mod
+        contains
+          subroutine my_sub1()
+            some_var = 0.0
+          end subroutine
+          subroutine my_sub2()
+            some_var = 0.0
+          end subroutine
+        end module b_mod
+        ''')
+    mod = psyir.children[0]
+    assert "some_var" not in mod.symbol_table
+    for subroutine in psyir.walk(Routine):
+        assert "some_var" in subroutine.symbol_table
+        symbol = subroutine.symbol_table.lookup("some_var")
+        # pylint: disable=unidiomatic-typecheck
+        assert type(symbol) is Symbol
+    mod.symbol_table.resolve_imports()
+    for subroutine in psyir.walk(Routine):
+        assert "some_var" not in subroutine.symbol_table
+    assert "some_var" in mod.symbol_table
+    symbol = mod.symbol_table.lookup("some_var")
+    # pylint: disable=unidiomatic-typecheck
+    assert type(symbol) is DataSymbol
+    assert isinstance(symbol.datatype, UnknownFortranType)
+    assert isinstance(symbol.interface, ImportInterface)
+    assert symbol.interface.container_symbol.name == "a_mod"
+
+    # Check that all References to the symbol have been updated,
+    # i.e. that all References reference the new symbol. This has to
+    # be dealt with by the implementation when the symbol we want to
+    # reference already exists in the module symbol table and we want
+    # to remove the symbol from a subroutine symbol table.
+    some_var_symbol = mod.symbol_table.lookup("some_var")
+    for reference in psyir.walk(Reference):
+        assert reference.symbol is some_var_symbol
+
+
+def test_resolve_imports_from_child_symtab_with_import(
+        fortran_reader, tmpdir, monkeypatch):
+    '''Check that when an unresolved symbol is declared in a subroutine
+    with at least one wildcard use statement resolve imports can't
+    resolve it from a parent. This shows one of the current
+    limitations of resolve_imports i.e. it should be able to be done
+    on routines as well as modules and should recurse up a hierarchy
+    of symbol tables by default or have an option to do so. At the
+    moment we end up with a symbol in the subroutine but also a
+    datasymbol with the same name in the module symbol table.
+
+    '''
+    # Set up include_path to import the proper modules
+    monkeypatch.setattr(Config.get(), '_include_paths', [str(tmpdir)])
+    filename = os.path.join(str(tmpdir), "a_mod.f90")
+    with open(filename, "w", encoding='UTF-8') as module:
+        module.write('''
+        module a_mod
+            integer :: some_var
+            integer :: rau0 = 1
+        end module a_mod
+        ''')
+    filename = os.path.join(str(tmpdir), "b_mod.f90")
+    with open(filename, "w", encoding='UTF-8') as module:
+        module.write('''
+        module b_mod
+            integer :: rau0 = 2
+        end module b_mod
+        ''')
+    psyir = fortran_reader.psyir_from_source('''
+        module c_mod
+            use a_mod
+        contains
+          subroutine my_sub()
+            use b_mod
+            some_var = rau0
+          end subroutine
+        end module c_mod
+        ''')
+    mod = psyir.children[0]
+    subroutine = mod.children[0]
+    for symbol_name in ["some_var", "rau0"]:
+        assert symbol_name not in mod.symbol_table
+        assert symbol_name in subroutine.symbol_table
+        symbol = subroutine.symbol_table.lookup(symbol_name)
+        # pylint: disable=unidiomatic-typecheck
+        assert type(symbol) is Symbol
+    mod.symbol_table.resolve_imports()
+    pytest.xfail(reason="issue #2331: Routine symbol table not checked "
+                 "with resolve_imports")
+    assert "rau0" in subroutine.symbol_table
+    assert "rau0" not in mod.symbol_table
+    data_symbol = subroutine.symbol_table.lookup("rau0")
+    # pylint: disable=unidiomatic-typecheck
+    assert type(data_symbol) is DataSymbol
+    assert "some_var" not in subroutine.symbol_table
+    assert "some_var" in mod.symbol_table
+    data_symbol = mod.symbol_table.lookup("some_var")
+    # pylint: disable=unidiomatic-typecheck
+    assert type(data_symbol) is DataSymbol
+
+
 def test_scope():
     ''' Test that the scope property returns the SymbolTable associated with
     the node. '''