From eb789949bc7d37692df3d01aafac208d95388741 Mon Sep 17 00:00:00 2001 From: Dan Bonachea Date: Tue, 22 Jul 2025 14:38:01 -0700 Subject: [PATCH 1/5] CI: Add LFortran --- .github/workflows/build.yml | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 011816d..4763ec3 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -73,6 +73,14 @@ jobs: error_stop_code: 128 container: intel/fortran-essentials:2025.0.0-0-devel-ubuntu22.04 + # --- LFortran coverage --- + + # https://hub.docker.com/r/phhargrove/lfortran/tags + - os: ubuntu-24.04 + compiler: lfortran + version: 0.54.0 + container: phhargrove/lfortran:0.54.0-1 + container: image: ${{ matrix.container }} @@ -122,6 +130,9 @@ jobs: echo "FPM_FC=flang-new" >> "$GITHUB_ENV" ; \ elif test "$FC" = "ifx" ; then \ echo "FPM_FC=ifx" >> "$GITHUB_ENV" ; \ + elif test "$FC" = "lfortran" ; then \ + echo "FPM_FC=lfortran" >> "$GITHUB_ENV" ; \ + echo "FFLAGS=--cpp $FFLAGS" >> "$GITHUB_ENV" ; \ else \ echo "FPM_FC=gfortran-${COMPILER_VERSION}" >> "$GITHUB_ENV" ; \ echo "FFLAGS=-ffree-line-length-0 $FFLAGS" >> "$GITHUB_ENV" ; \ From 8d02da3f2b72742520fa98a728f861b4cff3eb7b Mon Sep 17 00:00:00 2001 From: Dan Bonachea Date: Tue, 22 Jul 2025 16:26:31 -0700 Subject: [PATCH 2/5] Fix error stop output from LFortran Previously assertion failures on LFortran were printing garbage --- src/assert_m.F90 | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/assert_m.F90 b/src/assert_m.F90 index 7a49bd3..5c4ac36 100644 --- a/src/assert_m.F90 +++ b/src/assert_m.F90 @@ -104,6 +104,7 @@ pure subroutine assert_always(assertion, description, file, line) allocate(character(len=0)::message) allocate(character(len=0)::location) + ! format source location, if known location = '' if (present(file)) then @@ -146,7 +147,13 @@ pure subroutine assert_always(assertion, description, file, line) ; ! deliberate fall-thru endif #endif +#ifdef __LFORTRAN__ + ! workaround a defect observed in LFortran 0.54: + ! error stop with an allocatable character argument prints garbage + error stop message//'', QUIET=.false. +#else error stop message, QUIET=.false. +#endif end if check_assertion From 0d8254cacbb0ba1f4f7196b522382e0234c6d8b9 Mon Sep 17 00:00:00 2001 From: Dan Bonachea Date: Tue, 22 Jul 2025 16:32:58 -0700 Subject: [PATCH 3/5] example/simple-assertions: add an explicit allocation LFortran generates a segfault upon first assignemnt to the allocatable array without a preceding allocation. Also, add some comments to clarify the math error in this example code is deliberate, and update the documentation to match the actual program. --- example/README.md | 10 +++++----- example/simple-assertions.f90 | 3 +++ 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/example/README.md b/example/README.md index c4c9508..74410f0 100644 --- a/example/README.md +++ b/example/README.md @@ -23,7 +23,7 @@ fpm run --example invoke-via-macro --flag "-DASSERTIONS" Simple examples --------------- -The [simple_assertions.f90] example demonstrates a precondition and a +The [simple-assertions.f90] example demonstrates a precondition and a postcondition, each with an assertion that checks the truth of a logical expression based on scalar, real values. @@ -32,20 +32,20 @@ Running the examples ### Single-image execution ``` -fpm run --example simple_assertions +fpm run --example simple-assertions --flag "-DASSERTIONS" ``` where `fpm run` automatically invokes `fpm build` if necessary, .e.g., if the package's source code has changed since the most recent build. If `assert` is working correctly, the `fpm run` above will error-terminate with the character stop code similar to the following ``` -Assertion failure on image 1: reciprocal: abs(error) < tolerance +Assertion failure on image 1: All residuals within tolerance ``` ### Multi-image execution with `gfortran` and OpenCoarrays ``` git clone git@github.com/sourceryinstitute/assert cd assert -fpm run --compiler caf --runner "cafrun -n 2" --example simple_assertions +fpm run --compiler caf --runner "cafrun -n 2" --example simple-assertions --flag "-DASSERTIONS" ``` Replace either instance of `2` above with the desired number of images to run for parallel execution. If `assert` is working correctly, both of the latter `fpm run` commands will error-terminate with one @@ -55,7 +55,7 @@ or more images providing stop codes analogous to those quoted in the [Single-ima [OpenCoarrays]: https://github.com/sourceryinstitute/opencoarrays [Enforcing programming contracts]: #enforcing-programming-contracts [Single-image execution]: #single-image-execution -[simple_assertions.f90]: ./simple_assertions.f90 +[simple-assertions.f90]: ./simple-assertions.f90 [invoke-via-macro.F90]: ./invoke-via-macro.F90 [UML]: https://en.wikipedia.org/wiki/Unified_Modeling_Language [OCL]: https://en.wikipedia.org/wiki/Object_Constraint_Language diff --git a/example/simple-assertions.f90 b/example/simple-assertions.f90 index da9dfd7..43cb6c2 100644 --- a/example/simple-assertions.f90 +++ b/example/simple-assertions.f90 @@ -18,9 +18,12 @@ pure function roots(a,b,c) result(zeros) associate(discriminant => b**2 - 4*a*c) call assert(assertion = discriminant >= 0., description = "discriminant >= 0") ! precondition + allocate(zeros(2)) + ! there's a deliberate math bug in the following line, to help demonstrate assertion failure zeros = -b + [sqrt(discriminant), -sqrt(discriminant)] end associate + ! This assertion will fail (due to the defect above) when ASSERTIONS are enabled: call assert(all(abs(a*zeros**2 + b*zeros + c) < tolerance), "All residuals within tolerance.") ! postcondition end function From 9d54ab32561af8f2afa043fd5185257bbd3f0711 Mon Sep 17 00:00:00 2001 From: Dan Bonachea Date: Tue, 22 Jul 2025 16:45:06 -0700 Subject: [PATCH 4/5] CI: Add --example simple-assertions --- .github/workflows/build.yml | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 4763ec3..db85ec9 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -174,6 +174,7 @@ jobs: set -x fpm test ${FPM_FLAGS} --flag "$FFLAGS" fpm run --example false-assertion ${FPM_FLAGS} --flag "$FFLAGS" + fpm run --example simple-assertions ${FPM_FLAGS} --flag "$FFLAGS" fpm run --example invoke-via-macro ${FPM_FLAGS} --flag "$FFLAGS" - name: Build and Test (Assertions ON) @@ -181,14 +182,16 @@ jobs: FPM_FLAGS: ${{ env.FPM_FLAGS }} --flag -DASSERTIONS run: | set -x - fpm test ${FPM_FLAGS} --flag "$FFLAGS" - ( set +e ; fpm run --example false-assertion ${FPM_FLAGS} --flag "$FFLAGS" ; test $? = $ERROR_STOP_CODE ) - ( set +e ; fpm run --example invoke-via-macro ${FPM_FLAGS} --flag "$FFLAGS" ; test $? = $ERROR_STOP_CODE ) + fpm test ${FPM_FLAGS} --flag "$FFLAGS" + ( set +e ; fpm run --example false-assertion ${FPM_FLAGS} --flag "$FFLAGS" ; test $? = $ERROR_STOP_CODE ) + ( set +e ; fpm run --example simple-assertions ${FPM_FLAGS} --flag "$FFLAGS" ; test $? = $ERROR_STOP_CODE ) + ( set +e ; fpm run --example invoke-via-macro ${FPM_FLAGS} --flag "$FFLAGS" ; test $? = $ERROR_STOP_CODE ) - name: Test Assertions w/ Parallel Callbacks env: FPM_FLAGS: ${{ env.FPM_FLAGS }} --flag -DASSERTIONS --flag -DASSERT_MULTI_IMAGE --flag -DASSERT_PARALLEL_CALLBACKS run: | set -x - ( set +e ; fpm run --example false-assertion ${FPM_FLAGS} --flag "$FFLAGS" ; test $? = $ERROR_STOP_CODE ) - ( set +e ; fpm run --example invoke-via-macro ${FPM_FLAGS} --flag "$FFLAGS" ; test $? = $ERROR_STOP_CODE ) + ( set +e ; fpm run --example false-assertion ${FPM_FLAGS} --flag "$FFLAGS" ; test $? = $ERROR_STOP_CODE ) + ( set +e ; fpm run --example simple-assertions ${FPM_FLAGS} --flag "$FFLAGS" ; test $? = $ERROR_STOP_CODE ) + ( set +e ; fpm run --example invoke-via-macro ${FPM_FLAGS} --flag "$FFLAGS" ; test $? = $ERROR_STOP_CODE ) From 32c340a0af86b208c27d9fce0b2305e0db95e5b4 Mon Sep 17 00:00:00 2001 From: Dan Bonachea Date: Tue, 22 Jul 2025 20:30:54 -0700 Subject: [PATCH 5/5] CI: Adjust stage Test Assertions w/ Parallel Callbacks Add build without assertions to ensure the code actually compiles without error, and disable the stage entirely for LFortran, which is currently unable to correctly compile this code (issue #68) --- .github/workflows/build.yml | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index db85ec9..7605ba0 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -188,10 +188,12 @@ jobs: ( set +e ; fpm run --example invoke-via-macro ${FPM_FLAGS} --flag "$FFLAGS" ; test $? = $ERROR_STOP_CODE ) - name: Test Assertions w/ Parallel Callbacks + if: ${{ matrix.compiler != 'lfortran' }} # issue #68 env: - FPM_FLAGS: ${{ env.FPM_FLAGS }} --flag -DASSERTIONS --flag -DASSERT_MULTI_IMAGE --flag -DASSERT_PARALLEL_CALLBACKS + FPM_FLAGS: ${{ env.FPM_FLAGS }} --flag -DASSERT_MULTI_IMAGE --flag -DASSERT_PARALLEL_CALLBACKS run: | set -x - ( set +e ; fpm run --example false-assertion ${FPM_FLAGS} --flag "$FFLAGS" ; test $? = $ERROR_STOP_CODE ) - ( set +e ; fpm run --example simple-assertions ${FPM_FLAGS} --flag "$FFLAGS" ; test $? = $ERROR_STOP_CODE ) - ( set +e ; fpm run --example invoke-via-macro ${FPM_FLAGS} --flag "$FFLAGS" ; test $? = $ERROR_STOP_CODE ) + fpm run --example false-assertion ${FPM_FLAGS} --flag "$FFLAGS" + fpm run --example invoke-via-macro ${FPM_FLAGS} --flag "$FFLAGS" + ( set +e ; fpm run --example false-assertion ${FPM_FLAGS} --flag "$FFLAGS -DASSERTIONS" ; test $? = $ERROR_STOP_CODE ) + ( set +e ; fpm run --example invoke-via-macro ${FPM_FLAGS} --flag "$FFLAGS -DASSERTIONS" ; test $? = $ERROR_STOP_CODE )