|
| 1 | +# PWR089: Remove unexpected arguments from procedure calls |
| 2 | + |
| 3 | +### Issue |
| 4 | + |
| 5 | +Calling a procedure while supplying excess arguments (i.e., more actual |
| 6 | +arguments than the procedure has dummy arguments) can lead to compilation |
| 7 | +failures or, if not caught, undefined behavior at runtime. |
| 8 | + |
| 9 | +### Actions |
| 10 | + |
| 11 | +Ensure the number of actual arguments supplied at the call site matches the |
| 12 | +number of dummy arguments in the procedure. If additional information is |
| 13 | +needed, adapt the procedure accordingly; for example, by adding new dummy |
| 14 | +arguments and handling them in the implementation. |
| 15 | + |
| 16 | +### Relevance |
| 17 | + |
| 18 | +When calling a Fortran procedure, the caller is expected to match the |
| 19 | +procedure's dummy argument list. If an explicit procedure interface is |
| 20 | +available at the call site, compilers can typically detect and report a |
| 21 | +mismatch in the number of supplied arguments during compilation. |
| 22 | + |
| 23 | +However, Fortran also allows calling procedures without information about the |
| 24 | +expected arguments; in such cases, an _implicit interface_ is used. The caller |
| 25 | +effectively passes a list of memory addresses, which the called procedure |
| 26 | +interprets according to its dummy argument declarations. If the actual |
| 27 | +arguments exceed the dummy arguments, the program is exposed to undefined |
| 28 | +behavior. |
| 29 | + |
| 30 | +A common misconception is that excess arguments are simply ignored. This may |
| 31 | +appear to work for some procedures, but it is not guaranteed. More |
| 32 | +specifically, some Fortran features can cause the compiler to supply hidden |
| 33 | +values to the called procedure; for example, character lengths, or array |
| 34 | +descriptors. If excess arguments are provided, those hidden values can be |
| 35 | +misinterpreted, resulting in incorrect results, "random" behavior, or even |
| 36 | +crashes. |
| 37 | + |
| 38 | +> [!TIP] |
| 39 | +> To enhance code safety and reliability, ensure procedures provide |
| 40 | +> explicit interfaces to their callers. Check the [PWR068 entry](../PWR068/) |
| 41 | +> for more details! |
| 42 | +
|
| 43 | +### Code examples |
| 44 | + |
| 45 | +The following example logs diagnostics for a simulation data field. The called |
| 46 | +procedure expects three arguments, but the caller accidentally passes an |
| 47 | +additional verbosity argument: |
| 48 | + |
| 49 | +```fortran showLineNumbers |
| 50 | +! io.f90 |
| 51 | +subroutine logField(name, step, value) |
| 52 | + use iso_fortran_env, only: real32 |
| 53 | + implicit none |
| 54 | +
|
| 55 | + character(*), intent(in) :: name |
| 56 | + integer, intent(in) :: step |
| 57 | + real(kind=real32), intent(in) :: value |
| 58 | +
|
| 59 | + write(*, *) trim(name), " step=", step, " value=", value |
| 60 | +end subroutine logField |
| 61 | +``` |
| 62 | + |
| 63 | +```fortran {6,10,17} showLineNumbers |
| 64 | +! example.f90 |
| 65 | +program call_with_excess_arguments |
| 66 | + use iso_fortran_env, only: real32 |
| 67 | + implicit none |
| 68 | +
|
| 69 | + external :: logField |
| 70 | + character(len=20) :: name |
| 71 | + integer :: step |
| 72 | + real(kind=real32) :: val |
| 73 | + logical :: verbose |
| 74 | +
|
| 75 | + name = "field" |
| 76 | + step = 10 |
| 77 | + val = 3.14 |
| 78 | + verbose = .true. |
| 79 | +
|
| 80 | + call logField(name, step, val, verbose) |
| 81 | +end program call_with_excess_arguments |
| 82 | +``` |
| 83 | + |
| 84 | +Because `logField()` is an `external` procedure, the call uses an implicit |
| 85 | +interface. Compilers will typically allow this program to compile despite the |
| 86 | +argument count mismatch, which can corrupt the call frame if hidden information |
| 87 | +is passed for the `character` argument. In fact, a crash is triggered at |
| 88 | +runtime: |
| 89 | + |
| 90 | +```txt |
| 91 | +$ gfortran --version |
| 92 | +GNU Fortran (Ubuntu 13.3.0-6ubuntu2~24.04) 13.3.0 |
| 93 | +$ gfortran io.f90 example.f90 |
| 94 | +$ ./a.out |
| 95 | +
|
| 96 | +Program received signal SIGSEGV: Segmentation fault - invalid memory reference. |
| 97 | +
|
| 98 | +Backtrace for this error: |
| 99 | +#0 0x76abe9223e59 in ??? |
| 100 | +#1 0x76abe9222e75 in ??? |
| 101 | +#2 0x76abe8e4532f in ??? |
| 102 | + at ./signal/../sysdeps/unix/sysv/linux/x86_64/libc_sigaction.c:0 |
| 103 | +#3 0x76abe94d20f9 in ??? |
| 104 | +#4 0x76abe94d2194 in ??? |
| 105 | +#5 0x63e416093238 in ??? |
| 106 | +#6 0x63e416093359 in ??? |
| 107 | +#7 0x63e416093395 in ??? |
| 108 | +#8 0x76abe8e2a1c9 in __libc_start_call_main |
| 109 | + at ../sysdeps/nptl/libc_start_call_main.h:58 |
| 110 | +#9 0x76abe8e2a28a in __libc_start_main_impl |
| 111 | + at ../csu/libc-start.c:360 |
| 112 | +#10 0x63e4160930f4 in ??? |
| 113 | +#11 0xffffffffffffffff in ??? |
| 114 | +``` |
| 115 | + |
| 116 | +A simple solution is to remove the excess `verbose` argument from the procedure |
| 117 | +call: |
| 118 | + |
| 119 | +```fortran {15} showLineNumbers |
| 120 | +! solution.f90 |
| 121 | +program correct_call |
| 122 | + use iso_fortran_env, only: real32 |
| 123 | + implicit none |
| 124 | +
|
| 125 | + external :: logField |
| 126 | + character(len=20) :: name |
| 127 | + integer :: step |
| 128 | + real(kind=real32) :: val |
| 129 | +
|
| 130 | + name = "field" |
| 131 | + step = 10 |
| 132 | + val = 3.14 |
| 133 | +
|
| 134 | + call logField(name, step, val) |
| 135 | +end program correct_call |
| 136 | +``` |
| 137 | + |
| 138 | +Now, the program will behave correctly: |
| 139 | + |
| 140 | +```txt |
| 141 | +$ gfortran io.f90 solution.f90 |
| 142 | +$ ./a.out |
| 143 | + field step= 10 value= 3.14000010 |
| 144 | +``` |
| 145 | + |
| 146 | +Ultimately, it is recommended to encapsulate all procedures within modules so |
| 147 | +that callers have explicit interfaces. This enables the compiler to verify the |
| 148 | +provided arguments against the actual dummy arguments, preventing |
| 149 | +difficult-to-diagnose runtime bugs. |
| 150 | + |
| 151 | +> [!TIP] |
| 152 | +> Check the [PWR068 entry](../PWR068/) for more details on implicit and |
| 153 | +> explicit interfaces! |
| 154 | +
|
| 155 | +### Related resources |
| 156 | + |
| 157 | +- [PWR089 |
| 158 | + examples](https://github.com/codee-com/open-catalog/tree/main/Checks/PWR089/) |
| 159 | + |
| 160 | +### References |
| 161 | + |
| 162 | +- ["Implicit |
| 163 | +Interfaces"](https://people.cs.vt.edu/~asandu/Courses/MTU/CS2911/fortran_notes/node44.html), |
| 164 | +Adrian Sandu. [last checked February 2026] |
| 165 | + |
| 166 | +- ["More on Implicit |
| 167 | +Interfaces"](https://people.cs.vt.edu/~asandu/Courses/MTU/CS2911/fortran_notes/node181.html), |
| 168 | +Adrian Sandu. [last checked February 2026] |
| 169 | + |
| 170 | +- ["Explicit |
| 171 | +Interfaces"](https://people.cs.vt.edu/~asandu/Courses/MTU/CS2911/fortran_notes/node182.html), |
| 172 | +Adrian Sandu. [last checked February 2026] |
| 173 | + |
| 174 | +- ["Doctor Fortran Gets |
| 175 | +Explicit!"](https://web.archive.org/web/20130803094211/http://software.intel.com/en-us/forums/topic/275071), |
| 176 | +Steve Lionel. [last checked February 2026] |
| 177 | + |
| 178 | +- ["Doctor Fortran Gets Explicit - Again! |
| 179 | +"](https://web.archive.org/web/20130113070703/http://software.intel.com/en-us/blogs/2012/01/05/doctor-fortran-gets-explicit-again), |
| 180 | +Steve Lionel. [last checked February 2026] |
0 commit comments