Repository: stfc/PSyclone

Issue:
[psyir] Parameter initialisation lost when initialisation expression results in a CodeBlock
The following declaration:

    INTEGER, PRIVATE, PARAMETER ::       &  !: Floating point section
      sp = SELECTED_REAL_KIND( 6, 37),  &  !:  Single precision (real 4)
      dp = SELECTED_REAL_KIND(12,307),  &  !:  Double precision (real 8)
      wp = dp                              !:  Working precision

is reproduced by PSyclone as:

    integer, private, parameter :: sp = SELECTED_REAL_KIND(6, 37)
    integer, private, parameter :: dp = SELECTED_REAL_KIND(12, 307)
    integer, private :: wp

i.e. `wp` is no longer given a value (and isn't declared as a parameter either).

Discussion:

Patch:
diff --git a/changelog b/changelog
index a2892e013..96aff2908 100644
--- a/changelog
+++ b/changelog
@@ -110,6 +110,9 @@
 	36) PR #1597 towards #753. Fixes naming bugs in GOcean Algorithm
 	PSyIR.
 
+	37) PR #1565 for #1505. Fix bug with UnknownFortranTypes inside other
+	fparser frontend declarations.
+
 release 2.1.0 3rd November 2021
 
 	1) PR #1183 for #1075. Improves (tightens) the checking performed
diff --git a/src/psyclone/psyir/frontend/fparser2.py b/src/psyclone/psyir/frontend/fparser2.py
index b94d3361a..adbd44ccc 100644
--- a/src/psyclone/psyir/frontend/fparser2.py
+++ b/src/psyclone/psyir/frontend/fparser2.py
@@ -508,15 +508,18 @@ def _kind_find_or_create(name, symbol_table):
     parameter. If the supplied Symbol Table (or one of its ancestors)
     does not contain an appropriate entry then one is created. If it does
     contain a matching entry then it must be either a Symbol or a
-    DataSymbol. If it is a DataSymbol then it must have a datatype of
-    'integer' or 'deferred'. If it is deferred then the fact that we now
-    know that this Symbol represents a KIND
-    parameter means that we can change the datatype to be 'integer'.
+    DataSymbol.
+
+    If it is a DataSymbol then it must have a datatype of
+    'integer', 'deferred' or 'unknown'. If it is deferred then the fact
+    that we now know that this Symbol represents a KIND parameter means we
+    can change the datatype to be 'integer'.
+
     If the existing symbol is a generic Symbol then it is replaced with
     a new DataSymbol of type 'integer'.
 
     :param str name: the name of the variable holding the KIND value.
-    :param symbol_table: the Symbol Table associated with the code being\
+    :param symbol_table: the Symbol Table associated with the code being \
                          processed.
     :type symbol_table: :py:class:`psyclone.psyir.symbols.SymbolTable`
 
@@ -530,6 +533,7 @@ def _kind_find_or_create(name, symbol_table):
 
     '''
     lower_name = name.lower()
+
     try:
         kind_symbol = symbol_table.lookup(lower_name)
         # pylint: disable=unidiomatic-typecheck
@@ -558,10 +562,11 @@ def _kind_find_or_create(name, symbol_table):
                     "variable '{0}' used as a kind parameter but it is not"
                     "a 'deferred', 'unknown' or 'scalar integer' type.".
                     format(lower_name))
-            # A KIND parameter must be of type integer so set it here
-            # (in case it was previously 'deferred'). We don't know
-            # what precision this is so set it to the default.
-            kind_symbol.datatype = default_integer_type()
+            # A KIND parameter must be of type integer so set it here if it
+            # was previously 'deferred'. We don't know what precision this is
+            # so set it to the default.
+            if isinstance(kind_symbol.datatype, DeferredType):
+                kind_symbol.datatype = default_integer_type()
         else:
             raise TypeError(
                 "A symbol representing a kind parameter must be an "
diff --git a/src/psyclone/psyir/symbols/datasymbol.py b/src/psyclone/psyir/symbols/datasymbol.py
index fb110ab43..1505f229a 100644
--- a/src/psyclone/psyir/symbols/datasymbol.py
+++ b/src/psyclone/psyir/symbols/datasymbol.py
@@ -144,8 +144,11 @@ class DataSymbol(TypedSymbol):
             instance, or 5) the provided PSyIR expression is unsupported.
 
         '''
