I presume you’re talking about this: https://j3-fortran.org/doc/year/25/25-168r2.txt. It’s been incorporated into the latest draft of the standard: https://j3-fortran.org/doc/year/25/25-156r1.txt
@everythingfunctional Thanks. Looks like we have more things to prototype! I have a few questions.
Let’s take this example from the paper:
GENERIC FUNCTION fun(x) RESULT(y)
TYPE(type1), RANK(0:7) :: x
TYPEOF(x), RANK(RANK(x)) :: y
SELECT GENERIC RANK (y)
RANK (0)
!! code if y is a scalar
RANK (1:3)
!! code if y is an array of 1 to 3 dimensions
RANK DEFAULT
!! code if y is an array of 4 to 7 dimensions
END SELECT
END FUNCTION fun
Questions:
-
This will generate implementations for all ranks 0..7? And in general if there are more generic arguments, it will generate all combinations? Or can the compiler wait until instantiation and just generate those that are actually used in any given program?
-
Is the
SELECT GENERIC RANKconstruct always compile time? So this will generate a very efficient code? -
What is the committee’s (or your) opinion on how this relates to the template feature also scheduled for F28? Cannot one achieve similar results with the templates? Or is this feature complementary?
I’ll probably have more questions as we implement this.
- Yes, this will generate implementations for all ranks, 0..7, and if there are more generic arguments the full cross-product of combinations. In theory a compiler would only need to generate code for those that are actually used, but in practice I think compilers will generate all of them due to not knowing ahead of time how the generated objects files will be used.
- Yes, the
SELECT GENERIC RANKandSELECT GENERIC TYPEconstructs are always compile time. - The committee (and I) think that while there may be some overlap in use cases, they mostly cover different use cases, and can work together in some complementary ways. For the “auto generic procedures” you have to know all the types you want to work with ahead of time, but can be a bit more succinct about it. With templates you can work with types that you don’t yet know about, and can define new types and multiple procedures to be created by a single instantiation, but they tend to be a bit more work to construct.
@everythingfunctional excellent, this is super helpful. Let’s implement this now in compilers.
So I used AI and did couple iterations of generating as many tests as I could, both valid and invalid:
And there is also a document with exact syntax/semantic rules and lots of examples, much more detailed than the standard. This might be a good start when implementing it in a compiler, together with all the tests.
While doing it, a few things I noticed:
- The actual draft of the standard differs from 25-156r1.txt, so I went with the standard draft.
- There are some ambiguities, documented here. For example how
bind(c)should behave (allowed, not allowed, how should the C symbols be named, etc.). @everythingfunctional do you mind going over the list and see if anything should be fixed or clarified? A lot of them seem like corner cases, but still. - Some of the tests might still be wrong — once we actually implement it, we’ll resolve it for sure, sometimes it’s hard to tell just by looking at the code, as things can get quite involved.
Yeah, the draft standard is the authoritative reference.
Almost certainly it shouldn’t be allowed. I can see no valid interpretation given that it is not allowed to have multiple global identifiers with the same name (and that’s what bind(C, name="name") would imply), nor would it be useful if bind(C) picked processor dependent names for the specific.
That’s a long list, but I’ll see if I can get to it.
Let me give a sightly different answer to your first question.
There is no requirement in the standard for implementations of all versions to be generated. At link time, it will be known which versions are required so just these can be generated. Or with dynamic linking, the version can be generated at the time if it is not already available.