Skip to content

Commit f08b55d

Browse files
authored
[flang][runtime] Emit leading spaces in NAMELIST output (llvm#76846)
As NAMELIST output is a variant of list-directed output, its editing must produce leading spaces on (most) output records to effect carriage control. These spaces are required by the language standard and implemented by nearly all other Fortran compilers (except GNU). Fixes llvm#76798.
1 parent 22c24be commit f08b55d

File tree

2 files changed

+21
-20
lines changed

2 files changed

+21
-20
lines changed

flang/runtime/namelist.cpp

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -30,16 +30,15 @@ bool IONAME(OutputNamelist)(Cookie cookie, const NamelistGroup &group) {
3030
IoStatementState &io{*cookie};
3131
io.CheckFormattedStmtType<Direction::Output>("OutputNamelist");
3232
io.mutableModes().inNamelist = true;
33-
char comma{static_cast<char>(GetComma(io))};
3433
ConnectionState &connection{io.GetConnectionState()};
35-
// Internal functions to advance records and convert case
36-
const auto EmitWithAdvance{[&](char ch) -> bool {
37-
return (!connection.NeedAdvance(1) || io.AdvanceRecord()) &&
38-
EmitAscii(io, &ch, 1);
39-
}};
40-
const auto EmitUpperCase{[&](const char *str) -> bool {
41-
if (connection.NeedAdvance(std::strlen(str)) &&
42-
!(io.AdvanceRecord() && EmitAscii(io, " ", 1))) {
34+
// Internal function to advance records and convert case
35+
const auto EmitUpperCase{[&](const char *prefix, std::size_t prefixLen,
36+
const char *str, char suffix) -> bool {
37+
if ((connection.NeedAdvance(prefixLen) &&
38+
!(io.AdvanceRecord() && EmitAscii(io, " ", 1))) ||
39+
!EmitAscii(io, prefix, prefixLen) ||
40+
(connection.NeedAdvance(std::strlen(str) + (suffix != ' ')) &&
41+
!(io.AdvanceRecord() && EmitAscii(io, " ", 1)))) {
4342
return false;
4443
}
4544
for (; *str; ++str) {
@@ -49,23 +48,25 @@ bool IONAME(OutputNamelist)(Cookie cookie, const NamelistGroup &group) {
4948
return false;
5049
}
5150
}
52-
return true;
51+
return suffix == ' ' || EmitAscii(io, &suffix, 1);
5352
}};
5453
// &GROUP
55-
if (!(EmitWithAdvance('&') && EmitUpperCase(group.groupName))) {
54+
if (!EmitUpperCase(" &", 2, group.groupName, ' ')) {
5655
return false;
5756
}
5857
auto *listOutput{io.get_if<ListDirectedStatementState<Direction::Output>>()};
58+
char comma{static_cast<char>(GetComma(io))};
59+
char prefix{' '};
5960
for (std::size_t j{0}; j < group.items; ++j) {
6061
// [,]ITEM=...
6162
const NamelistGroup::Item &item{group.item[j]};
6263
if (listOutput) {
6364
listOutput->set_lastWasUndelimitedCharacter(false);
6465
}
65-
if (!EmitWithAdvance(j == 0 ? ' ' : comma) || !EmitUpperCase(item.name) ||
66-
!EmitWithAdvance('=')) {
66+
if (!EmitUpperCase(&prefix, 1, item.name, '=')) {
6767
return false;
6868
}
69+
prefix = comma;
6970
if (const auto *addendum{item.descriptor.Addendum()};
7071
addendum && addendum->derivedType()) {
7172
const NonTbpDefinedIoTable *table{group.nonTbpDefinedIo};
@@ -77,7 +78,7 @@ bool IONAME(OutputNamelist)(Cookie cookie, const NamelistGroup &group) {
7778
}
7879
}
7980
// terminal /
80-
return EmitWithAdvance('/');
81+
return EmitUpperCase("/", 1, "", ' ');
8182
}
8283

8384
static constexpr bool IsLegalIdStart(char32_t ch) {

flang/unittests/Runtime/Namelist.cpp

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ TEST(NamelistTests, BasicSanity) {
8888
ASSERT_EQ(outStatus1, 0) << "Failed namelist output sanity, status "
8989
<< static_cast<int>(outStatus1);
9090

91-
static const std::string expect{"&GROUP1 INTS= 1 -2 4 -8 16 -32 "
91+
static const std::string expect{" &GROUP1 INTS= 1 -2 4 -8 16 -32 "
9292
" 64 -128 256 -512 1024 -2048 "
9393
" 4096 -8192 16384 -32768 65536 "
9494
" -131072 262144 -524288,REALS= "
@@ -157,7 +157,7 @@ TEST(NamelistTests, Subscripts) {
157157
<< "Failed namelist output subscripts rewrite, status "
158158
<< static_cast<int>(outStatus);
159159
std::string got{out, sizeof out};
160-
static const std::string expect{"&JUSTA A= 0 2 0 0 0 1/ "};
160+
static const std::string expect{" &JUSTA A= 0 2 0 0 0 1/ "};
161161
EXPECT_EQ(got, expect);
162162
}
163163

@@ -213,7 +213,7 @@ TEST(NamelistTests, ScalarSubstring) {
213213
ASSERT_TRUE(IONAME(OutputNamelist)(outCookie, group));
214214
ASSERT_EQ(IONAME(EndIoStatement)(outCookie), IostatOk) << "namelist output";
215215
std::string got{out, sizeof out};
216-
static const std::string expect{"&JUSTA A= 'aBCDEfgh'/ "};
216+
static const std::string expect{" &JUSTA A= 'aBCDEfgh'/ "};
217217
EXPECT_EQ(got, expect);
218218
}
219219

@@ -242,7 +242,7 @@ TEST(NamelistTests, ArraySubstring) {
242242
ASSERT_TRUE(IONAME(OutputNamelist)(outCookie, group));
243243
ASSERT_EQ(IONAME(EndIoStatement)(outCookie), IostatOk) << "namelist output";
244244
std::string got{out, sizeof out};
245-
static const std::string expect{"&JUSTA A= 'aBCDEfgh' 'iJKLMnop'/ "};
245+
static const std::string expect{" &JUSTA A= 'aBCDEfgh' 'iJKLMnop'/ "};
246246
EXPECT_EQ(got, expect);
247247
}
248248

@@ -270,7 +270,7 @@ TEST(NamelistTests, Skip) {
270270
ASSERT_TRUE(IONAME(OutputNamelist)(outCookie, group));
271271
ASSERT_EQ(IONAME(EndIoStatement)(outCookie), IostatOk) << "namelist output";
272272
std::string got{out, sizeof out};
273-
static const std::string expect{"&NML J= 123/ "};
273+
static const std::string expect{" &NML J= 123/ "};
274274
EXPECT_EQ(got, expect);
275275
}
276276

@@ -301,7 +301,7 @@ TEST(NamelistTests, Comma) {
301301
ASSERT_TRUE(IONAME(OutputNamelist)(outCookie, group));
302302
ASSERT_EQ(IONAME(EndIoStatement)(outCookie), IostatOk) << "namelist output";
303303
std::string got{out, sizeof out};
304-
static const std::string expect{"&NML Z= (-1,;2,) (-3,;,5)/ "};
304+
static const std::string expect{" &NML Z= (-1,;2,) (-3,;,5)/ "};
305305
EXPECT_EQ(got, expect);
306306
}
307307

0 commit comments

Comments
 (0)