[flang] Add checks for misuse of formatted I/O APIs in unformatted I/O statement

Add checking to I/O statement APIs to catch cases where the formatted
I/O data item transfer routines like OutputInteger64 are being
incorrectly used for unformatted I/O, which should use the
unformatted block or descriptor-based data item interfaces.

Differential revision: https://reviews.llvm.org/D88672
This commit is contained in:
peter klausler 2020-10-01 09:32:48 -07:00
parent 17640c5aac
commit cdfb95ad58
5 changed files with 64 additions and 24 deletions

View File

@ -922,14 +922,16 @@ bool IONAME(InputUnformattedBlock)(
}
bool IONAME(OutputInteger64)(Cookie cookie, std::int64_t n) {
cookie->CheckFormattedStmtType<Direction::Output>("OutputInteger64");
StaticDescriptor staticDescriptor;
Descriptor &descriptor{staticDescriptor.descriptor()};
descriptor.Establish(
TypeCategory::Integer, 8, reinterpret_cast<void *>(&n), 0);
TypeCategory::Integer, sizeof n, reinterpret_cast<void *>(&n), 0);
return descr::DescriptorIO<Direction::Output>(*cookie, descriptor);
}
bool IONAME(InputInteger)(Cookie cookie, std::int64_t &n, int kind) {
cookie->CheckFormattedStmtType<Direction::Input>("InputInteger");
StaticDescriptor staticDescriptor;
Descriptor &descriptor{staticDescriptor.descriptor()};
descriptor.Establish(
@ -938,6 +940,7 @@ bool IONAME(InputInteger)(Cookie cookie, std::int64_t &n, int kind) {
}
bool IONAME(OutputReal32)(Cookie cookie, float x) {
cookie->CheckFormattedStmtType<Direction::Output>("OutputReal32");
StaticDescriptor staticDescriptor;
Descriptor &descriptor{staticDescriptor.descriptor()};
descriptor.Establish(TypeCategory::Real, 4, reinterpret_cast<void *>(&x), 0);
@ -945,6 +948,7 @@ bool IONAME(OutputReal32)(Cookie cookie, float x) {
}
bool IONAME(OutputReal64)(Cookie cookie, double x) {
cookie->CheckFormattedStmtType<Direction::Output>("OutputReal64");
StaticDescriptor staticDescriptor;
Descriptor &descriptor{staticDescriptor.descriptor()};
descriptor.Establish(TypeCategory::Real, 8, reinterpret_cast<void *>(&x), 0);
@ -952,6 +956,7 @@ bool IONAME(OutputReal64)(Cookie cookie, double x) {
}
bool IONAME(InputReal32)(Cookie cookie, float &x) {
cookie->CheckFormattedStmtType<Direction::Input>("InputReal32");
StaticDescriptor staticDescriptor;
Descriptor &descriptor{staticDescriptor.descriptor()};
descriptor.Establish(TypeCategory::Real, 4, reinterpret_cast<void *>(&x), 0);
@ -959,6 +964,7 @@ bool IONAME(InputReal32)(Cookie cookie, float &x) {
}
bool IONAME(InputReal64)(Cookie cookie, double &x) {
cookie->CheckFormattedStmtType<Direction::Input>("InputReal64");
StaticDescriptor staticDescriptor;
Descriptor &descriptor{staticDescriptor.descriptor()};
descriptor.Establish(TypeCategory::Real, 8, reinterpret_cast<void *>(&x), 0);
@ -966,6 +972,7 @@ bool IONAME(InputReal64)(Cookie cookie, double &x) {
}
bool IONAME(OutputComplex32)(Cookie cookie, float r, float i) {
cookie->CheckFormattedStmtType<Direction::Output>("OutputComplex32");
float z[2]{r, i};
StaticDescriptor staticDescriptor;
Descriptor &descriptor{staticDescriptor.descriptor()};
@ -975,6 +982,7 @@ bool IONAME(OutputComplex32)(Cookie cookie, float r, float i) {
}
bool IONAME(OutputComplex64)(Cookie cookie, double r, double i) {
cookie->CheckFormattedStmtType<Direction::Output>("OutputComplex64");
double z[2]{r, i};
StaticDescriptor staticDescriptor;
Descriptor &descriptor{staticDescriptor.descriptor()};
@ -984,6 +992,7 @@ bool IONAME(OutputComplex64)(Cookie cookie, double r, double i) {
}
bool IONAME(InputComplex32)(Cookie cookie, float z[2]) {
cookie->CheckFormattedStmtType<Direction::Input>("InputComplex32");
StaticDescriptor staticDescriptor;
Descriptor &descriptor{staticDescriptor.descriptor()};
descriptor.Establish(
@ -992,6 +1001,7 @@ bool IONAME(InputComplex32)(Cookie cookie, float z[2]) {
}
bool IONAME(InputComplex64)(Cookie cookie, double z[2]) {
cookie->CheckFormattedStmtType<Direction::Input>("InputComplex64");
StaticDescriptor staticDescriptor;
Descriptor &descriptor{staticDescriptor.descriptor()};
descriptor.Establish(
@ -999,34 +1009,48 @@ bool IONAME(InputComplex64)(Cookie cookie, double z[2]) {
return descr::DescriptorIO<Direction::Input>(*cookie, descriptor);
}
bool IONAME(OutputAscii)(Cookie cookie, const char *x, std::size_t length) {
bool IONAME(OutputCharacter)(
Cookie cookie, const char *x, std::size_t length, int kind) {
cookie->CheckFormattedStmtType<Direction::Output>("OutputCharacter");
StaticDescriptor staticDescriptor;
Descriptor &descriptor{staticDescriptor.descriptor()};
descriptor.Establish(
1, length, reinterpret_cast<void *>(const_cast<char *>(x)), 0);
kind, length, reinterpret_cast<void *>(const_cast<char *>(x)), 0);
return descr::DescriptorIO<Direction::Output>(*cookie, descriptor);
}
bool IONAME(InputAscii)(Cookie cookie, char *x, std::size_t length) {
bool IONAME(OutputAscii)(Cookie cookie, const char *x, std::size_t length) {
return IONAME(OutputCharacter(cookie, x, length, 1));
}
bool IONAME(InputCharacter)(
Cookie cookie, char *x, std::size_t length, int kind) {
cookie->CheckFormattedStmtType<Direction::Input>("InputCharacter");
StaticDescriptor staticDescriptor;
Descriptor &descriptor{staticDescriptor.descriptor()};
descriptor.Establish(1, length, reinterpret_cast<void *>(x), 0);
descriptor.Establish(kind, length, reinterpret_cast<void *>(x), 0);
return descr::DescriptorIO<Direction::Input>(*cookie, descriptor);
}
bool IONAME(InputAscii)(Cookie cookie, char *x, std::size_t length) {
return IONAME(InputCharacter(cookie, x, length, 1));
}
bool IONAME(OutputLogical)(Cookie cookie, bool truth) {
cookie->CheckFormattedStmtType<Direction::Output>("OutputLogical");
StaticDescriptor staticDescriptor;
Descriptor &descriptor{staticDescriptor.descriptor()};
descriptor.Establish(
TypeCategory::Logical, 1, reinterpret_cast<void *>(&truth), 0);
TypeCategory::Logical, sizeof truth, reinterpret_cast<void *>(&truth), 0);
return descr::DescriptorIO<Direction::Output>(*cookie, descriptor);
}
bool IONAME(InputLogical)(Cookie cookie, bool &truth) {
cookie->CheckFormattedStmtType<Direction::Input>("InputLogical");
StaticDescriptor staticDescriptor;
Descriptor &descriptor{staticDescriptor.descriptor()};
descriptor.Establish(
TypeCategory::Logical, 1, reinterpret_cast<void *>(&truth), 0);
TypeCategory::Logical, sizeof truth, reinterpret_cast<void *>(&truth), 0);
return descr::DescriptorIO<Direction::Input>(*cookie, descriptor);
}

View File

@ -231,10 +231,12 @@ bool IONAME(SetSign)(Cookie, const char *, std::size_t);
// and avoid the following items when they might crash.
bool IONAME(OutputDescriptor)(Cookie, const Descriptor &);
bool IONAME(InputDescriptor)(Cookie, const Descriptor &);
// Contiguous transfers for unformatted I/O
bool IONAME(OutputUnformattedBlock)(
Cookie, const char *, std::size_t, std::size_t elementBytes);
bool IONAME(InputUnformattedBlock)(
Cookie, char *, std::size_t, std::size_t elementBytes);
// Formatted (including list directed) I/O data items
bool IONAME(OutputInteger64)(Cookie, std::int64_t);
bool IONAME(InputInteger)(Cookie, std::int64_t &, int kind = 8);
bool IONAME(OutputReal32)(Cookie, float);
@ -245,7 +247,9 @@ bool IONAME(OutputComplex32)(Cookie, float, float);
bool IONAME(InputComplex32)(Cookie, float[2]);
bool IONAME(OutputComplex64)(Cookie, double, double);
bool IONAME(InputComplex64)(Cookie, double[2]);
bool IONAME(OutputCharacter)(Cookie, const char *, std::size_t, int kind = 1);
bool IONAME(OutputAscii)(Cookie, const char *, std::size_t);
bool IONAME(InputCharacter)(Cookie, char *, std::size_t, int kind = 1);
bool IONAME(InputAscii)(Cookie, char *, std::size_t);
bool IONAME(OutputLogical)(Cookie, bool);
bool IONAME(InputLogical)(Cookie, bool &);

View File

@ -43,6 +43,13 @@ class ExternalFormattedIoStatementState;
template <Direction> class ExternalListIoStatementState;
template <Direction> class UnformattedIoStatementState;
struct InputStatementState {};
struct OutputStatementState {};
template <Direction D>
using IoDirectionState = std::conditional_t<D == Direction::Input,
InputStatementState, OutputStatementState>;
struct FormattedIoStatementState {};
// The Cookie type in the I/O API is a pointer (for C) to this class.
class IoStatementState {
public:
@ -90,6 +97,15 @@ public:
std::optional<char32_t> NextInField(std::optional<int> &remaining);
std::optional<char32_t> GetNextNonBlank(); // can advance record
template <Direction D> void CheckFormattedStmtType(const char *name) {
if (!get_if<FormattedIoStatementState>() ||
!get_if<IoDirectionState<D>>()) {
GetIoErrorHandler().Crash(
"%s called for I/O statement that is not formatted %s", name,
D == Direction::Output ? "output" : "input");
}
}
private:
std::variant<std::reference_wrapper<OpenStatementState>,
std::reference_wrapper<CloseStatementState>,
@ -132,17 +148,11 @@ struct IoStatementBase : public DefaultFormatControlCallbacks {
void BadInquiryKeywordHashCrash(InquiryKeywordHash);
};
struct InputStatementState {};
struct OutputStatementState {};
template <Direction D>
using IoDirectionState = std::conditional_t<D == Direction::Input,
InputStatementState, OutputStatementState>;
struct FormattedStatementState {};
// Common state for list-directed internal & external I/O
template <Direction> struct ListDirectedStatementState {};
template <> struct ListDirectedStatementState<Direction::Output> {
template <Direction> struct ListDirectedStatementState;
template <>
struct ListDirectedStatementState<Direction::Output>
: public FormattedIoStatementState {
static std::size_t RemainingSpaceInRecord(const ConnectionState &);
bool NeedAdvance(const ConnectionState &, std::size_t) const;
bool EmitLeadingSpaceOrAdvance(
@ -151,7 +161,9 @@ template <> struct ListDirectedStatementState<Direction::Output> {
IoStatementState &, int maxRepeat = 1);
bool lastWasUndelimitedCharacter{false};
};
template <> class ListDirectedStatementState<Direction::Input> {
template <>
class ListDirectedStatementState<Direction::Input>
: public FormattedIoStatementState {
public:
// Skips value separators, handles repetition and null values.
// Vacant when '/' appears; present with descriptor == ListDirectedNullValue
@ -199,7 +211,7 @@ protected:
template <Direction DIR, typename CHAR>
class InternalFormattedIoStatementState
: public InternalIoStatementState<DIR, CHAR>,
public FormattedStatementState {
public FormattedIoStatementState {
public:
using CharType = CHAR;
using typename InternalIoStatementState<DIR, CharType>::Buffer;
@ -275,7 +287,7 @@ public:
template <Direction DIR, typename CHAR>
class ExternalFormattedIoStatementState : public ExternalIoStatementState<DIR>,
public FormattedStatementState {
public FormattedIoStatementState {
public:
using CharType = CHAR;
ExternalFormattedIoStatementState(ExternalFileUnit &, const CharType *format,

View File

@ -78,13 +78,13 @@ TypeCode::TypeCode(TypeCategory f, int kind) {
raw_ = CFI_type_Bool;
break;
case 2:
raw_ = CFI_type_int16_t;
raw_ = CFI_type_int_fast16_t;
break;
case 4:
raw_ = CFI_type_int32_t;
raw_ = CFI_type_int_fast32_t;
break;
case 8:
raw_ = CFI_type_int64_t;
raw_ = CFI_type_int_fast64_t;
break;
}
break;

View File

@ -290,7 +290,7 @@ bool ExternalFileUnit::Receive(char *data, std::size_t bytes,
furthestPositionInRecord = furthestAfter;
return true;
} else {
handler.SignalEnd();
// EOF or error: can be handled & has been signaled
endfileRecordNumber = currentRecordNumber;
return false;
}