-        from psyclone.psyir.nodes import Node, Literal, Operation, Reference
-        from psyclone.psyir.symbols import ScalarType, ArrayType
+        # pylint: disable=import-outside-toplevel
+        from psyclone.psyir.nodes import (Node, Literal, Operation, Reference,
+                                          CodeBlock)
+        from psyclone.psyir.symbols.datatypes import ScalarType, ArrayType
+
         if new_value is not None:
             if self.is_argument:
                 raise ValueError(
@@ -161,12 +164,13 @@ class DataSymbol(TypedSymbol):
 
             if isinstance(new_value, Node):
                 for node in new_value.walk(Node):
-                    if not isinstance(node, (Literal, Operation, Reference)):
+                    if not isinstance(node, (Literal, Operation, Reference,
+                                             CodeBlock)):
                         raise ValueError(
-                            "Error setting constant value for symbol '{0}'. "
-                            "PSyIR static expressions can only contain PSyIR "
-                            "literal, operation or reference nodes but found:"
-                            " {1}".format(self.name, node))
+                            f"Error setting constant value for symbol "
+                            f"'{self.name}'. PSyIR static expressions can only"
+                            f" contain PSyIR Literal, Operation, Reference or "
+                            f"CodeBlock nodes but found: {node}")
                 self._constant_value = new_value
             else:
                 from psyclone.psyir.symbols.datatypes import TYPE_MAP_TO_PYTHON


