1

I just want to know for sure if this is valid Fortran or if I've misunderstood some usage. Is the following code valid?

Module MathFxns

    implicit none
    
    Type A_T
        procedure(DoStuff_F), nopass, pointer :: method => add
    contains
        
    End Type A_T
    
    Abstract Interface
    
        Function DoStuff_F(a, b) result(c)
            integer, intent(in) :: a, b
            integer :: c
        End Function DoStuff_F
        
    End Interface
    
    contains
    
    function add(a, b) result(c)
    
        integer, intent(in) :: a, b
        integer :: c
        
        c = a + b
        
    end function add
    
End Module MathFxns
    
program Main

    use MathFxns

    implicit none

    type(A_T) :: math
    
    print *, math%method(2, 5)
    
end program Main

I just had to track down a compiler bug, that was being caused by something I think is valid Fortran. I'd submit to the compiler team, but I don't have a way to replicate as it's buried pretty far down in the stack and down multiple libraries before it caused a compiler bug and it doesn't happen in every program that uses it.

Edit: I didn't mention it before because it is complicated to explain, but since there was some curiosity, I'll try.

The production code does work in some executables, but recently I implemented it in another project which caused a compiler bug. I'll try to make a pseudo code example to illustrate, but first is a description. In the production code I have a type that has a default procedure pointer to a function (just like above). An instance of that type is a field of an abstract type. That abstract type is extended by another abstract type, then in a subsequent library that type is extended by another abstract type, which is then extended by a concrete type in another library. Finally an executable makes use of that concrete type. The module that has an instance of the concrete type throws a compiler error.

In the production code, it is an ODE Solver, with functionality wrapped into an entity type that gets extended a few times before being implemented.

It took me 6 hours, but after commenting and uncommenting line after line, the cause of the error was shown to be the default procedure pointer in the type. Whether that is the actual error or not, I can't know, but removing the default pointer (and pointing in the construction subroutine) made the project work again.

!this is in the first static library project
Module Library1
    
    implicit none
    
    Type A_T
        !more stuff
        procedure(DoStuff_F), nopass, pointer :: method => add
    contains
        !more procedures
    End Type A_T
    
    Type, abstract :: B1_A
        type(A_T) :: a
        !more stuff and procedures
    End Type B1_A
    
    Type, extends(B1_A), abstract :: B2_A
        !more stuff and procedures
    End Type B2_A
    
    Abstract Interface
    
        Function DoStuff_F(a, b) result(c)
            integer, intent(in) :: a, b
            integer :: c
        End Function DoStuff_F
        
    End Interface
    
    contains
    
    function add(a, b) result(c)
    
        integer, intent(in) :: a, b
        integer :: c
        
        c = a + b
        
    end function add
    
End Module Library1

! this is in the second static library project
Module Library2
    
    use Library1
    
    implicit none
    
    Type, extends(B2_A), abstract :: B3_A
        !more stuff and procedures
    End Type B3_A
    
End Module Library2
    
! this is in the third static library project
Module Library3
    
    use Library2
    
    implicit none
    
    Type, extends(B3_A) :: C_T
        !more stuff and procedures
    End Type C_T
    
End Module Library3

!this is in a fourth executable project
program Main

    use Library3

    implicit none

    type(C_T) :: math
    
    print *, math%a%method(2, 5)
    
end program Main
TrippLamb
  • 1,422
  • 13
  • 24
  • It took me a few readthroughs to work out what your code is doing and why it's doing it. Could you add a brief text description to go along with the code? – veryreverie Jan 20 '22 at 18:16
  • 1
    For what it's worth your code looks fine to me, but it's sufficiently niche that I wouldn't be surprised if it's forbidden by the standard or if the compilers can't handle it. One possible source of error is that Fortran procedure pointers [don't have closure](https://stackoverflow.com/questions/67161321/procedural-pointer-in-fortran/67163911#67163911) so if your default function is any more complex than `add` you may end up referencing undefined variables. – veryreverie Jan 20 '22 at 18:22
  • 1
    Does the code behave incorrectly for you? Is this piece of code supposed to show the mentioned compiler bug or not? It looks OK to me. – Vladimir F Героям слава Jan 20 '22 at 18:24
  • 1
    I don't spot anything non-standard about the code, but I see that NAG Fortran doesn't like it, complaining that `add` is multiply defined (which I don't understand and will ask about.) I wonder if the unspecified misbehavior is not due to a bug here, but something else. – Steve Lionel Jan 20 '22 at 18:41
  • 2
    There are constraints on using an initial pointer target (whether the procedure pointer is a component or not), but `add` here is valid. As others say, please expand on why you think there may be a problem. – francescalus Jan 20 '22 at 18:47
  • 1
    @SteveLionel, which version of nagfor? I don't get a complaint. – francescalus Jan 20 '22 at 18:47
  • 1
    @francescalus 7.0.0.3(7048). (7041) was the same. – Steve Lionel Jan 20 '22 at 19:55
  • 1
    @SteveLionel, I'm using 7.1 7101 (latest public). – francescalus Jan 20 '22 at 20:05
  • @VladimirF this does not cause a compiler bug, I believe it to be correct Fortran, and it does behave properly on projects that do not cause a compiler bug error – TrippLamb Jan 20 '22 at 20:16
  • @francescalus I believe this to be correct Fortran code, but as I mentioned I got a compiler bug until I removed it, so I wanted to check. – TrippLamb Jan 20 '22 at 20:17
  • @SteveLionel It very well could be due to something else, but the project is large and after multiple hours of commenting, uncommenting, and rebuilding this was the line causing it to not finish compiling. I added further information in the original question. – TrippLamb Jan 20 '22 at 20:18
  • @veryreverie I understand that, and the actual code is far more complex, but it behaves correctly and does not have any undefined variables. This issue I'm asking about is it refusing to build rather than it running improperly. – TrippLamb Jan 20 '22 at 20:20
  • 1
    Your original example is valid code, but that doesn't mean that anything that is "similar" is also valid code. I'm afraid that means you can't use our conclusions to say anything about your full example. If you can share the symptoms of the full example we may be able to say more tuned to that. For example, what error message is given? (If it's an "internal compiler error", that's a bug in the compiler but if it's "invalid initial procedure target" then it may or may not be a bug.) – francescalus Jan 20 '22 at 20:22
  • @francescalus it is just an internal compiler error. I'm only attempting to ask if using a default procedure pointer within the type declaration is valid Fortran. The general consensus seems to be yes on that. As for everything else I'm very aware there could be another issue somewhere, but I don't know of a feasible way of narrowing it down any further as it is a fairly large project. Also, it's company code so I can't make it public. – TrippLamb Jan 20 '22 at 20:28
  • If it's an internal compiler error, then yes, you have a compiler bug. Whether you have valid Fortran code or not is at that point not terribly relevant. Report to vendor and find a workaround (as you have). (It's not relevant, because valid code or not your compiler doesn't accept it, so you have to change compiler or change the code. Same as if using a feature which isn't yet implemented.) – francescalus Jan 20 '22 at 20:30
  • @francescalus It is relevant because I wanted to make sure the code I was writing was standard Fortran. Which is why my question was about whether or not this was valid Fortran. I added some extra information at the end to give context. A solution to the compiler bug is the irrelevant information. – TrippLamb Jan 20 '22 at 20:36
  • What people are saying is that the original example you show is valid. What you cannot conclude from this is that all code which has a procedure pointer component with an initial pointer target is valid. Some initial targets are not allowed, some references of a procedure pointer component are not allowed, and so on. (And unfortunately, it's not a simple matter to write down in an answer here what the rules are - otherwise everyone would be happy.) – francescalus Jan 20 '22 at 20:41
  • @francescalus I definitely understand that saying one thing is valid is not saying everything similar is unequivocally valid. What is an example of an invalid initial pointer target that would build? That would be very helpful to know. – TrippLamb Jan 20 '22 at 20:45
  • A simple class of an invalid pointer target which "builds" would be to give the procedure pointer an implicit interface, while offering the compiler no chance to check whether the implicit interface is appropriate for the initial target. – francescalus Jan 20 '22 at 20:53
  • @francescalus, right that makes sense. I'm not doing anything like that the production code follows very closely to my original example, the only exception being that it isn't a nopass – TrippLamb Jan 20 '22 at 21:04

0 Answers0