brintos

brintos / llvm-project-archived public Read only

0
0
Text · 23.8 KiB · e9c0b8f Raw
694 lines · cpp
1//===-- lib/runtime/namelist.cpp --------------------------------*- C++ -*-===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8 9#include "flang-rt/runtime/namelist.h"10#include "descriptor-io.h"11#include "flang-rt/runtime/emit-encoded.h"12#include "flang-rt/runtime/io-stmt.h"13#include "flang-rt/runtime/type-info.h"14#include "flang/Runtime/io-api.h"15#include <algorithm>16#include <cstring>17#include <limits>18 19namespace Fortran::runtime::io {20 21RT_VAR_GROUP_BEGIN22// Max size of a group, symbol or component identifier that can appear in23// NAMELIST input, plus a byte for NUL termination.24static constexpr RT_CONST_VAR_ATTRS std::size_t nameBufferSize{201};25RT_VAR_GROUP_END26 27RT_OFFLOAD_API_GROUP_BEGIN28 29static inline RT_API_ATTRS char32_t GetComma(IoStatementState &io) {30  return io.mutableModes().GetSeparatorChar();31}32 33bool IODEF(OutputNamelist)(Cookie cookie, const NamelistGroup &group) {34  IoStatementState &io{*cookie};35  io.CheckFormattedStmtType<Direction::Output>("OutputNamelist");36  io.mutableModes().inNamelist = true;37  ConnectionState &connection{io.GetConnectionState()};38  // The following lambda definition violates the conding style,39  // but cuda-11.8 nvcc hits an internal error with the brace initialization.40 41  // Internal function to advance records and convert case42  const auto EmitUpperCase = [&](const char *prefix, std::size_t prefixLen,43                                 const char *str, char suffix) -> bool {44    if ((connection.NeedAdvance(prefixLen) &&45            !(io.AdvanceRecord() && EmitAscii(io, " ", 1))) ||46        !EmitAscii(io, prefix, prefixLen) ||47        (connection.NeedAdvance(runtime::strlen(str) + (suffix != ' ')) &&48            !(io.AdvanceRecord() && EmitAscii(io, " ", 1)))) {49      return false;50    }51    for (; *str; ++str) {52      char up{*str >= 'a' && *str <= 'z' ? static_cast<char>(*str - 'a' + 'A')53                                         : *str};54      if (!EmitAscii(io, &up, 1)) {55        return false;56      }57    }58    return suffix == ' ' || EmitAscii(io, &suffix, 1);59  };60  // &GROUP61  if (!EmitUpperCase(" &", 2, group.groupName, ' ')) {62    return false;63  }64  auto *listOutput{io.get_if<ListDirectedStatementState<Direction::Output>>()};65  char comma{static_cast<char>(GetComma(io))};66  char prefix{' '};67  for (std::size_t j{0}; j < group.items; ++j) {68    // [,]ITEM=...69    const NamelistGroup::Item &item{group.item[j]};70    if (listOutput) {71      listOutput->set_lastWasUndelimitedCharacter(false);72    }73    if (!EmitUpperCase(&prefix, 1, item.name, '=')) {74      return false;75    }76    prefix = comma;77    if (const auto *addendum{item.descriptor.Addendum()};78        addendum && addendum->derivedType()) {79      const NonTbpDefinedIoTable *table{group.nonTbpDefinedIo};80      if (!IONAME(OutputDerivedType)(cookie, item.descriptor, table)) {81        return false;82      }83    } else if (!descr::DescriptorIO<Direction::Output>(io, item.descriptor)) {84      return false;85    }86  }87  // terminal /88  return EmitUpperCase("/", 1, "", ' ');89}90 91static constexpr RT_API_ATTRS bool IsLegalIdStart(char32_t ch) {92  return (ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z') || ch == '_' ||93      ch == '@';94}95 96static constexpr RT_API_ATTRS bool IsLegalIdChar(char32_t ch) {97  return IsLegalIdStart(ch) || (ch >= '0' && ch <= '9');98}99 100static constexpr RT_API_ATTRS char NormalizeIdChar(char32_t ch) {101  return static_cast<char>(ch >= 'A' && ch <= 'Z' ? ch - 'A' + 'a' : ch);102}103 104static RT_API_ATTRS bool GetLowerCaseName(IoStatementState &io, char buffer[],105    std::size_t maxLength, bool crashIfTooLong = true) {106  std::size_t byteLength{0};107  if (auto ch{io.GetNextNonBlank(byteLength)}) {108    if (IsLegalIdStart(*ch)) {109      std::size_t j{0};110      do {111        buffer[j] = NormalizeIdChar(*ch);112        io.HandleRelativePosition(byteLength);113        ch = io.GetCurrentChar(byteLength);114      } while (++j < maxLength && ch && IsLegalIdChar(*ch));115      buffer[j++] = '\0';116      if (j <= maxLength) {117        return true;118      }119      if (crashIfTooLong) {120        io.GetIoErrorHandler().SignalError(121            "Identifier '%s...' in NAMELIST input group is too long", buffer);122      }123    }124  }125  return false;126}127 128static RT_API_ATTRS common::optional<SubscriptValue> GetSubscriptValue(129    IoStatementState &io) {130  common::optional<SubscriptValue> value;131  std::size_t byteCount{0};132  common::optional<char32_t> ch{io.GetCurrentChar(byteCount)};133  bool negate{ch && *ch == '-'};134  if ((ch && *ch == '+') || negate) {135    io.HandleRelativePosition(byteCount);136    ch = io.GetCurrentChar(byteCount);137  }138  bool overflow{false};139  while (ch && *ch >= '0' && *ch <= '9') {140    SubscriptValue was{value.value_or(0)};141    overflow |= was >= std::numeric_limits<SubscriptValue>::max() / 10;142    value = 10 * was + *ch - '0';143    io.HandleRelativePosition(byteCount);144    ch = io.GetCurrentChar(byteCount);145  }146  if (overflow) {147    io.GetIoErrorHandler().SignalError(148        "NAMELIST input subscript value overflow");149    return common::nullopt;150  }151  if (negate) {152    if (value) {153      return -*value;154    } else {155      io.HandleRelativePosition(-byteCount); // give back '-' with no digits156    }157  }158  return value;159}160 161static RT_API_ATTRS bool HandleSubscripts(IoStatementState &io,162    Descriptor &desc, const Descriptor &source, const char *name) {163  IoErrorHandler &handler{io.GetIoErrorHandler()};164  // Allow for blanks in subscripts; they're nonstandard, but not165  // ambiguous within the parentheses.166  SubscriptValue lower[maxRank], upper[maxRank], stride[maxRank];167  int j{0};168  std::size_t contiguousStride{source.ElementBytes()};169  bool ok{true};170  std::size_t byteCount{0};171  common::optional<char32_t> ch{io.GetNextNonBlank(byteCount)};172  char32_t comma{GetComma(io)};173  for (; ch && *ch != ')'; ++j) {174    SubscriptValue dimLower{0}, dimUpper{0}, dimStride{0};175    if (j < maxRank && j < source.rank()) {176      const Dimension &dim{source.GetDimension(j)};177      dimLower = dim.LowerBound();178      dimUpper = dim.UpperBound();179      dimStride =180          dim.ByteStride() / std::max<SubscriptValue>(contiguousStride, 1);181      contiguousStride *= dim.Extent();182    } else if (ok) {183      handler.SignalError(184          "Too many subscripts for rank-%d NAMELIST group item '%s'",185          source.rank(), name);186      ok = false;187    }188    if (auto low{GetSubscriptValue(io)}) {189      if (*low < dimLower || (dimUpper >= dimLower && *low > dimUpper)) {190        if (ok) {191          handler.SignalError("Subscript %jd out of range %jd..%jd in NAMELIST "192                              "group item '%s' dimension %d",193              static_cast<std::intmax_t>(*low),194              static_cast<std::intmax_t>(dimLower),195              static_cast<std::intmax_t>(dimUpper), name, j + 1);196          ok = false;197        }198      } else {199        dimLower = *low;200      }201      ch = io.GetNextNonBlank(byteCount);202    }203    if (ch && *ch == ':') {204      io.HandleRelativePosition(byteCount);205      ch = io.GetNextNonBlank(byteCount);206      if (auto high{GetSubscriptValue(io)}) {207        if (*high > dimUpper) {208          if (ok) {209            handler.SignalError(210                "Subscript triplet upper bound %jd out of range (>%jd) in "211                "NAMELIST group item '%s' dimension %d",212                static_cast<std::intmax_t>(*high),213                static_cast<std::intmax_t>(dimUpper), name, j + 1);214            ok = false;215          }216        } else {217          dimUpper = *high;218        }219        ch = io.GetNextNonBlank(byteCount);220      }221      if (ch && *ch == ':') {222        io.HandleRelativePosition(byteCount);223        ch = io.GetNextNonBlank(byteCount);224        if (auto str{GetSubscriptValue(io)}) {225          dimStride = *str;226          ch = io.GetNextNonBlank(byteCount);227        }228      }229    } else { // scalar230      dimUpper = dimLower;231      dimStride = 0;232    }233    if (ch && *ch == comma) {234      io.HandleRelativePosition(byteCount);235      ch = io.GetNextNonBlank(byteCount);236    }237    if (ok) {238      lower[j] = dimLower;239      upper[j] = dimUpper;240      stride[j] = dimStride;241    }242  }243  if (ok) {244    if (ch && *ch == ')') {245      io.HandleRelativePosition(byteCount);246      if (desc.EstablishPointerSection(source, lower, upper, stride)) {247        return true;248      } else {249        handler.SignalError(250            "Bad subscripts for NAMELIST input group item '%s'", name);251      }252    } else {253      handler.SignalError(254          "Bad subscripts (missing ')') for NAMELIST input group item '%s'",255          name);256    }257  }258  return false;259}260 261static RT_API_ATTRS bool HasDefinedIoSubroutine(common::DefinedIo definedIo,262    typeInfo::SpecialBinding::Which specialBinding,263    const typeInfo::DerivedType *derivedType,264    const NonTbpDefinedIoTable *table) {265  for (; derivedType; derivedType = derivedType->GetParentType()) {266    if ((table && table->Find(*derivedType, definedIo) != nullptr) ||267        derivedType->FindSpecialBinding(specialBinding)) {268      return true;269    }270  }271  return false;272}273 274static RT_API_ATTRS bool HasDefinedIoSubroutine(common::DefinedIo definedIo,275    typeInfo::SpecialBinding::Which specialBinding,276    const Descriptor &descriptor, const NonTbpDefinedIoTable *table) {277  const DescriptorAddendum *addendum{descriptor.Addendum()};278  return addendum &&279      HasDefinedIoSubroutine(280          definedIo, specialBinding, addendum->derivedType(), table);281}282 283static RT_API_ATTRS void StorageSequenceExtension(Descriptor &desc,284    const Descriptor &source, const io::NonTbpDefinedIoTable *table) {285  // Support the near-universal extension of NAMELIST input into a286  // designatable storage sequence identified by its initial scalar array287  // element.  For example, treat "A(1) = 1. 2. 3." as if it had been288  // "A(1:) = 1. 2. 3.".289  // (But don't do this for derived types with defined formatted READs,290  // since they might do non-list-directed input that won't stop at the291  // next namelist input item name.)292  if (desc.rank() == 0 && (source.rank() == 1 || source.IsContiguous()) &&293      !HasDefinedIoSubroutine(common::DefinedIo::ReadFormatted,294          typeInfo::SpecialBinding::Which::ReadFormatted, desc, table)) {295    if (auto stride{source.rank() == 1296                ? source.GetDimension(0).ByteStride()297                : static_cast<SubscriptValue>(source.ElementBytes())};298        stride != 0) {299      common::optional<DescriptorAddendum> savedAddendum;300      if (const DescriptorAddendum *addendum{desc.Addendum()}) {301        // Preserve a copy of the addendum, if any, before clobbering it302        savedAddendum.emplace(*addendum);303      }304      desc.raw().attribute = CFI_attribute_pointer;305      desc.raw().rank = 1;306      desc.GetDimension(0)307          .SetBounds(1,308              source.Elements() -309                  ((source.OffsetElement() - desc.OffsetElement()) / stride))310          .SetByteStride(stride);311      if (savedAddendum) {312        *desc.Addendum() = *savedAddendum;313      }314    }315  }316}317 318static RT_API_ATTRS bool HandleSubstring(319    IoStatementState &io, Descriptor &desc, const char *name) {320  IoErrorHandler &handler{io.GetIoErrorHandler()};321  auto pair{desc.type().GetCategoryAndKind()};322  if (!pair || pair->first != TypeCategory::Character) {323    handler.SignalError("Substring reference to non-character item '%s'", name);324    return false;325  }326  int kind{pair->second};327  SubscriptValue chars{static_cast<SubscriptValue>(desc.ElementBytes()) / kind};328  // Allow for blanks in substring bounds; they're nonstandard, but not329  // ambiguous within the parentheses.330  common::optional<SubscriptValue> lower, upper;331  std::size_t byteCount{0};332  common::optional<char32_t> ch{io.GetNextNonBlank(byteCount)};333  if (ch) {334    if (*ch == ':') {335      lower = 1;336    } else {337      lower = GetSubscriptValue(io);338      ch = io.GetNextNonBlank(byteCount);339    }340  }341  if (ch && *ch == ':') {342    io.HandleRelativePosition(byteCount);343    ch = io.GetNextNonBlank(byteCount);344    if (ch) {345      if (*ch == ')') {346        upper = chars;347      } else {348        upper = GetSubscriptValue(io);349        ch = io.GetNextNonBlank(byteCount);350      }351    }352  }353  if (ch && *ch == ')') {354    io.HandleRelativePosition(byteCount);355    if (lower && upper) {356      if (*lower > *upper) {357        // An empty substring, whatever the values are358        desc.raw().elem_len = 0;359        return true;360      }361      if (*lower >= 1 && *upper <= chars) {362        // Offset the base address & adjust the element byte length363        desc.raw().elem_len = (*upper - *lower + 1) * kind;364        desc.set_base_addr(reinterpret_cast<void *>(365            reinterpret_cast<char *>(desc.raw().base_addr) +366            kind * (*lower - 1)));367        return true;368      }369    }370    handler.SignalError(371        "Bad substring bounds for NAMELIST input group item '%s'", name);372  } else {373    handler.SignalError(374        "Bad substring (missing ')') for NAMELIST input group item '%s'", name);375  }376  return false;377}378 379static RT_API_ATTRS bool HandleComponent(IoStatementState &io, Descriptor &desc,380    const Descriptor &source, const char *name) {381  IoErrorHandler &handler{io.GetIoErrorHandler()};382  char compName[nameBufferSize];383  if (GetLowerCaseName(io, compName, sizeof compName)) {384    const DescriptorAddendum *addendum{source.Addendum()};385    if (const typeInfo::DerivedType *386        type{addendum ? addendum->derivedType() : nullptr}) {387      if (const typeInfo::Component *comp{388              type->FindDataComponent(compName, runtime::strlen(compName))}) {389        bool createdDesc{false};390        if (comp->rank() > 0 && source.rank() > 0) {391          // If base and component are both arrays, the component name392          // must be followed by subscripts; process them now.393          std::size_t byteCount{0};394          if (common::optional<char32_t> next{io.GetNextNonBlank(byteCount)};395              next && *next == '(') {396            io.HandleRelativePosition(byteCount); // skip over '('397            StaticDescriptor<maxRank, true, 16> staticDesc;398            Descriptor &tmpDesc{staticDesc.descriptor()};399            comp->CreatePointerDescriptor(tmpDesc, source, handler);400            if (!HandleSubscripts(io, desc, tmpDesc, compName)) {401              return false;402            }403            createdDesc = true;404          }405        }406        if (!createdDesc) {407          comp->CreatePointerDescriptor(desc, source, handler);408        }409        if (source.rank() > 0) {410          if (desc.rank() > 0) {411            handler.SignalError(412                "NAMELIST component reference '%%%s' of input group "413                "item %s cannot be an array when its base is not scalar",414                compName, name);415            return false;416          }417          desc.raw().rank = source.rank();418          for (int j{0}; j < source.rank(); ++j) {419            const auto &srcDim{source.GetDimension(j)};420            desc.GetDimension(j)421                .SetBounds(1, srcDim.UpperBound())422                .SetByteStride(srcDim.ByteStride());423          }424        }425        return true;426      } else {427        handler.SignalError(428            "NAMELIST component reference '%%%s' of input group item %s is not "429            "a component of its derived type",430            compName, name);431      }432    } else if (source.type().IsDerived()) {433      handler.Crash("Derived type object '%s' in NAMELIST is missing its "434                    "derived type information!",435          name);436    } else {437      handler.SignalError("NAMELIST component reference '%%%s' of input group "438                          "item %s for non-derived type",439          compName, name);440    }441  } else {442    handler.SignalError("NAMELIST component reference of input group item %s "443                        "has no name after '%%'",444        name);445  }446  return false;447}448 449// Advance to the terminal '/' of a namelist group or leading '&'/'$'450// of the next.451static RT_API_ATTRS void SkipNamelistGroup(IoStatementState &io) {452  std::size_t byteCount{0};453  while (auto ch{io.GetNextNonBlank(byteCount)}) {454    io.HandleRelativePosition(byteCount);455    if (*ch == '/' || *ch == '&' || *ch == '$') {456      break;457    } else if (*ch == '\'' || *ch == '"') {458      // Skip quoted character literal459      char32_t quote{*ch};460      while (true) {461        if ((ch = io.GetCurrentChar(byteCount))) {462          io.HandleRelativePosition(byteCount);463          if (*ch == quote) {464            break;465          }466        } else if (!io.AdvanceRecord()) {467          return;468        }469      }470    }471  }472}473 474bool IODEF(InputNamelist)(Cookie cookie, const NamelistGroup &group) {475  IoStatementState &io{*cookie};476  io.CheckFormattedStmtType<Direction::Input>("InputNamelist");477  io.mutableModes().inNamelist = true;478  IoErrorHandler &handler{io.GetIoErrorHandler()};479  auto *listInput{io.get_if<ListDirectedStatementState<Direction::Input>>()};480  RUNTIME_CHECK(handler, listInput != nullptr);481  // Find this namelist group's header in the input482  io.BeginReadingRecord();483  common::optional<char32_t> next;484  char name[nameBufferSize];485  RUNTIME_CHECK(handler, group.groupName != nullptr);486  char32_t comma{GetComma(io)};487  std::size_t byteCount{0};488  while (true) {489    next = io.GetNextNonBlank(byteCount);490    while (next && *next != '&' && *next != '$') {491      // Extension: comment lines without ! before namelist groups492      if (!io.AdvanceRecord()) {493        next.reset();494      } else {495        next = io.GetNextNonBlank(byteCount);496      }497    }498    if (!next) {499      handler.SignalEnd();500      return false;501    }502    if (*next != '&' && *next != '$') {503      handler.SignalError(504          "NAMELIST input group does not begin with '&' or '$' (at '%lc')",505          *next);506      return false;507    }508    io.HandleRelativePosition(byteCount);509    if (!GetLowerCaseName(io, name, sizeof name)) {510      handler.SignalError("NAMELIST input group has no name");511      return false;512    }513    if (runtime::strcmp(group.groupName, name) == 0) {514      break; // found it515    }516    SkipNamelistGroup(io);517  }518  // Read the group's items519  while (true) {520    next = io.GetNextNonBlank(byteCount);521    if (!next || *next == '/' || *next == '&' || *next == '$') {522      break;523    }524    if (!GetLowerCaseName(io, name, sizeof name)) {525      handler.SignalError(526          "NAMELIST input group '%s' was not terminated at '%c'",527          group.groupName, static_cast<char>(*next));528      return false;529    }530    std::size_t itemIndex{0};531    for (; itemIndex < group.items; ++itemIndex) {532      if (runtime::strcmp(name, group.item[itemIndex].name) == 0) {533        break;534      }535    }536    if (itemIndex >= group.items) {537      handler.SignalError(538          "'%s' is not an item in NAMELIST group '%s'", name, group.groupName);539      return false;540    }541    // Handle indexing and components, if any.  No spaces are allowed.542    // A copy of the descriptor is made if necessary.543    const Descriptor &itemDescriptor{group.item[itemIndex].descriptor};544    const Descriptor *useDescriptor{&itemDescriptor};545    StaticDescriptor<maxRank, true, 16> staticDesc[2];546    int whichStaticDesc{0};547    next = io.GetCurrentChar(byteCount);548    bool hadSubscripts{false};549    bool hadSubstring{false};550    if (next && (*next == '(' || *next == '%')) {551      const Descriptor *lastSubscriptBase{nullptr};552      Descriptor *lastSubscriptDescriptor{nullptr};553      do {554        Descriptor &mutableDescriptor{staticDesc[whichStaticDesc].descriptor()};555        whichStaticDesc ^= 1;556        io.HandleRelativePosition(byteCount); // skip over '(' or '%'557        lastSubscriptDescriptor = nullptr;558        lastSubscriptBase = nullptr;559        if (*next == '(') {560          if (!hadSubstring && (hadSubscripts || useDescriptor->rank() == 0)) {561            mutableDescriptor = *useDescriptor;562            mutableDescriptor.raw().attribute = CFI_attribute_pointer;563            if (!HandleSubstring(io, mutableDescriptor, name)) {564              return false;565            }566            hadSubstring = true;567          } else if (hadSubscripts) {568            handler.SignalError("Multiple sets of subscripts for item '%s' in "569                                "NAMELIST group '%s'",570                name, group.groupName);571            return false;572          } else if (HandleSubscripts(573                         io, mutableDescriptor, *useDescriptor, name)) {574            lastSubscriptBase = useDescriptor;575            lastSubscriptDescriptor = &mutableDescriptor;576          } else {577            return false;578          }579          hadSubscripts = true;580        } else {581          if (!HandleComponent(io, mutableDescriptor, *useDescriptor, name)) {582            return false;583          }584          hadSubscripts = false;585          hadSubstring = false;586        }587        useDescriptor = &mutableDescriptor;588        next = io.GetCurrentChar(byteCount);589      } while (next && (*next == '(' || *next == '%'));590      if (lastSubscriptDescriptor) {591        StorageSequenceExtension(*lastSubscriptDescriptor, *lastSubscriptBase,592            group.nonTbpDefinedIo);593      }594    }595    // Skip the '='596    next = io.GetNextNonBlank(byteCount);597    if (!next || *next != '=') {598      handler.SignalError("No '=' found after item '%s' in NAMELIST group '%s'",599          name, group.groupName);600      return false;601    }602    io.HandleRelativePosition(byteCount);603    // Read the values into the descriptor.  An array can be short.604    if (const auto *addendum{useDescriptor->Addendum()};605        addendum && addendum->derivedType()) {606      const NonTbpDefinedIoTable *table{group.nonTbpDefinedIo};607      listInput->ResetForNextNamelistItem(&group);608      if (!IONAME(InputDerivedType)(cookie, *useDescriptor, table) &&609          handler.InError()) {610        return false;611      }612    } else {613      listInput->ResetForNextNamelistItem(614          useDescriptor->rank() > 0 ? &group : nullptr);615      if (!descr::DescriptorIO<Direction::Input>(io, *useDescriptor) &&616          handler.InError()) {617        return false;618      }619    }620    next = io.GetNextNonBlank(byteCount);621    if (next && *next == comma) {622      io.HandleRelativePosition(byteCount);623    }624  }625  if (next && *next == '/') {626    io.HandleRelativePosition(byteCount);627    if (auto *listInput{628            io.get_if<ListDirectedStatementState<Direction::Input>>()}) {629      // Don't let the namelist's terminal '/' mess up a parent I/O's630      // list-directed input.631      listInput->set_hitSlash(false);632    }633  } else if (*next && (*next == '&' || *next == '$')) {634    // stop at beginning of next group635  } else {636    handler.SignalError(637        "No '/' found after NAMELIST group '%s'", group.groupName);638    return false;639  }640  return true;641}642 643RT_API_ATTRS bool IsNamelistNameOrSlash(IoStatementState &io) {644  auto *listInput{io.get_if<ListDirectedStatementState<Direction::Input>>()};645  if (!listInput || !listInput->namelistGroup()) {646    return false; // not namelist647  }648  SavedPosition savedPosition{io};649  std::size_t byteCount{0};650  auto ch{io.GetNextNonBlank(byteCount)};651  if (!ch) {652    return false;653  } else if (!IsLegalIdStart(*ch)) {654    return *ch == '/' || *ch == '&' || *ch == '$';655  }656  char id[nameBufferSize];657  if (!GetLowerCaseName(io, id, sizeof id, /*crashIfTooLong=*/false)) {658    return true; // long name659  }660  // It looks like a name, but might be "inf" or "nan".  Check what661  // follows.662  ch = io.GetNextNonBlank(byteCount);663  if (!ch) {664    return false;665  } else if (*ch == '=' || *ch == '%') {666    return true;667  } else if (*ch != '(') {668    return false;669  } else if (runtime::strcmp(id, "nan") != 0) {670    return true;671  }672  // "nan(" ambiguity673  int depth{1};674  while (true) {675    io.HandleRelativePosition(byteCount);676    ch = io.GetNextNonBlank(byteCount);677    if (depth == 0) {678      // nan(...) followed by '=', '%', or '('?679      break;680    } else if (!ch) {681      return true; // not a valid NaN(...)682    } else if (*ch == '(') {683      ++depth;684    } else if (*ch == ')') {685      --depth;686    }687  }688  return ch && (*ch == '=' || *ch == '%' || *ch == '(');689}690 691RT_OFFLOAD_API_GROUP_END692 693} // namespace Fortran::runtime::io694