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

Associate and intent(in) and deallocate and pointer components and a spurious error

IanH
Honored Contributor III
1,193 Views

I get a complaint about an INTENT(IN) violation with the attached.  I would like to complain about the complaint. 

PROGRAM p
  IMPLICIT NONE
  TYPE :: reference
    TYPE(ta), POINTER :: ta_ptr(:)
  END TYPE reference
  
  TYPE :: tb
    INTEGER, ALLOCATABLE :: comp
  END TYPE tb
  
  TYPE :: ta
    TYPE(tb) :: b
  END TYPE ta

  CALL execute
CONTAINS
  SUBROUTINE execute
    TYPE(ta), TARGET :: a(5)
    TYPE(reference) :: ref
    
    ALLOCATE(a(2)%b%comp)
    ref%ta_ptr => a
    CALL proc(ref)
  END SUBROUTINE execute

  SUBROUTINE proc(ref)
    TYPE(reference), INTENT(IN) :: ref
    ASSOCIATE(outer => ref%ta_ptr)    
      ASSOCIATE(inner => outer(2)%b)   
        DEALLOCATE(inner%comp)
      END ASSOCIATE
    END ASSOCIATE
  END SUBROUTINE proc
END PROGRAM p

 

>ifort /check:all /warn:all /standard-semantics "2015-02-07 associate.f90"
Intel(R) Visual Fortran Intel(R) 64 Compiler XE for applications running on Intel(R) 64, Version 15.0.2.179 Build 20150121
Copyright (C) 1985-2015 Intel Corporation.  All rights reserved.

2015-02-07 associate.f90(30): error #6780: A dummy argument with the INTENT(IN) attribute shall not be defined nor become undefined nor be the topic of excessive conversation on c.l.f.   [INNER]
        DEALLOCATE(inner%comp)
-------------------^
compilation aborted for 2015-02-07 associate.f90 (code 1)

My logic is that ref%ta_ptr references something that is not a subobject of ref, hence outer is not a suboject of ref, hence while inner is a subobject of outer, it isn't a subobject of ref, hence inner%comp isn't a subobject of ref, hence the compiler should pull its head in when I merrily go a-deallocating.

0 Kudos
12 Replies
mecej4
Honored Contributor III
1,193 Views

I agree with your logic, and it may be that the compiler got fooled by the fact that in Fortran one has to look at context to distinguish between pointer and pointee.

Your example code is valuable in a way that you may not have intended: it proves that pointers are risky and subversive things. Here is a subroutine with only one argument, that one with INTENT(IN), no host-associated variables present in the subroutine, yet one of the host's variables gets changed as a result of calling this subroutine. I admire your skill in avoiding shooting yourself in the foot!

0 Kudos
Steven_L_Intel1
Employee
1,193 Views

Sorry, this is Abuse. Complaints are next door.

We'll take a look at this - thanks.

0 Kudos
FortranFan
Honored Contributor III
1,193 Views

It'll be great to learn if and how the compiler can distinguish between the case below (where the compiler error is genuine, in my opinion; correct me if I'm wrong) versus the one shown above:

PROGRAM p
  IMPLICIT NONE
  TYPE :: reference
    TYPE(ta), POINTER :: ta_ptr(:)
  END TYPE reference

  TYPE :: tb
    INTEGER, ALLOCATABLE :: comp
  END TYPE tb

  TYPE :: ta
    TYPE(tb) :: b
  END TYPE ta

  CALL execute
CONTAINS
  SUBROUTINE execute
    !TYPE(ta), TARGET :: a(5)
    TYPE(reference) :: ref

    !ALLOCATE(a(2)%b%comp)
    !ref%ta_ptr => a
    ALLOCATE(ref%ta_ptr(5))
    CALL proc(ref)
  END SUBROUTINE execute

  SUBROUTINE proc(ref)
    TYPE(reference), INTENT(IN) :: ref
    ASSOCIATE(outer => ref%ta_ptr)
      ASSOCIATE(inner => outer(2)%b)
        DEALLOCATE(inner%comp)
      END ASSOCIATE
    END ASSOCIATE
  END SUBROUTINE proc
