[flang][runtime] Tweak width-free I/G formatted I&O (#135047)

For Fujitsu test case 0561/0561_0168.f90, adjust both input and output
sides of the extension I (and G) edit descriptors with no width (as
distinct from I0/G0). On input, be sure to halt on a separator character
rather than complaining about an invalid character; on output, be sure
to emit a leading space.
This commit is contained in:
Peter Klausler
2025-04-09 12:31:36 -07:00
committed by GitHub
parent 0ae9bb96d5
commit e0950ebb9c
2 changed files with 30 additions and 26 deletions

View File

@@ -17,7 +17,7 @@ namespace Fortran::runtime::io {
RT_OFFLOAD_API_GROUP_BEGIN
// In output statement, add a space between numbers and characters.
static RT_API_ATTRS void addSpaceBeforeCharacter(IoStatementState &io) {
static RT_API_ATTRS void AddSpaceBeforeCharacter(IoStatementState &io) {
if (auto *list{io.get_if<ListDirectedStatementState<Direction::Output>>()}) {
list->set_lastWasUndelimitedCharacter(false);
}
@@ -29,7 +29,7 @@ static RT_API_ATTRS void addSpaceBeforeCharacter(IoStatementState &io) {
template <int LOG2_BASE>
static RT_API_ATTRS bool EditBOZOutput(IoStatementState &io,
const DataEdit &edit, const unsigned char *data0, std::size_t bytes) {
addSpaceBeforeCharacter(io);
AddSpaceBeforeCharacter(io);
int digits{static_cast<int>((bytes * 8) / LOG2_BASE)};
int get{static_cast<int>(bytes * 8) - digits * LOG2_BASE};
if (get > 0) {
@@ -110,27 +110,11 @@ static RT_API_ATTRS bool EditBOZOutput(IoStatementState &io,
template <int KIND>
bool RT_API_ATTRS EditIntegerOutput(IoStatementState &io, const DataEdit &edit,
common::HostSignedIntType<8 * KIND> n, bool isSigned) {
addSpaceBeforeCharacter(io);
char buffer[130], *end{&buffer[sizeof buffer]}, *p{end};
bool isNegative{isSigned && n < 0};
using Unsigned = common::HostUnsignedIntType<8 * KIND>;
Unsigned un{static_cast<Unsigned>(n)};
int signChars{0};
AddSpaceBeforeCharacter(io);
switch (edit.descriptor) {
case DataEdit::ListDirected:
case 'G':
case 'I':
if (isNegative) {
un = -un;
}
if (isNegative || (edit.modes.editingFlags & signPlus)) {
signChars = 1; // '-' or '+'
}
while (un > 0) {
auto quotient{un / 10u};
*--p = '0' + static_cast<int>(un - Unsigned{10} * quotient);
un = quotient;
}
break;
case 'B':
return EditBOZOutput<1>(
@@ -152,7 +136,22 @@ bool RT_API_ATTRS EditIntegerOutput(IoStatementState &io, const DataEdit &edit,
edit.descriptor);
return false;
}
char buffer[130], *end{&buffer[sizeof buffer]}, *p{end};
bool isNegative{isSigned && n < 0};
using Unsigned = common::HostUnsignedIntType<8 * KIND>;
Unsigned un{static_cast<Unsigned>(n)};
int signChars{0};
if (isNegative) {
un = -un;
}
if (isNegative || (edit.modes.editingFlags & signPlus)) {
signChars = 1; // '-' or '+'
}
while (un > 0) {
auto quotient{un / 10u};
*--p = '0' + static_cast<int>(un - Unsigned{10} * quotient);
un = quotient;
}
int digits = end - p;
int leadingZeroes{0};
int editWidth{edit.width.value_or(0)};
@@ -181,6 +180,10 @@ bool RT_API_ATTRS EditIntegerOutput(IoStatementState &io, const DataEdit &edit,
return false;
}
leadingSpaces = 1;
} else if (!edit.width) {
// Bare 'I' and 'G' are interpreted with various default widths in the
// compilers that support them, so there's always some leading space.
leadingSpaces = std::max(1, leadingSpaces);
}
return EmitRepeated(io, ' ', leadingSpaces) &&
EmitAscii(io, n < 0 ? "-" : "+", signChars) &&
@@ -291,7 +294,7 @@ static RT_API_ATTRS bool IsInfOrNaN(const char *p, int length) {
template <int KIND>
RT_API_ATTRS bool RealOutputEditing<KIND>::EditEorDOutput(
const DataEdit &edit) {
addSpaceBeforeCharacter(io_);
AddSpaceBeforeCharacter(io_);
int editDigits{edit.digits.value_or(0)}; // 'd' field
int editWidth{edit.width.value_or(0)}; // 'w' field
int significantDigits{editDigits};
@@ -427,7 +430,7 @@ RT_API_ATTRS bool RealOutputEditing<KIND>::EditEorDOutput(
// 13.7.2.3.2 in F'2018
template <int KIND>
RT_API_ATTRS bool RealOutputEditing<KIND>::EditFOutput(const DataEdit &edit) {
addSpaceBeforeCharacter(io_);
AddSpaceBeforeCharacter(io_);
int fracDigits{edit.digits.value_or(0)}; // 'd' field
const int editWidth{edit.width.value_or(0)}; // 'w' field
enum decimal::FortranRounding rounding{edit.modes.round};
@@ -702,7 +705,7 @@ RT_API_ATTRS auto RealOutputEditing<KIND>::ConvertToHexadecimal(
template <int KIND>
RT_API_ATTRS bool RealOutputEditing<KIND>::EditEXOutput(const DataEdit &edit) {
addSpaceBeforeCharacter(io_);
AddSpaceBeforeCharacter(io_);
int editDigits{edit.digits.value_or(0)}; // 'd' field
int significantDigits{editDigits + 1};
int flags{0};

View File

@@ -631,10 +631,11 @@ Fortran::common::optional<char32_t> IoStatementState::GetCurrentChar(
Fortran::common::optional<char32_t> IoStatementState::NextInField(
Fortran::common::optional<int> &remaining, const DataEdit &edit) {
std::size_t byteCount{0};
if (!remaining) { // Stream, list-directed, or NAMELIST
if (!remaining) { // Stream, list-directed, NAMELIST, &c.
if (auto next{GetCurrentChar(byteCount)}) {
if (edit.IsListDirected()) {
// list-directed or NAMELIST: check for separators
if (edit.width.value_or(0) == 0) {
// list-directed, NAMELIST, I0 &c., or width-free I/G:
// check for separator character
switch (*next) {
case ' ':
case '\t':