Is there a way to do a static assert in Fortran similar to the following in C11 / C23? Preferably, failing the assert should produce a compile time error.
For runtime assert: we have an issue for this at our proposals repository: Add assert statement to Fortran · Issue #70 · j3-fortran/fortran_proposals · GitHub. There is no intrinsic feature like that yet, but you can use C macros to achieve it today.
For compile time assert: I think that is not possible, but it’s an interesting idea. What are some use cases that you have in mind?
Some assertion libraries for Fortran are listed here, but I think they produce run-time errors. Placing procedures in modules, using named constants (parameters) when possible, and specifying argument intents helps to identify problems at compile time, but these practices are not a complete answer to your question.
I’m mainly thinking of cross-compilation scenarios. In this case, I may not be able to execute software in the host environment but I would like to do some sanity checks at compilation.
I see. What kind of checks? Do you want to be executing user functions at compile time?
I’m mainly trying to cross compile HDF5 without having to look up the values via an emulator. They are checking what size reals are available and what C sizes match.
You can assign a negative integer as kind parameter to cause compilation to fail. Here is an example related to detecting quad precision: Compile time detection of extended-double or quad precision - #8 by ivanpribec
Here is an example how you may apply this to storage size:
program assert use iso_c_binding, only: c_double integer, parameter :: fortran_dp = kind(1.0d0) integer, parameter :: wp = merge(fortran_dp, -1, & storage_size(1.0_fortran_dp) == storage_size(1.0_c_double)) real(wp) :: foo foo = 1.0_wp print *, foo end program
Thanks to Peter Klausler at Add assert statement to Fortran · Issue #70 · j3-fortran/fortran_proposals · GitHub
$ cat a.f90 #define STATIC_ASSERT(x) block; real(merge(kind(1.),-1,(x))), parameter :: fail = 1.; end block !STATIC_ASSERT(1 > 2) STATIC_ASSERT(sin(0.5) > 0.5) end $ gfortran -cpp a.f90 a.f90:3:48: 3 | STATIC_ASSERT(sin(0.5) > 0.5) | 1 Error: Kind -1 not supported for type REAL at (1)
So it can be done after all!
Here is @ivanpribec’s example:
#define STATIC_ASSERT(x) block; real(merge(kind(1.),-1,(x))), parameter :: fail = 1.; end block program assert use iso_c_binding, only: c_double, c_float integer, parameter :: fortran_dp = kind(1.0d0) STATIC_ASSERT(storage_size(1.0_fortran_dp) == storage_size(1.0_c_float)) end program
It would be nice if it were possible to include a message for cases when it is not obvious from the test expression.
Once can (mis-)use the variable name to get a message in the output:
! assert.f90 integer, parameter :: size_check = merge(kind(0), -1, & storage_size(1) == storage_size(1.0d0)) integer(size_check) :: int_size_not_equal_to_double end
$ gfortran assert.f90 assert.f90:4:19: 4 | integer(size_check) :: int_size_not_equal_to_double | 1 Error: Kind -1 not supported for type INTEGER at (1)
A variation of Klausler’s macro can be used to include a message:
#define STATIC_ASSERT(x,msg) block; real(kind=merge(kind(1.),-1,(x))), parameter :: fail = len(msg); end block STATIC_ASSERT(storage_size(1.d0) >= 80,"double precision must be at least 80-bit") end
$ gfortran -cpp -ffree-line-length-none assert.f90 assert.f90:2:63: 2 | STATIC_ASSERT(storage_size(1.d0) >= 80,"double precision must be at least 80-bit") | 1 Error: Kind -1 not supported for type REAL at (1) $ gfortran -fdefault-real-16 -cpp -ffree-line-length-none assert.f90 $
Edit: an alternative version without the
len intrinsic would be:
#define STATIC_ASSERT(x,msg) block; character(kind=merge(kind('a'),-1,(x))), parameter :: fail = msg; end block
I wonder if something like this can be included in the Fortran
stdlib, considering it relies on a CPP macro
I think the compiler should just support it directly.
Static assertions have long been built into Fortran via numbered syntax rules and constraints the violations of which are required by a conforming processor to detect and report e.g.,
type :: t end type type(t) :: x, y, z z = x + y end
C:\temp>gfortran -c -ffree-form p.f p.f:4:7: 4 | z = x + y | 1 Error: Unexpected derived-type entities in binary intrinsic numeric operator '+' at (1)
static_assert type of clause to be introduced in Fortran, a la C++, should be a last resort I feel. A better option will be to firm up and expand the rules and constraints, as needed for the greater benefit of practitioners.
The “canonical” examples toward
static_assert in C++ involve
typedefs and working with structs and classes. The link in the original class has one with nocopy objects. By default, a derived type in Fortran is “copiable” to some extent given the intrinsic assignment semantics If practitioners have such specific needs in Fortran, addressing them at the language level will be better in my opinion.
I think we will want something along those lines for templates. E.g. making sure a kind parameter is in fact a valid kind, or a dimension/size parameter is positive, etc. That isn’t an aspect that has made it top of the list yet. It will likely be spelled