Postfix character size specifications

I am writing a handler to coalesce all attributes of an object into a single declaration. An issue arose with postfix length specifications for character variables. My test program is:

! ****************************************************************************************
! postfix_character_sizes.f90                  15-Apr-26                  John Collins
! ****************************************************************************************
!
! The Fortran standard allows constructs of the form:
!
!      CHARACTER(LEN=8) :: str*16
!
! where str gets a length of 16.  The (LEN=8) sets a default for a list.
!
! What can you put in the postfix size?
!
!                                       Integer Bare  Paren Param  Bare
!                                               Param Param Expr   Expr

!       gfortran 13.3.0                   OK    Fails   OK    OK   Fails
!       ifx 2025.2.1 20250806             OK     OK     OK    OK   Fails
!       lfortran 0.51.0, LLVM 19.1.1      OK     OK     OK    OK    OK
!
! ****************************************************************************************

PROGRAM postfix_character_size

        IMPLICIT NONE

        INTEGER,PARAMETER :: p4 = 4
        INTEGER,PARAMETER :: p8 = 8
        CHARACTER(LEN=20),PARAMETER :: numbers = "12345678901234567890"

!       Simple integer, should be OK
        CHARACTER(LEN=128) :: str2*2
!
!       Simple bare PARAMETER
        CHARACTER(LEN=128) :: str4*p4
!
!       Parenthesised PARAMETER
        CHARACTER(LEN=128) :: str8*(p8)
!
!       Parenthesised expression
        CHARACTER(LEN=128) :: str12*(p4+p8)

!       Un-parenthesised expression
        CHARACTER(LEN=128) :: str16*p8+p8

        str2 = numbers
        str4 = numbers
        str8 = numbers
        str12 = numbers
        str16 = numbers

        WRITE(*,'("str2:  ",A)')str2
        WRITE(*,'("str4:  ",A)')str4
        WRITE(*,'("str8:  ",A)')str8
        WRITE(*,'("str12: ",A)')str12
        WRITE(*,'("str16: ",A)')str16

END PROGRAM postfix_character_size ! *****************************************************

So: What is supposed to happen? What would we like to happen?

I would say that a bare expression should not be accepted. Of course the asterisk right after the variable name is not a multiplication operator, but the example requires me to do some mental arithmetic to parse it correctly. To be consistent, I would say that a bare parameter also constitutes an expression and should be rejected. In which case gfortran’s behaviour is the only correct one.

Just my two small-value coins, here.

Syntactically, R803 (J3/24-007) rule specified a char-length, which is, by R723, either a ( type-param-value ) or int-literal-constant. The latter must not contain a _ kind-param. The former, by R701 is scalar-int-expr, or *, or :. So, I would say, your *p4 and your *p8+p8 are outside the spec.

I was bothered by this - perhaps you meant that if one wrote:

CHARACTER (LEN=8) :: str1*16, str2

that str1 would have a length of 16 and str2 would be length 8. Both LEN= and *n specify the character length - you’re allowed to have both with the *n taking precedence.

Yes @sblionel, that is exactly right, though I find it ironic that

CHARACTER*8 thing which works on every compiler I use, is non-standard, but

CHARACTER :: thing*8 which doesn’t work everywhere is standard.

From our point of view this is largely academic since we have to process anything which works on the compilers in use, but we can convert the code to something widely accepted.

I think it unfortunate that the rules for expressions differ in different contexts - in arithmetic expressions, data statements, postfix character sizes, the integer arguments to I/O statements… The legacy rules won’t go away any time soon, but I feel that the Standard is moving towards providing more regular alternatives. I welcome this!

@Jcollins if LFortran is the only compiler that allows:

        CHARACTER(LEN=128) :: str12*(p4+p8)

!       Un-parenthesised expression
        CHARACTER(LEN=128) :: str16*p8+p8

and it is not valid Fortran, then we can restrict it. I don’t know why we did it this way, perhaps we reused a function that can handle any expression, but it should actually just be an integer parameter, nothing else, right?

That syntax is standard:
image
image
image

(Sorry for the multiple edits here.)

What isn’t standard is the use of * for other types, such as INTEGER*8

