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

Possible bug in ifortran? Allocated polymorphic object does not seem to survive function returning to main

mSSM_B_
Beginner
2,020 Views

I was playing around with polymorphic types in Fortran, and I seem to have stumbled over a potential bug in the Intel Fortran compiler. Compilation with GNU gfortran compiler yields the expected results, while ifort produces an odd trace. I am using polymorphism to allocate a general type depending on the source object (which has a type extending the general type), and then returning that newly allocated object as the function result. It looks like, the object does not survive the end of the function.

The respective compiler versions are:

% gfortran -v
gcc version 4.9.2 (GCC) 
% icc -v
icc version 15.0.0 (gcc version 4.9.0 compatibility)

 

Here are the respective results for ifort and gfortran:

 

% gfortran -c parameter-minimal.f90
% gfortran -o polymorphic-minimal{,.f90} parameter-minimal.o
% ./polymorphic-minimal 
   2.00000000    
   2.00000000    

 

% ifort -standard-semantics -c parameter-minimal.f90                          
% ifort -standard-semantics -o polymorphic-minimal{,.f90} parameter-minimal.o 
% ./polymorphic-minimal                                                   
 2.000000
forrtl: severe (71): integer divide by zero
Image              PC                Routine            Line        Source             
polymorphic-minim  000000000047A621  Unknown               Unknown  Unknown
polymorphic-minim  0000000000478D77  Unknown               Unknown  Unknown
polymorphic-minim  000000000042C994  Unknown               Unknown  Unknown
polymorphic-minim  000000000042C7A6  Unknown               Unknown  Unknown
polymorphic-minim  000000000040BB5F  Unknown               Unknown  Unknown
polymorphic-minim  00000000004111BF  Unknown               Unknown  Unknown
libpthread.so.0    00007F4CAD642200  Unknown               Unknown  Unknown
Unknown            00007FFF55107B48  Unknown               Unknown  Unknown
zsh: exit 71    ./polymorphic-minimal

 

Here is the MWE of the program; the main file:

program polymorphic
    use parameter_minimal
    implicit none

    class(generic_evaluator), allocatable :: evaluator

    allocate(evaluator, source=return_eval_type())

    print *, evaluator%eval(1.0)

    contains
        function return_eval_type() result(evaluator) !{{{

            class(generic_evaluator), allocatable :: evaluator
            type(function_evaluator) :: function_type

            function_type%f_ptr => linear
            allocate(evaluator, source=function_type)

            print *, evaluator%eval(1.0)
        end function return_eval_type !}}}

        real pure function linear(x) result(y) !{{{
            real, intent(in) :: x
            y = 1 + x
        end function linear !}}}

end program polymorphic

 

And the module file:

 

module parameter_minimal
    implicit none
    private

    public generic_evaluator, function_evaluator

    type, abstract :: generic_evaluator
        contains
            procedure(eval_with), deferred, pass :: eval
    end type generic_evaluator

    abstract interface
        real pure function generic_func(x) result(y)
            real, intent(in) :: x
        end function generic_func

        real function eval_with(this,x) result(y)
            import generic_evaluator
            class(generic_evaluator), intent(in) :: this
            real,intent(in) :: x
        end function eval_with
    end interface

    type, extends(generic_evaluator) :: function_evaluator !{{{
        procedure(generic_func), pointer, nopass :: f_ptr
        contains
            procedure :: eval => eval_with_function
    end type function_evaluator !}}}

    contains

        real function eval_with_function(this,x) result(y) !{{{
            class(function_evaluator), intent(in) :: this
            !procedure(generic_func) :: f
            real, intent(in) :: x
            y = this%f_ptr(x)
        end function eval_with_function !}}}

end module parameter_minimal

 

0 Kudos
15 Replies
mSSM_B_
Beginner
2,020 Views

