fparser
fparser copied to clipboard
Inconsistent matching of inline calls and derived type member references
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.
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.
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.
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?
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
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.
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.