[Flang] Port test_folding.sh to Python

To enable Flang testing on Windows,
shells scripts have to be ported to Python.
The following changes have been made:
Ported `test_folding.sh` to Python;
Additional changes to the tests themselves
to use the new script.

LIBPGMATH support for testing
not available at this point.

Reviewed By: Meinersbur

Differential Revision: https://reviews.llvm.org/D108217

GitOrigin-RevId: 93c3e765ac99f6ce40cbe4340ab9967fb7593a0a
diff --git a/test/Evaluate/folding01.f90 b/test/Evaluate/folding01.f90
index fddc034..cb7a9eb 100644
--- a/test/Evaluate/folding01.f90
+++ b/test/Evaluate/folding01.f90
@@ -1,5 +1,4 @@
-! RUN: %S/test_folding.sh %s %t %flang_fc1
-! REQUIRES: shell
+! RUN: %python %S/test_folding.py %s %flang_fc1
 
 ! Test intrinsic operation folding
 
diff --git a/test/Evaluate/folding02.f90 b/test/Evaluate/folding02.f90
index 1033c4d..9b0ab2b 100644
--- a/test/Evaluate/folding02.f90
+++ b/test/Evaluate/folding02.f90
@@ -1,5 +1,5 @@
-! RUN: %S/test_folding.sh %s %t %flang_fc1
-! REQUIRES: shell
+! RUN: %python %S/test_folding.py %s %flang_fc1
+
 ! Check intrinsic function folding with host runtime library
 
 module m
diff --git a/test/Evaluate/folding03.f90 b/test/Evaluate/folding03.f90
index 47fab1a..47f8ec0 100644
--- a/test/Evaluate/folding03.f90
+++ b/test/Evaluate/folding03.f90
@@ -1,5 +1,4 @@
-! RUN: %S/test_folding.sh %s %t %flang_fc1
-! REQUIRES: shell
+! RUN: %python %S/test_folding.py %s %flang_fc1
 ! Test operation folding edge case (both expected value and messages)
 ! These tests make assumptions regarding real(4) and integer(4) extrema.
 
diff --git a/test/Evaluate/folding04.f90 b/test/Evaluate/folding04.f90
index 54d1c38..254a653 100644
--- a/test/Evaluate/folding04.f90
+++ b/test/Evaluate/folding04.f90
@@ -1,5 +1,4 @@
-! RUN: %S/test_folding.sh %s %t %flang_fc1
-! REQUIRES: shell
+! RUN: %python %S/test_folding.py %s %flang_fc1
 ! Test intrinsic function folding edge case (both expected value and messages)
 ! These tests make assumptions regarding real(4) extrema.
 
diff --git a/test/Evaluate/folding05.f90 b/test/Evaluate/folding05.f90
index b5e955a..22c0290 100644
--- a/test/Evaluate/folding05.f90
+++ b/test/Evaluate/folding05.f90
Binary files differ
diff --git a/test/Evaluate/folding06.f90 b/test/Evaluate/folding06.f90
index fe553b8..4393b3b 100644
--- a/test/Evaluate/folding06.f90
+++ b/test/Evaluate/folding06.f90
@@ -1,5 +1,4 @@
-! RUN: %S/test_folding.sh %s %t %flang_fc1
-! REQUIRES: shell
+! RUN: %python %S/test_folding.py %s %flang_fc1
 ! Test transformational intrinsic function folding
 
 module m
diff --git a/test/Evaluate/folding07.f90 b/test/Evaluate/folding07.f90
index edee483..6848a1e 100644
--- a/test/Evaluate/folding07.f90
+++ b/test/Evaluate/folding07.f90
@@ -1,5 +1,4 @@
-! RUN: %S/test_folding.sh %s %t %flang_fc1
-! REQUIRES: shell
+! RUN: %python %S/test_folding.py %s %flang_fc1
 ! Test numeric model inquiry intrinsics
 
 module m