I made a slight mistake above (why can't I edit my post??); the error actually had error code 174 (also, it seems to be changing from time to time; I saw 71, 168 and 174 so far).

forrtl: severe (174): SIGSEGV, segmentation fault occurred
Image              PC                Routine            Line        Source             
polymorphic-minim  000000000047A4E1  Unknown               Unknown  Unknown
polymorphic-minim  0000000000478C37  Unknown               Unknown  Unknown
polymorphic-minim  000000000042C854  Unknown               Unknown  Unknown
polymorphic-minim  000000000042C666  Unknown               Unknown  Unknown
polymorphic-minim  000000000040BA1F  Unknown               Unknown  Unknown
polymorphic-minim  000000000041087D  Unknown               Unknown  Unknown
libpthread.so.0    00007F9A9491B200  Unknown               Unknown  Unknown
Unknown            00007FFF2BEC3567  Unknown               Unknown  Unknown

 

0 Kudos
FortranFan
Honored Contributor III
2,020 Views

You may want to track down the requirements and restrictions around internal procedures as procedure pointers, especially in the Fortran 2008 standard.  I vaguely remember running into something very similar to this and finding the code worked in Intel Fortran when the function pointed to by the procedure pointer is not an internal procedure.  Not saying there isn't a bug somewhere or there shouldn't be better diagnostics, but could this be a workaround? 

0 Kudos
mSSM_B_
Beginner
2,020 Views

Amazing! How do you even get the idea of putting that into an extra module to check if that might be the problem?

To understand you correctly: you assume that the problem essentially is the pointer to the function “linear” contained in my program block above, correct? Indeed, simply moving the function to that module imported in the first line already fixes the issue.

I punched some of these keywords into Google and got this as a result:

https://software.intel.com/en-us/articles/intel-fortran-compiler-support-for-fortran-language-standards

According to that post, at least the Intel Visual Fortran compiler has supported that Fortran 2008 feature since 2012.

0 Kudos
FortranFan
Honored Contributor III
2,020 Views

"To understand you correctly: you assume that the problem essentially is the pointer to the function “linear” contained in my program block above, correct? Indeed, simply moving the function to that module imported in the first line already fixes the issue."

      - yes, that's exactly what I meant.  So is that an acceptable workaround for you?

Some of my derived types have constructs somewhat similar to yours and I recall one of my unit tests having an internal procedure pointee which got flagged as invalid per Fortran 2008 standard and had problems.

Yes, even though Intel Fortran is not fully compliant yet with 2008 standard, it has been incrementally getting 2008 features added to it and I like that - a lot of our work wouldn't have taken place in Fortran but for this.  I can't wait for the day Intel Fortran supports all of the 2008 standard as well as the advanced interoperability features from the next standard (2015?).

 

0 Kudos
Steven_L_Intel1
Employee
2,020 Views

I think you need to read Doctor Fortran in "Think, Thank, Thunk" While your situation is expressed differently, it's the same problem. Yes, you can assign an internal procedure to a procedure pointer, but once you return from the host procedure, that pointer becomes invalid. Whether or not it works depends on how memory is laid out and gets reused.

FWIW, we've supported internal procedures as arguments and pointers for a long, long time.

0 Kudos
mSSM_B_
Beginner
2,020 Views

So this is working as intended? I am not sure I exactly grok the article...

For my own understanding: what is the qualitative difference between having an internal procedure as compared to one in a module? Do all variables/procedures from the module stay in scope?

If I understood that part correctly, the pointer is losing its reference the moment the functions ends. But why is that exactly? The host function, within which the pointer is assigned, does not have an internal procedure itself. It makes the pointer point to a procedure contained in the host (in my mind, they are on the same “level”).

0 Kudos
Steven_L_Intel1
Employee
2,020 Views

An internal procedure has context of its containing procedure that is valid only as long as the call to the containing procedure is active. A module procedure has static context so there is no issue about going out of scope.

I may have misinterpreted what your program is doing. I'll look at it closer next week.

0 Kudos
Steven_L_Intel1
Employee
2,020 Views

Ok, I just tried this in the 15.0 compiler and it works correctly. I can't make it fail.

0 Kudos
mSSM_B_
Beginner
2,020 Views

That is odd. :/ In my case, I can make it always fails as long as the function pointed to is contained in the main program. Moving the function to the external module will fix it. Any ideas what else I could look at?

 

I have changed the class of the allocated object to the type of the source object, see below (the module is kept the same); it looks like the compiler believes that the pointer in the allocated, polymorphic object is indeed associated with something, but it will still fail to actually call the function (I have dropped the -standard-semantics flag here and also when compiling the module):

% ifort -o polymorphic-minimal polymorphic-minimal.f90 parameter-minimal.o
% ./polymorphic-minimal 
   2.000000    
 T
forrtl: severe (168): Program Exception - illegal instruction
Image              PC                Routine            Line        Source             
polymorphic-minim  000000000047A531  Unknown               Unknown  Unknown
polymorphic-minim  0000000000478C87  Unknown               Unknown  Unknown
polymorphic-minim  000000000042C8A4  Unknown               Unknown  Unknown
polymorphic-minim  000000000042C6B6  Unknown               Unknown  Unknown
polymorphic-minim  000000000040BA6F  Unknown               Unknown  Unknown
polymorphic-minim  0000000000411121  Unknown               Unknown  Unknown
libpthread.so.0    00007F90D9105200  Unknown               Unknown  Unknown
Unknown            00007FFFF84AE6DC  Unknown               Unknown  Unknown
zsh: exit 168   ./polymorphic-minimal
program polymorphic
    use parameter_minimal
    implicit none

    !class(generic_evaluator), allocatable :: evaluator
    class(function_evaluator), allocatable :: evaluator

    allocate(evaluator, source=return_eval_type())

    print *, associated(evaluator%f_ptr)
    print *, evaluator%eval(1.0)

    contains
        function return_eval_type() result(evaluator) !{{{

            !class(generic_evaluator), allocatable :: evaluator
            class(function_evaluator), allocatable :: evaluator

            type(function_evaluator) :: function_type

            function_type%f_ptr => linear
            allocate(evaluator, source=function_type)

            print *, evaluator%eval(1.0)
        end function return_eval_type !}}}

        real pure function linear(x) result(y) !{{{
            real, intent(in) :: x
            y = 1 + x
        end function linear !}}}

end program polymorphic

 

0 Kudos
Steven_L_Intel1
Employee
2,020 Views

Please show the result of ifort -logo  You didn't show us the ifort version (just the icc version).

0 Kudos
mSSM_B_
Beginner
2,020 Views

Was a bit late and didn't realize I posted icc instead of ifort. Here is the info you asked for

% ifort -logo
Intel(R) Fortran Intel(R) 64 Compiler XE for applications running on Intel(R) 64, Version 15.0 Build 20140723
Copyright (C) 1985-2014 Intel Corporation.  All rights reserved.
FOR NON-COMMERCIAL USE ONLY

 

0 Kudos
Steven_L_Intel1
Employee
2,020 Views

Ok, thanks. I'll see what I can figure out.

0 Kudos
Steven_L_Intel1
Employee
2,020 Views

Ok, now that I'm back in the office I can look at this more closely.

I can now reproduce the error, though it is elusive. My guess is that my earlier reference to thunks is still relevant, though I don't yet see that you're doing anything wrong. I'd be more comfortable if "linear" was an external procedure rather than an internal procedure of the main program.

I'll pass this on to the developers for them to take a look. Issue ID is DPD200363031.

0 Kudos
FortranFan
Honored Contributor III
2,020 Views

Steve Lionel (Intel) wrote:

... I'd be more comfortable if "linear" was an external procedure rather than an internal procedure of the main program.

..

Steve,

Also, do you think Intel Fortran is correct in its warning that Fortran 2008 standard disallows an procedure pointer target to be an internal procedure: [plain]warning #8266: Standard F2008 does not allow an internal procedure to be a procedure target.   [LINEAR][/plain].  I've not had the chance to look this up, but you can better comment on this aspect in the 2008 standard and its relevance to the code by OP.

0 Kudos
Steven_L_Intel1
Employee
2,020 Views

The warning is wrong. F2008 says (in discussing pointer assignment):

"C729 (R740) A procedure-name shall be the name of an internal, module, or dummy procedure, a procedure pointer, a specific intrinsic function listed in 13.6 and not marked with a bullet, or an external procedure that is accessed by use or host association, referenced in the scoping unit as a procedure or that has the EXTERNAL attribute." (Text modified by interpretation F08/0060). I will report this to the developers. Issue ID is DPD200363033.

0 Kudos
Reply