END PROGRAM p

 

0 Kudos
IanH
Honored Contributor III
1,193 Views

The value of the object pointed at by a pointer component is not considered part of the value of the object with the component (see F2008 4.5.8 and Note 5.16).  ref may have the INTENT(IN) attribute, but the object pointed at by ref%ta_ptr does not, and hence ref%ta_ptr(2)%b%comp does not - which is what the deallocate statement ultimately references.  How the object pointed at by ref%ta_ptr came into existence doesn't matter, bar the specific case where the object pointed at by ref%ta_ptr is actually another part of ref, or part of some other INTENT(IN) argument.

The pointer association status of a pointer component of an object of derived type is considered part of its value, so if, inside proc, an attempt was made to deallocate or re-pointer-assign ref%ta_ptr itself, then the code would be non-conforming and the compiler would be required to issue a diagnostic.

 

0 Kudos
mecej4
Honored Contributor III
1,193 Views

Good explanation. However, I suspect that this is actually a defect in the language, and I should like to see you argue that such is not the case. Consider this scenario. Subroutine proc() is in a third party library, and all that a user is given is the following interface:

  SUBROUTINE proc(ref)
    use some_mod                      ! which contains a definition of type REFERENCE
    TYPE(reference), INTENT(IN) :: ref
  END SUBROUTINE proc

The user observes that some of the variables in the caller(s) of this subroutine are getting mysteriously deallocated. How is one going to suspect that the rules that you just explained are at play and take steps to prevent these unexpected and unwanted deallocations? How does one even suspect these things, when the INTENT(IN) in the interface so conveniently gives false assurances of safety?

0 Kudos
IanH
Honored Contributor III
1,193 Views

As a data point, the real world code that this is from does many things that are mysterious, unexpected, and, most of all, unwanted.  But I'm not sure that the language specification can be blamed for that.

The reference object needs to have been set-up in some fashion.  In the context of a library that is typically done by another procedure call  (perhaps the components of the reference type are private).  The implications of that pair (or more) of procedure calls on the state of the program relevant to the client programmer should be something which they can glean through library documentation.

Consider also a global array of a type with allocatable components, indexed by an integer.  You pass that integer into some library procedure as an INTENT(IN) - the library procedure deallocates an allocatable component of that particular element of the array.  I don't think people would find this a surprising possibility.  The pointer case isn't really any different.  The passed integer in this latter example doesn't own the object that it references via indexing, an object with a pointer component doesn't own the object that the pointer references via pointer association.

0 Kudos
Steven_L_Intel1
Employee
1,193 Views

Ian's issue has been escalated as DPD200366483. I agree with Ian that FortranFan's alternative is also not an error.

To mecej4's comment, I'll note that the language can at most check what the language can see. INTENT(IN), like PROTECTED, is not a chastity belt for your variables, it's more like a chaperone in that it's possible to sneak by when the compiler isn't looking. (How's THAT for an analogy?)

0 Kudos
mecej4
Honored Contributor III
1,193 Views

Steve, to go with your analogy, we need an ILLICIT NONE statement in Fortran!

0 Kudos
IanH
Honored Contributor III
1,193 Views

I just want to know whether the abuse department is INTENT(IN) or INTENT(OUT).

0 Kudos
FortranFan
Honored Contributor III
1,193 Views

Interesting.  But as mecej4 points out, this shows the dangers of pointers.  Mere mortals like me better stay away from them!

0 Kudos
Steven_L_Intel1
Employee
1,193 Views

Pointers have their uses, but in many cases they are used where ALLOCATABLE would be the better choice.

0 Kudos
Steven_L_Intel1
Employee
1,193 Views

This has been fixed in our sources - I expect the fix to go into Update 3.

0 Kudos
Reply