Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Inconsistent matching of inline calls and derived type member references #384

Open
reuterbal opened this issue Jan 11, 2023 · 7 comments
Open
Assignees
Labels

Comments

@reuterbal
Copy link
Collaborator

Yet another edge case, which is most likely not possible to resolve without having procedures in the symbol table. However, I felt it might be useful to document it:

Function calls in expressions and derived type member references are represented inconsistently. One distinction seems to be made based on whether named arguments are used or not.
A little example:

fcode = """
module inline_call_mod
    implicit none

    type mytype
        integer :: val
        integer :: arr(3)
    contains
        procedure :: some_func
    end type mytype

contains

    function check(val, thr) result(is_bad)
        integer, intent(in) :: val
        integer, intent(in), optional :: thr
        integer :: eff_thr
        logical :: is_bad
        if (present(thr)) then
            eff_thr = thr
        else
            eff_thr = 10
        end if
        is_bad = val > thr
    end function check

    function some_func(this) result(is_bad)
        class(mytype), intent(in) :: this
        logical :: is_bad

        is_bad = check(this%val, thr=10) &
            &   .or. check(this%arr(1)) .or. check(val=this%arr(2)) .or. check(this%arr(3))
    end function some_func
end module inline_call_mod
""".strip()

from fparser.common.readfortran import FortranStringReader
from fparser.two.parser import ParserFactory
reader = FortranStringReader(fcode)
parser = ParserFactory().create(std='f2003')
ast = parser(reader)
ast

The four calls to check in the is_bad = ... expression are represented as:

  • check(this%val, thr=10):
Structure_Constructor(Type_Name('check'), Component_Spec_List(',', (Proc_Component_Ref(Name('this'), '%', Name('val')), Component_Spec(Name('thr'), Int_Literal_Constant('10', None))))
  • check(this%arr(1)):
Part_Ref(Name('check'), Section_Subscript_List(',', (Data_Ref('%', (Name('this'), Part_Ref(Name('arr'), Section_Subscript_List(',', (Int_Literal_Constant('1', None),))))),)))
  • check(val=this%arr(2)):
Structure_Constructor(Type_Name('check'), Component_Spec_List(',', (Component_Spec(Name('val'), Data_Ref('%', (Name('this'), Part_Ref(Name('arr'), Section_Subscript_List(',', (Int_Literal_Constant('2', None),)))))),)))
  • check(this%arr(3)):
Part_Ref(Name('check'), Section_Subscript_List(',', (Data_Ref('%', (Name('this'), Part_Ref(Name('arr'), Section_Subscript_List(',', (Int_Literal_Constant('3', None),))))),)))

What should, to my understanding, be a Function_Reference, is either represented as Structure_Constructor (named argument present) or Part_Ref (no named argument present).
Additionally, the reference to this%val is represented as a Proc_Component_Ref in the first case, but (correctly?) identified as a Data_Ref in all others.

@arporter
Copy link
Member

To add a further example to this, Daley has found that this behaviour (of matching a Structure_Constructor can be triggered by simply having a real, literal as argument to a function. If that argument is instead an integer literal then a Part_Ref results.

@arporter
Copy link
Member

Fundamentally, until we can resolve the type of e.g. check in @reuterbal's example then we have no way of know whether such an access is a Part_Ref, Structure_Constructor or Function_Reference.

@arporter arporter added the bug label Oct 19, 2023
@arporter arporter self-assigned this Oct 19, 2023
@arporter
Copy link
Member

In the following code:

      thread_count = OMP_GET_NUM_THREADS()
      thread_id = OMP_GET_THREAD_NUM()

both of the OMP calls are parsed as Structure_Constructor. Can a structure constructor have no arguments?

@reuterbal
Copy link
Collaborator Author

I think that's allowed as long as all derived type components are allocatable or have default initialisers.
https://j3-fortran.org/doc/year/10/10-007.pdf#page=98

@sergisiso
Copy link
Collaborator

sergisiso commented Jul 15, 2024

Couldn't we simply avoid matching Structure_Constructors? We would still be wrong (as not returning what the standard says it is - now we do the same just the other way around) but the tools that use fparser would have them as Function Calls, which is a reasonable approximation as they behave like them, and we won't be missing actual functions.

sergisiso added a commit that referenced this issue Jul 15, 2024
@sergisiso
Copy link
Collaborator

I created #449 to test this. Note that this does not attempt to solve the conflict with PartRef also mentioned in the original comment, only the Structure_Constructor.

@arporter
Copy link
Member

This would be good to have. I've just hit it again in PSyclone/#2769 where I've changed the Fortran backend to automatically add ".0" to reals.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

3 participants