1 Like

int-literal-constant only - not even a named constant (parameter). (It can also be a parenthesized length parameter in a PDT.)

1 Like

I think this is right. Moreover, C803 requires that the type-param-value be a specification expression (if it’s not * or :).

So gfortran is correct to reject str4*p4 and str16*p8+p8. flang does likewise.

I think in f77, this was the only way to declare character variables. The keyword len= did not appear until f90.

I assume that there is a reason why a generic expression cannot be used in this context. May I ask why?

Type parameters participate in type-checking actual arguments and the compiler is obligated to prove that the actual fits (i.e. is “type compatible with”) the dummy (unlike array extents where the compiler is not similarly obligated). This is referred to as “TKR compatibility” and the thing to notice is that Shape is not part of it. Allowing a generic expression would require the compiler to run too much of the program at compile time in order to prove properties. Some reasonable cut had to be devised and that is known as “specification expression”. For anything that doesn’t fit, there is the assumed type-parameter option, *. If the expression ends up some “constant expression”, then you might as well go ahead and call it a kind-type parameter, and the compiler will jump with joy because there is little work to do.

I think the above is not allowed, must be int-literal-constant only, as @sblionel said. But what about this:

str4*(p4) and str16*(p8+p8)

Is that allowed, as long as p4 and p8 are parameters? The standard says char-length is either int-literal-constant or ( type-param-value )?

Separate question for @Jcollins: do you see this character :: a*4 syntax usage a lot in codes? I don’t remember seeing it myself. Is it used in legacy codes a lot?

As mentioned above:

1 Like

The :: part is f90, but the variable*4 part is a commonly used f77 feature. The use of parentheses in these declarations is tricky because parentheses are also used to declare arrays. The keyword (len=4) form (since f90) can be used to make it clear to a human, but the compiler must be able to disambiguate the two cases even without it. In most codes I’ve seen, programmers use either the character*4 variable convention or the variable*4 convention. I was unaware that a programmer could use both, with the latter overriding the former.

1 Like

Happily, fpt has a diagnostic for the situation where the user writes something of the form:

CHARACTER*8 alpha, beta*16, gamma

where the *16 supersedes the *8 after the CHARACTER keyword. (We have this, BTW, because the construct defeated an early version of a compiler widely in use). Checking our library, I found only 349 occurrences, all in a legacy package of 650K lines of fixed format code.

This didn’t trap the case where there is no length specification after the keyword CHARACTER, but a cunning use of grep didn’t find anything in over 5 million lines of code varying from extended Fortran 77 to at least Fortran 2018. It doesn’t happen often.

1 Like

I don’t think that parentheses will cause a problem because the postfix length specification is preceded by a * character and the array bounds are not.

I was referring there to using parentheses (4) instead of the *4 notation.

Here are some declarations with both character length and array size specified.

   character*4  :: a(3)
   character    :: b*4(3)
   character    :: c(3)*4
   character(4) :: d(3)
   character    :: e(len=4)(3)
   character    :: f(3)(len=4)
   character    :: g(3)(4)
   character(4), dimension(3) :: h

Some of these are allowed, and some are not. I sometimes get some of these wrong (e.g. the declaration of b is wrong) when I write codes, while some of the forms are clear to me and I would never get them wrong (e.g. a, d, and h). If I see one like c, I scratch my head and wonder if that is the legal one or if it is instead the b form that is correct. f is not legal, while c is, and I don’t really know why. g is not legal, and I guess I’m sort of glad that it isn’t; that would confuse humans, even if the compiler could always keep it straight.

Referencing a substring of an array element looks like c(2)(3:4) or c(2:3)(3:4), which dates back to f77 for the former and to f90 array syntax for the latter.

These issues could also arise to some extent when declaring arrays of parameterized derived types, but here the allowed declarations are more limited.

   type xyz(l)
      integer, len :: l
      integer :: array(l)
   end type xyz
   type(xyz(4)) :: i(3)
   type(xyz(l=4)), dimension(3) :: j

Here, even without the longer keyword form in the j declaration, I think the meaning of the i declaration is clear to both the compiler and a human.