In Fortran namelists, it is possible to have namelist members of the intrinsic type that are allocatable, but derived types with allocatable components are forbidden. What is there such an extra constraint on derived type components but not on allocatable objects of intrinsic type? Could this behavior change in a future standard release?
There is a bit of a false premise behind the question. A component of a thing is not the thing itself.
Namelist group objects may be allocatable. But it doesn't matter whether the namelist group object is of intrinsic or derived type. There's no distinction here.
Namelist group objects that are allocated must be allocated when the relevant namelist input/output statement is executed. Again, it doesn't matter whether the namelist group object is of intrinsic or derived type. There's also no distinction here.
There are no intrinsic types with allocatable components, so any discussion about restrictions or requirements on allocatable components is simply not relevant for intrinsic types. There would be no point specifying restrictions and requirements on something that doesn't exist.
Who knows what the future could bring, but a reason that a defined input/output procedure is required for objects that have allocatable or pointer components is because there are multiple plausible options for how such a component should be represented. Rather than pick a winner - let the programmer be explicit about what they want and expect.
Thanks, @IanH for your response, although my question still remains unanswered. If we can have allocatable objects or derived types in namelist, why not allow derived-types with allocatable components in namelist? If you already answered this, could you elaborate with an example of an ambiguity case that you mentioned? Thanks.
This limitation is a major drawback in Fortran namelist IO in my opinion. Certainly a barrier to improving our Fortran IO codebase.
(I don't understand why it is a major drawback - you write the defined input/output procedures to represent and read the object with allocatable or pointer components, as you desire, and away you go.)
Whether an object is allocatable or not is different to whether the object has an allocatable component or not. The allocation status of an object is not part of its value, the allocation status of a component of the object is part of the value of the object with the component.
Input/output statements, generally, transfer values between the objects in their input/output list and a file - to transfer the values of objects with allocatable components you therefore need to come up with some way of representing the allocation status (and other things also considered part of the value of an object with a component that are enabled by a component being allocatable - see F2018 7.5.8) in a file.
Consider a type and object:
type :: t integer, allocatable :: comp(:) end type t type(t) :: x, y, z x%comp = [1, 2, 3, 4] allocate(y%comp(2:3)) ; y%comp(2) = 2 ; y%comp(3) = 3
x, y, and z all have very well defined values under the rules of the language. How do you represent those values of x, y and z in a formatted (or unformatted) file? Now consider higher rank components. Then consider polymorphic components. Then give up, and make the programmer write the relevant code.
Thank you again @IanH. What I get from your response is that it is all because of storing the allocation status of the component as part of the object itself, that the following code does not work. Here is an example code that illustrates my point: case 1 & 2 compile and are conforming, but case 3 returns an error.
program nml_prog character(:), allocatable :: nml integer, allocatable :: comp(:) namelist /mynml/ comp type :: static_type integer :: comp(4) end type static_type type(static_type) :: static namelist /mynml/ static type :: dynamic_type integer, allocatable :: comp(:) end type dynamic_type type(dynamic_type) :: dynamic namelist /mynml/ dynamic ! 1: read and write allocatable in namelist allocate(comp(4)) nml = "&mynml comp = 4*1 /" read(nml, nml = mynml) write(*, nml = mynml) ! 2: read write derived type with static array in namelist nml = "&mynml static%comp = 4*1 /" read(nml, nml = mynml) write(*, nml = mynml) ! 3: read write derived type with dynamic array in namelist allocate(dynamic%comp(4)) nml = "&mynml dynamic%comp(1:4) = 4*1 /" read(nml, nml = mynml) write(*, nml = mynml) end program nml_prog
Error: NAMELIST object ‘dynamic’ in namelist ‘mynml’ at (1) has ALLOCATABLE or POINTER components and thus requires a defined input/output procedure main.f95:25:25
I do not know the inner workings of compilers, but still, I am not fully convinced why allowing the above code in Fortran would be more difficult than writing user-defined IO. It is a major drawback, in the sense that the Fortran programmer has to at least double their work to read a derived type that happens to have a dynamic component. If the work takes one month to write, add another month to read the alternatives to the components one by one from the input namelist, match them with the corresponding components in the derived types, copy data, and only then, away the user goes.
This hasn't got anything to do with compiler workings, apart from the fact that they cannot read minds.
Your example isn't general - somehow the programmer knew that the component needed to be allocated, and allocated with a lower bound of one and an extent of four. To be general, you need to encode that information in the text that you provide for namelist input, not separately.
A requirement that all components be allocated prior to namelist input or output (if that is what you are suggesting) ignores perfectly valid derived type values where components are unallocated, and ruins any attempt at encapsulation (stick the type definition in a module and mark the component private - then how is the main program supposed to read in the value?). This might depend on other program requirements, but if a non-polymorphic component always has to be allocated for the derived type object to be considered valid, then its questionable whether the component needs to be allocatable in the first place.
The timeframe to knock out the relevant defined input/output procedures seems a bit excessive to me.