Home     |     .Net Programming    |     cSharp Home    |     Sql Server Home    |     Javascript / Client Side Development     |     Ajax Programming

Ruby on Rails Development     |     Perl Programming     |     C Programming Language     |     C++ Programming     |     IT Jobs

Python Programming Language     |     Laptop Suggestions?    |     TCL Scripting     |     Fortran Programming     |     Scheme Programming Language


 
 
Cervo Technologies
The Right Source to Outsource

MS Dynamics CRM 3.0

Fortran Programming Language

Should F95 compilers allow allocatable pointers in recursive subroutines?


I have a bug report in with Intel about this issue.  Their first
reponse was to tell me that there is no bug and that the behavior is by
design.  I'm still thinking that the code below should work and that
the compiler should be creating a new instance of the allocated pointer
each time the subroutine is invoked recursively.

PGI allows this and runs fine, gfortran exhibits the same problem as
the Intel compiler.

---------- test code ----------
program main
implicit none

integer karray(10)
call factarray(karray, 10, 3)

write (*,*) 'result = ', karray(1)
end

recursive subroutine factarray(karray, ksize, n)
implicit none
integer karray(ksize), ksize, n
integer, pointer :: ktemp(:) => NULL()

if(.not.associated(ktemp)) then
write (*,*) 'Allocating for n = ', n
allocate(ktemp(ksize))
else
write (*,*) 'Already allocated for n = ', n
endif
if (n.eq.1) then
karray = 1
else
call factarray(ktemp, ksize, n-1)
karray = ktemp * n
endif

write (*,*) 'Deallocating for n = ', n
deallocate(ktemp)
return
end

--------- end test code --------

$ ifort -recursive -o recursive.x recursive.f90
$ recursive.x

Allocating for n = 3
Already allocated for n = 2
Already allocated for n = 1
Deallocating for n = 1
forrtl: severe (174): SIGSEGV, segmentation fault occurred
Image PC Routine Line Source

recursive.x 0000000000402A71 Unknown Unknown Unknown
recursive.x 00000000004029D2 Unknown

David wrote:
> I have a bug report in with Intel about this issue.  Their first
> reponse was to tell me that there is no bug and that the behavior is by
> design.  I'm still thinking that the code below should work and that
> the compiler should be creating a new instance of the allocated pointer
> each time the subroutine is invoked recursively.

In general, you are correct.  The pointer itself would be local to
each instance of the routine.

But, you wrote this:

 integer, pointer :: ktemp(:) => NULL()

that gives the pointer the SAVE attribute, and therefore it is the
same variable across all instances.

Quoting the standard:

"Each instance [of a RECURSIVE subprogram] has an independent sequence
of execution and an independent set of dummy arguments and *local
unsaved data objects* ... All other entities are shared by all
instances of the subprogram." [Emphasis mine, 12.5.2.3, Instances of a
subprogram]

Rather than use an initial value of NULL() this way, leave off the
initialization and know that as you enter the routine, the pointer is
undefined. You are then free to allocate it without asking if it is
allocated. (Or you can add a NULLIFY statement if you want.)

I'm sorry that the Intel support rep who took your issue didn't notice
this fine point.  I was consulted about it but what I was asked was if
-recursive caused allocations to go on the stack, which is another
thing entirely (it doesn't.) I didn't look at your original question.

You say that the PGI compiler "works" with your code.  I suggest you
ask PGI about that.

Steve Lionel
Developer Products Division
Intel Corporation
Nashua, NH

User communities for Intel Software Development Products
  http://softwareforums.intel.com/
Intel Fortran Support
  http://developer.intel.com/software/products/support/
My Fortran blog
  http://www.intel.com/software/drfortran

David <dogun@yahoo.com> wrote:
> I have a bug report in with Intel about this issue.  Their first
> reponse was to tell me that there is no bug and that the behavior is by
> design.  I'm still thinking that the code below should work and that
> the compiler should be creating a new instance of the allocated pointer
> each time the subroutine is invoked recursively.

I'll start with your terminology, which threw me off at first. There is
no such thing as an "allocatable pointer". Yes, you can use an allocate
statement with a pointer, but the term "allocatable" means something
different in Fortran. It is perhaps unfortunate that it does not have
the English meaning of "capable of being allocated", but it doesn't.
What you have here is just a pointer. Anyway...

Yes, you may have a pointer in a recursive procedure. However...

> integer, pointer :: ktemp(:) => NULL()

The critical bit here, which you overlooked in your description (I'm
glad you showed the code) is that the pointer is initialized. Because it
is initialized, there is only one instance of the variable shared by all
instances of the recursive procedure. You do *NOT* get a separate
instance of the variable for each invocation of the procedure.

If you think about what initialization means, you'll realize that it
pretty much has to be this way. (Well, even if you don't realize that,
that's the way it is, but I'm trying to explain the rationale).
Initialization is designed to be a one-time thing on program
load/startup. Other implementations are allowed, but that's the way it
is designed. It specifically is *NOT*, I repeat that "not" something
that happens on every entry to a procedure. Having the initialization be
a one-time thing is fundamentally incompatible with having an arbitrary
number of instances, depending on how many levels of recursion there
are. That's assuming you intend a single initilization for each level,
even if there are multiple instances of that level (as, for example, if
the top-level one is called multiple times). If you expect a separate
initialization every time the procedure is called, then you just don't
understand Fortran initialization at all; it doesn't work that way.

Note that this has nothinbg in particular to do with pointers. It is
also true of non-pointer local variables with initialization. The
initialization causes the variable to implicitly get the SAVE attribute,
which in turn causes it to be shared by all instances of the procedure.

At a quick glance, I think your crash is because you attempt to
deallocate the variable multiple times. (You intend it to be multiple
variables, but it isn't.)

I could imagine that someone might want to start debating yet again
whether it was or was not a good idea for initialization to imply save.
If that happens, I won't participate, as all the points on all sides
have already been said many times. I restrict myself here to the fact
that it is what the standard specifies and thus is not a compiler bug.
Well, it isn't a compiler bug for those that follow the standard. SOunds
to me as though you might have found a compiler bug in the PGI compiler.

--
Richard Maine                    | Good judgement comes from experience;
email: last name at domain . net | experience comes from bad judgement.
domain: summertriangle           |  -- Mark Twain

Thanks to both of you, Richard and Steve.  Your explanations made it
perfectly clear and I had overlooked what the assignment was doing.  
Removing the NULL assignment allows this to work.

-david

David <dogun@yahoo.com> wrote:
> Thanks to both of you, Richard and Steve.  Your explanations made it
> perfectly clear and I had overlooked what the assignment was doing.  
> Removing the NULL assignment allows this to work.

It isn't assignment. It is initialization. They are *VERY* different.
Assignment would not have the same effect.

--
Richard Maine                    | Good judgement comes from experience;
email: last name at domain . net | experience comes from bad judgement.
domain: summertriangle           |  -- Mark Twain

Richard Maine wrote:

(snip)

> I could imagine that someone might want to start debating yet again
> whether it was or was not a good idea for initialization to imply save.

I don't want to start a debate either, but note that one of the good
things about Fortran is maintaining compatibility with programs written
long ago.  If being designed today, without any backward compatibility,
some decisions might be made differently.

-- glen

Add to del.icio.us | Digg this | Stumble it | Powered by Megasolutions Inc