Test patch:
diff --git a/src/psyclone/tests/psyir/frontend/fparser2_kind_params_test.py b/src/psyclone/tests/psyir/frontend/fparser2_kind_params_test.py
new file mode 100644
index 000000000..fb3e3d8fc
--- /dev/null
+++ b/src/psyclone/tests/psyir/frontend/fparser2_kind_params_test.py
@@ -0,0 +1,231 @@
+# -----------------------------------------------------------------------------
+# BSD 3-Clause License
+#
+# Copyright (c) 2017-2022, Science and Technology Facilities Council.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+#
+# * Redistributions of source code must retain the above copyright notice, this
+#   list of conditions and the following disclaimer.
+#
+# * Redistributions in binary form must reproduce the above copyright notice,
+#   this list of conditions and the following disclaimer in the documentation
+#   and/or other materials provided with the distribution.
+#
+# * Neither the name of the copyright holder nor the names of its
+#   contributors may be used to endorse or promote products derived from
+#   this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+# COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+# -----------------------------------------------------------------------------
+# Authors R. W. Ford, A. R. Porter and S. Siso, STFC Daresbury Lab
+# Modified I. Kavcic, Met Office
+# -----------------------------------------------------------------------------
+
+''' Performs py.test tests on the support for KIND parameters in the
+    fparser2 PSyIR front-end '''
+
+import pytest
+
+from fparser.common.readfortran import FortranStringReader
+from fparser.two import Fortran2003
+
+from psyclone.psyir.frontend.fparser2 import (Fparser2Reader,
+                                              _kind_find_or_create)
+from psyclone.psyir.nodes import KernelSchedule
+from psyclone.psyir.symbols import (DataSymbol, ScalarType, UnknownFortranType,
+                                    RoutineSymbol, SymbolTable, Symbol,
+                                    DeferredType)
+
+
+def process_declarations(code):
+    '''
+    Utility routine to create PSyIR for Fortran variable declarations.
+
+    :param str code: Fortran declaration statement(s)
+
+    :returns: a 2-tuple consisting of a KernelSchedule with populated Symbol \
+              Table and the parse tree for the specification part.
+    :rtype: (:py:class:`psyclone.psyir.nodes.KernelSchedule`, \
+             :py:class:`fparser.two.Fortran2003.Specification_Part`)
+    '''
+    sched = KernelSchedule("dummy_schedule")
+    processor = Fparser2Reader()
+    reader = FortranStringReader(code)
+    fparser2spec = Fortran2003.Specification_Part(reader).content
+    processor.process_declarations(sched, fparser2spec, [])
+    return sched, fparser2spec
+
+
+@pytest.mark.usefixtures("f2008_parser")
+def test_process_declarations_kind_new_param():
+    ''' Test that process_declarations handles variables declared with
+    an explicit KIND parameter that has not already been declared. Also
+    check that the matching on the variable name is not case sensitive.
+
+    '''
+    fake_parent, fp2spec = process_declarations("real(kind=wp) :: var1\n"
+                                                "real(kind=Wp) :: var2\n")
+    var1_var = fake_parent.symbol_table.lookup("var1")
+    assert isinstance(var1_var.datatype.precision, DataSymbol)
+    # Check that this has resulted in the creation of a new 'wp' symbol
+    wp_var = fake_parent.symbol_table.lookup("wp")
+    assert wp_var.datatype.intrinsic == ScalarType.Intrinsic.INTEGER
+    assert var1_var.datatype.precision is wp_var
+    # Check that, despite the difference in case, the second variable
+    # references the same 'wp' symbol.
+    var2_var = fake_parent.symbol_table.lookup("var2")
+    assert var2_var.datatype.precision is wp_var
+    # Check that we get a symbol of unknown type if the KIND expression has
+    # an unexpected structure
+    # Break the parse tree by changing Name('wp') into a str
+    fp2spec[0].items[0].items[1].items = ("(", "blah", ")")
+    # Change the variable name too to prevent a clash
+    fp2spec[0].children[2].children[0].items[0].string = "var3"
+    processor = Fparser2Reader()
+    processor.process_declarations(fake_parent, [fp2spec[0]], [])
+    sym = fake_parent.symbol_table.lookup("var3")
+    assert isinstance(sym, DataSymbol)
+    assert isinstance(sym.datatype, UnknownFortranType)
+
+
+@pytest.mark.xfail(reason="Kind parameter declarations not supported - #569")
+@pytest.mark.usefixtures("f2008_parser")
+def test_process_declarations_kind_param():
+    ''' Test that process_declarations handles the kind attribute when
+    it specifies a previously-declared symbol.
+
+    '''
+    fake_parent = KernelSchedule("dummy_schedule")
+    processor = Fparser2Reader()
+    reader = FortranStringReader("integer, parameter :: r_def = KIND(1.0D0)\n"
+                                 "real(kind=r_def) :: var2")
+    fparser2spec = Fortran2003.Specification_Part(reader)
+    processor.process_declarations(fake_parent, fparser2spec.content, [])
+    assert isinstance(fake_parent.symbol_table.lookup("var2").precision,
+                      DataSymbol)
+
+
+@pytest.mark.usefixtures("f2008_parser")
+def test_process_declarations_kind_use():
+    ''' Test that process_declarations handles the kind attribute when
+    it specifies a symbol accessed via a USE.
+
+    '''
+    fake_parent, _ = process_declarations("use kind_mod, only: r_def\n"
+                                          "real(kind=r_def) :: var2")
+    var2_var = fake_parent.symbol_table.lookup("var2")
+    assert isinstance(var2_var.datatype.precision, DataSymbol)
+    assert fake_parent.symbol_table.lookup("r_def") is \
+        var2_var.datatype.precision
+
+    # If we change the symbol_table default visibility, this is respected
+    # by new kind symbols
+    fake_parent.symbol_table.default_visibility = Symbol.Visibility.PRIVATE
+    _kind_find_or_create("i_def", fake_parent.symbol_table)
+    assert (fake_parent.symbol_table.lookup("i_def").visibility
+            is Symbol.Visibility.PRIVATE)
+
+
+@pytest.mark.usefixtures("f2008_parser")
+def test_kind_param_unknowntype():
+    ''' Check that the _kind_find_or_create() routine preserves the type of
+    a Symbol representing a kind parameter if it is of UnknownType. '''
+    symbol_table = SymbolTable()
+    wp_sym = DataSymbol("wp",
+                        UnknownFortranType("integer, parameter :: wp = dp"))
+    symbol_table.add(wp_sym)
+    kind_sym = _kind_find_or_create("wp", symbol_table)
+    assert kind_sym is wp_sym
+    assert wp_sym.datatype.declaration == "integer, parameter :: wp = dp"
+
+
+@pytest.mark.usefixtures("f2008_parser")
+def test_kind_param_deferredtype():
+    ''' Check that the _kind_find_or_create() routine changes the type of
+    a Symbol representing a kind parameter if it is of DeferredType. '''
+    symbol_table = SymbolTable()
+    wp_sym = DataSymbol("wp", DeferredType())
+    symbol_table.add(wp_sym)
+    kind_sym = _kind_find_or_create("wp", symbol_table)
+    assert kind_sym is wp_sym
+    assert wp_sym.datatype.intrinsic == ScalarType.Intrinsic.INTEGER
+
+
+@pytest.mark.usefixtures("f2008_parser")
+def test_wrong_type_kind_param():
+    ''' Check that we raise the expected error if a variable used as a KIND
+    specifier is not a DataSymbol or has already been declared with non-integer
+    type.
+
+    '''
+    fake_parent, _ = process_declarations("integer :: r_def\n"
+                                          "real(kind=r_def) :: var2")
+    r_def = fake_parent.symbol_table.lookup("r_def")
+    # Monkeypatch this DataSymbol so that it appears to be a RoutineSymbol
+    r_def.__class__ = RoutineSymbol
+    with pytest.raises(TypeError) as err:
+        _kind_find_or_create("r_def", fake_parent.symbol_table)
+    assert ("found an entry of type 'RoutineSymbol' for variable 'r_def'" in
+            str(err.value))
+    # Repeat but declare r_def as real
+    with pytest.raises(TypeError) as err:
+        process_declarations("real :: r_def\n"
+                             "real(kind=r_def) :: var2")
+    assert ("already contains a DataSymbol for variable 'r_def'" in
+            str(err.value))
+
+
+@pytest.mark.parametrize("vartype, kind, precision",
+                         [("real", "1.0d0", ScalarType.Precision.DOUBLE),
+                          ("real", "1.0D7", ScalarType.Precision.DOUBLE),
+                          ("real", "1_t_def", None),
+                          ("real", "1.0", ScalarType.Precision.UNDEFINED),
+                          ("real", "1.0E3", ScalarType.Precision.SINGLE),
+                          # 32-bit integer
+                          ("integer", "1", ScalarType.Precision.UNDEFINED),
+                          # 64-bit integer
+                          ("integer", str(1 << 31 + 4)+"_t_def", None)])
+@pytest.mark.usefixtures("f2008_parser")
+def test_process_declarations_kind_literals(vartype, kind, precision):
+    ''' Test that process_declarations handles variables declared with
+    an explicit KIND specified using a literal constant.
+
+    '''
+    fake_parent, _ = process_declarations(f"{vartype}(kind=KIND({kind})) :: "
+                                          f"var")
+    if not precision:
+        assert fake_parent.symbol_table.lookup("var").datatype.precision is \
+            fake_parent.symbol_table.lookup("t_def")
+    else:
+        assert (fake_parent.symbol_table.lookup("var").datatype.precision ==
+                precision)
+
+
+@pytest.mark.parametrize("vartype, kind",
+                         [("logical", ".false."),
+                          ("real", "-1.0D7"),
+                          ("real", "kvar"),
+                          ("real", "kvar(1)")])
+@pytest.mark.usefixtures("f2008_parser")
+def test_unsupported_kind(vartype, kind):
+    ''' Check that we get an unknown type for an unsupported kind specifier.
+        TODO #569 - add support for some/all of these.
+
+    '''
+    sched, _ = process_declarations(f"{vartype}(kind=KIND({kind})) :: var")
+    assert isinstance(sched.symbol_table.lookup("var").datatype,
+                      UnknownFortranType)
diff --git a/src/psyclone/tests/psyir/frontend/fparser2_test.py b/src/psyclone/tests/psyir/frontend/fparser2_test.py
index 2b7a3404a..1a0c27e4a 100644
--- a/src/psyclone/tests/psyir/frontend/fparser2_test.py
+++ b/src/psyclone/tests/psyir/frontend/fparser2_test.py
@@ -61,27 +61,7 @@ from psyclone.psyir.symbols import (
 from psyclone.psyir.frontend.fparser2 import Fparser2Reader, \
     _is_array_range_literal, _is_bound_full_extent, \
     _is_range_full_extent, _check_args, default_precision, \
-    default_integer_type, default_real_type, _kind_find_or_create, \
-    _first_type_match
-
-
-def process_declarations(code):
-    '''
-    Utility routine to create PSyIR for Fortran variable declarations.
-
-    :param str code: Fortran declaration statement(s)
-
-    :returns: a 2-tuple consisting of a KernelSchedule with populated Symbol \
-              Table and the parse tree for the specification part.
-    :rtype: (:py:class:`psyclone.psyir.nodes.KernelSchedule`, \
-             :py:class:`fparser.two.Fortran2003.Specification_Part`)
-    '''
-    sched = KernelSchedule("dummy_schedule")
-    processor = Fparser2Reader()
-    reader = FortranStringReader(code)
-    fparser2spec = Specification_Part(reader).content
-    processor.process_declarations(sched, fparser2spec, [])
-    return sched, fparser2spec
+    default_integer_type, default_real_type, _first_type_match
 
 
 # Tests
@@ -839,22 +819,74 @@ def test_process_unsupported_declarations(fortran_reader):
                       UnknownFortranType)
 
     # Unsupported initialisation of a parameter which comes after a valid
