Skip to content
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

Add assert statement to Fortran #70

Open
certik opened this issue Nov 4, 2019 · 28 comments
Open

Add assert statement to Fortran #70

certik opened this issue Nov 4, 2019 · 28 comments
Labels
Clause 10 Standard Clause 10: Expressions and assignment

Comments

@certik
Copy link
Member

certik commented Nov 4, 2019

One very common use case for a preprocessor is a custom ASSERT macro, that is empty in Release mode, but in Debug mode it checks the condition and prints out the filename and line number. As discussed in #65, Fortran is moving away from a preprocessor, and so by adding an assert statement into the language would eliminate one more use case for a preprocessor.

From a user perspective, the syntax can be something like:

integer :: i
i = 5
assert(i == 5)

In Release mode, the assert would do nothing, in Debug mode the assert would evaluate the condition (e.g., i == 5) and if it fails, it would print a nice stacktrace. This would be a much better behavior than the current practice, where typically the ASSERT macro only prints a filename+line, but not a full stacktrace, so it can be hard to figure out why it happened.

@sblionel
Copy link
Member

sblionel commented Nov 4, 2019

Assert has been proposed before (https://j3-fortran.org/doc/year/04/04-376.txt) This was before my time on the committee, but I gather from what evidence I can find that there was not sufficient interest to move it forward. I have seen it implemented as a vendor extension.

@certik
Copy link
Member Author

certik commented Nov 5, 2019

@sblionel thanks! I would like for each idea at this GitHub repository to eventually have a technical response from the committee why it was rejected. And if there is no technical reason, just a lack of sufficient interest at some point in the past, then I think we can revisit such an idea. For example in this particular case in light of #65 we can argue that rather than standardizing a preprocessor, let's improve Fortran so that the preprocessor is not needed (as much).

@klausler
Copy link

klausler commented Nov 5, 2019

ASSERT is also a potential optimization aid:

  ASSERT (K >= 0)
  DO J = 1, N
    A(J) = A(J + K)  ! known to not cause a data dependence cycle
  END DO

@aradi
Copy link
Contributor

aradi commented Nov 12, 2019

Probably, ASSERT should not be used for optimization. As evaluation of an assert could be time consuming (imagine ASSERT(all(big_array > 0)), I'd wish that it is not considered when the code is compiled for production. Therefore, it could even be a pre-processor feature instead of a compiler feature (see also #65).

@certik
Copy link
Member Author

certik commented Nov 12, 2019

@aradi the proposal here, as I understand it, is that ASSERT(all(big_array > 0)) will do nothing in Release mode. But I do not understand your argument why the compiler couldn't still use it for optimization.

@sblionel
Copy link
Member

sblionel commented Nov 12, 2019

DEC implemented ASSERT as follows: It is a declaration from the programmer to the optimizer that the given condition is met. The optimizer can then use that information. A separate option tells the compiler to generate run-time code to check that assertion, so you can choose to do that or not.

Intel Fortran currently supports an ASSUME directive that provides information, but there is not currently any run-time code associated with that.

@aradi
Copy link
Contributor

aradi commented Nov 12, 2019

@certik What happens, if in production mode (for whatever buggy reasons) the criteria is not met. The run-time code does not check for it, but it was taken into account during the optimization. Then the optimized code may produce results which are hard to understand/debug. But on the other hand, probably you can say, it is the programmers fault...

@certik
Copy link
Member Author

certik commented Nov 12, 2019

@certik What happens, if in production mode (for whatever buggy reasons) the criteria is not met. The run-time code does not check for it, but it was taken into account during the optimization. Then the optimized code may produce results which are hard to understand/debug. But on the other hand, probably you can say, it is the programmers fault...

Yes, it would give wrong results in Release mode, but not in Debug mode. In exactly the same way as you get wrong results if you go out of bounds (in Release mode). But in Debug mode, out of bounds, as well as the assert conditions are all checked.

@aradi
Copy link
Contributor

aradi commented Nov 12, 2019

@certik OK, that's a good point. It makes sense, I agree, and would be consistent with current compiler behavior as well.

@rweed
Copy link

rweed commented Jan 3, 2020

If we can't have an ASSERT statement, I would be in favor of a standard assert subroutine. You can then use the pre-processor to create a macro ASSERT (or ERROR_ABORT) command. Here is my version that I "borrowed" from the FTL project

! Macros for assertions

#ifdef GFORTRAN
#define ASSERT(x) call assert(x,"x",FILE,LINE)
#define ERROR_ABORT(x,y) call assert(x,"x",FILE,LINE,y)
#else
#define ASSERT(x) call assert(x,#x,FILE,LINE)
#define ERROR_ABORT(x,y) call assert(x,#x,FILE,LINE,y)
#endif

ERROR_ABORT just adds a logical to signal if you want to do a hard aborut (STOP,
ERROR_STOP) or continue after printing an error message

@certik
Copy link
Member Author

certik commented Jan 3, 2020

@rweed I think this would be a perfect addition to stdlib. Can you please open up an issue there? We already have a minimal assert subroutine there:

https://github.com/fortran-lang/stdlib/blob/924ee5459fab5c9fa4deb30e75c12c00b2864598/src/stdlib_experimental_error.f90#L8

So let's extend it, and then let's discuss the macros also, per your comment.

@wclodius2
Copy link
Contributor

C++ 2020 and Ada (2012?) defined three statements intended to make "Design by Contract" (DbC) an intrinsic part of the language. In C++ these three statements define the attributes: requires, asserts, and ensures, where requires and ensures are in effect assert statements that are executed upon subprogram entry and exit respectively. requires, in effect, defines the pre-conditions the caller must meet for correct operation of the subprogram, and ensures the post-conditions the caller can expect to be satisfied by the subprogram if it has operated correctly.

I have written a draft proposal for the incorporation of DbC in Fortran 202y. It, of course, includes an ASSERT statement as part of the proposal. If there is interest I can upload the proposal to my branch of the proposals directory. Once there, given the problems with my last PR, where it got added on to an existing PR, I don't know if I should do a PR or have someone else do the PR for me.

@certik
Copy link
Member Author

certik commented Aug 1, 2020

@wclodius2 Yes, please upload it here. Try to create a new branch and put it there. Once you learn the process, it becomes simple. I can help.

@wclodius2
Copy link
Contributor

wclodius2 commented Aug 1, 2020 via email

@wclodius2
Copy link
Contributor

FWIW in not letting me fork on my repository it tells me "Cannot fork because you own this repository and are not a member of any organization."

@certik
Copy link
Member Author

certik commented Aug 4, 2020

@wclodius2 here is how you do it. You already have a fork at GitHub (https://github.com/wclodius2/fortran_proposals) so that is all you need. Let's start from scratch on your computer, that way things will work exactly as I write them here. You will see that once you learn this workflow, you can apply it to any other project at GitHub.

Go to some place on your computer where you want to have the fortran_proposals directory. Then initialize it as follows:

git clone https://github.com/j3-fortran/fortran_proposals
cd fortran_proposals
git remote add william [email protected]:wclodius2/fortran_proposals.git

Then create a branch assert (you can name it any other way) and put your document in:

git checkout -b assert
# copy your document to proposals/assert, let's call it `assert.pdf` (I don't know what you call it)
git add proposals/assert/assert.pdf
git commit # an editor will open, write a commit message
git push william

This will push the assert branch into your fork, and it will also give you a link that will allow you to submit the Pull Request (PR).

Let me know if this works, and if something does not work, I'll help.

@wclodius2
Copy link
Contributor

wclodius2 commented Aug 4, 2020 via email

@certik
Copy link
Member Author

certik commented Aug 4, 2020

The meaning of <name> is an arbitrary identifier of your choosing that you then reference in git push. When you do git remote -v, it will show what is setup. And you can always rename <name> using git remote rename william something_else. Here is how I have it setup for myself:

ondrej@pn1707483:~/repos/fortran_proposals(master)$ git remote -v
ondrej	[email protected]:certik/fortran_proposals (fetch)
ondrej	[email protected]:certik/fortran_proposals (push)
origin	https://github.com/lfortran/fortran_proposals (fetch)
origin	https://github.com/lfortran/fortran_proposals (push)

Which is analogous to what I suggested for you. If I add william to mine, see what happens:

ondrej@pn1707483:~/repos/fortran_proposals(master)$ git remote add william [email protected]:wclodius2/fortran_proposals.git
ondrej@pn1707483:~/repos/fortran_proposals(master)$ git remote -v
ondrej	[email protected]:certik/fortran_proposals (fetch)
ondrej	[email protected]:certik/fortran_proposals (push)
origin	https://github.com/lfortran/fortran_proposals (fetch)
origin	https://github.com/lfortran/fortran_proposals (push)
william	[email protected]:wclodius2/fortran_proposals.git (fetch)
william	[email protected]:wclodius2/fortran_proposals.git (push)

Now I can pull branches from you using git fetch william. So that's the idea.

@wclodius2
Copy link
Contributor

wclodius2 commented Aug 4, 2020 via email

@certik
Copy link
Member Author

certik commented Aug 4, 2020

That means your ssh key is not at GitHub (I assume it already was there). All you need to do is to create an ssh key:

ssh-keygen

and copy ~/.ssh/id_rsa.pub (assuming you are on linux) to your GitHub profile: https://github.com/settings/keys

wclodius2 added a commit to wclodius2/fortran_proposals that referenced this issue Aug 4, 2020
The new paper, design_by_contract.txt, contains a proposal to allow Fortran
programmers to use "Design by Contract", proposing three new statements for
the Fortran language: REQUIRES, ASSERT, and ENSURES. As such, among other
things, it addresses issue j3-fortran#70 'Add assert statement to Fortran'.

[ticket: j3-fortran#70]
wclodius2 added a commit to wclodius2/fortran_proposals that referenced this issue Aug 4, 2020
In reviewing the paper prior to submitting a pull request I realized that
I had misspelled .EQV. as .EQUIV. and that I had forgotten to mention
ASSERT at one point where I discussed REQUIRES and ENSURES.

[ticket: j3-fortran#70]
wclodius2 added a commit to wclodius2/fortran_proposals that referenced this issue Aug 5, 2020
Changed ASSERTS, ENSURES, and REQUIRES to ASSERT, ENSURE, and REQUIRE, respectively

[ticket: j3-fortran#70]
@certik certik added the Clause 10 Standard Clause 10: Expressions and assignment label Apr 23, 2022
@rouson
Copy link

rouson commented Apr 7, 2023

@wclodius2 I would like to see your proposal included in the Fortran 202Y work list if it's not too late. It look like the above comment thread trailed off without resolution of how to contribute the proposal. If I help, my preference would be to do so interactively via Zoom or something similar because I'm more likely to be able to help if there's dedicated synchronous time than if we bounce back and forth asynchronously. @certik would you like to join such a call if it happens?

@wclodius2
Copy link
Contributor

wclodius2 commented Apr 8, 2023 via email

@shahmoradi
Copy link

Our research library currently contains over 1300 FPP ASSERT macros that can be (de)activated under any compilation mode. This has proven vital to the health of the codebase and catching tens of intricate bugs that could have remained undetected.
While adding an intrinsic assert statement to the language will resolve most of our use cases, this appears more of a piecemeal approach that does not resolve the more significant issue: the need for macros or similar concepts in Fortran. There is not a single day that I do not wish Fortran had macros beyond what can be currently done with FPP.

@tkoenig1
Copy link

tkoenig1 commented Apr 8, 2023

@shahmoradi

hile adding an intrinsic assert statement to the language will resolve most of our use cases, this appears more of a piecemeal approach that does not resolve the more significant issue: the need for macros or similar concepts in Fortran. There is not a single day that I do not wish Fortran had macros beyond what can be currently done with FPP.

This was one of the reasons why I submitted issue #293 , this was motivated by people using macro preprocessors. Generics know the language, and can be much more expessive than (rather simple-minded) macros.

Could you maybe list your use cases there, so we could try to formulate them there?

@shahmoradi
Copy link

@tkoenig1 #293 is close to what I dreamed of having in Fortran someday, hopefully very soon. I encounter many instances almost daily where macros appear as the only solution, to my knowledge (with the alternative being code duplication). I am not talking about generic types here, merely code patterns. For example, many logical branches of BLAS/LAPACK routines that I have inspected differ only in some array indices order, e.g., (i, j) vs. (j, i). With macros or similar functionality, the size of those routines could be reduced multifold, making them more readable and maintainable with fewer opportunities for introducing bugs. Add generic programming to the routines to get another multifold size reduction for BLAS/LAPACK libraries and similar.

@klausler
Copy link

A compile-time static assert macro:

$ cat assert.F90
#define STATIC_ASSERT(x) block; real(merge(kind(1.),-1,(x))), parameter :: __fail = 1.; end block
STATIC_ASSERT(1 > 2)
end
$ f18 assert.F90
assert.F90:2:1: error: REAL(KIND=-1) is not a supported type
  STATIC_ASSERT(1 > 2)
  ^^^^^^^^^^^^^^^^^^^^
assert.F90:1:26: in a macro defined here
  #define STATIC_ASSERT(x) block; real(merge(kind(1.),-1,(x))), parameter :: __fail = 1.; end block
                           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
that expanded to:
  block; real(merge(kind(1.),-1,(1 > 2))), parameter :: __fail = 1.; end block
         ^

@certik
Copy link
Member Author

certik commented Apr 28, 2023

@klausler clever! Here is how to do with with gfortran:

$ 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)

@ivan-pi
Copy link

ivan-pi commented Apr 28, 2023

You can even add a message:

define STATIC_ASSERT(x,msg) block; character(kind=merge(kind('a'),-1,(x))), parameter :: fail = msg; end block
$ gfortran -cpp -ffree-line-length-none assert.f90 
assert.f90:2:69:

    2 | STATIC_ASSERT(storage_size(1.d0) >= 80,"double precision must be at least 80-bit")
      |                                                                     1
Error: Kind -1 is not supported for CHARACTER at (1)
$ gfortran -fdefault-real-16 -cpp -ffree-line-length-none assert.f90 
$ 

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Clause 10 Standard Clause 10: Expressions and assignment
Projects
None yet
Development

No branches or pull requests

10 participants