Skip to main content
GitHub

MSC01-F. Avoid using legacy or obsolescent Fortran constructs

Programmers shall avoid using features identified as "deleted" or "obsolescent" by the Fortran standard, as well as legacy constructs that reduce clarity, maintainability, or compiler optimization potential.

Modern Fortran standard classifies legacy features into two categories:

  1. Deleted features. Redundant or removed constructs that are no longer standard-conforming (e.g., PAUSE , ASSIGN ).
  2. Obsolescent features. Redundant constructs for which safer or clearer alternatives exist (e.g., COMMON blocks, EQUIVALENCE , Arithmetic IF ).

Relying on these features increases the risk of errors, reduces portability, and can inhibit compiler optimizations.

Deleted Features

These constructs are no longer part of the Fortran standard and are not permitted in strictly conforming modern code:

  • Non-block DO loops (implicit DO without END DO or CONTINUE ).
  • Arithmetic IF .
  • ASSIGN and assigned GO TO statements.
  • PAUSE statement.
  • H edit descriptor.
  • Real and double precision DO loop control variables.
  • Branching to END IF from outside its block.
  • Assigned FORMAT specifier.

Obsolescent Features

These constructs are still supported by many compilers for backward compatibility, but are considered outdated and should be replaced with modern alternatives:

  • Control flow
    • Alternate return ( CALL sub(arg, 10, 20, ...) with numerical labels embedded).
    • Computed GOTO statements.
    • Statement functions.
  • Data and data sharing
    • COMMON blocks.
    • EQUIVALENCE statements.
  • Program structure
    • ENTRY statement for multiple entry points.
    • BLOCK DATA program units.
  • Looping and branching
    • Labeled DO loops (using numeric labels instead of structured END DO ).
    • FORALL construct and statement.
  • Legacy syntax and definitions
    • Specific names for intrinsic procedures that duplicate generic functionality (e.g., DSIN vs SIN ).
    • DATA statements interspersed within executable code (considered obsolescent; use initialization in declarations instead).
    • Assumed-length character functions (deprecated approach; prefer explicit length or modern procedures).
    • Fixed source form (classic column-based formatting).
    • The CHARACTER*length declaration syntax.

Noncompliant Code Example

The arithmetic IF statement branches to one of three labels depending on whether an expression is negative, zero, or positive. This construct, common in early Fortran, obscures the control flow and makes the code difficult to read and maintain

Non-compliant code
program arithmetic_if
  implicit none
  integer :: i, x(10)
    
  i = 1
  ! Noncompliant: Labeled loop with Arithmetic IF
10 continue
  x(i) = i * 10
  i = i + 1
  if (i - 11) 10, 20, 30

20 continue
  print *, "Final X:", x
  stop

30 continue
  print *, "Error: out of bounds"
end program arithmetic_if

Compliant Solution

This compliant solution demonstrates a bounded DO loop accessing an array strictly within its declared limits. The loop iteration space matches the array bounds, ensuring no out-of-range subscripting occurs and preserving defined program behavior.

Compliant code
program compliant_arithmetic_if
  implicit none
  integer :: i, x(10)

  ! Compliant: Modern DO loop with block IF logic implicit in bounds
  do i = 1, 10
    x(i) = i * 10
  end do

  print *, "Final X:", x
end program compliant_arithmetic_if

Noncompliant Code Example

Alternate returns allow a subroutine to control the execution flow of the calling program by branching to labels passed as arguments. This is an obsolete feature that results in unstructured control flow.

Non-compliant code
subroutine check_status(val, *, *)
  integer, intent(in) :: val
  if (val < 0) return 1
  if (val == 0) return 2
end subroutine check_status

program alternate_return
    integer :: v = -1
    ! Noncompliant: Passing labels (*10, *20) as arguments
    call check_status(v, *10, *20)
    print *, "Positive"
    goto 30
10  print * , "Negative"
    goto 30
20  print *, "Zero"
30  continue
end program alternate_return

Compliant Solution

The compliant solution returns an integer status code, and the calling program uses a SELECT CASE construct to handle control flow. This decouples the subroutine from the caller's line numbers and keeps the control flow logic within the caller.

Compliant code
subroutine check_status(val, status)
  integer, intent(in) :: val
  integer, intent(out) :: status

  if (val < 0) then
    status = 1
  else if (val == 0) then
    status = 2 
  else
    status = 0
 end if
end subroutine check_status

program alternate_return
  integer :: v = -1, code

  call check_status(v, code)

  ! Compliant: Structured handling of return status
  select case (code)
  case (1)
    print *, "Negative"
  case (2)
    print *, "Zero"
  case default
    print *, "Positive"
  end select
end program alternate_return

Risk Assessment

Legacy constructs often rely on implicit behaviors (like storage association in EQUIVALENTE and COMMON ) or unstructured jumps ( GO TO , Arithmetic IF ). These features prevent modern compilers from performing rigorous type checking, vectorization, and parallelization. They also significantly increase the cognitive load required to maintain the software.

RuleSeverityLikelihoodRemediation CostPriorityLevel
MSC01-FMediumlikelyMediumP12L1

Bibliography

[ Intel Fortran Compiler ]

Attachments: