there is an issue, I've found with user defined assignment when using the ifort compiler (version 16.0). Let's have a basic type with user defined assignment (as TBP) and an extending type which does not define its own assignment. When a non-polymorphic instance of the extended type is defined, an assignment to it invokes the user defined assignment of the base type. When I understood the explanations of Malcom Cohen (as response to my nagfor compiler bug report in this matter), this should, however, not happen. So, in the example below, the assignBasic() routine should not be invoked (as it happens in the ifort compiled binary).
module typedef implicit none type :: Basic contains procedure :: assignBasic generic :: assignment(=) => assignBasic end type Basic type, extends(Basic) :: Extended end type Extended contains subroutine assignBasic(this, other) class(Basic), intent(out) :: this type(Basic), intent(in) :: other print "(A)", "assignBasic invoked" end subroutine assignBasic end module typedef program test use typedef implicit none type(Extended) :: ext print "(A)", "Non-polymorphic assignment" ext = Extended() end program test
I think the behaviour is subject to interp F08/0146.
The issue is that the `other` dummy argument of the subroutine implementing defined assignment is not polymorphic, not the polymorphic/non-polymorphic nature of the thing being assigned. When it is not polymorphic, that `other` dummy argument, which is of type `Base`, is not type compatible with a right hand side object of type Extended. Consequently the overall assignment on line 32 is intrinsic.
But the process of intrinsic assignment invokes defined assignment for components of the type. Depending on F08/0146, this includes the parent component, which is of type `Base` and is therefore type compatible with the `other` dummy argument.
I think it makes little sense for the language to not consider defined assignment for the parent component ahead of subcomponents of that parent component i.e., I hope the result of the interp is such that you should see "assignBasic invoked". As a principle, objects of derived type should not break up into their components if there is the possibility of operating on the object as an aggregate.
As always, Ian can formulate the problem much more accurate, than I. Ian, I really appreciate your capability to reformulate my confusing explanations to a clear statement about the real problem. :-)
Just out of curiosity: The interpretation you refer to (the one you hope for), has that been already decided on in any form? Or is it something which is still being debated? Just because from the answer I got from Malcom Cohen, I had the feeling, he wants to point out explicitely, that intrinsic assignment is done nonancestor-componentwise, so assignBasic should not be invoked in the example above. But I'd for sure find your interpretation more satisfying, provided I can rely on the fact, that all compilers do it the same way.
For everyone's information, here's the content of interp F08/0146 including committee member Tom Clune's view of "how it should work", which Malcolm Cohen of NAG disagrees with. This is likely to be revisited at next month's meeting.
NUMBER: F08/0146 TITLE: Does intrinsic assignment copy inherited components twice? KEYWORDS: Intrinsic assignment, Type extension DEFECT TYPE: Erratum STATUS: J3 consideration in progress QUESTION: Consider Type base Integer a End Type Type,Extends(base) :: ext Real b End Type ... Type(ext) x,y ... x = y According to 18.104.22.168 Intrinsic assignment statement, p13, "An intrinsic assignment where the variable is of derived type is performed as if each component of the variable were assigned from the corresponding component of <expr>" This would seem to indicate that the above assignment "x = y" is interpreted as the following (in unspecified order): x%a = y%a x%b = y%b x%base = y%base and the assignment to x%base is treated as if it were x%base%a = y%base%a thus assigning to x%a twice, which does not seem to make sense. If a type is extended more than once, there can be a plethora of ancestor components inheritance-associated with a component, resulting in that component being assigned many times. Q1. Are these per-component assignment semantics intended to apply to ancestor components (and thus produce multiple assignments for inherited components). It is particularly problematic if the components have type-bound defined assignment, for example consider the program Module damod Type datype Contains Procedure asgnda Generic :: Assignment(=) => asgnda End Type Contains Subroutine asgnda(a,b) Class(datype),Intent(Out) :: a Class(datype),Intent(In) :: b Print *,'Hello asgnda' End Subroutine End Module Module types Use damod Type base Type(datype) c End Type Type,Extends(base) :: ext End Type End Module Program test Use types Type(ext) :: x = ext(datype()), y y = x End Program Q2. Does this program print "Hello asgnda" once or twice? ANSWER: A1. This sentence was not intended to apply to ancestor components; an edit is supplied to correct this oversight. A2. The program should print "Hello asgnda" only once. EDITS to 10-007r1: [156:3] 22.214.171.124 Intrinsic assignment statement, p13, Between "performed as if each" and "component" insert "nonancestor". SUBMITTED BY: Malcolm Cohen HISTORY: 15-218 m208 F08/0146 Submitted m208 Failed J3 meeting vote 3-4 << start of Tom Clune's comments on F08/0146>> expect defind assignment to work like finalization on the parent of an extended type [15-007r2 126.96.36.199 78:19] - if the parent has defined assignment, it is called on the parent and not on its components. From: Tom Clune Sent: Friday, October 16, 2015 9:41 AM Attached below is a motivating example of my objection to the interp we discussed on Tuesday. (Paper 218 I think.) I also have a few additional arguments to bolster my case: 1) If I understand correctly, the semantics of finalization work on the base object, not on the components of the base object. In 15-007r2.pdf 188.8.131.52 The finalization process (78:19), we have: "If the entity is of extended type and the parent type is finalizable, the parent component is finalized." The proposed interp seems to run contrary to this treatment of the base as a coherent entity. 2) Not that it really matters, but C++ implicit copy/assignment semantics also use the copy/assignment operator for the base object, not its components. 3) Just to reiterate my point on Tuesday. The proposed interp in 218 adds considerable burden to implementors of type extensions. If a base type overrides default assignment, then the type extension almost certainly must as well, or risk ruining some invariant property that the base otherwise preserves. Further, this burden propagates down the inheritance hierarchy. Perhaps on some occasions, the implementor may find that default assignment is actually ok, but it would require looking "inside" the base type implementation. (And I cannot construct a plausible motivating example of this situation, though I've also not tried very hard.) ! The following is an example of how many users intuitively expect ! instrinsic assignment of an extended type when the ancestor type ! has a derived assignment. ! Here, we desire _deep_ copy semantics for type base, which happens to ! use POINTER components. In this instance, an allocatable component ! could fix the issue, but consider the case of a base type which ! implements a binary tree, and pointer components were thus much harder ! to avoid. module the_classes type :: base integer, pointer :: ptr => null() contains procedure :: equals generic :: assignment(=) => equals end type base ! We now extend the base class in a relatively trivial manner. ! There needs to be at least one new component or the casual ! observer might think that the assignment defined below ! could just use CLASS for the RHS argument and allow inheritance ! to do the "right thing" (TM). Often we would like the new components ! defined in the extension to be copied as well, and that ! approach becomes inadequate. type, extends(base) :: child character(len=:), allocatable :: name end type child contains subroutine equals(a, b) class (base), intent(out) :: a type (base), intent(in) :: b allocate(a%ptr) a%ptr = b%ptr end subroutine equals end module the_classes program main use the_classes implicit none type (child) :: c1, c2 allocate(c1%ptr) c1%ptr = 1 c1%name = 'Alice' c2 = c1 ! Because we want "deep copy" semantics, we want to be able to change ! c1%ptr without impacting c2 c1%ptr = 2 print*,'Results:' print '(10x,a10,1x,i1)', c2%name, c2%ptr print '(a9,1x,a10,1x,i1)', 'Expected: ','Alice',1 end program main <<end of Tom Clune's comment on F08/0146>>
Steve Lionel (Intel) wrote:
For everyone's information, here's the content of interp F08/0146 including committee member Tom Clune's view of "how it should work", which Malcolm Cohen of NAG disagrees with. This is likely to be revisited at next month's meeting.NUMBER: F08/0146 TITLE: Does intrinsic assignment copy inherited components twice? .. This would seem to indicate that the above assignment "x = y" is interpreted as the following (in unspecified order): x%a = y%a x%b = y%b x%base = y%base and the assignment to x%base is treated as if it were x%base%a = y%base%a thus assigning to x%a twice, which does not seem to make sense. ..
But, Steve, what about "Inheritance association" (16.5.4 in WD 1539-1 J3-10-007r1 document for Fortran 2008)?
8 16.5.4 Inheritance association 9 1 Inheritance association occurs between components of the parent component and components inherited by type 10 extension into an extended type (184.108.40.206). This association is persistent; it is not affected by the accessibility of 11 the inherited components.
Doesn't inheritance association essentially imply there is really no such thing as "x%a", that it essentially only has "association" that is persistent with "x%base%a"? Therefore, isn't the assignment "x%a = y%a" meaningless, that it is only "x%base%a = y%base%a" which is relevant?
So how does the "twice" part of the question addressed by the interp even arise?