There is no need for (additional) name mangling here at all.
The procedures you write are completely ordinary normal module procedures. They just have a polymorphic dummy argument. You do not have to call them as type-bound, you can use them in a normal way.
What makes them type-bound is the creation of the binding in the type declaration.
procedure :: isFilled
that means there will be a virtual table and a pointer to the procedure in the virtual table and so on and it will point the right type-bound procedure for the right actual type.
But the procedures are completely ordinary ones. At least conceptually, I did not study the compiler, I am describing the standard concept.
In your case you can call
type(shape) :: instance
print *, isFilled(instance)
and it will work perfectly fine, there is nothing special about the procedure itself.
Note that when you are extending the type
type, extends(shape) :: outlined_shape
integer :: outline_color
contains
procedure :: isFilled => isFilled_outlined shape
end type shape
you have to use a different name for the procedure. Hence no name mangling is necessary, you already have a procedure with a different name
logical function isFilled_outlined_shape(this)
class(outlined_shape) :: this
Again, you can call it as a type bound procedure:
type(outlined_shape) :: instance
print *, instance%isFilled()
but you can also call it directly (non-virtually)
print *, isFilled_outlined_shape(instance)
and you can even call the parent one directly
print *, isFilled(instance)
The reason why C++ needs name mangling is because it uses the same name fir the virtual function in each type. It does not have these normal procedures that have different names as in Fortran. Because if that, C++ needs name-mangling.
Consider this example:
module m
type t1
contains
procedure :: s => s_t1
end type
type t2
contains
procedure :: s => s_t2
end type
contains
subroutine s_t1(self)
class(t1) :: self
end subroutine
subroutine s_t2(self)
class(t2) :: self
end subroutine
end module
In gfortran, the result contains these symbols:
000000000000000f T __m_MOD___copy_m_T1
0000000000000000 T __m_MOD___copy_m_T2
0000000000000000 B __m_MOD___def_init_m_T1
0000000000000001 B __m_MOD___def_init_m_T2
0000000000000029 T __m_MOD_s_t1
000000000000001e T __m_MOD_s_t2
0000000000000000 R __m_MOD___vtab_m_T1
0000000000000040 R __m_MOD___vtab_m_T2
their meaning is the following:
0000000000000029 T __m_MOD_s_t1
000000000000001e T __m_MOD_s_t2
the subroutine, you can call them directly this way (non-virtually).
000000000000000f T __m_MOD___copy_m_T1
0000000000000000 T __m_MOD___copy_m_T2
the intrinsic assignments of the two types.
0000000000000000 B __m_MOD___def_init_m_T1
0000000000000001 B __m_MOD___def_init_m_T2
the default constructors of the two types
0000000000000000 R __m_MOD___vtab_m_T1
0000000000000040 R __m_MOD___vtab_m_T2
the virtual table functions, one per type (not per procedure, it is not name-mangling of the procedures).
When you add more procedures, https://godbolt.org/z/3q4KWf you can see how the virtual tables contains pointers to them:
__m_MOD___vtab_m_T1:
.long 69979407
.zero 4
.quad 0
.quad 0
.quad __m_MOD___def_init_m_T1
.quad __m_MOD___copy_m_T1
.quad 0
.quad 0
.quad __m_MOD_r_t1
.quad __m_MOD_s_t1
__m_MOD___vtab_m_T2:
.long 69979408
.zero 4
.quad 0
.quad 0
.quad __m_MOD___def_init_m_T2
.quad __m_MOD___copy_m_T2
.quad 0
.quad 0
.quad __m_MOD_s_t2
.quad __m_MOD_r_t2