Skip to content

Add unit tests for Fortran's type finalization feature #13

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Fortran/UnitTests/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
# This file should only contain add_subdirectory(...) one for each test
add_subdirectory(hello)
add_subdirectory(fcvs21_f95) # NIST Fortran Compiler Validation Suite
add_subdirectory(finalization)
4 changes: 4 additions & 0 deletions Fortran/UnitTests/finalization/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# Configure the file below into the source tree with CMake so that the
# testing infrastructure can find it.

specification_expression_finalization.reference_output
38 changes: 38 additions & 0 deletions Fortran/UnitTests/finalization/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
include(CheckFortranCompilerFlag)

# LLVMFlang prefixes error stop output to stdout/stderr with "Fortran"
# and other compilers don't.
# The `specification_expression_finalization.f90` test requires
# examining the output of an `error_stop` statement.
# Configure the expected results based on the Fortran compiler in use.

set(MAYBE_LLVM_ERROR_STOP_PREFIX "")
set(MAYBE_LLVM_ERROR_STOP_COLON "")
if(CMAKE_Fortran_COMPILER_ID MATCHES "LLVMFlang")
set(MAYBE_LLVM_ERROR_STOP_PREFIX "Fortran ")
set(MAYBE_LLVM_ERROR_STOP_COLON ":")
endif()

configure_file(
specification_expression_finalization.reference_output.in
${CMAKE_CURRENT_SOURCE_DIR}/specification_expression_finalization.reference_output
@ONLY)

set(Source)
list(APPEND Source
allocatable_component.f90
allocated_allocatable_lhs.f90
block_end.f90
finalize_on_deallocate.f90
finalize_on_end.f90
intent_out.f90
lhs_object.f90
rhs_function_reference.f90
specification_expression_finalization.f90
target_deallocation.f90)

# set(FP_IGNOREWHITESPACE OFF)

llvm_singlesource()

file(COPY lit.local.cfg DESTINATION "${CMAKE_CURRENT_BINARY_DIR}")
151 changes: 151 additions & 0 deletions Fortran/UnitTests/finalization/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
Finalization Unit Tests
=======================

