From dea30aca3a56bb72d4e1eddb04f98c53bcb5992a Mon Sep 17 00:00:00 2001 From: Peter Klausler <35819229+klausler@users.noreply.github.com> Date: Tue, 2 Jan 2024 09:25:49 -0800 Subject: [PATCH] [flang][runtime] NAMELIST input into storage sequence (#76584) Nearly every Fortran compiler supports the extension of NAMELIST input into a storage sequence identified by its initial scalar array element. For example, &GROUP A(1) = 1. 2. 3. / should be processed as if the input had been &GROUP A(1:) = 1. 2. 3. / Fixes llvm-test-suite/Fortran/gfortran/regression/namelist_24.f90. --- flang/docs/Extensions.md | 4 ++++ flang/runtime/namelist.cpp | 38 ++++++++++++++++++++++++++++++++++---- 2 files changed, 38 insertions(+), 4 deletions(-) diff --git a/flang/docs/Extensions.md b/flang/docs/Extensions.md index ab040b61703c..da208f58da88 100644 --- a/flang/docs/Extensions.md +++ b/flang/docs/Extensions.md @@ -657,3 +657,7 @@ end but every Fortran compiler allows the encoding to be changed on an open unit. +* A `NAMELIST` input item that references a scalar element of a vector + or contiguous array can be used as the initial element of a storage + sequence. For example, "&GRP A(1)=1. 2. 3./" is treated as if had been + "&GRP A(1:)=1. 2. 3./". diff --git a/flang/runtime/namelist.cpp b/flang/runtime/namelist.cpp index d9908bf7089a..e6997bcf945b 100644 --- a/flang/runtime/namelist.cpp +++ b/flang/runtime/namelist.cpp @@ -247,6 +247,28 @@ static bool HandleSubscripts(IoStatementState &io, Descriptor &desc, return false; } +static void StorageSequenceExtension( + Descriptor &desc, const Descriptor &source) { + // Support the near-universal extension of NAMELIST input into a + // designatable storage sequence identified by its initial scalar array + // element. For example, treat "A(1) = 1. 2. 3." as if it had been + // "A(1:) = 1. 2. 3.". + if (desc.rank() == 0 && (source.rank() == 1 || source.IsContiguous())) { + if (auto stride{source.rank() == 1 + ? source.GetDimension(0).ByteStride() + : static_cast(source.ElementBytes())}; + stride != 0) { + desc.raw().attribute = CFI_attribute_pointer; + desc.raw().rank = 1; + desc.GetDimension(0) + .SetBounds(1, + source.Elements() - + ((source.OffsetElement() - desc.OffsetElement()) / stride)) + .SetByteStride(stride); + } + } +} + static bool HandleSubstring( IoStatementState &io, Descriptor &desc, const char *name) { IoErrorHandler &handler{io.GetIoErrorHandler()}; @@ -480,10 +502,14 @@ bool IONAME(InputNamelist)(Cookie cookie, const NamelistGroup &group) { bool hadSubscripts{false}; bool hadSubstring{false}; if (next && (*next == '(' || *next == '%')) { + const Descriptor *lastSubscriptBase{nullptr}; + Descriptor *lastSubscriptDescriptor{nullptr}; do { Descriptor &mutableDescriptor{staticDesc[whichStaticDesc].descriptor()}; whichStaticDesc ^= 1; io.HandleRelativePosition(byteCount); // skip over '(' or '%' + lastSubscriptDescriptor = nullptr; + lastSubscriptBase = nullptr; if (*next == '(') { if (!hadSubstring && (hadSubscripts || useDescriptor->rank() == 0)) { mutableDescriptor = *useDescriptor; @@ -497,11 +523,12 @@ bool IONAME(InputNamelist)(Cookie cookie, const NamelistGroup &group) { "NAMELIST group '%s'", name, group.groupName); return false; + } else if (HandleSubscripts( + io, mutableDescriptor, *useDescriptor, name)) { + lastSubscriptBase = useDescriptor; + lastSubscriptDescriptor = &mutableDescriptor; } else { - if (!HandleSubscripts( - io, mutableDescriptor, *useDescriptor, name)) { - return false; - } + return false; } hadSubscripts = true; } else { @@ -514,6 +541,9 @@ bool IONAME(InputNamelist)(Cookie cookie, const NamelistGroup &group) { useDescriptor = &mutableDescriptor; next = io.GetCurrentChar(byteCount); } while (next && (*next == '(' || *next == '%')); + if (lastSubscriptDescriptor) { + StorageSequenceExtension(*lastSubscriptDescriptor, *lastSubscriptBase); + } } // Skip the '=' next = io.GetNextNonBlank(byteCount);