-    # initialisation
+    # initialisation and is then followed by another, valid initialisation
+    # which references the second one.
     reader = FortranStringReader(
-        "INTEGER, PARAMETER :: happy=1, fbsp = SELECTED_REAL_KIND( 6, 37)")
+        "INTEGER, PARAMETER :: happy=1, fbsp=SELECTED_REAL_KIND(6,37), "
+        " sad=fbsp")
     fparser2spec = Specification_Part(reader).content[0]
     processor.process_declarations(fake_parent, [fparser2spec], [])
     fbsym = fake_parent.symbol_table.lookup("fbsp")
-    assert isinstance(fbsym.datatype, UnknownFortranType)
-    assert (fbsym.datatype.declaration ==
-            "INTEGER, PARAMETER :: fbsp = SELECTED_REAL_KIND(6, 37)")
+    assert fbsym.datatype.intrinsic == ScalarType.Intrinsic.INTEGER
+    assert isinstance(fbsym.constant_value, CodeBlock)
     # The first parameter should have been handled correctly
     hsym = fake_parent.symbol_table.lookup("happy")
     assert hsym.datatype.intrinsic == ScalarType.Intrinsic.INTEGER
     assert hsym.constant_value.value == "1"
-    # The fparser2 parse tree should still be intact
-    assert ("INTEGER, PARAMETER :: happy = 1, fbsp = SELECTED_REAL_KIND(6, 37)"
-            in str(fparser2spec))
+    # As should the third
+    ssym = fake_parent.symbol_table.lookup("sad")
+    assert ssym.datatype.intrinsic == ScalarType.Intrinsic.INTEGER
+    assert isinstance(ssym.constant_value, Reference)
+    assert ssym.constant_value.symbol.name == "fbsp"
+
+
+@pytest.mark.usefixtures("f2008_parser")
+def test_unsupported_decln_initial_value(monkeypatch):
+    ''' Check that an invalid constant value for a parameter is handled
+    correctly. '''
+    fake_parent = KernelSchedule("dummy_schedule")
+    reader = FortranStringReader(
+        "INTEGER, PRIVATE, PARAMETER :: happy=1, "
+        "fbsp=SELECTED_REAL_KIND(6,37), sad=fbsp")
+    fparser2spec = Specification_Part(reader).content[0]
+    # This error condition is very difficult to trigger so we monkeypatch
+    # the DataSymbol class itself with a setter that raises a ValueError
+    # for anything other than a Literal.
+
+    class BrokenDataSymbol(DataSymbol):
+        ''' Sub-class of DataSymbol with `constant_value` setter patched
+        so that it raises a ValueError for anything other than a Literal. '''
+        @property
+        def constant_value(self):
+            return self._constant_value
+
+        @constant_value.setter
+        def constant_value(self, value):
+            if isinstance(value, Literal):
+                self._constant_value = value
+            else:
+                raise ValueError("")
+
+    # At this point the fparser2 module will already have 'DataSymbol' in
+    # its namespace (due to the imports at the top of this file) so we
+    # monkeypatch that entry.
+    # pylint: disable=import-outside-toplevel
+    from psyclone.psyir.frontend import fparser2
+    monkeypatch.setattr(fparser2, "DataSymbol", BrokenDataSymbol)
+
+    processor = Fparser2Reader()
+    processor.process_declarations(fake_parent, [fparser2spec], [])
+    hsym = fake_parent.symbol_table.lookup("happy")
+    assert hsym.datatype.intrinsic == ScalarType.Intrinsic.INTEGER
+    assert hsym.constant_value.value == "1"
+    fbsym = fake_parent.symbol_table.lookup("fbsp")
+    assert isinstance(fbsym.datatype, UnknownFortranType)
+    assert (fbsym.datatype.declaration == "INTEGER, PRIVATE, PARAMETER :: "
+            "fbsp = SELECTED_REAL_KIND(6, 37)")
+    sadsym = fake_parent.symbol_table.lookup("sad")
+    assert isinstance(sadsym.datatype, UnknownFortranType)
+    assert (sadsym.datatype.declaration == "INTEGER, PRIVATE, PARAMETER :: "
+            "sad = fbsp")
 
 
 @pytest.mark.usefixtures("f2008_parser")
