Intel® Fortran Compiler
Build applications that can scale for the future with optimized code designed for Intel® Xeon® and compatible processors.

Generic assignment(=) can't distinguish between scalar and vector for infinite polymorphic objects

Brian_S_2
Beginner
584 Views

 

I've recently been getting into Fortran's OO capabilities (chiefly the ones in the F2003 standard).  In particular, I've been using infinite polymorphism in my code, but I've found a limitation I can't find an elegant solution to.  For example, if I have the following (redacted) code in a module,

interface assignment(=)
     procedure    ::    scalar_proc
     procedure    ::    vector_proc
end interface

contains

     subroutine scalar_proc(s, a)
           class(*)   ::    s
           type(some_type)   ::  a
       ..... set s to some scalar component of a....
     end subroutine

     subroutine vector_proc(v, b)
          class(*)    ::    v(:)
          type(some_type)   ::  b
      ..... set s to some vector component of a....
     end subroutine

I find that the compiler regards this as an ambiguous interface.  However, if I use intrinsic or derived types instead of "class(*)", the compiler has no issues.  Thus, I'm worried that my only way around this is to write a separate routine for each combination of input data type AND rank.  Is there a better way than that?


        

 

0 Kudos
1 Solution
Steven_L_Intel1
Employee
584 Views

I would not be as trusting of the Intel compiler as you are here - sometimes we get it wrong too. A version older than 14 might get this wrong. (This is why it REALLY helps if, when describing a potential compiler bug, you tell us exactly which version you're using!)

The rule about distinguishing dummy arguments in a generic interface is this:

- they are both data objects or known to be functions, and neither is TKR compatible with the other,

...

A dummy argument is type, kind, and rank compatible, or TKR compatible, with another dummy argument if the first is type compatible with the second, the kind type parameters of the first have the same values as the corresponding kind type parameters of the second, and both have the same rank or either is assumed-rank.

s and v are type and kind compatible, because they are class(*), but they don't have the same rank (and aren't assumed-rank, a F2015 concept), so they are not TKR compatible and thus are distinguishable.

Now it gets a bit more complicated with defined assignment, which is where gfortran might be having trouble.  Quoting from the F2015 draft here:

4 2 A subroutine defines the defined assignment x1 = x2 if
5 (1) the subroutine is specified with a SUBROUTINE (12.6.2.3) or ENTRY (12.6.2.6) statement that specifies
6 two dummy arguments, d1 and d2,
7 (2) either
8 (a) a generic interface (12.4.3.2) provides the subroutine with a generic-spec of ASSIGNMENT (=),
9 or
10 (b) there is a generic binding (4.5.5) in the declared type of x1 or x2 with a generic-spec of
11 ASSIGNMENT (=) and there is a corresponding binding to the subroutine in the dynamic
12 type of x1 or x2, respectively,
13 (3) the types of d1 and d2 are compatible with the dynamic types of x1 and x2, respectively,
14 (4) the type parameters, if any, of d1 and d2 match the corresponding type parameters of x1 and x2,
15 respectively, and
16 (5) either
17 (a) the ranks of x1 and x2 match those of d1 and d2 or
18 (b) the subroutine is elemental, x1 and x2 are conformable, and there is no other subroutine that
19 defines the assignment.

With intrinsic assignment, you can say array=scalar, but with defined assignment, this only works if you have a subroutine that has an array of the proper rank for the first argument and a scalar for the second, or if not that, the subroutine is ELEMENTAL. I could imagine a compiler getting tripped up on this and thinking these two procedures were ambiguous when they're not. (And even if the scalar version were elemental it would still not be ambiguous!)

I will send this example to one of the gfortran developers I know to see what he thinks.

View solution in original post

0 Kudos
6 Replies
Steven_L_Intel1
Employee
584 Views

I can't reproduce the problem with a test based on your "redacted" snippet. Please post an actual, compilable source that shows the problem.

0 Kudos
Brian_S_2
Beginner
584 Views

 

Hi, Steve,

Yes, I was being lazy when I wrote my question.  I've attached a file that demonstrates the "problem", if it is one.  When I try to compile it, the compiler complains that the 'vector_assign' and 'scalar_assign' procedures ambiguous for the generic assignment operator.

I think what I'm trying to do is simply ill-conceived, though.  I have to accept that I need to write separate procedures for each combination of scalar or vector with integer, real, logical, character, or whatever derived type I choose.  I had hoped for some way to pair that down.

Thanks,

Brian

0 Kudos
Steven_L_Intel1
Employee
584 Views

This compiles successfully for me as well, from versions 14-17.  Please show me the result of compiling this source with the -logo switch.

0 Kudos
Brian_S_2
Beginner
584 Views

 

Hi, Steve,

I'm on the road for the next week or so, and I don't have access to the Intel compiler that the group I'm working with part-time has.  However, I do believe the version they're using is older than 14.

