Skip to content

Commit d853532

Browse files
authored
Merge pull request #29 from sourceryinstitute/assert-macros
Feature: offer macros for invoking the assert subroutine only when -DDEBUG is passed
2 parents 0591a21 + 3eef611 commit d853532

18 files changed

Lines changed: 503 additions & 143 deletions

README.md

Lines changed: 12 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -52,14 +52,6 @@ The [examples/README.md] file shows examples of writing constraints in notes on
5252
Downloading, Building, and Running Examples
5353
-------------------------------------------
5454

55-
### Prerequisites
56-
1. A Fortran 2018 compiler.
57-
2. The [Fortran Package Manager].
58-
3. _Optional_: [OpenCoarrays] for parallel execution with the GNU Fortran compiler.
59-
60-
Assert was developed primarily with `gfortran` 11.2.0 and `nagfor` 7.1.
61-
Recent versions of the Cray and Intel compilers should also suffice.
62-
6355
### Downloading Assert
6456
```
6557
git clone git@github.com:sourceryinstitute/assert
@@ -70,29 +62,34 @@ cd assert
7062
#### Single-image (serial) execution
7163
The following command builds Assert and runs the full test suite in a single image:
7264
```
73-
fpm test
65+
fpm test --profile release
7466
```
75-
where `fpm test` builds the Assert library and runs the test suite, including the tests.
67+
which builds the Assert library and runs the test suite.
7668

7769
#### Multi-image (parallel) execution
7870
With `gfortran` and OpenCoarrays installed,
7971
```
80-
fpm test --compiler caf --runner "cafrun -n 2"
72+
fpm test --compiler caf --profile release --runner "cafrun -n 2"
8173
```
8274
To build and test with the Numerical Algorithms Group (NAG) Fortran compiler version
8375
7.1 or later, use
8476
```
85-
fpm test --compiler=nagfor --flag="-coarray=cosmp -fpp -f2018"
77+
fpm test --compiler=nagfor --profile release --flag="-coarray=cosmp -fpp -f2018"
8678
```
8779

8880
### Building and testing with the Intel `ifx` compiler
8981
```
90-
fpm test --compiler ifx --flag -coarray
82+
fpm test --compiler ifx --profile release --flag -coarray
83+
```
84+
### Building and testing with the LLVM `flang-new` compiler
85+
```
86+
fpm test --compiler flang-new --flag "-mmlir -allow-assumed-rank -O3"
87+
9188
```
9289

9390
### Building and testing with the Numerical Algorithms Group (NAG) compiler
9491
```
95-
fpm test --compiler nagfor --flag "-fpp -coarray=cosmp"
92+
fpm test --compiler nagfor --profile release --flag "-fpp -coarray=cosmp"
9693
```
9794

9895
### Building and testing with the Cray Compiler Environment (CCE)
@@ -108,7 +105,7 @@ ftn $@
108105
```
109106
Then build and test Assert with the command
110107
```
111-
fpm test --compiler crayftn.sh
108+
fpm test --compiler crayftn.sh --profile release
112109
```
113110

114111

example/false-assertion.F90

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
program false_assertion
2+
use assert_m, only : assert
3+
implicit none
4+
5+
call assert(.false., "false-assertion: unconditionally failing test")
6+
7+
end program

example/intentionally_false_assertions.f90

Lines changed: 0 additions & 7 deletions
This file was deleted.

example/invoke-via-macro.F90

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
#include "../src/assert/assert_macros.h"
2+
3+
program invoke_via_macro
4+
!! Demonstrate how to invoke the 'assert' subroutine using a preprocessor macro that facilitates
5+
!! the complete removal of the call in the absence of the compiler flag -DDEBUG.
6+
use assert_m, only : assert, intrinsic_array_t, string
7+
!! If an "only" clause is employed as above, it must include the "string" function that the
8+
!! call_assert* macros reference when transforming the code below into "assert" subroutine calls.
9+
implicit none
10+
11+
#ifndef DEBUG
12+
print *
13+
print *,'To enable the "assert" call, define -DDEBUG, e.g., fpm run --example invoke-via-macro --flag "-DDEBUG -fcoarray=single"'
14+
print *
15+
#endif
16+
17+
! The C preprocessor will convert each call_assert* macro below into calls to the "assert" subroutine
18+
! (if -DDEBUG is in the compiler command) or into nothing (if -DDEBUG is not in the compiler command).
19+
20+
call_assert(1==1) ! true assertion
21+
call_assert_describe(2>0, "example assertion invocation via macro") ! true assertion
22+
call_assert_diagnose(1+1==2, "example with scalar diagnostic data", 1+1) ! true assertion
23+
call_assert_diagnose(1+1>2, "example with array diagnostic data" , intrinsic_array_t([1,1,2])) ! false assertion
24+
25+
end program invoke_via_macro
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
program check_exit_status
2+
! Despite its location in the example subdirectory, this program is _not_ intended to
3+
! be a user-facing example. This program exists to work around an LLVM Flang (flang-new)
4+
! compiler issue. This program is invoked by test/test-assert-subroutine-error-termination.F90,
5+
! which reads the file this program writes to determine the exist status of the program
6+
! example/false-assertion.f90. The latter program intentionally error terminates in order
7+
! to test the case wehn assertion = .false.
8+
implicit none
9+
integer exit_status, unit
10+
read(*,*) exit_status
11+
open(newunit=unit, file="build/exit_status", status="unknown")
12+
write(unit,*) exit_status
13+
close(unit)
14+
end program