@@ -888,7 +920,7 @@ def test_process_declarations_precision(precision, type_name, fort_name):
     fake_parent = KernelSchedule("dummy_schedule")
     processor = Fparser2Reader()
 
-    reader = FortranStringReader("{0}*{1} :: l1".format(fort_name, precision))
+    reader = FortranStringReader(f"{fort_name}*{precision} :: l1")
     fparser2spec = Specification_Part(reader).content[0]
     processor.process_declarations(fake_parent, [fparser2spec], [])
     l1_var = fake_parent.symbol_table.lookup("l1")
@@ -1255,143 +1287,6 @@ def test_process_declarations_intent():
         ArgumentInterface.Access.UNKNOWN
 
 
-@pytest.mark.usefixtures("f2008_parser")
-def test_process_declarations_kind_new_param():
-    ''' Test that process_declarations handles variables declared with
-    an explicit KIND parameter that has not already been declared. Also
-    check that the matching on the variable name is not case sensitive.
-
-    '''
-    fake_parent, fp2spec = process_declarations("real(kind=wp) :: var1\n"
-                                                "real(kind=Wp) :: var2\n")
-    var1_var = fake_parent.symbol_table.lookup("var1")
-    assert isinstance(var1_var.datatype.precision, DataSymbol)
-    # Check that this has resulted in the creation of a new 'wp' symbol
-    wp_var = fake_parent.symbol_table.lookup("wp")
-    assert wp_var.datatype.intrinsic == ScalarType.Intrinsic.INTEGER
-    assert var1_var.datatype.precision is wp_var
-    # Check that, despite the difference in case, the second variable
-    # references the same 'wp' symbol.
-    var2_var = fake_parent.symbol_table.lookup("var2")
-    assert var2_var.datatype.precision is wp_var
-    # Check that we get a symbol of unknown type if the KIND expression has
-    # an unexpected structure
-    # Break the parse tree by changing Name('wp') into a str
-    fp2spec[0].items[0].items[1].items = ("(", "blah", ")")
-    # Change the variable name too to prevent a clash
-    fp2spec[0].children[2].children[0].items[0].string = "var3"
-    processor = Fparser2Reader()
-    processor.process_declarations(fake_parent, [fp2spec[0]], [])
-    sym = fake_parent.symbol_table.lookup("var3")
-    assert isinstance(sym, DataSymbol)
-    assert isinstance(sym.datatype, UnknownFortranType)
-
-
-@pytest.mark.xfail(reason="Kind parameter declarations not supported - #569")
-@pytest.mark.usefixtures("f2008_parser")
-def test_process_declarations_kind_param():
-    ''' Test that process_declarations handles the kind attribute when
-    it specifies a previously-declared symbol.
-
-    '''
-    fake_parent = KernelSchedule("dummy_schedule")
-    processor = Fparser2Reader()
-    reader = FortranStringReader("integer, parameter :: r_def = KIND(1.0D0)\n"
-                                 "real(kind=r_def) :: var2")
-    fparser2spec = Specification_Part(reader)
-    processor.process_declarations(fake_parent, fparser2spec.content, [])
-    assert isinstance(fake_parent.symbol_table.lookup("var2").precision,
-                      DataSymbol)
-
-
-@pytest.mark.usefixtures("f2008_parser")
-def test_process_declarations_kind_use():
-    ''' Test that process_declarations handles the kind attribute when
-    it specifies a symbol accessed via a USE.
-
-    '''
-    fake_parent, _ = process_declarations("use kind_mod, only: r_def\n"
-                                          "real(kind=r_def) :: var2")
-    var2_var = fake_parent.symbol_table.lookup("var2")
-    assert isinstance(var2_var.datatype.precision, DataSymbol)
-    assert fake_parent.symbol_table.lookup("r_def") is \
-        var2_var.datatype.precision
-
-    # If we change the symbol_table default visibility, this is respected
-    # by new kind symbols
-    fake_parent.symbol_table.default_visibility = Symbol.Visibility.PRIVATE
-    _kind_find_or_create("i_def", fake_parent.symbol_table)
-    assert fake_parent.symbol_table.lookup("i_def").visibility \
-        is Symbol.Visibility.PRIVATE
-
-
-@pytest.mark.usefixtures("f2008_parser")
-def test_wrong_type_kind_param():
-    ''' Check that we raise the expected error if a variable used as a KIND
-    specifier is not a DataSymbol or has already been declared with non-integer
-    type.
-
-    '''
-    fake_parent, _ = process_declarations("integer :: r_def\n"
-                                          "real(kind=r_def) :: var2")
-    r_def = fake_parent.symbol_table.lookup("r_def")
-    # Monkeypatch this DataSymbol so that it appears to be a RoutineSymbol
-    r_def.__class__ = RoutineSymbol
-    with pytest.raises(TypeError) as err:
-        _kind_find_or_create("r_def", fake_parent.symbol_table)
-    assert ("found an entry of type 'RoutineSymbol' for variable 'r_def'" in
-            str(err.value))
-    # Repeat but declare r_def as real
-    with pytest.raises(TypeError) as err:
-        process_declarations("real :: r_def\n"
-                             "real(kind=r_def) :: var2")
-    assert ("already contains a DataSymbol for variable 'r_def'" in
-            str(err.value))
-
-
-@pytest.mark.parametrize("vartype, kind, precision",
-                         [("real", "1.0d0", ScalarType.Precision.DOUBLE),
-                          ("real", "1.0D7", ScalarType.Precision.DOUBLE),
-                          ("real", "1_t_def", None),
-                          ("real", "1.0", ScalarType.Precision.UNDEFINED),
-                          ("real", "1.0E3", ScalarType.Precision.SINGLE),
-                          # 32-bit integer
-                          ("integer", "1", ScalarType.Precision.UNDEFINED),
-                          # 64-bit integer
-                          ("integer", str(1 << 31 + 4)+"_t_def", None)])
-@pytest.mark.usefixtures("f2008_parser")
-def test_process_declarations_kind_literals(vartype, kind, precision):
-    ''' Test that process_declarations handles variables declared with
-    an explicit KIND specified using a literal constant.
-
-    '''
-    fake_parent, _ = process_declarations("{0}(kind=KIND({1})) :: var".
-                                          format(vartype, kind))
-    if not precision:
-        assert fake_parent.symbol_table.lookup("var").datatype.precision is \
-            fake_parent.symbol_table.lookup("t_def")
-    else:
-        assert (fake_parent.symbol_table.lookup("var").datatype.precision ==
-                precision)
-
-
-@pytest.mark.parametrize("vartype, kind",
-                         [("logical", ".false."),
-                          ("real", "-1.0D7"),
-                          ("real", "kvar"),
-                          ("real", "kvar(1)")])
-@pytest.mark.usefixtures("f2008_parser")
-def test_unsupported_kind(vartype, kind):
-    ''' Check that we get an unknown type for an unsupported kind specifier.
-        TODO #569 - add support for some/all of these.
-
-    '''
-    sched, _ = process_declarations("{0}(kind=KIND({1})) :: var".format(
-        vartype, kind))
-    assert isinstance(sched.symbol_table.lookup("var").datatype,
-                      UnknownFortranType)
-
-
 @pytest.mark.usefixtures("f2008_parser")
 def test_process_declarations_stmt_functions():
     '''Test that process_declarations method handles statement functions
@@ -2547,28 +2442,38 @@ def test_handling_case_construct():
     assert len(ifnode.else_body[0].children) == 2  # SELECT CASE ends here
 
 
-@pytest.mark.usefixtures("disable_declaration_check", "f2008_parser")
+@pytest.mark.usefixtures("f2008_parser")
 def test_case_default():
     ''' Check that the fparser2Reader handles SELECT blocks with
     a default clause.
 
