A dummy target in a pure subroutine must be intent(inout) (?)

This code fails compiling:

    pure subroutine foo(a,b)
    real, pointer, intent(inout) :: a
    real, pointer, intent(in)    :: b
    a => b   ! compilation fails if b is not intent(inout) and foo is pure
    end subroutine

ifx output:

error #7145: This object must not be used as the pointer-object or target of a pointer-assignment-stmt in a PURE procedure or an internal procedure contained in a PURE procedure.   [B]
    a => b   ! compilation fails if b is not intent(inout)
---------^

It compiles if b is intent(inout) or if foo is not declared pure

What is the reason for that? In any case b is unmodified by the pointer assignment, I can’t understand why it has to be intent(inout)

Edit: gfortran also reports a compilation error, it’s not just ifx

It looks like the intent are referring not to the pointer assignment, but to the underlying pointed value.
I.e.:

a => b
a =  10  ! this changes b's pointed value, but intent(in) does not like it.

In C, it would be:

// This is what is happening
void func_1(float *a, const float *b)
{
   a  = b; // this throws a warning with gcc by default, not error.
   *a = 10.0f;
}

// This is what is expected in Fortran
void func_2(float *a, float *const b)
{
   a  = b;
   *a = 12.f;
}

void main(void)
{
   float a = 1;
   float b = 2;

   printf("%f   %f\n", a, b);
   func_1(&a, &b);
   printf("%f   %f\n", a, b);
   func_2(&a, &b);
   printf("%f   %f\n", a, b);
}

The point is: what should intent(in) refer to?

EDIT: following @themos comment, the answer seems to be both, so that any side effect is elided if the subroutine is marked pure.

The constraint is

C15104 In a pure subprogram any designator with a base object that
is in common or accessed by use or host
association, is a pointer dummy argument of a pure function,
is a dummy argument with the INTENT
(IN) attribute, is a coindexed object, or an object that is storage associated with any such variable, shall
not be used

(1) in a variable definition context( 19.6.7),
(2) in a pointer association context (19.6.8),

(3) as the data-target in a pointer-assignment-stmt


(4) as the expr corresponding to a component in a structure-constructor if the component has the
POINTER attribute or has a pointer component at any level of component selection,
(5) as the expr of an intrinsic assignment statement in which the variable is of a derived type if the
derived type has a pointer component at any level of component selection,
(6) as the source-expr in a SOURCE= specifi er if the designator is of a derived type that has a pointer
component at any level of component selection,
(7) as an actual argument corresponding to a dummy argument with the POINTER attribute, or
(8) as the actual argument to the function C_LOC from the intrinsic module ISO_C_BINDING.

The above is not a reason, but at least we know that the compilers are following the Standard (the restriction has been there since 1995, when PURE procedures were introduced).

1 Like

Pure means no side effects, ie the compiler can use it in parallelising loops for example. Messing with pointers might be tricky in a loop.

As you know what comes in and goes out, this works:

subroutine foo(a,b) 
  real, pointer, intent(out) :: a
  real, target, intent(in)   :: b
  a => b
end subroutine

I like eMe’s example.

I think the intention of the Standard with PURE and INTENT(IN) is to facilitate the keeping of some values in special hardware (like registers) for performance. As soon as you use such places as a target you are introducing the possibility of mutation via other names and the whole analysis becomes more complicated, possibly forcing the compiler to abandon the use of a register and resort to memory addresses. Shutting-off aliasing opportunities is quite valuable for performance.

OK, but in the above case this is somehow the opposite: promising intent(in), i.e. having no side effect on that argument, should make things easier the compiler.

Maybe.

My first though was that it was getting impossible for the compiler to check if the intent(in) was honored if the variable could be modified through another name. But then, the compilation would also fail if the routine was not pure.

AFAIK, for a pointer dummy argument, intent() refers to the pointer association, not to the content.

I think you are on the right track. The point of PURE is that, provided the easy-to-verify-at-compile-time but more-than-strictly-necessary conditions are satisfied, then there can be no side-effects, and PURE procedures can execute concurrently or in any order. The effect of restriction (3) above is to make it impossible, by Fortran means, to create an undesirable side-effect (by creating an alias). Really, a sufficient requirement would be to demand that such a pointer assignment is not actually encountered in actual execution, but then the compiler would have to be solving the halting problem to prove that a PURE procedure has no side-effect.