src/assert/assert_macros.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
#ifdef DEBUG
2+
# define call_assert(assertion) call assert(assertion, "No description provided (see file " // __FILE__ // ", line " // string(__LINE__) // ")")
3+
# define call_assert_describe(assertion, description) call assert(assertion, description // " in file " // __FILE__ // ", line " // string(__LINE__) // ": " )
4+
# define call_assert_diagnose(assertion, description, diagnostic_data) call assert(assertion, "file " // __FILE__ // ", line " // string(__LINE__) // ": " // description, diagnostic_data)
5+
#else
6+
# define call_assert(assertion)
7+
# define call_assert_describe(assertion, description)
8+
# define call_assert_diagnose(assertion, description, diagnostic_data)
9+
#endif

src/assert/intrinsic_array_s.F90

Lines changed: 33 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
submodule(intrinsic_array_m) intrinsic_array_s
2+
use assert_m, only : assert
23
implicit none
34

45
contains
@@ -11,21 +12,25 @@
1112
select type(array)
1213
type is(complex)
1314
allocate(intrinsic_array%complex_1D, source = array)
15+
type is(complex(kind(1.D0)))
16+
allocate(intrinsic_array%complex_double_1D, source = array)
1417
type is(integer)
1518
allocate(intrinsic_array%integer_1D, source = array)
1619
type is(logical)
1720
allocate(intrinsic_array%logical_1D, source = array)
1821
type is(real)
1922
allocate(intrinsic_array%real_1D, source = array)
2023
type is(double precision)
21-
allocate(intrinsic_array%double_precision_1D, source = array)
24+
intrinsic_array%double_precision_1D = array
2225
class default
23-
error stop "intrinsic_array_t construct: unsupported rank-1 type"
26+
error stop "intrinsic_array_s(construct): unsupported rank-1 type"
2427
end select
2528
rank(2)
2629
select type(array)
2730
type is(complex)
2831
allocate(intrinsic_array%complex_2D, source = array)
32+
type is(complex(kind(1.D0)))
33+
allocate(intrinsic_array%complex_double_2D, source = array)
2934
type is(integer)
3035
allocate(intrinsic_array%integer_2D, source = array)
3136
type is(logical)
@@ -35,13 +40,15 @@
3540
type is(double precision)
3641
allocate(intrinsic_array%double_precision_2D, source = array)
3742
class default
38-
error stop "intrinsic_array_t construct: unsupported rank-2 type"
43+
error stop "intrinsic_array_s(construct): unsupported rank-2 type"
3944
end select
4045

4146
rank(3)
4247
select type(array)
4348
type is(complex)
4449
allocate(intrinsic_array%complex_3D, source = array)
50+
type is(complex(kind(1.D0)))
51+
allocate(intrinsic_array%complex_double_3D, source = array)
4552
type is(integer)
4653
allocate(intrinsic_array%integer_3D, source = array)
4754
type is(logical)
@@ -51,11 +58,11 @@
5158
type is(double precision)
5259
allocate(intrinsic_array%double_precision_3D, source = array)
5360
class default
54-
error stop "intrinsic_array_t construct: unsupported rank-3 type"
61+
error stop "intrinsic_array_s(construct): unsupported rank-3 type"
5562
end select
5663

5764
rank default
58-
error stop "intrinsic_array_t construct: unsupported rank"
65+
error stop "intrinsic_array_s(construct): unsupported rank"
5966
end select
6067

6168
end procedure
@@ -71,7 +78,7 @@
7178
rank(3)
7279
allocate(intrinsic_array%complex_3D, source = array)
7380
rank default
74-
error stop "intrinsic_array_t complex_array: unsupported rank"
81+
error stop "intrinsic_array_s(complex_array): unsupported rank"
7582
end select
7683

7784
end procedure
@@ -86,7 +93,7 @@
8693
rank(3)
8794
allocate(intrinsic_array%integer_3D, source = array)
8895
rank default
89-
error stop "intrinsic_array_t integer_array: unsupported rank"
96+
error stop "intrinsic_array_s(integer_array): unsupported rank"
9097
end select
9198

