Why/When is the `target` attribute part of the characterisics of a procedure

Does anybody know, why (and when) the target attribute is part of the signature of a procedure? Is the call to a subroutine where a given dummy argument has the target attribute different from the call to a similar subroutine, where the target attribute is not present for that dummy argument?

The use case triggering my question is type extension, where an extending type overrides a type bound procedure. If the extending implementation needs a pointer to the class instance (e.g. because it wants to point to some components within the type), it can not add the target attribute to the dummy argument this, if it was not specified in the base type:

module typedefs
  implicit none

  type :: base_type
  contains
    procedure :: method => base_method
  end type base_type


  type, extends(base_type) :: extending_type
  contains
    procedure :: method => extending_method
    procedure :: helper_method => extending_helper_method
  end type extending_type

contains


  subroutine base_method(this)
    class(base_type), intent(in) :: this
  end subroutine base_method


  subroutine extending_method(this)
    !class(extending_type), target, intent(in) :: this   ! <- fails to compile
    class(extending_type), intent(in) :: this  ! <- this works, but needs workaround
    ! Workaround: we call a helper method, which then specifies the target attribute
    call this%helper_method()
  end subroutine extending_method

  subroutine extending_helper_method(this)
    class(extending_type), intent(in), target :: this  ! <- Note the target attribute
  end subroutine extending_helper_method

end module typedefs

My questions are:

  1. What is the rationale for not allowing to specify the target attribute in the overriding type bound procedure for the class instance argument?
  2. Why is the workaround above allowed/working? If one is not allowed to make this a target in extending_method, why is it OK to make it a target in a helper routine then. I don’t understand how/why do the two cases differ.

TL;DR: it’s a technicality.

  • TARGET is considered a characteristic of received arguments in procedures.
  • That the argument characteristics must all match between an overriding procedure and the one being overridden is a technical requirement
  • The standard has long permitted the extension where the effective argument need not have the TARGET attribute in a call to a procedure with an explicit interface where the corresponding received argument has the TARGET attribute.

Thus the compilers complain about 1 above but not 2, go figure!

2 Likes

OK, thanks, it is good to know, that the “workaround” with the helper function is standard conforming at least. Still, I am still wondering, why the target attribute is part of the characteristic of a procedure at all?

When a procedure declaring a dummy argument with the target attribute is called, there are two possibilities:

  • The caller passes an actual argument with the target/pointer attribute. If I understand it correctly now, in this case all pointers associated with the dummy argument will remain valid also after the exit from the procedure (and will be associated with the actual argument).

  • The caller passes an actual argument without the target/pointer attribute. In this case, all pointers associated with the dummy argument will become invalid after the exit from the procedure.

So, if I understand correctly, a procedure can never safely assume, that pointers it associates with a dummy argument with the target attribute remain valid after the exit, as it depends on how the procedure was called, which is beyond its control.

Therefore, in order to ensure deterministic behavior for all possible calls, a procedure should

  • either specify the target attribute for a dummy argument, and then avoid to pass back or save any pointers it associates with that target (as the processor will not ensure, that the actual argument has the target/pointer attribute)

  • or specify the pointer attribute for a dummy argument, as then all pointers associated with that dummy argument will remain valid after exit. (as the processor will enforce the caller to specify the target or the pointer attribute for the actual argument)

So, to me it seems, there are no scenarios, where in a robust programming model, the target attribute of a dummy argument should be used to anything else, as associating and using pointers to that argument within the declaring procedure only. So, it becomes an “internal affair” which the caller would not need to be aware of.

Do I overlook anything?

1 Like

In a private conversation, my attention was pointed to 15.5.2.4 §9:

If the dummy argument has the TARGET attribute, does not have the VALUE attribute, and either the effective
argument is simply contiguous or the dummy argument is scalar, assumed-rank, or assumed-shape, and does not
have the CONTIGUOUS attribute, and the effective argument has the TARGET attribute but is not a coindexed
object or an array section with a vector subscript then
• any pointers associated with the effective argument become associated with the corresponding dummy
argument on invocation of the procedure, and
• when execution of the procedure completes, any pointers that do not become undefined (19.5.2.5) and are
associated with the dummy argument remain associated with the effective argument.

The second bullet point (emphasis mine) seems to be indeed a reason for making the TARGET attribute of a dummy variable part of the intrinsic. However, I fail to see, how one could actually exploit this scenario in a real program.