diff --git a/test/Evaluate/folding08.f90 b/test/Evaluate/folding08.f90
index 6118e6b..c64a959 100644
--- a/test/Evaluate/folding08.f90
+++ b/test/Evaluate/folding08.f90
@@ -1,5 +1,4 @@
-! RUN: %S/test_folding.sh %s %t %flang_fc1
-! REQUIRES: shell
+! RUN: %python %S/test_folding.py %s %flang_fc1
 ! Test folding of LBOUND and UBOUND
 
 module m
diff --git a/test/Evaluate/folding09.f90 b/test/Evaluate/folding09.f90
index 6f9be6b..d78611f 100644
--- a/test/Evaluate/folding09.f90
+++ b/test/Evaluate/folding09.f90
@@ -1,5 +1,4 @@
-! RUN: %S/test_folding.sh %s %t %flang_fc1
-! REQUIRES: shell
+! RUN: %python %S/test_folding.py %s %flang_fc1
 ! Test folding of IS_CONTIGUOUS on simply contiguous items (9.5.4)
 ! When IS_CONTIGUOUS() is constant, it's .TRUE.
 
diff --git a/test/Evaluate/folding10.f90 b/test/Evaluate/folding10.f90
index c705741..651caa1 100644
--- a/test/Evaluate/folding10.f90
+++ b/test/Evaluate/folding10.f90
@@ -1,5 +1,4 @@
-! RUN: %S/test_folding.sh %s %t %flang_fc1
-! REQUIRES: shell
+! RUN: %python %S/test_folding.py %s %flang_fc1
 ! Tests folding of SHAPE(TRANSFER(...))
 
 module m
diff --git a/test/Evaluate/folding11.f90 b/test/Evaluate/folding11.f90
index 4fdb0ba..bf957c8 100644
--- a/test/Evaluate/folding11.f90
+++ b/test/Evaluate/folding11.f90
@@ -1,5 +1,4 @@
-! RUN: %S/test_folding.sh %s %t %flang_fc1
-! REQUIRES: shell
+! RUN: %python %S/test_folding.py %s %flang_fc1
 module m
   complex, parameter :: z1 = 1. + (2., 3.)
   logical, parameter :: test_z1 = z1 == (3., 3.)
diff --git a/test/Evaluate/folding12.f90 b/test/Evaluate/folding12.f90
index ac74c6c..016e692 100644
--- a/test/Evaluate/folding12.f90
+++ b/test/Evaluate/folding12.f90
@@ -1,5 +1,4 @@
-! RUN: %S/test_folding.sh %s %t %flang_fc1
-! REQUIRES: shell
+! RUN: %python %S/test_folding.py %s %flang_fc1
 ! Test folding of structure constructors
 module m1
   type parent_type
diff --git a/test/Evaluate/folding13.f90 b/test/Evaluate/folding13.f90
index 92a3009..9527acd 100644
--- a/test/Evaluate/folding13.f90
+++ b/test/Evaluate/folding13.f90
@@ -1,5 +1,4 @@
-! RUN: %S/test_folding.sh %s %t %flang_fc1
-! REQUIRES: shell
+! RUN: %python %S/test_folding.py %s %flang_fc1
 ! Test folding of array constructors with constant implied DO bounds;
 ! their indices are constant expressions and can be used as such.
 module m1
