diff --git a/flang/docs/Extensions.md b/flang/docs/Extensions.md --- a/flang/docs/Extensions.md +++ b/flang/docs/Extensions.md @@ -108,6 +108,7 @@ * Quad precision REAL literals with `Q` * `X` prefix/suffix as synonym for `Z` on hexadecimal literals * `B`, `O`, `Z`, and `X` accepted as suffixes as well as prefixes +* Support for using bare `L` in FORMAT statement * Triplets allowed in array constructors * `%LOC`, `%VAL`, and `%REF` * Leading comma allowed before I/O item list diff --git a/flang/runtime/edit-output.cpp b/flang/runtime/edit-output.cpp --- a/flang/runtime/edit-output.cpp +++ b/flang/runtime/edit-output.cpp @@ -139,6 +139,8 @@ case 'Z': return EditBOZOutput<4>( io, edit, reinterpret_cast(&n), KIND); + case 'L': + return EditLogicalOutput(io, edit, *reinterpret_cast(&n)); case 'A': // legacy extension return EditCharacterOutput( io, edit, reinterpret_cast(&n), sizeof n); @@ -589,6 +591,8 @@ common::BitsForBinaryPrecision(common::PrecisionOfRealKind(KIND)) >> 3); case 'G': return Edit(EditForGOutput(edit)); + case 'L': + return EditLogicalOutput(io_, edit, *reinterpret_cast(&x_)); case 'A': // legacy extension return EditCharacterOutput( io_, edit, reinterpret_cast(&x_), sizeof x_); @@ -712,6 +716,8 @@ case 'Z': return EditBOZOutput<4>(io, edit, reinterpret_cast(x), sizeof(CHAR) * length); + case 'L': + return EditLogicalOutput(io, edit, *reinterpret_cast(x)); default: io.GetIoErrorHandler().SignalError(IostatErrorInFormat, "Data edit descriptor '%c' may not be used with a CHARACTER data item", diff --git a/flang/runtime/format-implementation.h b/flang/runtime/format-implementation.h --- a/flang/runtime/format-implementation.h +++ b/flang/runtime/format-implementation.h @@ -479,7 +479,8 @@ } } } - if (edit.descriptor == 'A') { // width is optional for A[w] + if (edit.descriptor == 'A' || edit.descriptor == 'L') { + // width is optional for A[w] or L[w] auto ch{PeekNext()}; if (ch >= '0' && ch <= '9') { edit.width = GetIntField(context); diff --git a/flang/unittests/Runtime/CMakeLists.txt b/flang/unittests/Runtime/CMakeLists.txt --- a/flang/unittests/Runtime/CMakeLists.txt +++ b/flang/unittests/Runtime/CMakeLists.txt @@ -11,6 +11,7 @@ Format.cpp Inquiry.cpp ListInputTest.cpp + LogicalFormatTest.cpp Matmul.cpp MatmulTranspose.cpp MiscIntrinsic.cpp diff --git a/flang/unittests/Runtime/LogicalFormatTest.cpp b/flang/unittests/Runtime/LogicalFormatTest.cpp new file mode 100644 --- /dev/null +++ b/flang/unittests/Runtime/LogicalFormatTest.cpp @@ -0,0 +1,56 @@ +//===-- flang/unittests/Runtime/LogicalFormatTest.cpp -----------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "CrashHandlerFixture.h" +#include "flang/Runtime/descriptor.h" +#include "flang/Runtime/io-api.h" +#include +#include +#include +#include +#include + +using namespace Fortran::runtime; +using namespace Fortran::runtime::io; + +TEST(IOApiTests, LogicalFormatTest) { + static constexpr int bufferSize{29}; + char buffer[bufferSize]; + + // Create format for all types and values to be written + const char *format{"(L,L3,I3,L2,L2,I3,L2,A3,L2,L,F4.1,L2)"}; + auto cookie{IONAME(BeginInternalFormattedOutput)( + buffer, bufferSize, format, std::strlen(format))}; + + // Write string, integer, and logical values to buffer + IONAME(OutputLogical)(cookie, true); + IONAME(OutputLogical)(cookie, false); + IONAME(OutputInteger64)(cookie, 6); + IONAME(OutputInteger32)(cookie, 22); + IONAME(OutputInteger32)(cookie, 0); + IONAME(OutputInteger32)(cookie, -2); + IONAME(OutputCharacter)(cookie, "ABC", 3); + IONAME(OutputCharacter)(cookie, "AB", 2); + IONAME(OutputReal64)(cookie, 0.0); + IONAME(OutputCharacter)(cookie, "", 0); + IONAME(OutputReal32)(cookie, 2.3); + IONAME(OutputReal32)(cookie, 2.3); + + // Ensure IO succeeded + auto status{IONAME(EndIoStatement)(cookie)}; + ASSERT_EQ(status, 0) << "logical format: '" << format << "' failed, status " + << static_cast(status); + + // Ensure final buffer matches expected string output + static const std::string expect{"T F 6 T F -2 T AB FF 2.3 T"}; + + // expect.size() == bufferSize - 1 + std::string bufferStr = std::string(buffer, bufferSize - 1); + ASSERT_TRUE(expect == bufferStr) + << "Expected '" << expect << "', got '" << bufferStr << "'"; +}