Is there any way to find out within the procedure, whether the effective actual argument has the TARGET attribute? If not, wouldn’t relying on it result in very fragile code? (If the caller forgets to specify the TARGET attribute, the processor would not emit any compile-time errors/warnings, but the resulting code may behave indeterministic due to the usage of undefined pointers.) Wouldn’t it be more logical to drop the second bullet point, and just state, that pointers associated with a dummy argument with the TARGET attribute become generally undefined at exit? If one wanted to keep the validity of the pointers after exit from the procedure, one should define the pointer attribute for the dummy argument instead.

That would place some large restrictions on the valid use of pointers in the language. Sometimes you want to assign pointers to a target, and the best place to do that is in a subroutine rather than in the calling program.

There are many advantages to using nonpointer entities in fortran. These include local variables declared in the usual way and allocatable variables. I personally prefer in my programming to avoid pointers as much as possible in favor of these other safer types of entities because you do not need to worry about dangling pointers and memory leaks, the language itself enforces the consistency – it removes them from the stack at the right time, it deallocates them at the right time, and so on. If instead you declare everything as anonymous memory and pointers, then you open yourself up to all kinds of mayhem.

@RonShepard No, I don’t want to apply any of those constraints! (I am actually using the TARGET attribute a lot. )

What I am suggesting is, to make the usage of the TARGET attribute more systematic, easier to understand and probably also easier to implement in compilers by suggesting the following rules:

  • If one associates a pointer with a TARGET dummy argument within a procedure, the pointer shall always become undefined if the procedure is finished.

  • The current exception (the one marked in the quote), which requires the associated pointer to remain valid after the end of the procedure if the actual argument had the TARGETattribute, should be deleted.

Rationale: The caller has two different ways of calling a procedure (by passing an actual argument with or without the TARGET attribute). Depending which of the two the caller has chosen, the behavior of the pointers associated with the dummy argument with the TARGET attribute will behave differently. But the callee has no way to tell, which of the behavior it should expect! So, currently the behavior of a pointer associated within the procedure depends on something which the procedure can

  • neither control (compiler won’t be any help, as the TARGET attribute for the actual argument is not enforced)

  • nor check, not even at run-time.

So, the only sensible choice in any program ever is to go for safety / determinism and always assume, that the pointers associated with the dummy argument with the TARGET attribute become undefined when leaving the procedure. And if you wish to keep the pointers to a dummy argument valid after the end of the procedure, the only safe way is to declare the POINTER attribute for the dummy argument. This would enforce a compiler check on the caller, as the actual argument must have then the TARGET or the POINTER attribute.

(And as a byproduct, if the exception above would be deleted, maybe one could also drop the TARGET attribute from the characteristic of a procedure, allowing for a more flexible usage without the need of an additional call as a workaround, as shown in the original example above.)

As I said previously, this would place significant constraints on the programmer’s ability to use pointers. Sometimes the best place to make the pointer assignment is in the subroutine, not in the calling program, so preventing the programmer from doing that would constrain the programmer, in addition to making a couple of decades of standard conforming code nonstandard.

If the dummy argument is declared pointer, then the actual argument must be a pointer. I don’t think there are any exceptions to that longstanding rule. If the dummy argument is not a pointer, then the actual argument can be a pointer or a non pointer variable.

When the actual argument has the target attribute, then that prevents the compiler from making a temporary copy during the calling sequence. That is what allows the pointers to remain associated after the subroutine return. Without that target attribute (or pointer attribute, and maybe some other similar situations), then the compiler is allowed to make temporary copies during the calling sequence. In this case, the pointer assignments in the subroutine are associated with that copy, not with the original array. That is why they become undefined after the subroutine call.

As of Fortran 2008, you can pass an actual argument with the TARGET attribute for a dummy argument with the POINTER attribute. So, if you declare the POINTER attribute for the dummy argument, the compiler will enforce, that the passed actual argument has either the TARGET or the POINTER attribute. And you can be sure, that the pointers associated within the procedure are still valid after exiting the procedure. (So you get the best of both worlds!) In contrast to a dummy argument with the TARGET attribute, where the compiler does not enforce anything on the caller side, and therefore, the called procedure can not know, whether the associate pointers will have any meaning after it finishes.

So, even if the standard won’t be modified, we should probably teach people to always use the POINTER attribute for a dummy argument, if they want to associate a pointer within the procedure with it, and the pointer should remain valid after the procedure finishes.

The received argument in the procedure is required per the standard to have INTENT(IN) attribute if it has the POINTER and the effective argument has the TARGET attribute. This is the so-called auto-targeting feature.

I stand corrected, I was not aware of this corner case. The pointer dummy must be INTENT(IN), which means the pointer association cannot be modified by the subroutine. It is basically a shorthand for a local pointer assignment to a target dummy, which is probably what I would write anyway just to make the code clear.