This suite of tests was created originally by Wileam Phan, Damian Rouson,
and Brad Richardson as part of the
[[ https://github.com/sourceryinstitute/smart-pointers | Smart-Pointers ]]
library's test suite.
All compilers, except for NAG, did not initially have a working/correct
implmentation of finalization.
An all-in-one reproducer test was created to share with compiler
teams that was easy to run (just compile a single file and run it).
This is ideal for reporting bugs to compiler teams,
but not appropriate for inclusion in a compiler test suite.

The original adaptation for inclusion in the llvm-test-suite can be found here:

* https://github.com/BerkeleyLab/llvm-test-suite/tree/damians-fortran-type-finalization
SHA: `0268bcf0048e67cd1280f9ef65aebd2aa402130b`
* https://github.com/BerkeleyLab/llvm-test-suite/tree/berkely-lab-damian-v0.1
SHA: `0268bcf0048e67cd1280f9ef65aebd2aa402130b`

The test suite was then adapted to be made appropriate for inclusion
in a compiler test suite by Izaak Beekman.
Broadly, this required:

- Each test should be broken into in individual file.
- Each test should have a corresponding expected output.
- Use the compilers build system rather than a custom fortran driver program
(relying) on `execute_command_line`.
- The tests should be incorporated following the conventions adopted by the
compiler project.
- The README/documentation should be updated and made appropriate for keeping
in the compiler project's test suite repository.
- e.g., Describe the tests and how to use them
- Don't keep information about what version of which compiler works since
it will get stale quickly and be a maintainance headache.

To run these finalization tests, and only these tests,
first you must build a recent version of llvm flang.
LLVM version d585a8afdf2f70159759dccb11d775cdf432aba4,
from Fri Apr 7 18:12:12 2023 +0000 is known to work.
Newer versions should work as well unless a regression is introduced.

You can setup your directory structure as follows:

```
llvm-project # llvm-project/llvm source code
├── build # Build directory for llvm-project/flang
├── test-suite # llvm-project/llvm-test-suite source code
└── test-suite-build # Build directory for test-suite
```

Flang is built in the `build` subdirectory.
The test-suite-build directory is created by the user
and is initially empty until running CMake for the teset-suite.
To configure, build and run the tests once llvm/flang has been built,
a command similar to the following can be used from within test-suite-build:

``` shell
cmake -DCMAKE_BUILD_TYPE=Release \
-DCMAKE_Fortran_COMPILER:FILEPATH=/home/users/<you>/llvm-project/build/bin/flang-new \
-DCMAKE_Fortran_FLAGS=-flang-experimental-exec \
-DTEST_SUITE_FORTRAN:BOOL=On \
-DTEST_SUITE_SUBDIRS=Fortran/UnitTests/finalization \
../test-suite
make -j 4
../build/bin/llvm-lit Fortran/UnitTests/finalization
```

Summary of Tests
----------------

* [`allocatable_component.f90`]
* Finalizes an allocatable component object on deallocation of an intent out dymmy argument
* Test conformance with Fortran 2018 clause 7.5.6.3, para. 2 ("allocatable entity is deallocated")
+ 9.7.3.2, para. 6 ("INTENT(OUT) allocatable dummy argument is deallocated")
* [`allocated_allocatable_lhs.f90`]
* Finalizes an allocated allocatable LHS of an intrinsic assignment
* Test conformance with Fortran 2018 clause 7.5.6.3, paragraph 1 behavior:
"allocated allocatable variable"
* [`block_end.f90`]
* Finalizes a non-pointer non-allocatable object at the end of a block construct
* Test conformance with Fortran 2018 clause 7.5.6.3, paragraph 4:
"termination of the BLOCK construct"
* [`finalize_on_deallocate.f90`]
* Finalizes an object upon explicit deallocation
* Test conformance with Fortran 2018 clause 7.5.6.3, paragraph 2:
"allocatable entity is deallocated"
* [`finalize_on_end.f90`]
* finalizes a non-pointer non-allocatable object at the END statement
* Test conformance with Fortran 2018 clause 7.5.6.3, paragraph 3:
"before return or END statement"
* [`intent_out.f90`]
* Finalizes an intent(out) derived type dummy argument
* Test conformance with Fortran 2018 standard clause 7.5.6.3, paragraph 7:
"nonpointer, nonallocatable, INTENT (OUT) dummy argument"
* [`lhs_object.f90`]
* Finalizes a non-allocatable object on the LHS of an intrinsic assignment
* Test conformance with Fortran 2018 clause 7.5.6.3, paragraph 1 behavior:
"not an unallocated allocatable variable"
* [`rhs_function_reference.f90`]
* Finalizes a function reference on the RHS of an intrinsic assignment
* Test conformance with Fortran 2018 clause 7.5.6.3, paragraph 5 behavior:
"nonpointer function result"
* [`specification_expression_finalization.f90`]
* Finalizes a function result in a specification expression
* Test compiler conformance with clause 7.5.6.3, paragraph 6 in the Fortran
Interpretation Document (https://j3-fortran.org/doc/year/18/18-007r1.pdf):
"If a specification expression in a scoping unit references
a function, the result is finalized before execution of the executable
constructs in the scoping unit." (The same statement appears in clause
4.5.5.2, paragraph 5 of the Fortran 2003 standard.) In such a scenario,
the final subroutine must be pure. The only way to observe output from
a pure final subroutine is for the subroutine to execute an error stop
statement. A correct execution of this test will error-terminate and ouput
the text "finalize: intentional error termination to verify finalization".
* [`target_deallocation.f90`]
* Finalizes a target when the associated pointer is deallocated
* Test conformance with Fortran 2018 clause 7.5.6.3, paragraph 2 behavior:
"pointer is deallocated"


Common Code
-----------

* [`object_type_m.f90`]
* To reduce code duplication, yet allow each test to be treated by
CMake as a single source file, a small amount of common code is
`include`d from this file by each test file.
* Due to the way CMake handles `.mod` module files, it is important
that each of the test files uses unique module names, otherwise
CMake will encounter a race condition when building in parallel
wherein it might clobber a `.mod` module file or corresponding
timestamp when multiple `.mod` files are being created with the
same name.
* This file contains the main derived type object for testing and the
corresponding final subroutine, `count_finalizations` to verify that
finalization took pace (by counting finalizations in a public module
variable)

[`allocatable_component.f90`]: ./allocatable_component.f90
[`allocated_allocatable_lhs.f90`]: ./allocated_allocatable_lhs.f90
[`block_end.f90`]: ./block_end.f90
[`finalize_on_deallocate.f90`]: ./finalize_on_deallocate.f90
[`finalize_on_end.f90`]: ./finalize_on_end.f90
[`intent_out.f90`]: ./intent_out.f90
[`lhs_object.f90`]: ./lhs_object.f90
[`rhs_function_reference.f90`]: ./rhs_function_reference.f90
[`specification_expression_finalization.f90`]: ./specification_expression_finalization.f90
[`target_deallocation.f90`]: ./target_deallocation.f90
[`object_type_m.f90`]: ./object_type_m.f90
40 changes: 40 additions & 0 deletions Fortran/UnitTests/finalization/allocatable_component.f90
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
module allocatable_component_m
include "object_type_m.f90"

function allocatable_component() result(outcome)
!! Test conformance with Fortran 2018 clause 7.5.6.3, para. 2 ("allocatable entity is deallocated")
!! + 9.7.3.2, para. 6 ("INTENT(OUT) allocatable dummy argument is deallocated")
!! finalizes an allocatable component object
type(wrapper_t), allocatable :: wrapper
logical outcome
integer initial_tally

initial_tally = finalizations

allocate(wrapper)
allocate(wrapper%object)
call finalize_intent_out_component(wrapper)
associate(finalization_tally => finalizations - initial_tally)
outcome = finalization_tally==1
end associate

contains

subroutine finalize_intent_out_component(output)
type(wrapper_t), intent(out) :: output ! finalizes object component
allocate(output%object)
output%object%dummy = avoid_unused_variable_warning
end subroutine

end function

end module allocatable_component_m

program main
use allocatable_component_m, only : allocatable_component, report
implicit none
character(len=*), parameter :: description = "finalizes an allocatable component object"

write(*,"(A)") report(allocatable_component()) // description

end program
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Pass: finalizes an allocatable component object
exit 0
31 changes: 31 additions & 0 deletions Fortran/UnitTests/finalization/allocated_allocatable_lhs.f90
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
module allocated_allocatable_lhs_m
include "object_type_m.f90"

function allocated_allocatable_lhs() result(outcome)
!! Test conformance with Fortran 2018 clause 7.5.6.3, paragraph 1 behavior:
!! "allocated allocatable variable"
!! finalizes an allocated allocatable LHS of an intrinsic assignment
type(object_t), allocatable :: lhs
type(object_t) rhs
logical outcome
integer initial_tally

rhs%dummy = avoid_unused_variable_warning
initial_tally = finalizations
allocate(lhs)
lhs = rhs ! finalizes lhs
associate(finalization_tally => finalizations - initial_tally)
outcome = finalization_tally==1
end associate
end function

end module allocated_allocatable_lhs_m

program main
use allocated_allocatable_lhs_m, only : allocated_allocatable_lhs, report
implicit none
character(len=*), parameter :: description = "finalizes an allocated allocatable LHS of an intrinsic assignment"

write(*,"(A)") report(allocated_allocatable_lhs()) // description

end program
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Pass: finalizes an allocated allocatable LHS of an intrinsic assignment
exit 0
31 changes: 31 additions & 0 deletions Fortran/UnitTests/finalization/block_end.f90
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
module block_end_m
include "object_type_m.f90"

function block_end() result(outcome)
!! Test conformance with Fortran 2018 clause 7.5.6.3, paragraph 4:
!! "termination of the BLOCK construct"
!! finalizes a non-pointer non-allocatable object at the end of a block construct
logical outcome
integer initial_tally

initial_tally = finalizations
block
type(object_t) object
object % dummy = avoid_unused_variable_warning
end block ! Finalizes object
associate(finalization_tally => finalizations - initial_tally)
outcome = finalization_tally==1
end associate
end function

end module block_end_m

program main
use block_end_m, only : block_end, report
implicit none
character(len=*), parameter :: description = &
"finalizes a non-pointer non-allocatable object at the end of a block construct"

write(*,"(A)") report(block_end()) // description

end program
2 changes: 2 additions & 0 deletions Fortran/UnitTests/finalization/block_end.reference_output
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Pass: finalizes a non-pointer non-allocatable object at the end of a block construct
exit 0
31 changes: 31 additions & 0 deletions Fortran/UnitTests/finalization/finalize_on_deallocate.f90
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
module finalize_on_deallocate_m

include "object_type_m.f90"

function finalize_on_deallocate() result(outcome)
!! Test conformance with Fortran 2018 clause 7.5.6.3, paragraph 2:
!! "allocatable entity is deallocated"
!! finalizes an object upon explicit deallocation
type(object_t), allocatable :: object
logical outcome
integer initial_tally

initial_tally = finalizations
allocate(object)
object%dummy = 1
deallocate(object) ! finalizes object
associate(final_tally => finalizations - initial_tally)
outcome = final_tally==1
end associate
end function

end module finalize_on_deallocate_m

program main
use finalize_on_deallocate_m, only : finalize_on_deallocate, report
implicit none
character(len=*), parameter :: description = "finalizes an object upon explicit deallocation"

write(*,"(A)") report(finalize_on_deallocate()) // description

end program
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Pass: finalizes an object upon explicit deallocation
exit 0
37 changes: 37 additions & 0 deletions Fortran/UnitTests/finalization/finalize_on_end.f90
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
module finalize_on_end_m

include "object_type_m.f90"

function finalize_on_end() result(outcome)
!! Test conformance with Fortran 2018 clause 7.5.6.3, paragraph 3:
!! "before return or END statement"
!! finalizes a non-pointer non-allocatable object at the END statement
logical outcome
integer initial_tally

initial_tally = finalizations
call finalize_on_end_subroutine() ! Finalizes local_obj
associate(final_tally => finalizations - initial_tally)
outcome = final_tally==1
end associate

contains

subroutine finalize_on_end_subroutine()
type(object_t) :: local_obj
local_obj % dummy = avoid_unused_variable_warning
end subroutine

end function

end module finalize_on_end_m

program main
use finalize_on_end_m, only : finalize_on_end, report
implicit none
character(len=*), parameter :: description = &
"finalizes a non-pointer non-allocatable object at the END statement"

write(*,"(A)") report(finalize_on_end()) // description

end program
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Pass: finalizes a non-pointer non-allocatable object at the END statement
exit 0
Loading