-    TODO #754 fix test so that 'disable_declaration_check' fixture is not
-    required.
     '''
     case_clauses = ["CASE default\nbranch3 = 1\nbranch3 = branch3 * 2\n",
                     "CASE (label1)\nbranch1 = 1\n",
                     "CASE (label2)\nbranch2 = 1\n"]
+
+    # Create the symbols that the frontend will expect to already be
+    # present in the symbol table.
+    symbols = []
+    for idx in [1, 2, 3]:
+        symbols.append(DataSymbol(f"branch{idx}", INTEGER_TYPE))
+    for var_name in ["selector", "label1", "label2"]:
+        symbols.append(DataSymbol(var_name, INTEGER_TYPE))
+
     # Loop over the 3 possible locations for the 'default' clause
     for idx1, idx2, idx3 in [(0, 1, 2), (1, 0, 2), (1, 2, 0)]:
         fortran_text = (
-            "SELECT CASE (selector)\n"
-            "{0}{1}{2}"
-            "END SELECT\n".format(case_clauses[idx1], case_clauses[idx2],
-                                  case_clauses[idx3]))
+            f"SELECT CASE (selector)\n"
+            f"{case_clauses[idx1]}{case_clauses[idx2]}{case_clauses[idx3]}"
+            f"END SELECT\n")
         reader = FortranStringReader(fortran_text)
         fparser2case_construct = Execution_Part.match(reader)[0][0]
 
         fake_parent = Schedule()
+        # Ensure we have the necessary symbols in the symbol table.
+        for sym in symbols:
+            fake_parent.symbol_table.add(sym)
+
         processor = Fparser2Reader()
         processor.process_nodes(fake_parent, [fparser2case_construct])
         assigns = fake_parent.walk(Assignment)
diff --git a/src/psyclone/tests/psyir/symbols/datasymbol_test.py b/src/psyclone/tests/psyir/symbols/datasymbol_test.py
index 35f2dfded..2780786a6 100644
--- a/src/psyclone/tests/psyir/symbols/datasymbol_test.py
+++ b/src/psyclone/tests/psyir/symbols/datasymbol_test.py
@@ -41,12 +41,16 @@
 from __future__ import absolute_import
 import pytest
 
+from fparser.common.readfortran import FortranStringReader
+from fparser.two import Fortran2003
+
 from psyclone.psyir.symbols import DataSymbol, ContainerSymbol, \
     LocalInterface, ImportInterface, ArgumentInterface, UnresolvedInterface, \
     ScalarType, ArrayType, REAL_SINGLE_TYPE, REAL_DOUBLE_TYPE, REAL4_TYPE, \
     REAL8_TYPE, INTEGER_SINGLE_TYPE, INTEGER_DOUBLE_TYPE, INTEGER4_TYPE, \
     BOOLEAN_TYPE, CHARACTER_TYPE, DeferredType, Symbol, DataTypeSymbol
-from psyclone.psyir.nodes import Literal, Reference, BinaryOperation, Return
+from psyclone.psyir.nodes import (Literal, Reference, BinaryOperation, Return,
+                                  CodeBlock)
 
 
 def test_datasymbol_initialisation():
@@ -218,9 +222,9 @@ def test_datasymbol_constant_value_setter_invalid():
     ct_expr = Return()
     with pytest.raises(ValueError) as error:
         _ = DataSymbol('a', INTEGER_SINGLE_TYPE, constant_value=ct_expr)
-    assert "Error setting constant value for symbol 'a'. PSyIR static " \
-        "expressions can only contain PSyIR literal, operation or reference" \
-        " nodes but found:" in str(error.value)
+    assert ("Error setting constant value for symbol 'a'. PSyIR static "
+            "expressions can only contain PSyIR Literal, Operation, Reference "
+            "or CodeBlock nodes but found:" in str(error.value))
 
     with pytest.raises(ValueError) as error:
         DataSymbol('a', INTEGER_SINGLE_TYPE, interface=ArgumentInterface(),
@@ -265,6 +269,22 @@ def test_datasymbol_is_constant():
     assert sym.is_constant
 
 
+@pytest.mark.usefixtures("parser")
+def test_datasymbol_is_constant_codeblock():
+    ''' Test that a DataSymbol can have a CodeBlock as its constant value. '''
+    sym = DataSymbol('a', INTEGER_SINGLE_TYPE)
+    reader = FortranStringReader(
+        "INTEGER, PARAMETER :: a=SELECTED_REAL_KIND(6,37)")
+    fparser2spec = Fortran2003.Specification_Part(reader).children[0]
+    # We want the first child of the Initialization node in the parse tree as
+    # the basis for our CodeBlock
+    inits = Fortran2003.walk(fparser2spec, Fortran2003.Initialization)
+    cblock = CodeBlock([inits[0].children[1]], CodeBlock.Structure.EXPRESSION)
+    assert not sym.is_constant
+    sym.constant_value = cblock
+    assert sym.is_constant
+
+
 def test_datasymbol_scalar_array():
     '''Test that the DataSymbol property is_scalar returns True if the
     DataSymbol is a scalar and False if not and that the DataSymbol property