Skip to content

Commit 7aad873

Browse files
authored
[flang][runtime] Accept some real input for integer NAMELIST (llvm#108268)
A few other Fortran compilers silently accept real values for integer variables in NAMELIST input. Handling an exponent would be difficult, but it's easy to skip and ignore a fractional part when one is present.
1 parent 5242018 commit 7aad873

File tree

2 files changed

+51
-2
lines changed

2 files changed

+51
-2
lines changed

flang/runtime/edit-input.cpp

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,10 @@ static RT_API_ATTRS bool CheckCompleteListDirectedField(
5454
}
5555
}
5656

57+
static inline RT_API_ATTRS char32_t GetSeparatorChar(const DataEdit &edit) {
58+
return edit.modes.editingFlags & decimalComma ? char32_t{';'} : char32_t{','};
59+
}
60+
5761
template <int LOG2_BASE>
5862
static RT_API_ATTRS bool EditBOZInput(
5963
IoStatementState &io, const DataEdit &edit, void *n, std::size_t bytes) {
@@ -70,6 +74,7 @@ static RT_API_ATTRS bool EditBOZInput(
7074
// Count significant digits after any leading white space & zeroes
7175
int digits{0};
7276
int significantBits{0};
77+
const char32_t comma{GetSeparatorChar(edit)};
7378
for (; next; next = io.NextInField(remaining, edit)) {
7479
char32_t ch{*next};
7580
if (ch == ' ' || ch == '\t') {
@@ -84,7 +89,7 @@ static RT_API_ATTRS bool EditBOZInput(
8489
} else if (LOG2_BASE >= 4 && ch >= '8' && ch <= '9') {
8590
} else if (LOG2_BASE >= 4 && ch >= 'A' && ch <= 'F') {
8691
} else if (LOG2_BASE >= 4 && ch >= 'a' && ch <= 'f') {
87-
} else if (ch == ',') {
92+
} else if (ch == comma) {
8893
break; // end non-list-directed field early
8994
} else {
9095
io.GetIoErrorHandler().SignalError(
@@ -209,6 +214,7 @@ RT_API_ATTRS bool EditIntegerInput(
209214
common::UnsignedInt128 value{0};
210215
bool any{!!sign};
211216
bool overflow{false};
217+
const char32_t comma{GetSeparatorChar(edit)};
212218
for (; next; next = io.NextInField(remaining, edit)) {
213219
char32_t ch{*next};
214220
if (ch == ' ' || ch == '\t') {
@@ -221,9 +227,23 @@ RT_API_ATTRS bool EditIntegerInput(
221227
int digit{0};
222228
if (ch >= '0' && ch <= '9') {
223229
digit = ch - '0';
224-
} else if (ch == ',') {
230+
} else if (ch == comma) {
225231
break; // end non-list-directed field early
226232
} else {
233+
if (edit.modes.inNamelist && ch == GetRadixPointChar(edit)) {
234+
// Ignore any fractional part that might appear in NAMELIST integer
235+
// input, like a few other Fortran compilers do.
236+
// TODO: also process exponents? Some compilers do, but they obviously
237+
// can't just be ignored.
238+
while ((next = io.NextInField(remaining, edit))) {
239+
if (*next < '0' || *next > '9') {
240+
break;
241+
}
242+
}
243+
if (!next || *next == comma) {
244+
break;
245+
}
246+
}
227247
io.GetIoErrorHandler().SignalError(
228248
"Bad character '%lc' in INTEGER input field", ch);
229249
return false;

flang/unittests/Runtime/Namelist.cpp

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -305,4 +305,33 @@ TEST(NamelistTests, Comma) {
305305
EXPECT_EQ(got, expect);
306306
}
307307

308+
// Tests REAL-looking input to integers
309+
TEST(NamelistTests, RealValueForInt) {
310+
OwningPtr<Descriptor> scDesc{
311+
MakeArray<TypeCategory::Integer, static_cast<int>(sizeof(int))>(
312+
std::vector<int>{}, std::vector<int>{{}})};
313+
const NamelistGroup::Item items[]{{"j", *scDesc}};
314+
const NamelistGroup group{"nml", 1, items};
315+
static char t1[]{"&nml j=123.456/"};
316+
StaticDescriptor<1, true> statDesc;
317+
Descriptor &internalDesc{statDesc.descriptor()};
318+
internalDesc.Establish(TypeCode{CFI_type_char},
319+
/*elementBytes=*/std::strlen(t1), t1, 0, nullptr, CFI_attribute_pointer);
320+
auto inCookie{IONAME(BeginInternalArrayListInput)(
321+
internalDesc, nullptr, 0, __FILE__, __LINE__)};
322+
ASSERT_TRUE(IONAME(InputNamelist)(inCookie, group));
323+
ASSERT_EQ(IONAME(EndIoStatement)(inCookie), IostatOk)
324+
<< "namelist real input for integer";
325+
char out[16];
326+
internalDesc.Establish(TypeCode{CFI_type_char}, /*elementBytes=*/sizeof out,
327+
out, 0, nullptr, CFI_attribute_pointer);
328+
auto outCookie{IONAME(BeginInternalArrayListOutput)(
329+
internalDesc, nullptr, 0, __FILE__, __LINE__)};
330+
ASSERT_TRUE(IONAME(OutputNamelist)(outCookie, group));
331+
ASSERT_EQ(IONAME(EndIoStatement)(outCookie), IostatOk) << "namelist output";
332+
std::string got{out, sizeof out};
333+
static const std::string expect{" &NML J= 123/ "};
334+
EXPECT_EQ(got, expect);
335+
}
336+
308337
// TODO: Internal NAMELIST error tests

0 commit comments

Comments
 (0)