Passed-object dummy arguments
Consider the following module inspired by the multiple dispatch article on Wikipedia (https://en.wikipedia.org/wiki/Multiple_dispatch):
! c3.f90
module collisions
type, abstract :: SpaceObject
contains
procedure(collide_sub), deferred :: collide
end type
abstract interface
subroutine collide_sub(x,y)
import SpaceObject
class(SpaceObject) :: x,y
end subroutine
end interface
type :: Spaceship
contains
private
procedure, pass(x) :: p_collide_ss => collide_ss
procedure, pass(x) :: p_collide_sa => collide_sa
procedure, pass(y) :: p_collide_as => collide_as
generic, public :: collide => p_collide_ss, p_collide_sa, p_collide_as
end type
type :: Asteroid
end type
interface collide
module procedure collide_aa, collide_as, collide_sa, collide_ss
end interface
contains
subroutine collide_aa(x,y)
class(Asteroid) :: x
class(Asteroid) :: y
print *, "a/a"
end subroutine
subroutine collide_as(x,y)
class(Asteroid) :: x
class(Spaceship) :: y
print *, "a/s"
end subroutine
subroutine collide_sa(x,y)
class(Spaceship) :: x
class(Asteroid) :: y
print *, "s/a"
end subroutine
subroutine collide_ss(x,y)
class(Spaceship) :: x
class(Spaceship) :: y
print *, "s/s"
end subroutine
end module
program test
use collisions
type(Spaceship) :: s1, s2
type(Asteroid) :: a1, a2
print *, "using generic procedure"
call collide(a1,a2)
call collide(a1,s1)
call collide(s1,a1)
call collide(s1,s2)
print *, "using type-bound procedures"
call s1%collide(s2)
call s1%collide(x=a1)
call s1%collide(y=a1)
! call s1%collide(a1)
end program
The Fortran processors differ in their output:
$ gfortran-14 c3.f90
c3.f90:24:33:
24 | generic, public :: collide => p_collide_ss, p_collide_sa, p_collide_as
| 1
Error: 'collide_sa' and 'collide_as' for GENERIC 'collide' at (1) are ambiguous
$ ifort c3.f90
$ ./a.out
using generic procedure
a/a
a/s
s/a
s/s
using type-bound procedures
s/s
a/s
s/a
$ flang-new c3.f90
error: Semantic errors in c3.f90
./c3.f90:24:24: error: Generic 'collide' may not have specific procedures 'p_collide_sa' and 'p_collide_as' as their interfaces are not distinguishable
generic, public :: collide => p_collide_ss, p_collide_sa, p_collide_as
^^^^^^^
./c3.f90:46:12: Procedure 'p_collide_sa' of type 'spaceship' is bound to 'collide_sa'
subroutine collide_sa(x,y)
^^^^^^^^^^
./c3.f90:41:12: Procedure 'p_collide_as' of type 'spaceship' is bound to 'collide_as'
subroutine collide_as(x,y)
^^^^^^^^^^
$ flang-new --version
Homebrew flang-new version 19.1.4
Target: x86_64-apple-darwin23.6.0
Thread model: posix
InstalledDir: /usr/local/Cellar/flang/19.1.4/libexec
Configuration file: /usr/local/Cellar/flang/19.1.4/libexec/flang.cfg
Configuration file: /usr/local/etc/clang/x86_64-apple-darwin23.cfg
> nagfor c3.f90
NAG Fortran Compiler Release 7.2(Shin-Urayasu) Build 7203
Warning: c3.f90, line 40: Unused dummy variable X
Warning: c3.f90, line 40: Unused dummy variable Y
Warning: c3.f90, line 45: Unused dummy variable X
Warning: c3.f90, line 45: Unused dummy variable Y
Warning: c3.f90, line 50: Unused dummy variable X
Warning: c3.f90, line 50: Unused dummy variable Y
Warning: c3.f90, line 55: Unused dummy variable X
Warning: c3.f90, line 55: Unused dummy variable Y
[NAG Fortran Compiler normal termination, 8 warnings]
> ./a.out
using generic procedure
a/a
a/s
s/a
s/s
using type-bound procedures
s/s
a/s
s/a
If we now uncomment the last line in c3.f90:
call s1%collide(a1)
we also trigger different behaviors:
$ ifort c3.f90
c3.f90: error #5286: Ambiguous generic interface COLLIDE: previously declared specific procedure COLLISIONS::COLLIDE_SA is not distinguishable from this declaration. [COLLISIONS::COLLIDE_AS]
compilation aborted for c3.f90 (code 1)
> nagfor c3.f90
NAG Fortran Compiler Release 7.2(Shin-Urayasu) Build 7203
Warning: c3.f90, line 40: Unused dummy variable X
Warning: c3.f90, line 40: Unused dummy variable Y
Warning: c3.f90, line 45: Unused dummy variable X
Warning: c3.f90, line 45: Unused dummy variable Y
Warning: c3.f90, line 50: Unused dummy variable X
Warning: c3.f90, line 50: Unused dummy variable Y
Warning: c3.f90, line 55: Unused dummy variable X
Warning: c3.f90, line 55: Unused dummy variable Y
[NAG Fortran Compiler normal termination, 8 warnings]
(base) di75yas@cm4login1:~/dispatch> ./a.out
using generic procedure
a/a
a/s
s/a
s/s
using type-bound procedures
s/s
a/s
s/a
a/s
ifort does not want to make an ambiguous choice. nagfor selects the method p_collide_as for whatever reason that may be.
I suppose this could fall under a subcase of non-portability related to generic interface (https://github.com/klausler/fortran-wringer-tests/tree/main?tab=readme-ov-file#generic-interfaces).
In this example the combination of keyword arguments and types is needed to complete the analysis.
The Fortran standard, to the extent that it matters, requires that the specific procedures of a generic interface be distinguishable according to rules that it lays out, whether any particular reference to the generic procedure turns out to be ambiguous or not. This was a really good idea in Fortran '90 since it allowed one to write a generic library API that one could be assured would never lead to an ambiguous reference in any client of the library. Because some compilers don't implement these checks, or don't do so correctly, I sometimes get bug reports from users complaining that their code compiles fine with $OTHER_COMPILER, or asking that ambiguity checking only be done on a per-call basis.
Unfortunately, the rules for distinguishing specific procedures were no longer perfect after F'2003 added polymorphism to the language. So a compiler still has to check for ambiguity on each reference anyway now.