On Wednesday, February 3, 2021 at 7:22:43 AM UTC-5,
gro...@gmail.com wrote:
> ..
> Is this a loophole in the standard? It seems like depending on whether TYP has or has not a defined assignment, the behavior can be strikingly different, which looks like a mistake to me. ..
@Dominik,
I suggest you try the Fortran Discourse site for such discussions mainly because code formatting and syntax highlighting can be preserved:
https://fortran-lang.discourse.group/
As pointed out by @gah4, it's more like a "feature" in the standard. Considering the semantics of ELEMENTAL generally (usually pure, shape matching, or one a scalar) and polymorphic arguments (cannot be INTENT(OUT) in pure procedures) and type-bound procedures (passed dummy argument has to be polymorphic but not INTENT(OUT) nor ALLOCATABLE), you effectively need to stay away from generic binding for ASSIGNMENT(=) (and other defined operations) IF YOU need the same facilities as intrinsic assignment such as allocation and/or reallocation of objects of RANK 1 and greater.
Better if you can manage with intrinsic assignments, but if you require defined assignments due to some requirements with special instructions, then go with Fortran 90 style generic interfaces which you can combine with Fortran 2018 to achieve a bit of code brevity..
Shown below is a variant of your code, I suggest you review it closely. By the way, Intel compiler with support of full Fortran 2018 (bugs notwithstanding) as available for FREE in their Intel oneAPI toolkit is what the code below runs with:
--- begin modified example ---
module m
type :: typ
integer :: a, b
contains
procedure :: assign
! comment/uncomment this line
!generic :: assignment(=) => assign
end type
interface typ
module procedure ctor
end interface
generic :: assignment(=) => rank1_assign, rank2_assign !<-- Use generic interface instead of bindings with TBP
contains
elemental function ctor(n) result(self)
type(typ) :: self
integer, intent(in) :: n
integer :: i
self%a = n
self%b = n**2
end function
elemental subroutine assign(self, them)
class(typ), intent(inout) :: self
type(typ), intent(in) :: them
self%a = them%a
self%b = them%b
end subroutine
subroutine rank1_assign(self, them)
type(typ), allocatable, intent(inout) :: self(:)
type(typ), intent(in) :: them(:)
! Code below can be in an INCLUDE file
!Local variables
logical :: realloc
print *, "In rank1_assign:"
realloc = .true.
if ( allocated(self) ) then
if ( size(self) == size(them) ) realloc = .false.
if ( realloc ) deallocate( self )
end if
if ( realloc ) then
allocate( self, source=them )
else
call self%assign( them )
end if
end subroutine rank1_assign
subroutine rank2_assign(self, them)
type(typ), allocatable, intent(inout) :: self(:,:)
type(typ), intent(in) :: them(:,:)
! Code below can be in an INCLUDE file
!Local variables
logical :: realloc
print *, "In rank2_assign:"
realloc = .true.
if ( allocated(self) ) then
if ( size(self) == size(them) ) realloc = .false.
if ( realloc ) deallocate( self )
end if
if ( realloc ) then
allocate( self, source=them )
else
call self%assign( them )
end if
end subroutine rank2_assign
end module m
program test
use m
blk1: block
type(typ), allocatable :: arr(:)
print *, "Block 1: cases with Rank 1 and *RE*allocation on assignment"
allocate(arr(2))
arr(1) = typ(1)
arr(2) = typ(2)
print *, size(arr), ':', arr
arr = [arr, typ(3)]
print *, size(arr), ':', arr
arr = [typ(4)]
print *, size(arr), ':', arr
end block blk1
print *
blk2: block
type(typ), allocatable :: arr(:)
print *, "Block 2: cases with Rank 1 and *A*llocation on assignment"
allocate(arr(2))
arr(1) = typ(1)
arr(2) = typ(2)
print *, size(arr), ':', arr
deallocate(arr)
arr = [typ(1), typ(2)]
print *, size(arr), ':', arr
end block blk2
print *
blk3: block
type(typ), allocatable :: arr(:,:)
print *, "Block 3: cases with Rank 2 and *A*llocation on assignment"
allocate(arr(2,2))
arr(1,1) = typ(1)
arr(2,1) = typ(2)
arr(1,2) = typ(3)
arr(2,2) = typ(4)
print *, shape(arr), ':', arr
deallocate(arr)
arr = reshape( [typ(1), typ(2), typ(3),typ(4)], shape=[2,2] )
print *, shape(arr), ':', arr
end block blk3
end program
--- end example ---
Program output with Intel Fortran Classic as part of Intel oneAPI:
--- begin output ---
C:\Temp>ifort /standard-semantics /warn:all /stand:f18 a.f90
Intel(R) Fortran Intel(R) 64 Compiler Classic for applications running on Intel(R) 64, Version 2021.1.2 Build 20201208_000000
Copyright (C) 1985-2020 Intel Corporation. All rights reserved.
a.f90(22): remark #7712: This variable has not been used. [I]
integer :: i
-----------------^
Microsoft (R) Incremental Linker Version 14.26.28806.0
Copyright (C) Microsoft Corporation. All rights reserved.
-out:a.exe
-subsystem:console
a.obj
C:\Temp>a.exe
Block 1: cases with Rank 1 and *RE*allocation on assignment
2 : 1 1 2 4
In rank1_assign:
3 : 1 1 2 4 3 9
In rank1_assign:
1 : 4 16
Block 2: cases with Rank 1 and *A*llocation on assignment
2 : 1 1 2 4
In rank1_assign:
2 : 1 1 2 4
Block 3: cases with Rank 2 and *A*llocation on assignment
2 2 : 1 1 2 4 3 9 4 16
In rank2_assign:
2 2 : 1 1 2 4 3 9 4 16
C:\Temp>
--- end output ---