On my machine, I only have gfortran 6.2.  I tried it with that compiler, and it bombs out, too, with this message:

test.f90:12:33:

   generic    ::  assignment(=) => scalar_assign, vector_assign
                                 1
Error: ‘scalar_assign’ and ‘vector_assign’ for GENERIC ‘=’ at (1) are ambiguous

Of course, GNU is generally behind Intel, so this may not be surprising.  Since you're able to compile it, I think that tells me what I most wanted to know: whether this is allowed in the standard.

Thanks,

Brian

0 Kudos
Steven_L_Intel1
Employee
585 Views

I would not be as trusting of the Intel compiler as you are here - sometimes we get it wrong too. A version older than 14 might get this wrong. (This is why it REALLY helps if, when describing a potential compiler bug, you tell us exactly which version you're using!)

The rule about distinguishing dummy arguments in a generic interface is this:

- they are both data objects or known to be functions, and neither is TKR compatible with the other,

...

A dummy argument is type, kind, and rank compatible, or TKR compatible, with another dummy argument if the first is type compatible with the second, the kind type parameters of the first have the same values as the corresponding kind type parameters of the second, and both have the same rank or either is assumed-rank.

s and v are type and kind compatible, because they are class(*), but they don't have the same rank (and aren't assumed-rank, a F2015 concept), so they are not TKR compatible and thus are distinguishable.

Now it gets a bit more complicated with defined assignment, which is where gfortran might be having trouble.  Quoting from the F2015 draft here:

4 2 A subroutine defines the defined assignment x1 = x2 if
5 (1) the subroutine is specified with a SUBROUTINE (12.6.2.3) or ENTRY (12.6.2.6) statement that specifies
6 two dummy arguments, d1 and d2,
7 (2) either
8 (a) a generic interface (12.4.3.2) provides the subroutine with a generic-spec of ASSIGNMENT (=),
9 or
10 (b) there is a generic binding (4.5.5) in the declared type of x1 or x2 with a generic-spec of
11 ASSIGNMENT (=) and there is a corresponding binding to the subroutine in the dynamic
12 type of x1 or x2, respectively,
13 (3) the types of d1 and d2 are compatible with the dynamic types of x1 and x2, respectively,
14 (4) the type parameters, if any, of d1 and d2 match the corresponding type parameters of x1 and x2,
15 respectively, and
16 (5) either
17 (a) the ranks of x1 and x2 match those of d1 and d2 or
18 (b) the subroutine is elemental, x1 and x2 are conformable, and there is no other subroutine that
19 defines the assignment.

With intrinsic assignment, you can say array=scalar, but with defined assignment, this only works if you have a subroutine that has an array of the proper rank for the first argument and a scalar for the second, or if not that, the subroutine is ELEMENTAL. I could imagine a compiler getting tripped up on this and thinking these two procedures were ambiguous when they're not. (And even if the scalar version were elemental it would still not be ambiguous!)

I will send this example to one of the gfortran developers I know to see what he thinks.

0 Kudos
Steven_L_Intel1
Employee
584 Views

I filled in the source with a program that uses the assignments. Seems to work fine.

module test

	implicit none

	type my_type
		class(*), allocatable	::		scal
		class(*), allocatable	::		vec(:)
	contains
		procedure, pass(m)		::		scalar_assign
		procedure, pass(m)		::		vector_assign
		generic				::		assignment(=) => scalar_assign, vector_assign
	end type

contains

	subroutine scalar_assign(s, m)
		class(*), allocatable, intent(out)		::		s
		class(my_type), intent(in)			::		m
		integer							::		i

		allocate(s, source=m%scal)

	end subroutine
	
	subroutine vector_assign(v, m)
		class(*), allocatable, intent(out)		::		v(:)
		class(my_type), intent(in)			::		m

		allocate(v, source=m%vec)

	end subroutine

end module test
program U702749
use test
implicit none

class(*), allocatable :: anyscalar
class(*), allocatable :: anyarray(:)
type(my_type) :: mt

allocate (mt%scal,source=1)
allocate (mt%vec(3),source=[2.,3.,4.])

anyscalar = mt
anyarray = mt

select type (anyscalar)
type is (integer)
  print *, "Correct type for anyscalar, 1=",anyscalar
class default
  print *, "Unknown type for anyscalar"
end select

select type (anyarray)
type is (real)
  print *, "Correct type for anyarray, 2. 3. 4. =",anyarray
class default
  print *, "Unknown type for anyarray"
end select

end program U702749
C:\Projects>U702749.exe
 Correct type for anyscalar, 1=           1
 Correct type for anyarray, 2. 3. 4. =   2.000000       3.000000       4.000000

I did send this to a gfortran developer for his comments.

0 Kudos
Reply