9299
end procedure
@@ -101,7 +108,7 @@
101108
rank(3)
102109
allocate(intrinsic_array%logical_3D, source = array)
103110
rank default
104-
error stop "intrinsic_array_t logical_array: unsupported rank"
111+
error stop "intrinsic_array_s(logical_array): unsupported rank"
105112
end select
106113

107114
end procedure
@@ -116,7 +123,7 @@
116123
rank(3)
117124
allocate(intrinsic_array%real_3D, source = array)
118125
rank default
119-
error stop "intrinsic_array_t real_array: unsupported rank"
126+
error stop "intrinsic_array_s(real_array): unsupported rank"
120127
end select
121128

122129
end procedure
@@ -131,28 +138,32 @@
131138
rank(3)
132139
allocate(intrinsic_array%double_precision_3D, source = array)
133140
rank default
134-
error stop "intrinsic_array_t double_precision_array: unsupported rank"
141+
error stop "intrinsic_array_s(double_precision_array): unsupported rank"
135142
end select
136143

137144
end procedure
138145

139146
#endif
140147

141-
pure function one_allocated_component(self) result(one_allocated)
148+
pure function allocated_components(self)
142149
type(intrinsic_array_t), intent(in) :: self
143-
logical one_allocated
144-
one_allocated = 1 == count( &
145-
[ allocated(self%complex_1D), allocated(self%complex_double_1D), allocated(self%integer_1D), allocated(self%logical_1D), &
146-
allocated(self%real_1D), allocated(self%complex_2D), allocated(self%complex_double_2D), allocated(self%integer_2D), &
147-
allocated(self%logical_2D), allocated(self%real_2D), allocated(self%complex_3D), allocated(self%complex_double_3D), &
148-
allocated(self%integer_3D), allocated(self%logical_3D), allocated(self%real_3D) &
149-
])
150+
logical, allocatable :: allocated_components(:)
151+
allocated_components = [ &
152+
allocated(self%complex_1D), allocated(self%real_1D), allocated(self%integer_1D), allocated(self%complex_double_1D) &
153+
,allocated(self%complex_2D), allocated(self%real_2D), allocated(self%integer_2D), allocated(self%complex_double_2D) &
154+
,allocated(self%complex_3D), allocated(self%real_3D), allocated(self%integer_3D), allocated(self%complex_double_3D) &
155+
,allocated(self%logical_1D), allocated(self%double_precision_1D) &
156+
,allocated(self%logical_2D), allocated(self%double_precision_2D) &
157+
,allocated(self%logical_3D), allocated(self%double_precision_3D) &
158+
]
150159
end function
151160

152161
module procedure as_character
153-
integer, parameter :: single_number_width=32
162+
integer, parameter :: single_number_width=64
154163

155-
if (.not. one_allocated_component(self)) error stop "intrinsic_array_s(as_character): invalid number of allocated components"
164+
associate(a => allocated_components(self))
165+
call assert(count(a) == 1, "intrinsic_array_s(as_character): invalid number of allocated components", intrinsic_array_t(a))
166+
end associate
156167

157168
if (allocated(self%complex_1D)) then
158169
character_self = repeat(" ", ncopies = single_number_width*size(self%complex_1D))
@@ -182,7 +193,7 @@ pure function one_allocated_component(self) result(one_allocated)
182193
character_self = repeat(" ", ncopies = single_number_width*size(self%integer_2D))
183194
write(character_self, *) self%integer_2D
184195
else if (allocated(self%logical_2D)) then
185-
character_self = repeat(" ", ncopies = single_number_width*size(self%logical_1D))
196+
character_self = repeat(" ", ncopies = single_number_width*size(self%logical_2D))
186197
write(character_self, *) self%logical_2D
187198
else if (allocated(self%real_2D)) then
188199
character_self = repeat(" ", ncopies = single_number_width*size(self%real_2D))
@@ -200,7 +211,7 @@ pure function one_allocated_component(self) result(one_allocated)
200211
character_self = repeat(" ", ncopies = single_number_width*size(self%integer_3D))
201212
write(character_self, *) self%integer_3D
202213
else if (allocated(self%logical_3D)) then
203-
character_self = repeat(" ", ncopies = single_number_width*size(self%logical_1D))
214+
character_self = repeat(" ", ncopies = single_number_width*size(self%logical_3D))
204215
write(character_self, *) self%logical_3D
205216
else if (allocated(self%real_3D)) then
206217
character_self = repeat(" ", ncopies = single_number_width*size(self%real_3D))

src/assert/string_m.f90

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
module string_m
2+
implicit none
3+
4+
contains
5+
6+
pure function string(number) result(number_as_string)
7+
integer, intent(in) :: number
8+
integer, parameter :: max_len=128
9+
character(len=max_len) :: untrimmed_string
10+
character(len=:), allocatable :: number_as_string
11+
12+
write(untrimmed_string, *) number
13+
number_as_string = trim(adjustl(untrimmed_string))
14+
end function
15+
16+
end module string_m

0 commit comments

Comments
 (0)