diff --git a/test/Evaluate/folding14.f90 b/test/Evaluate/folding14.f90
index a76fe23..1b94935 100644
--- a/test/Evaluate/folding14.f90
+++ b/test/Evaluate/folding14.f90
@@ -1,5 +1,4 @@
-! RUN: %S/test_folding.sh %s %t %flang_fc1
-! REQUIRES: shell
+! RUN: %python %S/test_folding.py %s %flang_fc1
 ! Test folding of isnan() extension
 module m1
   logical, parameter :: results(*) = isnan([ &
diff --git a/test/Evaluate/folding15.f90 b/test/Evaluate/folding15.f90
index 658f91f..e79cbda 100644
--- a/test/Evaluate/folding15.f90
+++ b/test/Evaluate/folding15.f90
@@ -1,5 +1,4 @@
-! RUN: %S/test_folding.sh %s %t %flang_fc1
-! REQUIRES: shell
+! RUN: %python %S/test_folding.py %s %flang_fc1
 ! Test folding of array constructors with duplicate names for the implied
 ! DO variables
 module m1
diff --git a/test/Evaluate/folding16.f90 b/test/Evaluate/folding16.f90
index a407be3..04474bd 100644
--- a/test/Evaluate/folding16.f90
+++ b/test/Evaluate/folding16.f90
@@ -1,5 +1,4 @@
-! RUN: %S/test_folding.sh %s %t %flang_fc1
-! REQUIRES: shell
+! RUN: %python %S/test_folding.py %s %flang_fc1
 ! Ensure that lower bounds are accounted for in intrinsic folding;
 ! this is a regression test for a bug in which they were not
 module m
diff --git a/test/Evaluate/folding17.f90 b/test/Evaluate/folding17.f90
index 51e4258..73ddeab 100644
--- a/test/Evaluate/folding17.f90
+++ b/test/Evaluate/folding17.f90
@@ -1,5 +1,4 @@
-! RUN: %S/test_folding.sh %s %t %flang_fc1
-! REQUIRES: shell
+! RUN: %python %S/test_folding.py %s %flang_fc1
 ! Test implementations of STORAGE_SIZE() and SIZEOF() as expression rewrites
 module m1
   type :: t1
diff --git a/test/Evaluate/folding18.f90 b/test/Evaluate/folding18.f90
index eda7004..9110689 100644
--- a/test/Evaluate/folding18.f90
+++ b/test/Evaluate/folding18.f90
@@ -1,5 +1,4 @@
-! RUN: %S/test_folding.sh %s %t %flang_fc1
-! REQUIRES: shell
+! RUN: %python %S/test_folding.py %s %flang_fc1
 ! Test implementations of IEEE inquiry functions
 module m
   use ieee_arithmetic
diff --git a/test/Evaluate/folding20.f90 b/test/Evaluate/folding20.f90
index 7d14e97..be01207 100644
--- a/test/Evaluate/folding20.f90
+++ b/test/Evaluate/folding20.f90
@@ -1,5 +1,4 @@
-! RUN: %S/test_folding.sh %s %t %flang_fc1
-! REQUIRES: shell
+! RUN: %python %S/test_folding.py %s %flang_fc1
 ! Tests reduction intrinsic function folding
 module m
   implicit none
diff --git a/test/Evaluate/folding21.f90 b/test/Evaluate/folding21.f90
index 2a7923d..8306e98 100644
--- a/test/Evaluate/folding21.f90
+++ b/test/Evaluate/folding21.f90
@@ -1,5 +1,4 @@
-! RUN: %S/test_folding.sh %s %t %flang_fc1
-! REQUIRES: shell
+! RUN: %python %S/test_folding.py %s %flang_fc1
 ! Check array sizes with varying extents, including extents where the upper
 ! bound is less than the lower bound
 module m
diff --git a/test/Evaluate/folding22.f90 b/test/Evaluate/folding22.f90
index a5f923b..3a9812c 100644
--- a/test/Evaluate/folding22.f90
+++ b/test/Evaluate/folding22.f90
@@ -1,5 +1,4 @@
-! RUN: %S/test_folding.sh %s %t %flang_fc1
-! REQUIRES: shell
+! RUN: %python %S/test_folding.py %s %flang_fc1
 
 ! Test character concatenation folding
 
diff --git a/test/Evaluate/folding23.f90 b/test/Evaluate/folding23.f90
index 405f927..f31478e 100644
--- a/test/Evaluate/folding23.f90
+++ b/test/Evaluate/folding23.f90
@@ -1,5 +1,4 @@
-! RUN: %S/test_folding.sh %s %t %flang_fc1
-! REQUIRES: shell
+! RUN: %python %S/test_folding.py %s %flang_fc1
 ! Tests folding of EOSHIFT (valid cases)
 module m
   integer, parameter :: arr(2,3) = reshape([1, 2, 3, 4, 5, 6], shape(arr))
diff --git a/test/Evaluate/folding24.f90 b/test/Evaluate/folding24.f90
index b5c429f..e101c09 100644
--- a/test/Evaluate/folding24.f90
+++ b/test/Evaluate/folding24.f90
@@ -1,5 +1,4 @@
-! RUN: %S/test_folding.sh %s %t %flang_fc1
-! REQUIRES: shell
+! RUN: %python %S/test_folding.py %s %flang_fc1
 ! Tests folding of PACK (valid cases)
 module m
   integer, parameter :: arr(2,3) = reshape([1, 2, 3, 4, 5, 6], shape(arr))
diff --git a/test/Evaluate/folding25.f90 b/test/Evaluate/folding25.f90
index a94565f..dd01880 100644
--- a/test/Evaluate/folding25.f90
+++ b/test/Evaluate/folding25.f90
@@ -1,5 +1,4 @@
-! RUN: %S/test_folding.sh %s %t %flang_fc1
-! REQUIRES: shell
+! RUN: %python %S/test_folding.py %s %flang_fc1
 ! Tests folding of UNPACK (valid cases)
 module m
   integer, parameter :: vector(*) = [1, 2, 3, 4]
diff --git a/test/Evaluate/folding26.f90 b/test/Evaluate/folding26.f90
index 09fd08a..0fb5f91 100644
--- a/test/Evaluate/folding26.f90
+++ b/test/Evaluate/folding26.f90
@@ -1,5 +1,4 @@
-! RUN: %S/test_folding.sh %s %t %flang_fc1
-! REQUIRES: shell
+! RUN: %python %S/test_folding.py %s %flang_fc1
 ! Tests folding of TRANSPOSE
 module m
   integer, parameter :: matrix(0:1,0:2) = reshape([1,2,3,4,5,6],shape(matrix))
diff --git a/test/Evaluate/folding27.f90 b/test/Evaluate/folding27.f90
index 4497396..0d3d333 100644
--- a/test/Evaluate/folding27.f90
+++ b/test/Evaluate/folding27.f90
@@ -1,5 +1,4 @@
-! RUN: %S/test_folding.sh %s %t %flang_fc1
-! REQUIRES: shell
+! RUN: %python %S/test_folding.py %s %flang_fc1
 ! Tests folding of CSHIFT (valid cases)
 module m
   integer, parameter :: arr(2,3) = reshape([1, 2, 3, 4, 5, 6], shape(arr))
diff --git a/test/Evaluate/test_folding.py b/test/Evaluate/test_folding.py
new file mode 100755
index 0000000..ac1c8e5
--- /dev/null
+++ b/test/Evaluate/test_folding.py
@@ -0,0 +1,155 @@
+#!/usr/bin/env python3
+
+"""This script verifies expression folding.
+It compiles a source file with '-fdebug-dump-symbols'
+and looks for parameter declarations to check
+they have been folded as expected.
+To check folding of an expression EXPR,
+the fortran program passed to this script
+must contain the following:
+
+  logical, parameter :: test_x = <compare EXPR to expected value>
+
+This script will test that all parameter
+with a name starting with "test_"
+have been folded to .true.
+For instance, acos folding can be tested with:
+
+  real(4), parameter :: res_acos = acos(0.5_4)
+  real(4), parameter :: exp_acos = 1.047
+  logical, parameter :: test_acos = abs(res_acos - exp_acos).LE.(0.001_4)
+
+There are two kinds of failure:
+  - test_x is folded to .false..
+    This means the expression was folded
+    but the value is not as expected.
+  - test_x is not folded (it is neither .true. nor .false.).
+    This means the compiler could not fold the expression.
+
+Parameters:
+    sys.argv[1]: a source file with contains the input and expected output
+    sys.argv[2]: the Flang frontend driver
+    sys.argv[3:]: Optional arguments to the Flang frontend driver"""
+
+import os
+import sys
+import tempfile
+import re
+import subprocess
+
+from difflib import unified_diff
+from pathlib import Path
+
+def check_args(args):
+    """Verifies that the number is arguments passed is correct."""
+    if len(args) < 3:
+        print(f"Usage: {args[0]} <fortran-source> <flang-command>")
+        sys.exit(1)
+
+def set_source(source):
+    """Sets the path to the source files."""
+    if not Path(source).is_file():
+        print(f"File not found: {src}")
+        sys.exit(1)
+    return Path(source)
+
+def set_executable(exe):
+    """Sets the path to the Flang frontend driver."""
+    if not Path(exe).is_file():
+        print(f"Flang was not found: {exe}")
+        sys.exit(1)
+    return str(Path(exe))
+
+check_args(sys.argv)
+cwd = os.getcwd()
+srcdir = set_source(sys.argv[1]).resolve()
+with open(srcdir, 'r', encoding="utf-8") as f:
+    src = f.readlines()
+src1 = ""
+src2 = ""
+src3 = ""
+src4 = ""
+messages = ""
+actual_warnings = ""
+expected_warnings = ""
+warning_diffs = ""
+
+flang_fc1 = set_executable(sys.argv[2])
+flang_fc1_args = sys.argv[3:]
+flang_fc1_options = ""
+LIBPGMATH = os.getenv('LIBPGMATH')
+if LIBPGMATH:
+    flang_fc1_options = ["-fdebug-dump-symbols", "-DTEST_LIBPGMATH"]
+    print("Assuming libpgmath support")
+else:
+    flang_fc1_options = ["-fdebug-dump-symbols"]
+    print("Not assuming libpgmath support")
+
+cmd = [flang_fc1, *flang_fc1_args, *flang_fc1_options, str(srcdir)]
+with tempfile.TemporaryDirectory() as tmpdir:
+    proc = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE,
+                          check=True, universal_newlines=True, cwd=tmpdir)
+    src1 = proc.stdout
+    messages = proc.stderr
+
+for line in src1.split("\n"):
+    m = re.search(r"(\w*)(?=, PARAMETER).*init:(.*)", line)
+    if m:
+        src2 += f"{m.group(1)} {m.group(2)}\n"
+
+for line in src2.split("\n"):
+    m = re.match(r"test_*", line)
+    if m:
+        src3 += f"{m.string}\n"
+
+for passed_results, line in enumerate(src3.split("\n")):
+    m = re.search(r"\.false\._.$", line)
+    if m:
+        src4 += f"{line}\n"
+
+for line in messages.split("\n"):
+    m = re.search(r"[^:]*:(\d*):\d*: (.*)", line)
+    if m:
+        actual_warnings += f"{m.group(1)}: {m.group(2)}\n"
+
+passed_warnings = 0
+warnings = []
+for i, line in enumerate(src, 1):
+    m = re.search(r"(?:!WARN:)(.*)", line)
+    if m:
+        warnings.append(m.group(1))
+        continue
+    if warnings:
+        for x in warnings:
+            passed_warnings += 1
+            expected_warnings += f"{i}:{x}\n"
+        warnings = []
+
+for line in unified_diff(actual_warnings.split("\n"),
+                         expected_warnings.split("\n"), n=0):
+    line = re.sub(r"(^\-)(\d+:)", r"\nactual at \g<2>", line)
+    line = re.sub(r"(^\+)(\d+:)", r"\nexpect at \g<2>", line)
+    warning_diffs += line
+
+if src4 or warning_diffs:
+    print("Folding test failed:")
+    # Prints failed tests, including parameters with the same
+    # suffix so that more information can be obtained by declaring
+    # expected_x and result_x
+    if src4:
+        for line in src4.split("\n"):
+            m = re.match(r"test_(\w+)", line)
+            if m:
+                for line in src2.split("\n"):
+                    if m.group(1) in line:
+                        print(line)
+    if warning_diffs:
+        print(warning_diffs)
+    print()
+    print("FAIL")
+    sys.exit(1)
+else:
+    print()
+    print(f"All {passed_results+passed_warnings} tests passed")
+    print("PASS")
+
diff --git a/test/Evaluate/test_folding.sh b/test/Evaluate/test_folding.sh
deleted file mode 100755
index 74ca9c8..0000000
--- a/test/Evaluate/test_folding.sh
+++ /dev/null
@@ -1,105 +0,0 @@
-#!/usr/bin/env bash
-# This script verifies expression folding.
-# It compiles a source file with '-fdebug-dump-symbols' and looks for
-# parameter declarations to check they have been folded as expected.
-# To check folding of an expression EXPR, the fortran program passed to this script
-# must contain the following:
-#   logical, parameter :: test_x = <compare EXPR to expected value>
-# This script will test that all parameter with a name starting with "test_"
-# have been folded to .true.
-# For instance, acos folding can be tested with:
-#
-#   real(4), parameter :: res_acos = acos(0.5_4)
-#   real(4), parameter :: exp_acos = 1.047
-#   logical, parameter :: test_acos = abs(res_acos - exp_acos).LE.(0.001_4)
-#
-# There are two kinds of failure:
-#   - test_x is folded to .false.. This means the expression was folded
-#     but the value is not as expected.
-#   - test_x is not folded (it is neither .true. nor .false.). This means the
-#     compiler could not fold the expression.
-
-if [[ $# < 3 ]]; then
-  echo "Usage: $0 <fortran-source> <temp test dir> <f18-executable>"
-  exit 1
-fi
-
-src=$1
-[[ ! -f $src ]] && echo "File not found: $src" && exit 1
-shift
-
-temp=$1
-mkdir -p $temp
-shift
-
-CMD="$* -fdebug-dump-symbols"
-
-# Check if tests should assume folding is using libpgmath
-if [[ $LIBPGMATH ]]; then
-  CMD="$CMD -DTEST_LIBPGMATH"
-  echo "Assuming libpgmath support"
-else
-  echo "Not assuming libpgmath support"
-fi
-
-src1=$temp/symbols.log
-src2=$temp/all_parameters.log
-src3=$temp/tested_parameters.log
-src4=$temp/failures.log
-messages=$temp/messages.log
-actual_warnings=$temp/actwarnings.log
-expected_warnings=$temp/expwarnings.log
-warning_diffs=$temp/warnings.diff
-
-if ! ( cd $temp; $CMD $src ) > $src1 2> $messages # compile, dumping symbols
-then
-  cat $messages
-  echo FAIL compilation
-  exit 1
-fi
-
-# Get all PARAMETER declarations
-sed -e '/, PARAMETER/!d' -e 's/, PARAMETER.*init:/ /' \
-   -e 's/^ *//'  $src1 > $src2
-
-# Collect test results
-sed -e '/^test_/!d' $src2 > $src3
-
-# Check all tests results (keep tests that do not resolve to true)
-sed -e '/\.true\._.$/d' $src3 > $src4
-
-
-#Check warnings
-sed -n 's=^[^:]*:\([0-9]*\):[0-9]*: =\1: =p' $messages > $actual_warnings
-
-awk '
-  BEGIN { FS = "!WARN: "; }
-  /^ *!WARN: / { warnings[nwarnings++] = $2; next; }
-  { for (i = 0; i < nwarnings; ++i) printf "%d: %s\n", NR, warnings[i]; nwarnings = 0; }
-' $src > $expected_warnings
-
-diff -U0 $actual_warnings $expected_warnings > $warning_diffs
-
-if [ -s $src4 ] || [ -s $warning_diffs ]; then
-  echo "folding test failed:"
-  # Print failed tests (It will actually print all parameters
-  # that have the same suffix as the failed test so that one can get more info
-  # by declaring expected_x and result_x for instance)
-  if [[ -s $src4 ]]; then
-    sed -e 's/test_/_/' -e 's/ .*//' $src4 | grep -f - $src2
-  fi
-  if [[ -s $warning_diffs ]]; then
-    echo "$cmd"
-    < $warning_diffs \
-      sed -n -e 's/^-\([0-9]\)/actual at \1/p' -e 's/^+\([0-9]\)/expect at \1/p' \
-      | sort -n -k 2
-  fi
-  echo FAIL
-  exit 1
-else
-  passed_results=$(wc -l < $src3)
-  passed_warnings=$(wc -l < $expected_warnings)
-  passed=$(($passed_warnings + $passed_results))
-  echo all $passed tests passed
-  echo PASS
-fi